Utility Village 숫자 야구는 서로 다른 4개의 숫자로 이루어진 비밀 코드를 7번 안에 맞추는 게임입니다. 이번 포스트에서는 게임 로직과 힌트 알고리즘을 설명합니다.
규칙
- 비밀 코드: 0~9 중 서로 다른 4개의 숫자 (첫 자리 0 허용)
- 추측: 마찬가지로 서로 다른 4자리 숫자 입력
- 결과: 스트라이크(숫자와 위치 모두 일치) / 볼(숫자는 있지만 위치 다름)
- 제한: 최대 7번의 시도, 최대 2번의 힌트
비밀 코드 생성
function createSecretCode(): string {
const digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const result: string[] = [];
for (let i = 0; i < 4; i++) {
const idx = Math.floor(Math.random() * digits.length);
result.push(digits[idx]);
digits.splice(idx, 1); // 선택된 숫자 제거로 중복 방지
}
return result.join('');
}
첫 자리에 0이 오는 경우도 허용합니다. "0123"은 유효한 비밀 코드입니다.
스트라이크/볼 계산
interface GuessResult {
strikes: number;
balls: number;
}
function evaluateGuess(secret: string, guess: string): GuessResult {
let strikes = 0;
let balls = 0;
for (let i = 0; i < 4; i++) {
if (guess[i] === secret[i]) {
strikes++;
} else if (secret.includes(guess[i])) {
balls++;
}
}
return { strikes, balls };
}
힌트 알고리즘: 정답 후보 역추적
게임 중 최대 2번 사용할 수 있는 힌트는 현재까지의 추측 기록과 일치하지 않는 숫자를 제외합니다.
서로 다른 4자리 숫자 조합의 전체 경우의 수는 다음과 같습니다.
10 × 9 × 8 × 7 = 5,040개
그러나 실제 비밀 코드는 어떤 숫자든 첫 자리가 될 수 있으므로 5,040개가 전부입니다. (단, 첫 자리가 0인 경우도 포함합니다.)
// 사전에 모든 가능한 정답 목록 생성 (5,040개 → 실제로는 중복 제거 후)
function createAllSecretCodes(): string[] {
const codes: string[] = [];
for (let a = 0; a <= 9; a++)
for (let b = 0; b <= 9; b++) { if (b === a) continue;
for (let c = 0; c <= 9; c++) { if (c === a || c === b) continue;
for (let d = 0; d <= 9; d++) { if (d === a || d === b || d === c) continue;
codes.push(`${a}${b}${c}${d}`);
}}}
return codes; // 5,040개
}
힌트 계산 과정
- 전체 5,040개 후보 중 추측 기록과 일치하는 결과를 내는 후보만 필터링
- 남은 후보들에서 한 번도 등장하지 않는 숫자 식별 = 제외 가능한 숫자
function findExcludableDigits(history: GuessEntry[]): number[] {
const allCodes = createAllSecretCodes();
// 추측 기록과 호환되는 정답 후보만 필터링
const candidates = allCodes.filter(candidate =>
history.every(entry => {
const result = evaluateGuess(candidate, entry.guess);
return result.strikes === entry.result.strikes &&
result.balls === entry.result.balls;
})
);
// 후보들에 등장하지 않는 숫자 = 정답에 없는 숫자
const presentDigits = new Set(candidates.flatMap(c => c.split('')));
return [0,1,2,3,4,5,6,7,8,9].filter(d => !presentDigits.has(String(d)));
}
예를 들어 "1234"를 추측해서 0S 0B가 나왔다면, 1, 2, 3, 4는 정답에 없는 것이 확정됩니다. 이런 경우 4개의 숫자를 한 번에 제거할 수 있습니다.
UI 구성
- DigitSlots: 4자리 입력 슬롯 — 숫자 키보드 또는 방향키로 조작
- GuessHistoryTable: 추측 기록과 S/B 결과 테이블
- HintDigitBoard: 0~9 숫자 보드에서 제외 가능한 숫자를 시각적으로 표시
7번 안에 4S 0B를 달성하면 성공입니다. 시도 횟수가 늘어날수록 힌트의 유효성도 높아집니다.
사이트 바로가기: https://utility.dreamurl.biz/minigame/number-baseball