- 컴퓨터가 무작위로 숫자 네 개를 뽑는다.
- 사용자는 답을 입력창에 입력한다.
- 입력된 답의 형식이 올바른지 검사하고
- 홈런인지 검사한다.
- 10번 초과 실시했는지 검사하고
- 10번을 초과했다면 패배
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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');
</script>
</body>
</html>
Math.random()
:0 <= x < 1
사이 무작위 숫자를 만드는 함수, 0 이상 1 미만의 수를 무작위로 생성해준다.
숫자야구게임에서는 1부터 9까지의 숫자가 필요하기 때문에 Math.random()
에 9를 곱한 후 1을 더하면 된다.
Math.random() * 9
:0 <= x < 9
Math.random() * 9 + 1
:0 <= x < 10
숫자를 자연수로 만들어주기 위해 내림(Math.floor()
), 올림(Math.ceil()
), 반올림(Math.round()
) 등을 해야 한다.
이 경우에는 내림을 해주기 위해 Math.floor()
메소드를 사용한다.
Math.floor(Math.random() * 9 + 1)
:x = {1, 2, 3, 4, 5, 6, 7, 8, 9}
중복된 번호를 뽑는 걸 방지하기 위한 방법으로는 로또 번호 뽑기를 떠올리면 된다.
한 곳에 모아두고, 뽑은 건 밖에 둔다. -> 뽑히지 않은 것들 중에서만 다시 뽑고 반복.
const numbers = [];
for (let n = 1; n < 10; n++) {
numbers.push(n);
}
push
를 9번 하는 것을 위와 같이 반복문을 통해 짧고 간결하게 표현할 수 있다.
answer
라는 빈 배열을 만든다.numbers
배열로부터 숫자를 하나씩 꺼낸다.- 4번 반복한다.
numbers
에서 뽑은 숫자를answer
에push
하고 나면numbers
배열에서는 제거(splice
)한다.
const numbers = [];
for (let n = 1; n < 10; n++) {
numbers.push(n);
}
const answer = [];
for (let n = 0; n <= 3; n += 1) { // 네 번 반복
const index = Math.floor(Math.random() * 9); // 0~8 정수
answer.push(numbers[index]);
numbers.splice(index, 1);
}
console.log(answer); // undefined가 섞여나올 때가 있다.
splice
될 때마다 인덱스의 개수가 줄어드므로 undefined
가 출력될 때가 있다.[1, 2, 3, 4, 5, 7, 8, 9][8] === undefined
따라서 인덱스를 뽑을 때 9
라는 고정적 숫자 대신 numbers
배열의 길이를 따라야 한다.
const answer = [];
for (let n = 0; n <= 3; n += 1) {
// numbers 길이에 따라 달라짐
const index = Math.floor(Math.random() * numbers.length);
answer.push(numbers[index]);
numbers.splice(index, 1);
}
console.log(answer);
이번에는 버튼 click
이벤트 대신에 $form
태그에 submit
이벤트를 사용한다.
submit
이벤트를 사용하는 것이 좋은데, 그 이유는 Enter
키를 눌렀을 때도 값을 제출할 수 있기 때문이다.<form id="form">
<input type="text" id="input">
<button>확인</button>
</form>
const tries = [];
function checkInput(input) {}
$form.addEventListener('submit', (event) => {
event.preventDefault(); // submit 기본 이벤트 취소
const value = $input.value;
$input.value = '';
const valid = checkInput(value);
});
event.preventDefault()
: 폼 태그의 기본 동작을 취소하는 메소드폼 태그는
submit
이벤트가 발생할 때 기본으로 브라우저를 새로고침한다.
숫자야구게임에서 브라우저가 새로고침되면 모든 게 초기화되므로 새로고침되는 것을 막아야 한다.
입력한 값은 $input.value
로 가져온다.
이때 숫자가 아니라 문자열로 값이 들어온다. 이 값을 checkInput
이라는 함수를 통해 검증한다.
function checkInput(input) {
if (input.length !== 4) { // 길이는 4가 아닌가
return alert('4자리 숫자를 입력해 주세요.');
}
if (new Set(input).size !== 4) { // 중복된 숫자가 있는가
return alert('중복되지 않게 입력해 주세요.');
}
if (tries.includes(input)) { // 이미 시도한 값은 아닌가
return alert('이미 시도한 값입니다.'); // return undefined
}
return true;
}
참고) HTML5의 검증 기능 활용하기
HTML 자체에서도 입력값 검사 기능을 제공한다.<input required type="text" id="input" minlength="4" maxlength="4" pattern="^(?!.*(.).*\1)\d{4}$">
$form.addEventListener('submit', (event) => {
event.preventDefault();
const value = $input.value;
$input.value = '';
const valid = checkInput(value);
if (!valid) return;
if (answer.join('') === value) {
$logs.textContent = '홈런!';
return;
}
if (tries.length >= 9) {
const message = document.createTextNode(`패배! 정답은 ${answer.join('')}`);
$logs.appendChild(message);
return;
}
// 몇 스트라이크 몇 볼인지 검사 추가 예정
}
checkInput()
함수를 통해 입력값 검증을 통과했는지 확인 후- 홈런인지 확인한다.
- 10번의 기회를 다 날리면 패배하는 것은
tries.length
가 9 이상인지 검사하면 알 수 있다.- 10번 시도했다면 패배했다고 알리고
- 정답을 공개한다.
$logs
태그의 내용을 유지하면서 추가로 다음 줄에 기록을 남기려면, document.createTextNode
로 먼저 텍스트를 만들고 appendChild
로 화면에 추가해야 한다. // 몇 스트라이크 몇 볼인지 검사 추가
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;
}
}
}
$logs.append(`${value}: ${strike} 스트라이크 ${ball} 볼`, document.createElement('br'));
tries.push(value);
});
- 먼저정답 숫자를 하나씩 선택한다. 반복할 때
answer.length
는4
인데 숫자로는0~3
을 반복해야 하니까4 미만(< 4)
으로 적는다.- 정답 숫자 하나에 대해
value.indexOf(answer[i])
로 일치하는 숫자가 있는지 찾아낸다.tries
배열에 방금 입력한 값을 저장해서 시도 횟수를 1 늘려준다.
$logs
태그에 append
메서드로 텍스트와 태그를 동시에 추가할 수 있다.appendChild
메서드는 하나의 텍스트나 태그만 추가할 수 있지만, append
는 여러 개가 가능하다.