Web Front-end 공부/Java script
Js 숫자야구게임 만들기
Hanachoi
2022. 5. 30. 16:51
- 야구의 스트라이크, 볼, 아웃의 원리를 가져온 게임
- 상대편이 1~9중에서 중복되지 않는 숫자 4개를 고른다. 10번의 기회가 주어지고 상대편이 고른 숫자 네 개를 맞추면 된다. 이 때 숫자의 순서까지 맞아야 한다. 맞힌 숫자의 개수(볼), 숫자뿐만 아니라 순서까지 맞힌 개수(스트라이크)를 알려준다.
- 반복문을 연습할 수 있다.
#순서도 그리기
- 사용자가 무엇인가를 입력하는 형식은 그 형식이 맞는지를 먼저 검사해줘야 한다. ex. 숫자 4자리 입력인데, 3자리만 했다던가, 글자를 입력했다던가 식의 형식에 어긋나는 것들은 에러를 띄워줘야한다.
- 1. 시작 -> 숫자 4개 뽑는다. -> 사용자가 입력할 때 까지 대기
- 2. 사용자가 답을 제출 -> 답이 형식에 맞는 지 검사한다 ->yes : 홈런인지 검사한다./ No : 에러를 표시한다 ->끝
- 3.홈런인지 검사한다 -> yes : 홈런이라고 표시 -> 끝 / No : 10번 시도 했는가?
- 4. 10번 시도 했는가? -> yes : 졌다고 표시 -> 끝 / No : 몇 스트라이크, 몇 볼 인지 표시한다. -> 시도 횟수를 1 늘린다 -> 대기
#랜덤 사용하기
- 무작위 숫자를 만들 수 있는 함수는 Math.random() 얘의 범위는 0이상 1미만(0<=Math.random()<1)
- 얘를 콘솔창에 쳐보면 소수점 자리로 나오게 된다. 그럼 우리가 소수점의 첫째 자리만 가져오거나, 숫자식을 바꿔서 정1~9 사이의 정수만 나올 수 있도록 해줄 수 있다.
- 0 <= Math.random() <1 에서, 각 식에 9를 곱해준다. 0 <= Math.random * 9< 9 가 된다. 우리가 만들고 싶은 숫자는 1부터 9까지니까 다시 각 항에 1을 더해준다. 1<= Math.random*9 + 1 < 10 이렇게 되면 소수점이지만 1부터 10미만의 숫자들이 입력될 것이다. 그럼 여기서 애들을 정수로 다시 만들어 준다.
- 소수를 내림하는 함수가 Math.floor(math.random()*9+1) 얘를 실행하면 숫자는 랜덤하게 정수만 나오게 된다.
- Math는 브라우저가 알아서 제공해주는 함수 중 하나.
- 올림은 Math.ceil() // 반올림은 Math.round() 로 할 수 있다.
- 그렇다면 여기서 어떻게 랜덤한 4개의 숫자를 뽑을 수 있을까? 여기서는 알고리즘의 원리가 필요하다.
- 사람이라면 어떻게 할 수 있을까? 를 생각해보고 알고리즘을 대략적으로 짤 수 있다. 숫자가 30이하인 경우에는 사람이 어떻게 할 수 있을까?를 생각해보면 된다. 여기서는 간단히 숫자 4개만 뽑아서 하기 때문에 상관없음.
#무작위로 숫자 네 개 뽑기
- 빈 배열을 하나 만들어준다. 이 배열안에는 1~9까지의 숫자를 넣어줄거고 여기에 맞게 반복문을 돌려준다.
const numbers = []; //1~9
for(let n = 0; n < 9; n++;){
numbers.push(n+1);
}
- 보통 반복문은 n=0부터 시작하는 경우가 많기 때문에 numbers.push(n+1)로 넣을 숫자를 한 개 더 올려준다.
- 다음으로 사용자가 넣는 답에 대한 배열과 코드를 작성한다
const answer = [];
for(let n=0; n<=3; n++){ // 4번 반복
const index = Math.floor(Math.random()*9); 0~8정수
answer.push(numbers[index]); // 숫자를 뽑는 과정
numbers.splice(index,1); // 뽑은 숫자는 지워준다.
}
console.log(answer);
- 4개의 숫자를 뽑는거니까 반복문은 4번을 돌리게 된다. 똑같이 n=0부터 시작
- 변수 index는 0부터 8까지의 숫자가 나오도록 함. 우리가 고르고 싶은 숫자는 numbers 배열의 인덱스값으로 구해야하기 때문에 여기서 Math.random() * 9로 넣어준다. 0~8까지의 정수를 고르게 만들어줌. (+1을 하지 않은 이유)
- 그리고 마지막에 numbers.splice로 index로 뽑은 값을 하나 지워준다. 그래야 중복되지 않는 숫자가 나올테니까
- 이렇게 알고리즘을 하나 구성했다면 실제로 예제를 하나 돌려보는게 좋다.
- 지금 이 알고리즘에서는 예제를 돌려보면 하나의 문제점을 발견 할 수 있다.
//numbers = [2,5,6,7,8,9]
//answers = [3,1,4,undefined]
- const index 변수는 0~8까지의 정수를 뽑아주기 때문에 우리가 numbers의 인덱스 값을잘 가져올 수 있기는 하지만, 문제는 숫자롤 뽑고 나면 그 배열안에서 지워버리기 때문에 나타난다. 숫자가 지워지기 때문에 인덱스의 값은 줄어드는데 여전히 0~8까지의 정수를 뽑아내기 때문. 그래서 정확하게 4개의 정수가 나타날 수 있지만, 배열에서 빠진 값때문에 undefined가 뜰 수도 있음. 그렇게 되면 코드가 잘 못 작동하는게 된다. 보통 끝 쪽에 있는 값에서 많이 발생한다.
- 이렇게 예제를 돌려볼때는 보통 끝 값을 위주로 예제를 실행해보자. 그럼 이렇게 오류가 발생하는걸 찾을 수 있다. ex. 1이나 9를 계속 뽑는다고 생각해보자.
- [const index = Math.floor(Math.random()*9); ]여기 부분을 수정해준다. 반복문이 돌 때 마다 n의 값이 하나씩 올라가니까 9에서 그 숫자를 빼주면 9, 8, 7, 6 순으로 인덱스의 갯수가 낮아질 것이다.
const answer = [];
for(let n=0; n<=3; n++){
const index = Math.floor(Math.random()*(9 -n)); // (Math.random()*numbers.length)로 수정
answer.push(numbers[index]);
numbers.splice(index,1);
}
console.log(answer);
- 코드를 짜다보면 반복문에서 실수가 나올 가능성이 높다. 여기서 계속 예제를 돌려보면 예외를 찾을 수 있으니 그걸 해결해주면 됨. 사실 예외는 완벽하게 찾기는 힘들다. 순서도를 예외없이 잘 짜는게 실력의 척도가 되는 것
- [const index = Math.floor(Math.random()*9); ] 의 부분에서 9라는 숫자는 우리가 처음에 만든 numbers 배열의 갯수가 되는 것이다. 여기를 9 대신에 .numbers.length로 바꿔준다면 나중에 우리가 마음이 바껴서 배열을 수정한다고 해도 이 부분에서는 에러가 나는 걸 막을 수 있다.
- tip : 코드를 짜면서 수정되야 할 순간들이 많이 오는데, 코드의 한부분을 수정했더니 줄줄히 그 뒤가 수정되어야 한다고 하면, 그 코드는 좋은 코드라고 할 수 없다. 수정이 필요한 부분만 수정을 해줘도 나머지 부분이 수정되지 않도록 짜야 좋은 코드다
# 입력값 검증하기
- 우리가 기본적으로 만들어놓은 html에서 <input>과 <button>태그를 <form>으로 감싸서 form의 id 값으로 addEventListener를 걸어주는게 좋다. 이게 좀 더 웹 표준에 잘 맞는 코드. 특히 input이 버튼이 달렸다면 더욱 이렇게 해주는게 좋다.
- 웹페이지에 입력창이 있다면 <form>태그로 감싸서 submit이벤트를 사용하는게 좋다. 그래야 버튼을 클릭하지 않고도 Enter버튼을 눌러 값을 제출 할 수있음.
<form id="form">
<input type="text" id="input">
<button>확인</button>
</form>
- <form>은 submit이라는 함수를 사용할 수 있다. <form>안에 들어있는 버튼을 누르면 submit이 호출됨.
- <form>의 submit을 호출하기 위해서 console.log로 찍어보면, 콘솔창에 딱 찍히지는 않지만 재빠르게 사라지고, 주소창에 ?(물음표)가 하나 더 붙는걸 볼 수있다. submit 의 기본적인 기능 중 하나가 바로 호출될 때 마다 페이지를 새로고침 하는 것이다. 우리는 이 기본동작을 수정할 수는 없지만 이 동작을 취소하는 방법은 있다.
- 이렇게 페이지가 새로고침되면 우리가 위에서 numbers / answers 로 뽑아놓은 것들이 다 날라가기 때문에 취소가 필요한 것이다. 이걸 막는 방법은 함수의 매개변수에 event를 넣고 event.preventDefault();로 기본 동작을 막 을수 있다. 이렇게 되면 확인버튼을 계속 눌러도 새로고침되지 않고 콘솔창에 그대로 찍히는걸 볼 수 있음.
$form.addEventListener('submit',(event)=>{
event.preventDefault(); //기본동작 막기//
console.log('서브밋')
})
- console.log('서브밋',event)로 찍어보면 우리가 event 속성들을 다 볼 수 있다.
- 이벤트 속성 중에서 target 부분을 보면 form 태그가 마치 배열처럼, 그 안에 들어있는 속성들이 몇 번째 인덱스인지 볼 수 있다.

- 이렇게 되면 나중에 event.target[0] 이런식으로 선택을 하면 input박스를 선택할 수 잇고, event.target[1] 하면 button을 선택할 수 있게 된다.
const tries = [];
function checkInput(input) {}
$form.addEventListener('submit', (event) => {
event.preventDefault();
const value = $input.value; //event.target[0] 도 가능//
$input.value = '';
const valid = checkInput(value);
});
- [$input.value = ''; ]처럼, 게임을 만들때는 기본적으로 값을 입력했으면, 다음 사람을 위해서 input창을 비워주는게 센스라고 생각하자.
- 그 다음으로, 들어온 문자열의 값을 검증하는 코드가 필요하다. 여기 코드를 그냥 밑에 쭉 써도 되지만, 복잡해질 수 있기 때문에 함수로 빼서 정리를 하는게 더 좋다 .
- 6개월 뒤에 이 코드를 보면 무슨 코드인지 모르고 헷갈리는 경우가 많기 때문에, 주석을 많이 달아서 설명을 보충하고 알아보기 쉽게 코드를 작성하도록 항상 연습하자. 가능하면 함수로 계속 쪼개놓는게 좋다.
- 실무에서도 변수는 수만개가 될 수 있기 때문에 변수를 잘 구분 할 수 있도록 자신만의 스타일을 정해놓는게 좋다. ex) html 태그를 선택한 코드에는 $input / Tinput/ inputTag 이런식으로 자기가 알아볼 수 있도록 규칙을 정해놓는다.
- 그래서 checkInput함수는 이렇게 생겼다.
const tries = []; // 시도한 값이 들어가는 배열
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 true; // 모든 검증과정을 다 거치면 true가 나옴
} // 검사하는 코드
- 사용자가 3146, 314 이렇게 두가지 값을 입력했다면 첫번째 if 에서 4자리 숫자가 아니기 때문에 경고창을 받게된다.
- [new Set(input)]은 중복을 제거한 배열이다. Set은 배열과 비슷한 것인데 여기서는 알아서 중복을 제거해준다. 만약에 314 다음으로 3144를 다시한번 입력하게 되면 알아서 중복을 제거해준다. 여기서는 length가 아닌 size를 사용하게 된다. 중복된 값이 있다면 사이즈가 4보다 작아지기 때문에 여기 if문에서 걸리게 되는 것이다. 3144에서 4가 중복되어 나오기 때문에 size 값이 4가 아닌 3이된다.
- [tries.includes(input)]의 부분은 사용자가 이미 시도한 값을 깜빡하고 다시 넣었을 때 띄워주는 경고창이다. tries 배열이 포함하고 있는애들을 걸러준다.
- 이런 검증값이 모두 통과되면 return true가 실행된다.
- true값은 if문의 조건식에 넣을 수 있음. 그러면 위에서 만든 checkInput(value);를 if 문을 만들어 그 안의 조건식으로 넣어줄 수 있게 된다. 만약 function checkInput 의 조건식에 걸리게 된다는건 새로 만든 if문에서 true가 아닌 false가 나오는 것이라고 보면되니까, 에러가 발생했을 상황에 실행될 else 문을 만들 수 있다.
const tries = []; // 시도한 값이 들어가는 배열
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 true; // 모든 검증과정을 다 거치면 true가 나옴
} // 검사하는 코드
$form.addEventListener('submit', (event) => {
event.preventDefault();
const value = $input.value;
$input.value = '';
if(checkInput(value)){//입력값문제없음
tries.push(value); // tries 배열안에 값을 넣어서 중복된 애들을 거른다//
}else{ //에러있음
}
});
- checkInput 함수를 통해서 값이 들어오면 tries에 그 밸류값을 넣어준다. tries.push(value);
- html 문법으로도 사실 이렇게 값을 검사할 수 있는 기능이 있긴 하다. 하지만 잘 안쓰는 추세이고 자바스크립트로 확인하는게 더 좋다.