AX 노트 · 방법

블록 퍼즐 난이도 설계: 지지 않는 게임을 시뮬레이션 봇으로 고친 과정

트레이마다 세 조각을 모두 놓을 수 있게 보장하면, 해법을 찾는 플레이어는 절대 지지 않습니다. 그래서 그 보장은 처음 10트레이에만 둡니다.

작성 SugarMount 게임 개발 읽는 시간 8분 최종 업데이트

한판블록 클래식 모드는 처음 설계대로라면 게임 오버가 나지 않는 게임이었습니다. PRD 초안 그대로 만든 첫 공급 규칙(v1)에 트레이 전체를 계산하는 계획 봇을 붙였더니 80판이 전부 3,000수 상한까지 갔습니다. 사람이 해 보기 전, 2026년 10월 3일 봇 실험에서 나온 결과입니다. 출시본에 들어간 v7에서는 계획 봇 판당 중앙값이 122수, 탐욕 봇이 47수입니다.

숫자는 sugar-game 저장소의 공급 실험 기록(docs/block/lab.md, 2026-10-03 측정), PRD 검증 보고서, 엔진 코드와 테스트에서 가져왔습니다. 기준일은 2026년 10월 19일입니다.

목차 (7)
  1. "항상 놓을 수 있다"는 약속이 게임 오버를 지웠습니다
  2. 탐욕 봇과 계획 봇을 같은 시드로 돌렸습니다
  3. 결정에 쓴 지표 네 개
  4. 공급 규칙 v1~v7 비교
  5. AI의 첫 검증은 반대 방향을 권했습니다
  6. 다음에 확인할 것
  7. 자주 묻는 질문

"항상 놓을 수 있다"는 약속이 게임 오버를 지웠습니다

한판블록은 8×8 판에 조각 세 개를 받아 놓고, 가로나 세로 줄이 차면 지워지는 블록 퍼즐입니다. 이 세 조각 묶음을 트레이라고 부릅니다. 세 조각을 다 놓으면 다음 트레이가 나옵니다.

출발점이 된 PRD v2.0 초안의 7.1절은 이렇게 약속했습니다. "새 세 조각을 받을 때, 현재 판에서 그 세 조각을 모두 놓을 수 있는 순서가 적어도 하나 있다." 블록 공급이 억울하다는 반응을 막으려는 문장이었고, "배치 선택 때문에 중간에 막힐 수 있다"는 단서도 붙어 있었습니다.

이 단서는 배치를 잘못하는 사람에게만 해당합니다. 잘하는 사람에게는 이렇게 됩니다.

  1. 트레이를 받는 순간, 세 조각을 다 놓는 해법이 있습니다.
  2. 그 해법대로 두면 트레이가 빕니다.
  3. 다음 트레이도 같은 보장을 받습니다. 1번으로 돌아갑니다.

판이 끝나지 않습니다. 이 증명은 조각 가중치와 상관이 없어서, 어려운 조각을 많이 섞어도 결론이 바뀌지 않습니다(아래 표의 v3, v4).

탐욕 봇과 계획 봇을 같은 시드로 돌렸습니다

봇은 두 종류입니다. 엔진이 판을 64비트 정수 하나로 다루기 때문에, 판 평가도 비트 연산 몇 번으로 끝납니다.

두 봇이 쓰는 점수는 같습니다.

점수 = 지운 줄 × 10 − 3 × 구멍 − 0.3 × 찬 칸 + 2 × 큰 조각 자리
구멍 = 상하좌우가 모두 막힌 빈칸 수
큰 조각 자리 = 3×3 정사각형, 가로 4칸, 세로 4칸이 각각 들어갈 곳이 있는지 (0~3)

최종 비교에서는 버전마다 탐욕 봇 160판, 계획 봇 80판을 돌렸고, 판 시드는 모든 버전이 같습니다. 3,000수에 닿으면 판을 끊습니다. 봇은 사람의 모형이 아닙니다. 사람은 두 봇 사이 어딘가에 있다고 보고, 봇 숫자는 버전끼리 순위를 매기는 데만 씁니다.

결정에 쓴 지표 네 개

공급 규칙 v1~v7 비교

어려운 조각은 3×3 정사각형, 2×3 직사각형 두 방향, 4칸 직선 두 방향입니다. "압력"은 일정 수마다 이 조각들의 가중치를 기본값의 1배씩 더 올리는 규칙입니다.

버전공급 규칙탐욕 p50계획 p50계획 봇 상한 도달판정
v1PRD 그대로: 모든 트레이에서 세 조각 전부 보장56수3,000수80/80탈락
v2v1 + 빈칸이 28개 미만이면 3칸 이하 조각 가중치 3배결과 표에 없음폐기(판을 쉽게 만드는 방향)
v3v1 + 압력(30수마다, 최대 5배)결과 표에 없음60/60탈락
v4v1 + 압력(20수마다, 최대 8배)결과 표에 없음60/60탈락
v5v7 + 압력(30수마다, 최대 5배)44수80수0A/B 후보
v6v7 + 압력(20수마다, 최대 8배)44수82수0채택 안 함
v7처음 10트레이만 전부 보장, 이후에는 "한 조각 이상 놓을 수 있음"만 보장47수122수0기본값

v1에서 계획 봇은 판당 트레이 중앙값 1,001개, 점수 중앙값 26,772점을 찍었습니다. v7에서는 41트레이, 998점입니다. 수치가 남은 네 버전(v1, v5, v6, v7)에서 탐욕 봇은 한 판도 상한에 닿지 않았습니다.

v7을 고른 이유는 실력 차이입니다. 계획 122수 대 탐욕 47수로 2.6배인데, 압력을 얹은 v5와 v6는 이 차이가 1.8~1.9배로 줄어듭니다. 판이 짧으면 판이 끝나는 횟수는 늘지만, 잘하는 사람이 덜 보상받습니다. 그래서 v7로 출시하고 v5를 A/B 후보로 남겼습니다.

막대 그림: v1에서 계획 봇은 3,000수 상한까지 갔고, v7에서는 계획 봇 122수, 탐욕 봇 47수입니다.
v7은 계획 봇이 상한에 닿지 않으면서도 탐욕 봇과 2.6배 차이를 냅니다.

공급 비용은 걱정할 수준이 아니었습니다. 트레이 생성 p95는 표에 수치가 있는 버전 모두 0.2ms 이하였고, PRD가 둔 마지막 복구 단계(1칸 조각 세 개)는 Python 시뮬레이션 2,710트레이에서 한 번도 쓰이지 않았습니다.

PRD에 "항상 ~할 수 있다"는 약속이 있다면, 그 약속을 끝까지 이용하는 봇부터 돌리십시오. 문장 검토로는 이 결함이 보이지 않습니다.

AI의 첫 검증은 반대 방향을 권했습니다

같은 날 앞서 나온 PRD 검증 보고서(AI 코딩 에이전트가 작성)는 Python으로 공급을 그대로 구현해 무작위 봇과 한 수 탐욕 봇을 150판씩 돌렸습니다. 판당 중앙값은 17수와 26수였습니다. 트레이 전체를 읽는 봇도 시도했지만, 빈 판에서 트레이 하나를 탐색하는 데 10분이 넘어 중단했습니다.

보고서는 짧은 판만 보고 "해법 보장은 판 길이를 보장하지 않는다"고 결론 냈습니다. 그리고 판이 꽉 차면 작은 조각을 더 주는 "여유 공급"을 추가하자고 권했습니다. 표의 v2가 그것입니다.

보고서는 가장 약한 봇으로, 가장 강한 플레이어에게만 드러나는 약속을 검증했습니다. Kotlin 엔진(64비트 비트보드)이 나온 뒤에야 계획 봇이 현실적인 시간에 돌았고, 거기서 v1의 계획 봇 판이 전부 상한에 닿았습니다. v2는 폐기됐고 PRD는 v2.1에서 공급 약속을 v7로 고쳤습니다. 이 과정은 커밋 cd62087(2026-10-04)에 엔진, 실험 도구, 테스트와 함께 들어갔습니다.

(Python 쪽 탐욕 봇은 판 평가 식이 달랐습니다. 큰 조각 자리 항이 없었죠. 그래서 Kotlin 탐욕 봇의 v1 중앙값 56수와 직접 비교하지 않습니다.)

v1이 지지 않는다는 사실은 ClassicTest의 a planner cannot be stopped under the full guarantee로 고정했습니다. 시드 20개에서 매 트레이 해법대로 두면 300수를 넘기는지 확인하는 테스트입니다.

다음에 확인할 것

같은 저장소에서 앱 네 개를 먼저 낸 과정은 AI 코딩 에이전트로 20일 만에 Android 앱 4개를 출시한 방법에 있습니다.

자주 묻는 질문

블록 퍼즐 게임 오버 조건은 어떻게 정하나요?

남은 조각 중 어느 것도 놓을 곳이 없으면 게임 오버입니다. 다만 공급 규칙이 "항상 전부 놓을 수 있는 트레이"만 주면 잘하는 사람에게는 이 조건이 오지 않습니다. 한판블록은 처음 10트레이 뒤로는 "한 조각 이상 놓을 수 있음"만 보장합니다.

어려운 조각 가중치를 올리면 해결되지 않나요?

해결되지 않았습니다. 어려운 조각 가중치를 최대 8배까지 올린 v4에서도 계획 봇 판이 전부 3,000수 상한까지 갔습니다. 전부 보장이 남아 있는 한, 가중치는 어떤 풀리는 트레이가 나올지만 바꿉니다.

밸런스 시뮬레이션 봇은 몇 판이나 돌려야 하나요?

한판블록은 최종 비교에서 버전마다 탐욕 봇 160판, 계획 봇 80판을 돌렸고, 같은 시드를 모든 버전에 썼습니다(앞선 v3, v4 실험은 계획 봇 60판). 이 정도로도 상한 도달 80/80과 0/80처럼 결론이 갈리는 차이는 분명하게 보였습니다.

이 글은 SugarMount가 직접 측정한 값으로 쓰고, 초안 작성에 AI 코딩 에이전트를 썼습니다.

게임 개발 글 전체 보기 · AX 노트 전체 보기