[렛츠기릿 자바스크립트] 숫자 야구

EOH·2023년 5월 28일
0
post-thumbnail

💻 실습환경

vscode로 코드를 작성하고 chrome 웹 브라우저와 개발자도구를 이용해 코드를 실행해보았다.

💬 숫자 야구

1~9까지 숫자 중 겹치지 않는 무작위의 4자리 숫자를 맞추는 게임이다. 10번까지 시도할 수 있으며, 각 시도에 따라 자리수와 숫자 모두 맞으면 스트라이크, 숫자만 맞으면 볼, 다 틀리면 아웃으로 힌트를 준다. 예를 들어 정답은 1234인데 사용자가 1356을 입력했으면 1스트라이크 1볼 이라고 화면에 띄워준다. 10번 시도횟수를 다 사용하거나 3번 아웃이 나오면 패배로 게임이 종료된다. 삼진아웃!⚾️

🔎 순서도

1️⃣ 난수 생성 매서드

난수를 생성하기 위해 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()가 있다.


2️⃣ form 태그 [HTML]

이전 과제에서는 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자리 숫자를 뽑아준다. 그런데 버튼을 누를 때마다 새로고침이 된다면 코드가 다시 실행이 될 것이고 정답으로 지정한 숫자도 다시 지정이 될 것이다. 그렇기 때문에 기본동작을 막아서 처음 지정한 정답을 사용자가 게임을 끝낼 때까지 가지고 있을 수 있게 한다.

3️⃣ 여러가지 함수들

1️⃣ join(), split()

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"]

2️⃣ indexOf(), includes()

배열에서 사용 가능한 매서드로 indexOf는 인덱스를 반환하고 includes는 포함되어 있는지 여부만 true / false로 알려준다.

arr.indexOf();
//기본 형태

arr.indexOf(5);
//5의 index를 반환함

arr.indexOf('5');
//문자 5의 index를 반환함

arr.includes();
//기본형태

arr.includes(5);
//5의 포함여부를 true, false로 알려줌

3️⃣ forEach(), map(), fill()

배열에서 사용 가능한 매서드이다.

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()를 배워야 할 수있다고 했다. 차후 배워서 수정해 놓겠다. 또 많은 함수들을 배웠는데 함수를 더 익숙하게 사용할 수 있도록 연습해봐야겠다.

📚 github & reference

My github

렛츠기릿 자바스크립트 강의

Math.random (Mdn web docs)

form 태그 (TCP 코딩스쿨)

profile
에-오

0개의 댓글