게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌7 - JavaScript - 9년 전 등록 / 7년 전 수정

야구게임 만들기(1)

야구 게임이라고 아시나요? 스포츠 야구는 아니고요. 숫자 게임입니다. 다음 링크를 참조하세요!

나무위키 숫자야구

수비자가 0 ~ 9 중에 4 개의 수를 중복되지 않게 뽑으면 공격자가 10번의 기회 안에 4개의 수를 맞추는 겁니다. 매 시도마다 4개의 수 중 맞춘 개수를 볼이나 스트라이크로 알려줍니다. 숫자만 맞으면 , 숫자와 자리까지 다 맞으면 스트라이크입니다.

예를 들어 수비자가 1357이라는 수를 정했습니다. 공격자가 숫자를 찍어야겠죠? 1479라고 찍었습니다. 이 때 1은 숫자와 자리가 맞기 때문에 스트라이크, 7은 숫자만 맞기 때문에 볼입니다. 그래서 수비자는 원 스트라이크 원 볼이라고 알려주는 거죠.

10번에 기회 안에 수비자가 불러주는 힌트로 맞추면 됩니다. 어려울 거 같다고요? 막상 해보면 별로 안 어렵습니다.

이 게임을 자바스크립트로 짜보죠. 아직 그래픽도 안 배웠고, 간단한 프로그래밍밖에 못 할 때는 텍스트 게임 정도가 적당할 것 같네요.

먼저 수비자(컴퓨터)가 랜덤한 숫자를 짜는 것을 만들어야겠죠? 답을 보기 전에 한 번 생각해보세요. 알고리즘이라는 게 여기서 나옵니다. 알고리즘이란 어떤 문제를 해결하기 위한 규칙과 절차입니다. 어떠한 알고리즘을 만들어야 랜덤한 4 개의 숫자를 중복없이 짤 수 있을까요?

한 문제를 해결하기 위해 여러가지 접근법이 있는데요. 각각의 접근법이 모두 다 알고리즘입니다. 

제가 쉽게 생각해본 것(무식한 방법)으로는 0~9까지의 숫자(첫 번째)를 하나 뽑고, 두 번째 자리 숫자를 뽑을 때 첫 번째와 같으면 다시 뽑고, 다르면 세 번째 자리 숫자로 넘어가는 겁니다. 세 번째 자리 숫자도 첫 번째, 두 번째 숫자와 비교한 후에 다를 때 이제 네 번째 숫자를 뽑는거죠. 

다른 방식으로는 배열에 0~9까지 숫자를 넣고, 하나씩 뽑습니다. 배열에서는 숫자를 뽑을 때마다 숫자가 사라지기 때문에 겹칠 일이 없습니다. 만약 첫 숫자를 9를 뽑으면 이제 배열에는 0~8까지밖에 없습니다. 다음에 뽑을 때 9를 뽑을 일은 없겠죠.

두 번째 방법이 훨씬 나은 것 같네요. 물론 첫 번째가 성능이 더 좋을 수도 있습니다. 테스트해보기 전에는 모르는 거에요. 하지만 두 번째가 보기가 훨씬 좋네요. 둘 다 만들어보죠.

먼저 알고 넘어가야할 것이 Math.random() 함수입니다. Math 객체의 random이라는 메소드입니다. Math 객체는 자바스크립트에서 기본적으로 제공하는 수학 객체입니다. 수학적인 계산을 하고 싶을 때 사용하면 됩니다. 링크에 더 자세한 설명이 있습니다.

아무런 인자 없이 Math.random(); 을 하면 0<=x<1의 x값이 나옵니다. 그 값에다 10을 곱하면, 0<=x<10의 값이 나오겠죠? 그 값을 내림해주면 0~9사이의 정수가 나옵니다. 내림하는 함수는 Math.floor(숫자)입니다. 

Math.random(); // 0~1 사이의 유리수
Math.random() * 10; // 0~10 사이의 유리수
Math.floor(Math.random() * 10); // 0~9

콘솔창에 바로 쳐보세요. 0~9 사이의 수가 나오죠? 갑자기 Math같은게 나와서 어려우면 질문을 남겨주세요!

첫 번째 방법입니다. number라는 배열에 뽑은 수를 넣을겁니다. 배열은 첫 번째 자리가 0부터 시작한다고 했죠? 첫 번째 자리에 Math.random한 값을 넣습니다.

var number = [];
number[0] = Math.floor(Math.random() * 10);
do {
  number[1] = Math.floor(Math.random() * 10);
} while(number[1] === number[0])
do {
  number[2] = Math.floor(Math.random() * 10);
} while(number[2] === number[0] || number[2] === number[1])
do {
  number[3] = Math.floor(Math.random() * 10);
} while(number[3] === number[0] || number[3] === number[1] || number[3] === number[2])

do ~ while은 거의 쓸 일이 없다고 전 시간에 했는데, 바로 써버렸죠... 죄송합니다... 엄청나게 코드가 복잡해졌네요. 

잘 보시면 처음에 number[0]을 먼저 뽑습니다. 그 후 number[1], number[2], number[3]은 반복적으로 앞 수들과 같지 않을 때까지 숫자를 새로 뽑습니다. 콘솔창에 복사에서 실행해보시고, number을 쳐보면 뽑힌 숫자가 나올겁니다. 이게 첫 번째 알고리즘이고요. 두 번째 알고리즘은 짧지만, 어렵습니다.

var list = [0,1,2,3,4,5,6,7,8,9];
var number = [];
for (var i = 0; i < 4; i++) {
  var select = Math.floor(Math.random() * list.length);
  console.log('list', list, 'number', number, 'length', list.length);
  number[i] = list.splice(select, 1)[0];
}

처음에 숫자 list가 있고, for문으로 4개의 숫자를 반복적으로 뽑는다는 것은 알겠죠? 처음 보는 게 lengthsplice일텐데요. 배열.length는 말 그대로 배열의 길이입니다. list.length는 0부터 9까지 숫자 10개니까 10입니다. 그 후 반복문을 돌 때마다 배열에서 숫자가 하나씩 빠지니까 9,8,7 이렇게 줄어들고요.

배열.splice(시작위치, 길이)는 배열을 조각내는 겁니다. 배열의 시작위치(0부터 시작)부터 주어진 길이만큼 잘라서 원래 배열에서 빼냅니다. 그리고 빼낸 배열을 반환(return)합니다. 예시에서는 list.splice(select, 1)이었는데 select는 랜덤한 값이니까, 랜덤 값에서 1개의 길이만큼 배열에서 빼낸 것입니다. 즉 0~9까지 있었을 때 랜덤 값이 4가 나오면 [0,1,2,3,4,5,6,7,8,9] 다섯 번째에 있는 [4]를 빼내는 거죠. 4라는 값으로 빠지는 게 아니라, [4]라는 배열로 빠집니다. list.splice(select, 2)였으면 [4,5]가 빠졌겠죠? 

빼낸 배열이 [4]이기 때문에 4의 값만 뽑기 위해서 splice 뒤에 [0]을 붙여 list.splice(select, 1)[0] 를 해줍니다. 배열의 첫 번째 값을 뽑기 위해서 [0]을 붙인 겁니다. 이렇게 하면 이제 list는 4가 빠져 [0,1,2,3,5,6,7,8,9]가 되고 number==[4], list.length==9 입니다. 이 상태로 다음 반복문으로 가서 4개를 뽑을 때까지 반복됩니다.

쉽게 눈으로 보시라고 console.log를 추가했습니다. list 배열과, number 배열 그리고 list.length가 어떻게 바뀌나 확인해보세요.

코드가 첫 번째 것에 비해 압축되어있고 간결하죠? 두 번째 코드를 앞으로 사용하겠습니다. 더 좋은 알고리즘이 있다면 알려주시면 감사하겠습니다. 다음 글로 이어집니다!

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

20개의 댓글이 있습니다.
3년 전
첫 번째 값을 뽑기 위해서 [0]을 붙인다는게 이해가 안가요 ㅠㅠㅠㅠㅠㅠㅠㅠㅠ [] 이거는 헤당 위치 베열에 객체? 그걸 넣을떄 쓰는거 아닌가요
3년 전
두번째 예시 do - while 문을 사용해서 랜덤값을 넣고 조건을 비교하는데 === 은 같다 라는 조건이라고 배웠습니다.
근데 왜 이 3개의 do while 문들은 같지 않을때 까지 값들을 뽑는다는건지 이해가 안됩니다. 같을때 까지 뽑아야 do while 무을 탈출하는게 아닌가요?? 제가 잘못이해한 부분이 있을까요?
3년 전
같지 않을때까지 값을 다시 뽑는다는 것은 다른 말로 하면 같을 때는 계속 다시 뽑는다는 겁니다. 그러니까 while 안에 ===이 들어가는 겁니다.
4년 전
const defender = new Set();
while (defender.size \u003c 4) {
defender.add(Math.floor(Math.random() * 10));
}
저는 set의 특성을 이용해서 짜봤습니다. 잘못된 부분이 있으시면 댓글로 달아주시면 감사하겠습니다.
5년 전
이렇게 써봤는데 잠재적 오류가 있을까요?

const answer = [];

while(answer.length\u003c4){
let tmp = Math.floor(Math.random()*10);
console.info(tmp);

if(answer.find(item => item===tmp)){
continue;
} else {
answer.push(tmp);
}
};
5년 전

var number =[], i, ii, chk;
while(true){
chk=true;
for (i=0; i\u003c4; i++){
number[i]=Math.floor(Math.random()*10);
for(ii=0; ii\u003ci; ii++) { if (number[i] == number[ii]) chk = false; }
}
if (chk) break;
}
console.log(number);
5년 전
알고리즘 짜봣는데
if 조건문에 배열에 음수가 들어간 경우가 생기지만,
결과는 잘 나옵니다. 이런 방법을 써도 될까요?

var number2 = [];
number2 [0] = Math.floor(Math.random()*10);
for(var i = 1; i\u003c4; i++){
while(true){
number2[i] = Math.floor(Math.random()*10);
if(number2[i] !== number2[i-1] && number2[i] !== number2[i-2]
&& number2[i] !== number2[i-3]){
break;
}
}
}
6년 전
질문이 있어서요.
2번째 방법에서, list.length를 곱하면,
두번째 뽑는 수는 8이하, 세번째는 7이하,
네번째는 6이하만 가능하지 않나요?
처음에 2, 다음에 3, 다음에 5를 뽑고,
마지막 네 번째 수로 9를 뽑으려고 할때 불가능할 것 같습니다.
네번째라 list.length는 7이고, 생성된 최대 수는 6이 될테니까요.
좋은 글 감사합니다.
6년 전
list.length가 7인 것과 뽑는 수는 관계가 없습니다. 뽑을 배열도 개수가 줄어 7개입니다. 잘 생각해보세요.
7년 전
2번째 방법에서 number[i]=list.splice(select,1)[0];의 경우 "자바스크립트에서는 배열안에 배열을 넣을 수 있다는 부분"을 리마인드 할 수 있게 추가해주시면 조금더 이해하기 쉬울 것 같습니다. 언제나 강좌 잘 보고 있습니다. 감사합니다.
7년 전
배열 안에 배열을 넣을 수 있는 것은 맞지만, number[i]=list.splice(select,1)[0] 코드는 배열 안에 배열을 넣는 코드가 아닙니다.
7년 전
제가 잘못 적었네요... number[i]=list(select,1) 코드를 실행해보니 number배열에 배열이 저장되더군요. 배열안에 배열을 넣을 수 있다는걸 깜빡해서 한참 헤멘지라 댓글을 달았습니다 ㅠㅠ
7년 전
많은 분들이 이미 질문해주셨는데도 조금 이해가 안돼서 질문드립니다

number[i] = list.splice(select, 1)[0];
이게 i가 0~3까지 진행이 되는 거면
number[0] = [4], number[1] = [2], number[2] = [6], number[3] = [9]
number = [4, 2, 6, 9]
대충 이런 식으로 진행되는 거 아닌가요?
number[i] 이 부분이 이미 배열을 의미하는 거 같은데 뒤에 왜 또 [0]이 붙는지 이해가 잘 안됩니다ㅜㅜ
[i]나 [0] 둘 중에 하나만 붙어야 될 거 같은데 둘 다 붙는 게 이해가 안됩니다
number[0] 안에는 어차피 한개의 배열만 있는 것 아닌가요?
7년 전
number과 list.splice(select, 1)은 배열입니다. 뒤에 [0]을 붙이면 배열 안의 요소를 찾는 거고요. number[i]는 number의 i 번째 인덱스를 의미합니다. number의 i번째에 list.splice(select, 1) 배열의 첫 번째([0]) 요소를 넣으라는 뜻입니다.
7년 전
아 지금 실험해보다가 이해가 되었습니다!ㅜㅜ
list.splice(select, 1)[0]에 [0]을 써주지 않으면
number에 새로운 배열이 들어가게 되는군요
number; // [[4],[2],[6],[9]] 이런 식으로요
근데 저 안에 들은 배열이 정의되지 않아서
number; // [Array(1),Array(1),Array(1),Array(1)] 이라고 뜨나 봅니다
혹시 저처럼 이해가 안되는 분들을 위해 답글 달아놓겠습니다

var list1 = [1,2,3,4];
var list2 = [5,6,7,8];
list1[0] = list2.splice(1,1); // [6]
list1[0] = list2.splice(1,1)[0]; // 6
이 두 개로 해보니 이해가 됩니다
7년 전
좀 더 쉽게 설명하자면 배열을 주머니라고 생각해보세요. number과 list.splice(select, 1)이라는 주머니 두 개가 있는데, list.splice(select, 1) 주머니의 첫 번째 물건을 number 주머니의 i 번째 칸에 넣는 것입니다.
7년 전
이해가 드디어 되었습니다 감사합니다
7년 전
여기가 이해가 안돼서 질문드립니다
var select = Math.floor(Math.random() * list.length);
랜덤 0 ~ 1 값 * 10 (처음 시작 갯수) = 내림 값 출력을 select에 저장 이거 아닌가요?
랜덤값 0.4 * 10(배열갯수) 이거면 select값은 4가 나와야 하는거 아닌가요?
다른 값이 나와서 헷갈리네요
첫번째꺼는 랜덤값에 10 곱하니까 정수가 나와서 알겠는데
두번째꺼는 왜 계속 10곱하는게 아니라 배열값으로 곱하는지 이해가 안되네요..
7년 전
첫 번째 것에서 하나를 이미 뽑았죠? 이제 남은 9개 중에서 고르는 것입니다. 두 번째 것은 0~1 * 9를 해야 0부터 9 사이의 값이 나옵니다. 세 번째 것은 남은 8개 중에서 고르는 것이라 0~1 * 8을 해야 0부터 8 사이의 값이 나오고요.
7년 전
아~ 이해 됐습니다. 감사합니다~~
7년 전
글 잘 보고 있어요. 기초부터 다시 보니까 놓치거나 잊거나 중요하게 생각 안 했던 것을 되짚어볼 수 있어서 좋네요.

두번째 접근법에서
number[i] = list.splice(select, 1)[0]; 보다
number = number.concat(list.splice(select, 1)); 이 좀 더 깔끔할 것 같습니다.
7년 전
이 부분 입문자분들의 질문이 엄청 많더군요. 직관적이지 않나봅니다. 제의해주신 코드가 더 나아 보입니다! push를 쓰는 것까지도 한 번 고려해보고 수정해보겠습니다.
7년 전
두번째 방법과 관련해 질문이 있습니다.

" 예시에서는 list.splice(select, 1)이었는데 select는 랜덤한 값이니까, 랜덤 값에서 1개의 길이만큼 배열에서 빼낸 것입니다. 즉 0~9까지 있었을 때 랜덤 값이 5가 나오면 [0,1,2,3,4,5,6,7,8,9] 다섯 번째에 있는 [4]를 빼내는 거죠. 4라는 값으로 빠지는 게 아니라, [4]라는 배열로 빠집니다. list.splice(select, 2)였으면 [4,5]가 빠졌겠죠? "

위에서 설명한대로 해보면, 랜덤값이 5가 나올때 list.splice(select, 1)에서 다섯번째에 위치한 [4]가 아니라 여섯번째에 위치한 [5]가 빠집니다. 아마 인덱스 5이기 때문에 "offset 5", 즉 시작 인덱스부터 다섯번째에 있는 [5]가 나오는 것 같네요. 확인부탁드립니다.
7년 전
랜덤값 4입니다. 오타 맞네요.
8년 전
정말 쉽게 설명해주시네요. 잼있어요 ㅎㅎ
8년 전
var select = Math.floor(Math.random() * list.length); 이 코드에서 왜 list.length를 사용하는지 궁금합니다~ Math.random()은 0\u003c=x\u003c1 값이 나오고 여기에 10을 곱해서 0\u003c=x\u003c10으로 만들어준다고 했는데 갑자기 실제 게임에서는 왜 랜덤이 for문으로 실행될때마다 list.length로 각각 변경되는 숫자를 곱해주는 건지 이해가 되지 않습니다~ 어차피 Math.random()는 0\u003c=x\u003c1의 랜덤한 소수가 나올꺼고 앞자리를 변경해주기 위해서는 10을 곱해주기만 하면 되는 걸텐데 리스트의 길이값을 곱해주는 것은 어떤 의미가 있는지 궁금합니다~
8년 전
처음에 10개에서 하나를 뽑으면 9개가 됩니다. 다음에는 9개 중에서 하나를 뽑아야하기 때문에 매번 1이 줄어드는 list.length를 사용한 겁니댜.
8년 전
console.log는 어떻게 보는 건가요? Notepad++ 사용중입니다.
8년 전
브라우저에서 F12를 누르면 콘솔 메뉴가 있습니다. 거기에서 log가 표시됩니다
8년 전
착각했네요 ㅎㅎ 나이스합니다
8년 전
책에서도 알려주지 않았던 왕초보부터 알려주는 강좌는 요기가 처음인것같아요. 매일매일 여기보면서 공부하고있습니다. 좋은자료감사합니다!! 하나 질문이 있는데요 .윗분도 질문을 해주셨는데 [0]이라는게 어떤 의미로써 작동되는지가 궁금합니다 .ㅠㅠ
8년 전
list.splice(select, 1)을 하면 [4] 이렇게 배열로 나옵니다. 배열에서 첫 번째 요소를 선택하려면 [0]을 붙이죠? 그래서 [0]을 한 겁니다. 숫자 4만 선택하려고요!
8년 전
아아! 이해됬어요!! 빠른 답변도 감사하고 좋은 강좌도 감사해요ㅎㅎ
8년 전
이해 안 가는 부분이 있으면 언제든지 질문해주세요. 더 쉽게 설명하도록 노력하겠습니다.
8년 전
다시 보니까 강좌3 - 객체에서 배열에 대한 설명이 부족했던 것 같네요. 바로 보충하겠습니다!
8년 전
아무튼 좋은 강좌 잘보고 갑니다~
8년 전
감사합니다!
8년 전
보니까 number[i] = list.splice(select, 1)[0]; 이 구문에서 [0]이게 없어도 정상적으로 작동이 되네요 저부분대문에 좀 이해가 안됐거든요 List.splice(select//4라고하면 3임), 1 즉 3이 제거되어서 반환됨. [0]? 이게 왜 들어가는지.. 이해가 잘 안되네요!
8년 전
list.splice(select, 1)을 하면 결과값이 배열에 담깁니다. 예를 들면 select가 5일 때, [4] 이렇게요. 따라서 list.slice(select, 1)[0]을 해야 4가 반환됩니다.
8년 전
덕분에 포스팅을 보완했습니다! 감사합니다!
8년 전
그나저나 웹이 진짜 깔끔하게 잘만들어 졌네요 ㅎㅎ