⚠️ 글이 좀 길어질 것 같으니 결과는 여기서 확인
(아직 반도 완성 못함 주의😂)
wecode 사전스터디 3주차 (나 혼자 정한) 과제는 숫자 야구 게임 만들기!
우선, 구현하고 싶은 기능등을 README.md로 정리했다.
음.... 근데 이렇게 적는거 아닌것 같아...
README 처음 작성하다보니 주절주절 글이 엄청 길어졌다😂
thinking 부분은 삭제하는게 낫겠당.. 일단 이건 다음주에!!
아무튼 3주차에 구현한 기능은
- 1~9 사이의 중복되지 않는 무작위 정수 출력
- 이를 토대로 정답 체크 => 정답이면 Homerun!!
- 오답이면 스트라이크, 볼 체크해서 출력
- 기회 10번 이상 소모하면 Game Over
까지이당!!
(와 되게 별거없어 보이네 짱 힘들었는데😩)
아무튼 여기까지 구현하면서 힘들었던 점과 새로 배운 내용 정리!!
numberList.pop();
numberList.shift();
.pop()
= 배열의 맨 끝에 요소를 제거한다.
.shift()
= 배열의 맨 앞에 요소를 제거한다.
numberList.push();
numberList.unshift();
.push()
= 배열의 맨 끝에 요소를 추가한다.
.unshift()
= 배열의 맨 앞에 요소를 추가한다.
⇒ 하지만 pop과 shift는 차례대로 뽑기 때문에 랜덤이 아니다. 기각!!
const numberList = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const correctNumber = [];
for (let i = 0; i < 4; i += 1) {
const randomNumber = numberList.splice(
Math.floor(Math.random() * numberList.length), 1)[0];
correctNumber.push(randomNumber);
}
.splice()
= 배열의 특정 위치의 요소를 제거하거나 추가한다.
우와 정말이지 어렵고 낯설고 긴 코드다.....
이럴땐 무식한 방법이 짱이지, 하나하나 뜯어보자
☝️numberList.splice(Math.floor(Math.random() * numberList.length), 1)[0];
⇒splice
로 numberList 배열 안의 값을 제거한다.
.splice (수정할 배열 요소의 시작 위치, 삭제할 요소 갯수(0 = 삭제 안함), 추가할 요소) 의 형식으로 작성할 수 있다.var arr = [1, 2, 3, 4, 5]; arr.splice(3, 2); // arr = [1, 2, 3]; arr.splice(2, 0, 6, 7, 8); // arr = [1, 2, 3, 6, 7, 8];
위의 코드를 다시 보면, 뭔가 복잡해 보이지만 결국 numberList.splice(시작 위치(Math.random() 함수로 랜덤 위치 지정), 1(숫자를 1개만 제거)) 이다!
✌️numberList.splice (Math.floor(Math.random() * numberList.length), 1)[0];
⇒ Math.random 으로 뽑은 소수값에서 소수점 이하는 버림한다.💁🏻♀️ 소수점을 없애는 메서드
Math.floor()
: 소수점 이하를 버림 한다.
Math.ceil()
: 소수점 이하를 올림 한다.
Math.round()
: 소수점 이하를 반올림 한다.
여러 메서드 중에서 Math.floor()를 사용하는 이유는?
우리가 얻고자 하는 값은 1~9 사이의 중복되지 않는 랜덤한 정수! 즉, 다시 말해 numberList 배열 안에 있는 숫자들이다.
컴퓨터는 위치를 셀때 0부터 카운트하기 때문에 0~8까지의 랜덤 정수를 splice의 시작 위치에 넣어줘야 한다.
그런데, Math.ceil() 을 사용하면 0.xx가 올림 되어 1~8 사이의 정수가 되어버리고
Math.round() 를 사용하면, 0~8까지의 정수가 나오지만, 0과 8이 나올 확률이 낮아진다.
0이 나오는 값: 0 ~ 0.4999
1이 나오는 값: 0.5 ~ 1.4999
...
7이 나오는 값: 6.5 ~ 7.4999
8이 나오는 값: 7.5 ~ 7.999
🤟numberList.splice(Math.floor(Math.random() * numberList.length), 1)[0];
⇒Math.random()
은 0 이상 1 미만의 랜덤한 소수값을 반환하는 함수이다.
여기에* numberList.length
를 곱해주어, 0 이상 numberList 배열의 갯수 (처음은 9) 미만의 랜덤 소숫값을 얻을 수 있다.
앞 과정에서 소수점 이하는 버려지기 때문에 (처음은 0~8, 그 다음은 0~7) 사이의 랜덤한 정수를 얻을 수 있다.
👉 혹은* 9
를 해주어 0이상 9미만의 랜덤 소수값을 얻어도 된다. 단, 이 경우에는 splice를 반복할때마다 numberList 배열에 남아있는 숫자는 하나씩 줄어드는데, splice의 시작 위치는 계속 0~8까지 이기 때문에undefined
값이 뽑힐 수 있다.
그래서 이렇게* 9
를 해줬을 경우에는 이렇게 코드를 작성해주면undefined
값이 사라진다.(Math.random() * (9 - i)), 1)
numberList.splice(Math.floor(Math.random() * numberList.length), 1)[0];
splice
는 기존 배열에서 분리한 값을 배열로 다시 반환하는 메서드이다.
즉, 실컷 splice를 돌려도, 결과값은 [2, 5, 6, 8] 이 아닌, [2], [5], [6], [8]이 되는것.
이 때, 마지막에[0];
을 붙여주면 해당 배열에서 0번째 값만 반환할 수 있다.
([2]의 0번째 값이니까 그냥 숫자 2!!)
특정 이벤트가 발생했을 때, 특정 함수를 실행하게 한다!
gameForm.addEventListener("submit", function callBack(event) {
event.preventDefault();
const correct = gameInput.value;
});
이벤트를 실행할 요소.addEventListener("이벤트 종류", function 함수명(생략 가능) (event) { }); 의 형식으로 작성하며, { } 안에는 이벤트가 발생했을 때 실행할 내용을 적는다.
예제에서
"submit"
칸에 들어갈 수 있는 다른 event 종류
event 내용 blur 포커스를 다른곳으로 옮길 경우 click 링크나 폼의 구성원을 클릭할 때 change 선택값을 바꿀 경우 dbclick 더블 클릭할 때 dragdrop 마우스를 누른채 움직일 경우 focus 포커스가 위치할 경우 mouseover 마우스가 올라올 경우 mouseout 마우스가 떠날 경우 mousedown 마우스를 누를 경우 mousemove 마우스를 움직일 경우 mouseup 마우스를 눌렀다 놓을 경우 keydown 키보드를 눌렀을 때 keyup 키보드를 눌렀다 떼는 순간 keypress 키보드를 눌러 어떤 텍스트가 작성되는 순간 select 입력 양식의 하나가 선택될 때 submit 폼을 전송하는 경우 load 페이지, 윈도우가 브라우저로 읽혀질 때 unload 현재의 브라우저, 윈도우를 떠날 때 error 문서나 이미지에서 에러를 만났을 때 reset 리셋 버튼이 눌렸을 때 move 윈도우나 프레임을 움직일 경우 resize 윈도우나 프레임 사이즈를 움직일 경우
event.preventDefault();
⇒ 이벤트의 전파를 막지 않고 이벤트를 취소할 수 있다.
"submit" 이벤트는 폼을 전송할때, 즉 매번 정답을 입력할 때 마다 새로고침 되는 기본 동작을 갖고 있기 때문에 이벤트를 취소하는 event.preventDefault();
를 입력한다.
addEventListener는 사실 아직도 이해를 못했당... 다음에 다시 정리하기로!!
if (guess === answer.join("")) {
gameHint.textContent = "Homerun!!";
} else { 어쩌구 저쩌구
}
guess
⇒ 정답자가 입력한 정답 ⇒ input
으로 정보를 받아옴 ⇒ 문자열로 저장
answer
⇒ 1~9까지의 numberList 배열에서 랜덤으로 뽑은 숫자 4개 ⇒ splice
메서드로 뽑았기 때문에, 배열로 저장
guess
와 answer
를 비교해야 하는데, 둘의 type이 다르기 때문에 guess에 어떤 값이 와도 무조건 불일치!
비교하기 전에 먼저 둘을 같은 type으로 바꿔줘야 한다!
이럴때 사용할 수 있는 Array.join()
: 배열을 문자열로 변환
test = ["A", "B", "C"];
test.join(); // "A,B,C"
test.join(''); // "ABC"
test.join('-'); // "A-B-C"
test.join('/'); // "A/B/C"
join( ) 을 비우면 콤마 , 로 구분하고 다른 문자를 넣으면 입력한 문자로 구분된다.
answer
은 배열,guess
는 문자열이다.
배열끼리의 비교는 복잡하고 어렵다는 느낌이어서,join
메서드를 이용해answer
을 문자열로 변경후 비교해주었다.
(시간나면 배열끼리의 비교도 정리하는걸로!)
for (let i = 0; i < 3; i += 1) {
if (Number(guessList[i]) === answer[i]) { //스트라이크 체크
strike += 1;
} else if (answer.indexOf(Number(guessList[i])) > -1) { //볼 체크
ball += 1;
}
if (Number(guessList[i]) === answer[i])
else if (answer.indexOf(Number(guessList[i])) > -1)
guessList
= 문자열을 조각내서 만든, 문자가 모여있는 배열 (ex : ["1", "3", "5", "8"])
answer
= 1~9까지의 숫자 배열에서 무작위로 뽑아낸, 숫자 배열 (ex : [1, 3, 5, 8])
그래서 guessList[i]) === answer[i]
만 비교하면 type이 다르기 때문에 무조건 불일치!
비교하기 전에 먼저 둘을 같은 type으로 바꿔줘야 한다!
이럴때 사용할 수 있는 Number()
: 문자열을 숫자로 변환
(숫자로 변환할 수 없는 값인 경우 NaN
를 반환한다.)
처음엔 이렇게 작성했지만,
Number()
가 계속 반복되서 나중에map
메서드를 이용해guessList
(문자열) 자체를 숫자 배열로 바꿔주었다.
이 내용은 2편에 정리했음!
Number 배우면서 뽀너스로~!!!
String은 말 그대로 숫자를 문자열로 변환해준다.
if (string[i] === String(number[i]))
let guessList = guess.split("");
split
은 반대로, 문자열을 배열로 바꿔준다.
정확히 말하자면 바꿔주는게 아니라, 문자열을 특정 기준으로 자르고 그 값을 배열로 반환한다.
guess
에 정답자가 4자리의 숫자를 입력한다.
예를들어 1234 를 입력하고 submit 하면.... 이 데이터는 우리에게 어떻게 전송될까?
정답은 "1234"!! 그냥 문자열 통으로 도착해버린다.
정답자가 입력한 정보를 guessList
라는 배열로 저장하고 싶다. (정답 배열과 비교해야 하니까!)
그럼 우선 "1234"를 "1", "2", "3", "4" 총 4개의 문자로 잘라줘야 한다.
test = "A는 B가 C"; //가운데 스페이스가 있음
test.split(""); // "A", "는", " ", "B", "가", " ", "C" (스페이스까지 출력)
test.split(" "); // "A는", "B가", "C"
test.split("",2); // "A", "는"
split( ) 에 ("")를 입력하면 모든 단어, 공백을 분리해서 배열로 만든다.
(" ")는 띄워쓰기(스페이스) 한 부분을 기준으로 자른다.
("",2) 두번째 인자에 숫자를 넣으면, 그 수만큼만 문자를 분리한다. (나머지 남은 문자는 반환되지 않는다.)
for (let i = 0; i < 3; i += 1) {
if (Number(guessList[i]) === answer[i]) { //스트라이크 체크
strike += 1;
} else if (answer.**indexOf**(Number(guessList[i])) > -1) { //볼 체크
ball += 1;
}
스트라이크, 볼 체크할때 사용한 함수이다.
우선 스트라이크 체크 부분은 각 배열끼리 === 로 비교하는 거니 넘어가고, 볼 체크가 상당히 까다로웠다ㅠ
else if (answer.indexOf(Number(guessList[i])) > -1)
.indexOf 함수는, 앞에 지정된 문자열에서 특정 문자의 위치를 알려주는 함수이다.
쉽게 말하면 문자열 왼쪽부터 읽어 나가다가, 특정 문자를 만나면 해당 위치를 반환하는데 첫번째는 당연히 0으로 반환하고, 두번째는 2, 그다음은 3 이런식으로 반환한다.
만약, 특정 문자를 만나지 못하면 그때는 -1 을 반환한다.
이를 이용해서 함수 뒤에 > -1
을 붙여주면, 특정 문자를 만나지 못하는 경우는 제외할 수 있다.
배열의 위치와 값이 모두 일치하는 경우는 앞의 if
에서 스트라이크 체크로 걸려서 따로 빠지니까, .indexOf > -1 로 나오는 값은 자연스럽게 볼이 된다!!
우선 3주차에 구현한 부분은 이정도...!
이제 제일 큰 숙제가 남았당....
매 시도마다 도전한 정답과, 힌트를 input 창 밑에 log로 쭈르륵 나오게 하고 싶은데 이건 정말 내 능력 밖이고😂 구글링과의 사투인것.....
이거 개발 공부 이전에 영어 공부가 더 시급한거 아닐까....? 엉엉😭
혹시 잘못되었거나 더 좋은 방향으로 작성 가능한 코드는 댓글 남겨주시면 정말 감사하겠습니다!!