야구 게임이라고 아시나요? 스포츠 야구는 아니고요. 숫자 게임입니다. 다음 링크를 참조하세요!
수비자가 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개의 숫자를 반복적으로 뽑는다는 것은 알겠죠? 처음 보는 게 length와 splice일텐데요. 배열.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가 어떻게 바뀌나 확인해보세요.
코드가 첫 번째 것에 비해 압축되어있고 간결하죠? 두 번째 코드를 앞으로 사용하겠습니다. 더 좋은 알고리즘이 있다면 알려주시면 감사하겠습니다. 다음 글로 이어집니다!