heyday2024 님의 블로그
[사전캠프 퀘스트] 숫자 기억 게임 만들기 본문
숫자 기억 게임
랜덤으로 생성되는 숫자를 사용자가 기억하는 게임.
https://practicenumbergame.netlify.app/
넷리파이로 걍 배포해봄. 허술한 숫자 기억 게임 해보실분~~~~🙌🙌
(반응형 아니라서 모바일에서 열지 마셔요... 눈감..감아...)
게임로직:
- 시작버튼을 누르면 사용자는 1000~9999 사이에 랜덤으로 생성된 숫자를 보게되고, 시작 버튼이 사라짐과 동시에 제출버튼과 입력란이 생긴다.
// 1000~9000 범위의 자연수를 만들어내야함
// random으로 0~1사이에 숫자 아무거나 하나 생성하고,
// floor로 소수를 내림처리하기.
startButton.addEventListener("click", function () {
if (score < 6) {
randomNumber = Math.floor(1000 + Math.random() * 9000);
} else if (score < 11) {
randomNumber = Math.floor(10000 + Math.random() * 90000);
} else if (score < 16) {
randomNumber = Math.floor(100000 + Math.random() * 900000);
} else {
randomNumber = Math.floor(100000000 + Math.random() * 9000000);
}
numberDisplay.textContent = randomNumber;
inputArea.style.display = "none";
message.textContent = "";
startButton.style.display = "none";
- Math.random()은 JS에서 0이상 1 미만의 무작위 값을 생성하는 함수. 랜덤한 숫자를 만들고자 할 때 사용함. --> 내가 만들고자하는 숫자의 자리수를 위해 이 함수에 9*10^n를 곱하고, 10^n을 더해 사용하였다.
- Math.floor()은 숫자를 내림하여 정수로 변환해주는 함수.
- 예를 들어 1000~9999를 만들고 싶다면 1000+Math.random() * 9000를 사용해 1000~9999의 범위를 잡아주고, Math.floor()을 이용해서 소수점으로 나타난 수들을 내림하여 자연수로 바꿔준다.
- addEventListener를 이용해 시작버튼 클릭 시 score 점수에 따라 각각 자리수가 다른 랜덤한 수를 생성함. --> 5점씩 점수를 더 획득할 때마다 자리수 하나씩 증가.
2. 각 단계별(5점 획득 주기) 1점씩 점수 획득 시, 숫자가 더 빨리 사라지게 만들어 점점 난이도를 어렵게 설정하였다.
//단계별로 점점 어려워지게 만들고 싶음(자리수 하나씩 늘리기)
// 점수 획득한 것을 기준으로 5단계씩 만들어봄.
//점수가 0일때는 기존 조건처럼 3초
// 각 단계마다 점수가 하나씩 올라갈때마다 조금 더 빠르게 진행하고 싶음.
// 점수 나누기 5의 나머지를 기준으로 숫자 자리수가 하나씩 늘기전까지는 조금씩 더 빠르게 만들었음
let remainder = score % 5;
if (score == 0 || remainder == 1) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000);
} else if (remainder == 0) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000 * (1 / 5));
} else if (remainder == 2) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000 * (1 / 2));
} else if (remainder == 3) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000 * (1 / 3));
} else if (remainder == 4) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000 * (1 / 4));
}
});
- 점수를 5로 나눴을 때의 나머지를 기준으로 원래 시간보다 조금씨 점점 빠르게 숫자가 사라지도록 만들었다. (
아무래도 점수가 1점일때보다 점수를 5점일때 더 어려워야할 것 같아서 이렇게 만들어보았다.) - setTimeout(함수, 지연 시간(ms)): 특정 작업을 일정 시간 후에 한 번 실행하도록 예약하는 함수. 주어진 시간이 지나면 지정된 함수를 실행함. 비동기적으로 동작하고, 일정 시간이 지난 후에 원하는 작업을 수행하는데 유용함.
- 참고로, 1000 ms = 1초
- setTimeout 함수를 통해 지정된 지연시간이 지나면, 입력란과 제출 버튼 영역이 나타나게 만들었다.
- 입력란은 input태그에서 type = "number"로 설정해서, 번호만 쓰여질 수 있게 만들었음. 그런데, 갑자기 입력창 안에 번호 증감관련 스핀 버튼(?)이 생기면서
안 이뻐짐.그래서 그 것을 지우기 위해 검색한 결과..... 아래 코드를 css에 추가하니 없어짐.
/* input 타입 숫자로 바꾸고 생긴 토글 바꾸기 위한 코드 */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
설명문을 읽고 대강 요약하자면, input[type="number"]요소를 쓰면 브라우저 기본으로 숫자 조절 버튼이 생기고, Webkit 기반 브라우저(chorme이나 safari...)에서 스핀버튼을 없애기 위해 맨 위의 코드를 적용시키면, 기본적으로 적용된 Webkit 스타일을 제거해줌. 두번째 (-moz-appearance:) 부분은 Firefox에서 숫자 조절 버튼을 없애기 위해 사용됨. textfield값을 넣어주는 이유는 기본적으로 제공되는 숫자 입력 필드를 일반 텍스트 필드처럼 보이기 위함.
3. 제출 버튼을 누르면, 결과를 알수 있다. 정답일 경우(userInput.value(사용자가 input 영역에 입력한 값) == randomNumber(Math의 메소드로 만든 난수) ) 다양한 축하 멘트를 랜덤으로 표시. 오답일 경우 randomNumber값(정답) 보여줌. 이 게임에서는 20점을 얻는게 마지막 목표이기 때문에 점수가 19점일때 나오는 문제가 가장 어렵다.(하지만 정말 나는 캡처나 어디에 적지 않고 20번째 문제까지 풀어냈다. 결론은 충분히 누구나 할 수 있음. 팁은 앞에 5자리정도는 외우고, 나머지 4개 숫자정도는 눈으로 찍어내는 느낌으로 기억하면 풀 수 있음!!!) 하여튼, 그 마지막 문제를 풀면 엄청난 축하 멘트를 날려주고 모든 시작버튼, 제출버튼, 입력란 등의 영역이 사라지며, 다시 게임을 시작하려면 '새로고침' 버튼을 눌러야함.
submitButton.addEventListener("click", function () {
const Guess = userInput.value;
// 뭔가... 다양한 리액션을 제공하고 싶음.
let option = Math.floor(1 + Math.random() * 5); //1~5사이 숫자
if (Guess == randomNumber) {
if (score == 19) {
scoreDisplay.textContent = `✨✨우와! 😎마지막 문제😎까지 풀어내다니!!🎉 🤩🎈🎉축하합니다!!🎈🎉🤩
(다시 게임을 진행하고 싶으시면 새로고침을 눌러주세요!)`;
userInput.value = "";
inputArea.style.display = "none";
} else {
switch (option) {
case 1:
message.textContent = "정답입니다!!! 당신은 천재입니까?";
break;
case 2:
message.textContent = "정답입니다!!! 점수 획득!!!";
break;
case 3:
message.textContent = "정답입니다!!! 축하축하 ><";
break;
case 4:
message.textContent = "정답입니다!!! 당신의 IQ는 200인거죠?";
break;
case 5:
message.textContent = "정답입니다!!! 정말 대단해요 !!!!";
break;
}
score++;
startButton.style.display = "block";
scoreDisplay.textContent = `점수: ${score}`;
userInput.value = "";
inputArea.style.display = "none";
}
} else {
message.textContent = `오답입니다 ㅠㅠ. 정답은 ${randomNumber}입니다.`;
// 틀렸을떄는, 정중하게 말하기 ㅎㅎ
startButton.style.display = "block";
scoreDisplay.textContent = `점수: ${score}`;
userInput.value = "";
inputArea.style.display = "none";
}
});
- 다시 써먹는 marth.random()과 math.floor() --> 랜덤한 1~5까지의 숫자를 만들고 싶었다. option을 위함.
- switch, case를
왠지 모르게 갑자기 사용하고 싶어져서 걍 써봤다.정답일 경우 보여질 리액션 5개정도를 만들어주었고 랜덤하게 생성된 option값에 따라 사용자가 정답을 맞출경우 랜덤하게 리액션을 볼 수 있는 재미를 주었다. ㅋㅋㅋ 그리고 귀찮아져서 .....가 아닌, 오답일 경우 리액션이 다양하면 사용자의 짜증을 유발할 수 있기에 정중히 한 멘트만 사용했다.- 정답일 경우 점수가 19점일때는 마지막 문제임을 뜻하고, 그것을 결국해낸것임으로 if절을 사용해서 엄청나게 반짝거리는 환영의 멘트를 넣어주었다.
추가적인 내용
Bootstrap을 처음 배운만큼, 많이 사용해보고 싶어서 버튼도 꾸며보고, 자동으로 넘어가는 슬라이드로 게임 설명을 해보고자 carousel를 이용해봤다.
하지만, img 태그에 이미지를 파일을 넣고나니, 다음 슬라이드로 넘어갈 때마다 크기가 갑자기 커지고 다시 resizing하는 문제가 생겼다.
이를 해결하기 위해
1) 처음에 이미지 크기를 다 일정한 해상도 크기로 바꿨지만, 여전히 문제는 해결되지 않았다. 😑
2) 그래서 두번째로는 carousel-item 요소의 사이즈를 각각 설정해보았다. 여전히 해결되지 않았다. 😭
3) 마지막으로 영어로 검색해서 여기저기 외국 사이트 뒤지다가 해결했는데, 너무 간단한 방법이었다. 걍 부트스트랩에서 가져온 캐러셀 코드를 감싸는 다른 div태그를 만들어서 그 태그에 클래스 이름을 만들어준뒤 css에서 아예 그 틀 자체의 사이즈를 고정시켰다. 😩 (왜 이 생각을 못했지?? 하지만 이런 뻘 짓을 해봐야 기억에 남는 법!!)
그리고 메세지 보여지게 할때 보통 textContent를 사용했는데, 그러면 텍스트만 추가될 뿐 줄바꿈처리를 하기 어려워서. html <br> 태그 이런거로 줄바꿈하려면 어떤 것을 바꿔야하지? 하고 검색하다가 innerHTML을 사용하면 된다는 것을 깨달았다.
scoreDisplay.textContent = `✨✨우와! 😎마지막 문제😎까지 풀어내다니!!🎉 🤩🎈🎉축하합니다!!🎈🎉🤩
(다시 게임을 진행하고 싶으시면 새로고침을 눌러주세요!)`; //<br>태그로 줄바꿈 안됨
scoreDisplay.innerHTML = `✨✨우와! 😎마지막 문제😎까지 풀어내다니!!🎉 🤩🎈🎉축하합니다!!🎈🎉🤩
<br>(다시 게임을 진행하고 싶으시면 새로고침을 눌러주세요!)`; //줄바꿈됨
아래는 각각 html, css, js 코드.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>숫자 기억 게임</title>
<link rel="stylesheet" href="./main.css" />
<!-- 구글 폰트 위함 -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous"
/>
<!-- 부트스트랩 사용해보기 위해 위에 코드 이용 -->
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200"
/>
<!-- 구글 아이콘 사용해보기 -->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js"
integrity="sha512-ykZ1QQr0Jy/4ZkvKuqWn4iF3lqPZyij9iRv6sGqLRdTPkY69YX6+7wvVGmsdBbiIfN/8OdsI7HABjvEok6ZopQ=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<!-- 부트스트랩 js파일들 불러오기 -->
<script defer src="./main.js"></script>
</head>
<body>
<div class="top">
<div class="description_title">How To Play</div>
<div class="carousel_box">
<div
id="carouselExampleDark"
class="carousel carousel-dark slide"
data-bs-ride="carousel"
>
<div class="carousel-indicators">
<button
type="button"
data-bs-target="#carouselExampleDark"
data-bs-slide-to="0"
class="active"
aria-current="true"
aria-label="Slide 1"
></button>
<button
type="button"
data-bs-target="#carouselExampleDark"
data-bs-slide-to="1"
aria-label="Slide 2"
></button>
<button
type="button"
data-bs-target="#carouselExampleDark"
data-bs-slide-to="2"
aria-label="Slide 3"
></button>
</div>
<div class="carousel-inner">
<div class="carousel-item active" data-bs-interval="10000">
<img
src="./images/description1.png"
class="d-block w-100"
alt="설명 이미지1"
/>
<div class="carousel-caption d-none d-md-block">
<h5 class="des_title">1. 시작버튼을 누르세요.😉</h5>
<p class="des_small">
시작 버튼을 누르시면, 위에 이미지 처럼 숫자가 랜덤으로
생성됩니다. 그 숫자를 기억해주세요!
</p>
</div>
</div>
<div class="carousel-item" data-bs-interval="2000">
<img
src="./images/description2.png"
class="d-block w-100"
alt="설명 이미지2"
/>
<div class="carousel-caption d-none d-md-block">
<h5 class="des_title">
2. 기억한 숫자를 입력 칸에 적어주시고, '제출' 버튼을
눌러주세요.👍
</h5>
<p class="des_small">입력란에는 숫자만 입력하실 수 있습니다.</p>
</div>
</div>
<div class="carousel-item">
<img
src="./images/description3.png"
class="d-block w-100"
alt="설명 이미지3"
/>
<div class="carousel-caption d-none d-md-block">
<h5 class="des_title">
3. 정답과 획득한 점수를 확인해보세요.😊
</h5>
<p class="des_small">
입력한 숫자가 맞으면 점수가 올라가고, 틀리면 점수를 획득하지
못합니다. <br />20점에 도달할 때까지 문제는 계속됩니다.
<br />특정 점수 도달 시 숫자가 길어지거나 숫자가 빨리
사라지도록 점점 어렵게 설정해놨습니다. <br />이 점 유의하시고,
숫자게임 <span class="red">START!!!!!</span>
</p>
</div>
</div>
</div>
<button
class="carousel-control-prev"
type="button"
data-bs-target="#carouselExampleDark"
data-bs-slide="prev"
>
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button
class="carousel-control-next"
type="button"
data-bs-target="#carouselExampleDark"
data-bs-slide="next"
>
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
</div>
</div>
<div class="container">
<div class="title">숫자 기억 게임</div>
<div id="numberDisplay" class="number"></div>
<button type="button" id="startButton" class="btn btn-success">
시작
</button>
<!-- 처음에는 inputArea를 안보이게 하고, 시작 버튼 누르면 보이게 하고 싶음. -->
<div id="inputArea" style="display: none">
<div class="box">
<input type="number" id="userInput" placeholder="숫자를 입력하세요" />
<!-- 숫자만 입력할 수 있게 하고 싶음. text대신 number로 -->
<button type="button" id="submitButton" class="btn btn-danger">
제출
</button>
</div>
</div>
<p id="message"></p>
<p id="score">점수: 0</p>
</div>
</body>
</html>
@import url("https://fonts.googleapis.com/css2?family=Jua&family=Nanum+Gothic:wght@400;700;800&display=swap");
* {
font-family: "Jua", sans-serif;
font-weight: 400;
font-style: normal;
box-sizing: border-box;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 0;
background-image: url("https://media.istockphoto.com/id/1846133228/ko/%EC%82%AC%EC%A7%84/%EC%A0%95%EB%8B%A4%EB%A9%B4%EC%B2%B4-%EB%AA%A8%EC%96%91%EC%9D%98-%EA%B2%80%EC%9D%80-%EC%A3%BC%EC%82%AC%EC%9C%84-%EB%B0%B0%EA%B2%BD-3d-%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98%EC%9E%85%EB%8B%88%EB%8B%A4.webp?a=1&b=1&s=612x612&w=0&k=20&c=jxHwsC5wMQJ8Te_O2n_nHe31TOjOBq3o5gNZHwvkfSA=");
background-size: cover;
overflow: auto; /* 스크롤을 허용 */
}
.top {
text-align: center;
padding: 30px;
border-radius: 10px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
margin-top: 20px;
}
.container {
text-align: center;
background-color: white;
padding: 20px;
border-radius: 10px;
width: 50%;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
}
.container .title {
font-weight: 700;
font-size: 40px;
}
.number {
font-size: 50px;
margin: 20px 0;
}
button.btn.btn-success {
padding: 20px 50px;
font-size: 30px;
margin: 10px auto 10px auto;
border-radius: 10px;
}
.box {
display: flex;
justify-content: center;
align-items: center;
/* background-color: aquamarine; */
}
button.btn.btn-danger {
height: 100%;
padding: 20px 30px;
font-size: 25px;
margin: auto 10px auto 10px;
border-radius: 10px;
}
input#userInput {
height: 100%;
padding: 20px;
border-radius: 10px;
font-size: 30px;
}
#message {
font-weight: 100;
margin-top: 20px;
font-size: 25px;
color: #0a79f0;
}
#score {
margin-top: 10px;
font-size: 30px;
font-weight: 400;
}
#timer {
margin-bottom: 20px;
font-size: 20px;
}
h5,
.detail {
color: black;
}
.description_title {
font-size: 30px;
font-weight: 700;
margin-bottom: 20px;
color: rgb(255, 143, 7);
text-shadow: 2px 2px 0px black;
}
/* 캐러셀 이미지 resizing 문제 */
.carousel_box {
width: 1000px;
height: auto;
}
img.d-block.w-100 {
border-radius: 10px;
}
.red {
color: red;
}
.des_title {
font-size: 25px;
color: darkviolet;
}
.des_small {
font-size: 20px;
}
/* input 타입 숫자로 바꾸고 생긴 토글 바꾸기 위한 코드 */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
document.addEventListener("DOMContentLoaded", function () {
let randomNumber = 0;
let score = 0;
const numberDisplay = document.getElementById("numberDisplay");
const startButton = document.getElementById("startButton");
const inputArea = document.getElementById("inputArea");
const userInput = document.getElementById("userInput");
const submitButton = document.getElementById("submitButton");
const message = document.getElementById("message");
const scoreDisplay = document.getElementById("score");
// 1000~9000 범위의 자연수를 만들어내야함
// random으로 0~1사이에 숫자 아무거나 하나 생성하고,
// floor로 소수를 내림처리하기.
startButton.addEventListener("click", function () {
if (score < 6) {
randomNumber = Math.floor(1000 + Math.random() * 9000);
} else if (score < 11) {
randomNumber = Math.floor(10000 + Math.random() * 90000);
} else if (score < 16) {
randomNumber = Math.floor(100000 + Math.random() * 900000);
} else {
randomNumber = Math.floor(100000000 + Math.random() * 9000000);
}
numberDisplay.textContent = randomNumber;
inputArea.style.display = "none";
message.textContent = "";
startButton.style.display = "none";
//단계별로 점점 어려워지게 만들고 싶음(자리수 하나씩 늘리기)
// 점수 획득한 것을 기준으로 5단계씩 만들어봄.
//점수가 0일때는 기존 조건처럼 3초
// 각 단계마다 점수가 하나씩 올라갈때마다 조금 더 빠르게 진행하고 싶음.
// 점수 나누기 5의 나머지를 기준으로 숫자 자리수가 하나씩 늘기전까지는 조금씩 더 빠르게 만들었음
let remainder = score % 5;
if (score == 0 || remainder == 1) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000);
} else if (remainder == 0) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000 * (1 / 5));
} else if (remainder == 2) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000 * (1 / 2));
} else if (remainder == 3) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000 * (1 / 3));
} else if (remainder == 4) {
setTimeout(function () {
numberDisplay.textContent = "";
inputArea.style.display = "block";
}, 3000 * (1 / 4));
}
});
// 각 단계마다 메세지 넣고 싶음.
submitButton.addEventListener("click", function () {
const Guess = userInput.value;
// 뭔가... 다양한 리액션을 제공하고 싶음.
let option = Math.floor(1 + Math.random() * 5); //1~5사이 숫자
if (Guess == randomNumber) {
if (score == 19) {
scoreDisplay.innerHTML = `✨✨우와! 😎마지막 문제😎까지 풀어내다니!!🎉 🤩🎈🎉축하합니다!!🎈🎉🤩
<br>(다시 게임을 진행하고 싶으시면 새로고침을 눌러주세요!)`;
userInput.value = "";
inputArea.style.display = "none";
} else {
switch (option) {
case 1:
message.textContent = "정답입니다!!! 당신은 천재입니까?";
break;
case 2:
message.textContent = "정답입니다!!! 점수 획득!!!";
break;
case 3:
message.textContent = "정답입니다!!! 축하축하 ><";
break;
case 4:
message.textContent = "정답입니다!!! 당신의 IQ는 200인거죠?";
break;
case 5:
message.textContent = "정답입니다!!! 정말 대단해요 !!!!";
break;
}
score++;
startButton.style.display = "block";
scoreDisplay.textContent = `점수: ${score}`;
userInput.value = "";
inputArea.style.display = "none";
}
} else {
message.textContent = `오답입니다 ㅠㅠ. 정답은 ${randomNumber}입니다.`;
// 틀렸을떄는, 정중하게 말하기 ㅎㅎ
startButton.style.display = "block";
scoreDisplay.textContent = `점수: ${score}`;
userInput.value = "";
inputArea.style.display = "none";
}
});
});
여튼 다음 퀘스트하러 가야지 허허.......
'프론트엔드 부트캠프' 카테고리의 다른 글
[사전캠프 퀘스트] TO DO LIST 만들기 (1) | 2024.09.19 |
---|---|
[사전캠프 퀘스트] 한식 메뉴 렌더링하기 (1) | 2024.09.14 |
[사전캠프 3주차] Fetch 여기저기 사용해보기 (0) | 2024.09.11 |
[사전캠프 3주차] 클라이언트-서버 개념 이해와 Fetch 시작하기 (3) | 2024.09.10 |
[사전캠프 3주차] jQuery로 웹페이지 제어하기 (0) | 2024.09.09 |