숫자야구 게임
코드배틀
In English, please.
게임의 규칙을 확인하시고, 아래 함수들을 채운 후, 대결상대를 선택하고, 게임시작 버튼을 누르면 배틀이 시작됩니다.
상대와 내가 각각 자신의 숫자(서로다른 3개의 숫자로 이뤄진 3자리수)를 정하고, 먼저 상대의 숫자를 맞추면 승리하는 게임입니다. 단, 동시에 맞춘 경우에는 더 큰 숫자를 가진 쪽이 승리합니다.
round가 시작될 때 자신의 숫자를 정해서 리턴하고, 매 turn이 시작될 때 상대의 숫자를 예측한 숫자를 리턴하면, turn이 종료될 때 예측한 숫자와 상대의 숫자를 비교한 결과값을 전달받게 됩니다.
3자리 숫자 각각에 대하여, 숫자와 자리가 모두 일치하면 strike, 숫자는 일치하는데 자리가 다르면 ball입니다.

나의 코드 (JavaScript)

대결상대

온라인 대전은 로그인한 회원끼리 진행합니다. 로그인하면 참여할 수 있습니다.
나 또는 상대방의 화면이 Visible 상태가 아니면 통신 속도가 느려집니다.
TURN
COUNT
0
ROUND
WIN
0
LOSE
0
GAME
WIN
0
LOSE
0

게임 로그

최근 로그가 위에 표시됩니다.

결과를 받고 무엇을 지울 것인가

숫자야구는 정보를 모으는 게임입니다. 서로 다른 세 자리 숫자는 720가지뿐이고, 매 턴 스트라이크와 볼이 돌아옵니다. 그 결과 하나가 후보 수백 개를 지웁니다. 지우지 않으면 720가지를 계속 찍는 것과 같습니다. 아래는 지우는 방식 셋과 각각이 실제로 낸 성적입니다.

1. 결과를 안 쓰고 계속 찍는다 (기본 샘플)

화면에 실려 있는 기본 코드가 이렇게 합니다. 매 턴 무작위로 세 자리 숫자를 만들고, 이미 낸 것만 피합니다. 스트라이크와 볼이 돌아오는데 아무것도 하지 않습니다.

RANDOM 반반 LEVEL 1·2·3 전패

720개를 무작위로 훑으면 30턴 안에 맞을 확률이 낮습니다. RANDOM과는 반반인데 둘 다 결과를 안 쓰기 때문입니다.

2. 결과와 어긋나는 후보를 지운다

이 게임의 기본기입니다. 720개를 다 들고 있다가, 방금 낸 추측을 각 후보에 대어 봅니다. 그 후보가 정답이었다면 나왔어야 할 결과와 실제로 받은 결과가 다르면 그 후보는 정답이 아닙니다. 지웁니다.

012 → 1S 0B 정답에 0·1·2 중 하나가 그 자리에 있다. 이 결과와 안 맞는 후보는 전부 버린다.
345 → 0S 1B 3·4·5 중 하나가 있지만 자리는 틀렸다. 이 한 줄로도 후보가 크게 줄어든다.
var left = null;
var lastGuess = null;

// 서로 다른 세 자리 숫자 720가지. 10 x 9 x 8 이다.
function allNumbers() {
	var out = [];
	var a, b, c;

	for (a = 0; a <= 9; a += 1) {
		for (b = 0; b <= 9; b += 1) {
			for (c = 0; c <= 9; c += 1) {
				if (a !== b && b !== c && a !== c) {
					out.push("" + a + b + c);
				}
			}
		}
	}
	return out;
}

// 추측을 어떤 정답에 대었을 때 나오는 [스트라이크, 볼]. 규칙 그대로다.
function judge(guess, secret) {
	var strikes = 0, balls = 0;
	var i, j;

	for (i = 0; i < 3; i += 1) {
		for (j = 0; j < 3; j += 1) {
			if (guess.charAt(j) === secret.charAt(i)) {
				if (i === j) { strikes += 1; } else { balls += 1; }
			}
		}
	}
	return [strikes, balls];
}

function onGameStart() {}

function onRoundStart() {
	var pool = allNumbers();

	left = allNumbers();
	lastGuess = null;
	// 내 숫자는 아무거나 고른다.
	return pool[Math.floor(Math.random() * pool.length)];
}

function onTurnStart() {
	lastGuess = left.length ? left[0] : "012";
	return lastGuess;
}

// 방금 받은 결과와 어긋나는 후보를 모두 버린다. 이것이 이 게임의 기본기다.
function onTurnEnd(result) {
	var got = result.result;
	var kept = [];
	var i, r;

	for (i = 0; i < left.length; i += 1) {
		r = judge(lastGuess, left[i]);
		if (r[0] === got[0] && r[1] === got[1]) { kept.push(left[i]); }
	}
	left = kept;
}

function onRoundEnd(result) {}
function onGameEnd(result) {}

RANDOM 전승 LEVEL 1 반반 LEVEL 2·3 전패

RANDOM은 전승이고 LEVEL 1과 반반이 됩니다. 몇 턴이면 후보가 한두 개로 좁혀지니까요. 그런데 LEVEL 2·3에는 여전히 전패입니다. 상대도 같은 일을 하고 있고, 이 게임은 먼저 맞히는 쪽이 이기는 경쟁이기 때문입니다. 같은 속도로는 못 이깁니다.

3. 어느 쪽부터 훑을지 고른다

후보를 지우는 방식은 그대로 두고, 남은 후보 중 **무엇을 먼저 찍을지**만 바꿔 봅니다. 아래는 가장 큰 수부터 찍습니다. 코드에서 달라진 곳은 이 한 함수뿐입니다.

// 남은 후보 중 가장 큰 수를 찍는다. 순서만 바꾼 것이고 후보를 지우는
// 방식은 그대로다. 상대가 자기 숫자를 고르는 습관이 있다면, 그 습관 쪽을
// 먼저 훑는 것만으로 몇 턴을 아낀다.
function onTurnStart() {
	var best = left.length ? left[0] : "012";
	var i;

	for (i = 1; i < left.length; i += 1) {
		if (parseInt(left[i], 10) > parseInt(best, 10)) { best = left[i]; }
	}
	lastGuess = best;
	return lastGuess;
}

RANDOM 전승 LEVEL 1 반반 LEVEL 2 우세 LEVEL 3 열세

이 한 줄 차이로 LEVEL 2가 전패에서 우세로 바뀝니다. 60판을 10회 반복해 재면 승률이 55~73% 사이이고 중앙값이 65%입니다. 상대가 자기 숫자를 고르는 방식에 치우침이 있어서, 그쪽부터 훑으면 몇 턴을 아끼기 때문입니다. 반면 LEVEL 3에는 거의 통하지 않습니다. 특정 상대의 습관을 노린 것이므로 그 습관이 없는 상대에게는 이득이 없습니다.

4. 여기서부터는 당신 차례

두 갈래가 열려 있습니다. 하나는 추측을 더 좋게 하는 것입니다. 남은 후보를 결과별로 나눌 때 가장 큰 덩어리가 작아지는 추측을 고르면 최악의 경우가 줄어듭니다. 다른 하나는 아직 손대지 않은 쪽입니다. onRoundStart가 돌려주는 내 숫자도 전략입니다. 상대가 어떤 순서로 훑는지 알면 늦게 걸리는 숫자를 고를 수 있습니다. 위 코드를 에디터에 붙여 넣고 여기서부터 고쳐 보세요.

대전은 이렇게 돕니다

  • 코드배틀은 내가 작성한 코드와 상대가 작성한 코드가 일대일로 대결하는 방식이며, 언어는 JAVASCRIPT 입니다.
  • 코드를 작성하고, 대결상대를 RANDOM, LEVEL1, LEVEL2, LEVEL3, 온라인 상대 중에서 선택한 후 게임시작 버튼을 누르면 대결이 시작됩니다.
  • 대결은 game이며, 1 game은 31 round로 구성되고, 1 round는 숫자야구 한판을 의미하는데 누군가 상대 숫자를 맞추면 해당 round가 종료되고, 30번의 turn을 도는 동안 끝나지 않으면 더 큰 수를 가진 쪽이 승리하게 됩니다.

숫자야구는 훅이 여섯 개이고, onRoundStart가 서로 다른 세 자리 숫자를 문자열로 돌려줘야 합니다. 그것이 그 라운드의 내 숫자입니다. 위 2번 코드는 여섯 개를 모두 담고 있어 그대로 붙여 넣으면 돌아갑니다. 3번은 그 코드에서 달라진 함수 하나만 보여 줍니다.

성적은 상대별로 60판(라운드 1,860번)을 돌려 측정했고 저장소의 테스트가 실제 규칙 엔진으로 매번 다시 확인합니다. 상대 넷 모두에 무작위가 섞여 있어 판마다 달라집니다. 25판으로 재던 동안 같은 대결이 52%와 76% 사이를 오갔고, 그래서 60판으로 올렸습니다.