vscode로 코드를 작성하고 chrome 웹 브라우저와 개발자도구를 이용해 코드를 실행해보았다.
1~9까지 숫자 중 겹치지 않는 무작위의 4자리 숫자를 맞추는 게임이다. 10번까지 시도할 수 있으며, 각 시도에 따라 자리수와 숫자 모두 맞으면 스트라이크, 숫자만 맞으면 볼, 다 틀리면 아웃으로 힌트를 준다. 예를 들어 정답은 1234인데 사용자가 1356을 입력했으면 1스트라이크 1볼 이라고 화면에 띄워준다. 10번 시도횟수를 다 사용하거나 3번 아웃이 나오면 패배로 게임이 종료된다. 삼진아웃!⚾️
난수를 생성하기 위해 Math객체에 있는 random매서드를 사용한다.
Math.random()
단 이 함수는 정수가 아닌 1.234556과 같은 랜덤한 소수의 값을 주기 때문에 약간의 가공이 필요하다.
Math.floor()
Math.floor(Math.random() * 9 + 1);
Math.floor매서드는 소수를 내림하는 역할을 하기 때문에 이 매서드를 이용해서 0부터 9까지의 랜덤한 정수의 값을 구할 수 있다.
이번에 만든 숫자야구 게임에서는 1부터 9까지의 수가 필요했기 때문에 두 번째 줄에 있는 코드처럼 random으로 나온 값에 9를 곱하고 1을 더해주었다.
✏️ Math.random을 이용해 무작위 수를 뽑는 방법
Math.random()
//0 <= x < 10 (소수 포함)
Math.random() * 9
// 0 <= x < 9
Math.random() * 9 + 1
// 1 <= x < 10
Math.floor(Math.random() * 9 + 1)
// x = 1, 2, 3, 4, 5, 6, 7, 8, 9
Math.random()매서드는 암호화된 보안 난수를 제공하지 않는다고 한다. 보안과 관련된 용도로 사용하는 난수 생성 매서드로는 window.crypto.getRandomValues()가 있다.
이전 과제에서는 body태그 안에 input태그를 만들었는데 이번 과제에서는 input을 form태그로 감싸서 사용했다.
form태그는 사용자로부터 입력을 받을 수 있는 HTML입력 폼(form)을 정의할 때 사용하며, input도 form의 요소로 지정이 되어있기 때문에 input을 form태그로 감싸서 사용하는 것이 더 일반적이라고 한다.
<form id="form">
<input type="text" id="input">
<button>확인</button>
</form>
HTML에서 어떤 태그 중에는 기본 동작이 있는 요소들이 있다. form태그에서는 있는 버튼을 눌렀을 때 새로고침이 되는 기본동작이 들어가있다. 이때 기본 동작을 수정하기는 어려우나 막아줄 수는 있다.
$form.addEventListner('submit', (event) => {
event.preventDefault();
});
위 코드를 해석하면 form에 있는 버튼을 누르면 submit이라는 event를 발생시키는데 이때 event객체의 preventDefault라는 매서드를 실행시켜 기본 동작을 하지 못하게 막아준다. 매개변수에도 event를 써주어야한다.
❓ 기본동작을 왜 막아주어야 할까?
처음 코드가 실행되면서 정답으로 지정할 무작위의 4자리 숫자를 뽑아준다. 그런데 버튼을 누를 때마다 새로고침이 된다면 코드가 다시 실행이 될 것이고 정답으로 지정한 숫자도 다시 지정이 될 것이다. 그렇기 때문에 기본동작을 막아서 처음 지정한 정답을 사용자가 게임을 끝낼 때까지 가지고 있을 수 있게 한다.
join은 배열을 문자열로 이어붙이고, split은 문자열을 배열로 나눈다.
arr.join();
//기본 형태
[3, 1, 4, 6].join();
// 결과값 3,1,4,6
[3, 1, 4, 6].join('');
//빈 문자열이 join의 매개변수로 들어가면 3146이 된다.
str.split();
//기본 형태
'3146'.split()
// ["3146"] 문자열이 들어간 배열이 된다.
'3146'.split('')
//빈 문자열이 매개변수로 들어가면 각각의 요소로 들어간다. ["3", "1", "4", "6"]
배열에서 사용 가능한 매서드로 indexOf는 인덱스를 반환하고 includes는 포함되어 있는지 여부만 true / false로 알려준다.
arr.indexOf();
//기본 형태
arr.indexOf(5);
//5의 index를 반환함
arr.indexOf('5');
//문자 5의 index를 반환함
arr.includes();
//기본형태
arr.includes(5);
//5의 포함여부를 true, false로 알려줌
배열에서 사용 가능한 매서드이다.
array.forEach((element, index) => {
//실행할 함수가 들어가는 자리
});
forEach는 배열 1자리마다 매개변수로 들어온 함수를 실행시켜준다. 반복문보다 성능은 안 좋지만 기능을 연달아 쓸 수 있어 편리하다.
array.map((element, i) => {
return (element * 2);
});
map()은 반복문을 돌며 각각의 요소를 바꿔주는 것과 똑같은 역할을 한다. forEach와 다르게 map은 return을 반환한다.
array.fill(0, 2, 4)
fill()은 배열을 한 값으로 채워주는 역할을 한다. 매개변수에는 값, 시작 인덱스, 끝 인덱스 순으로 넣는다.
<head>
<meta charset="UTF-8">
<title>숫자야구</title>
</head>
<body>
<form id="form">
<input type="text" id="input">
<button>확인</button>
</form>
<div id="logs"></div>
<script>
const $input = document.querySelector('#input');
const $form = document.querySelector('#form');
const $logs = document.querySelector('#logs');
const out = 0;
const numbers = []; //단순히 값들만 모을때는 [] 배열, 각각의 값에 이름을 붙여야하면 객체의 모음으로 {}
for (let n = 0; n < 9; n += 1) {
numbers.push(n + 1);
}//반복문을 써보기 위해 numbers에 인수를 넣는 연습
const answer = []
for (let n = 0; n < 4; n++) {
const index = Math.floor(Math.random() * (numbers.length)); //0-8정수, length가 점점 짧아지니 index도 줄여야 됨
answer.push(numbers[index]);
numbers.splice(index, 1); //중복 숫자가 생기지 않게 뽑은것은 answer에 push하면서 원래 배열에서는 지워주기
}
console.log(answer);
const tries = [];
function checkInput(input) {
if (input.length !== 4) {
return alert('4자리 숫자를 입력해 주세요.');
}
if (new Set(input).size !== 4) {
return alert('중복되지 않게 입력해 주세요.');
}
if (tries.includes(input)) {
return alert('이미 시도한 값입니다.');
}
return true;
} //검사하는 코드. 함수로 잘 쪼갤수록 보기 좋은 코드가 된다.
$form.addEventListener('submit', (event) => {
//form에 있는 버튼을 누르면 submit이라는 event가 발생
event.preventDefault(); //기본동작 막는 매서드
const value = $input.value;
//$input.value대신 event.target[0]처럼 쓸 수도 있다.
$input.value = '';//입력하고 나면 입력창을 지워준다.
if (!checkInput(value)) {
return;
} //입력값 문제 없음
if (answer.join('') === value) {
$logs.textContent = '홈런!';
return;
}
if (tries.length >= 9) {
const message = document.createTextNode(`패배! 정답은 ${answer.join('')}`);
$logs.appendChild(message);
return;
}
let strike = 0;
let ball = 0;
for (let i = 0; i < answer.length; i++) {
const index = value.indexOf(answer[i]);
if (index > -1) {
if (index === i) {
strike += 1;
} else {
ball += 1;
}
}
}
if (strike === 0 && ball === 0)
{
$logs.append('아웃!', document.createElement('br'));
out++;
}
else
$logs.append(`${value}: ${strike} 스트라이크 ${ball} 볼`, document.createElement('br'));
});
</script>
</body>
</html>
추가 과제로는 out만들기와 3진 아웃시 게임 종료를 추가하는 것이다. out을 화면에 출력하는 것은 쉽게 했는데 3진 아웃은 removeEventListener()를 배워야 할 수있다고 했다. 차후 배워서 수정해 놓겠다. 또 많은 함수들을 배웠는데 함수를 더 익숙하게 사용할 수 있도록 연습해봐야겠다.