[Let's get it 자바스크립트 프로그래밍] - 5.1~5.4 반복문 사용하기_숫자야구 게임

신혜린·2025년 1월 12일
0
post-thumbnail

순서도 및 UI 그리기

  • 컴퓨터가 무작위로 숫자 네 개를 뽑는다.
  • 사용자는 답을 입력창에 입력한다.
  • 입력된 답의 형식이 올바른지 검사하고
  • 홈런인지 검사한다.
  • 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}

중복 방지

중복된 번호를 뽑는 걸 방지하기 위한 방법으로는 로또 번호 뽑기를 떠올리면 된다.
한 곳에 모아두고, 뽑은 건 밖에 둔다. -> 뽑히지 않은 것들 중에서만 다시 뽑고 반복.

  • 1부터 9까지 숫자를 배열에 담아 놓고 뽑아서 나온 숫자와 같은 숫자를 배열에서 빼는 식으로 중복을 제거하는 방법이 효율적이다.
const numbers = [];
for (let n = 1; n < 10; n++) {
  numbers.push(n);
}
  • push 를 9번 하는 것을 위와 같이 반복문을 통해 짧고 간결하게 표현할 수 있다.

숫자 뽑기

  • answer 라는 빈 배열을 만든다.
  • numbers 배열로부터 숫자를 하나씩 꺼낸다.
  • 4번 반복한다.
  • numbers에서 뽑은 숫자를 answerpush하고 나면 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가 섞여나올 때가 있다.
  • 인덱스는 여전히 0부터 8번까지인데, splice 될 때마다 인덱스의 개수가 줄어드므로 undefined가 출력될 때가 있다.
    ex) [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}$">

입력값과 정답 비교하기

홈런 & 시도횟수 10번 초과 여부 검사

$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.length4인데 숫자로는 0~3을 반복해야 하니까 4 미만(< 4)으로 적는다.
  • 정답 숫자 하나에 대해 value.indexOf(answer[i])로 일치하는 숫자가 있는지 찾아낸다.
  • tries 배열에 방금 입력한 값을 저장해서 시도 횟수를 1 늘려준다.
  • $logs 태그에 append 메서드로 텍스트와 태그를 동시에 추가할 수 있다.
    - appendChild 메서드는 하나의 텍스트나 태그만 추가할 수 있지만, append는 여러 개가 가능하다.
profile
개 발자국 🐾

0개의 댓글