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