메뉴
HN
Hacker News • 39일 전

쇼우 HN: 소코반 AI 솔버

IMP
3/10
핵심 요약

1980년대 퍼즐 게임 소코반(창고지기)을 순수 자바스크립트로 포팅한 최적 해 솔버를 해커뉴스에 공개했습니다. 이 솔버는 매크로 푸시 A* 탐색, 비트마스크 상태 압축, 데드락 가지치기 등을 활용해 증명된 최소 이동 수의 최적해를 브라우저에서 밀리초 단위로 계산하며, 원래 C++ 네이티브 솔버를 웹으로 옮긴 것입니다.

번역된 본문

소코반

소코반("창고지기")은 1980년대 퍼즐 게임으로, 모든 상자를 목표 지점으로 밀어 넣어야 합니다. 이 변형에서는 창고지기 역시 목표 지점 위에서 끝나야 합니다.

보드: 초기화, 되돌리기, AI로 풀기. AI 속도: 느림 / 보통 / 빠름. 다음 →

이동 횟수: 0 / 최적: – ▲ ◀ ▶ ▼

창고지기(플레이어) / 상자 / 목표 지점 / 목표 위의 상자 / 벽

게임 방법 및 규칙

창고는 격자(grid)로 되어 있습니다. 각 턴마다 창고지기는 위, 아래, 왼쪽, 오른쪽으로 한 칸씩 이동합니다. 창고지기는 벽이나 상자가 있는 칸으로 걸어 들어갈 수 없습니다. 상자 바로 너머의 칸(밀어내는 방향으로)이 빈 바닥이거나 목표 지점인 경우에만 상자 하나를 밀 수 있습니다. 한 턴에 움직이는 상자는 하나뿐이며, 공간을 만들기 위해 목표 지점 위의 상자를 다시 밀어낼 수도 있습니다.

조작법: 방향키 또는 W A S D, 혹은 화면상의 패드. '되돌리기'는 한 턴 뒤로 갑니다. '초기화'는 보드를 원래대로 되돌립니다.

목표: 움직일 수 있는 모든 개체, 즉 모든 상자와 창고지기가 목표 지점 위에 앉으면 퍼즐이 완성됩니다. 그래서 각 보드에는 상자보다 하나 더 많은 목표 지점이 있는 것입니다. 마지막 목표 지점은 창고지기의 것입니다.

목적: 최소한의 이동으로 그 상태에 도달하는 것입니다. 일부 보드의 경우 최적 이동 수가 알려져 있어 위에 표시됩니다. AI(허용 가능한 휴리스틱 사용)는 완전 탐색이 가능한 보드에서 최적해를 반환합니다.

AI 솔버의 작동 원리

소코반은 A* 탐색 문제이지만, 창고지기의 이동을 한 걸음씩 탐색하는 단순한 버전은 상자가 많은 보드에서 폭발적으로 늘어납니다. 여기서 실행되는 것은 제가 작성한 네이티브 C++ 최적 솔버의 순수 자바스크립트 포팅입니다. 그냥 어떤 해가 아니라 증명된 최소 이동 수의 해를 반환합니다:

이동 최적 매크로 푸시 A*. 각 탐색 간선(edge)은 상자 하나를 통째로 미는 것이며, 비용은 (밀 위치까지의 창고지기 최단 걷기 거리) + 1로 계산됩니다. 따라서 총합이 창고지기 이동 수의 실제 최솟값이 되는 동시에, 탐색은 개별 걸음을 건너뜁니다.

컴팩트 비트마스크 상태. 상자들은 보드의 도달 가능한 "살아있는" 칸들에 대해 32비트 정수 하나로 압축되고, 창고지기는 또 하나의 숫자로 표현됩니다. 즉 하나의 상태 전체가 약 1KB짜리 객체가 아니라 약 8바이트 키 하나가 됩니다. 수백만 개의 상태가 수십 MB에 들어갑니다.

다이얼 버킷 큐 + 개방 주소 해시. A* 프론티어는 비용을 키로 하는 버킷 큐이고, 방문 집합(해의 부모 링크 포함)은 평평한 타입드 배열 해시에 저장됩니다. 할당 없이(allocation-free) 캐시 친화적입니다.

데드락 가지치기. 정적 데드 스퀘어 테이블(목표 지점으로부터의 역방향 도달성)과 프리즈 검사를 통해 풀 수 없음이 증명된 상태를 버리며, 벽을 고려한 푸시 거리 하한으로 A*의 허용 가능성(admissibility, 즉 최적성)을 유지합니다.

보드 1~14는 브라우저에서 밀리초 만에 증명된 최적해로 실시간 해결됩니다(위에 "최적"으로 표시된 이동 수가 정확히 이 솔버가 반환하는 값입니다).

보드 15, 즉 8상자 미로는 예외입니다. 최적 탐색이 약 4,900만 개 상태를 살펴야 하고 1GB 이상의 메모리가 필요해 브라우저 탭 안에서 실행하기엔 너무 오래 걸립니다. 따라서 그 최적해(184 이동)는 이 알고리즘 자체의 네이티브 C++ 빌드(병렬 A* 탐색, 24코어에서 약 5초)로 오프라인 계산되고 재생으로 검증되었으며, 페이지는 단순히 그 미리 계산된 해를 재생합니다. 그래서 보드 15의 정답은 여기서 탐색되지 않고 하드코딩되어 있는 것입니다.

제 소코반 솔버에서 만들어졌습니다. 소코반에 대하여 →

원문 보기
원문 보기 (영어)
Sokoban Sokoban ("warehouse keeper") is a 1980s puzzle: push every box onto a goal. In this variant the keeper must also finish on a goal. Board: Reset Undo Solve with AI AI speed: Slow Normal Fast Next → Moves: 0 Optimal: – ▲ ◀ ▶ ▼ Keeper (you) Box Goal Box on goal Wall How to play & the rules The warehouse is a grid. On each step the keeper moves one square up, down, left or right. The keeper cannot walk into a wall or a box. It can push a single box if the square just beyond the box (in the push direction) is empty floor or a goal. Only one box moves per step, and a box can be pushed out of a goal again to make room. Controls: arrow keys or W A S D , or the on-screen pad. Undo steps back. Reset restores the board. Goal: the puzzle is won when every movable entity. Every box and the keeper. Is sitting on a goal. That is why each board has one more goal than it has boxes: the last goal is for the keeper. Objective: reach that state in as few moves as possible. For several boards the optimal move count is known and shown above. The AI (with an admissible heuristic) returns an optimal solution on the boards it can search exhaustively. How the AI solver works Sokoban is an A* search problem, but a naive version that explores one keeper step at a time explodes on crowded boards. What runs here is a plain-JavaScript port of a native C++ optimal solver I wrote. It returns the provably fewest-moves solution, not just some solution: Move-optimal macro-push A*. Each search edge is a whole box push costed as (the keeper's shortest walk to the push spot) + 1, so the total is the true minimum number of keeper moves , while the search skips over the individual walking steps. Compact bitmask states. The boxes are packed into a 32-bit integer over the board's reachable "live" cells and the keeper into one more number, so a whole state is a single ~8-byte key instead of a ~1 KB object. Millions of states fit in tens of MB. Dial bucket queue + open-addressed hash. The A* frontier is a bucket queue keyed by cost, and the visited set (with the solution's parent links) lives in a flat typed-array hash. Allocation-free and cache-friendly. Deadlock pruning. A static dead-square table (reverse-reachability from the goals) plus a freeze check discard provably-unsolvable positions, guided by a wall-aware push-distance lower bound that keeps A* admissible (hence optimal). Boards 1–14 are solved live to the proven optimum in milliseconds (the move counts shown as "Optimal" above are exactly what this solver returns). Board 15. The 8-box maze. Is the exception: its optimal search explores ~49 million states and needs >1 GB , which would take far too long to run inside a browser tab. So its optimum ( 184 moves ) was computed offline by the native C++ build of this exact algorithm (a parallel A* search, ~5 s across 24 cores) and verified by replay, and the page simply plays that precomputed solution back . That is why board 15's answer is hardcoded rather than searched here. Built from my Sokoban solver . About Sokoban →