장기판은 규칙이 둘 수 있는 칸을 먼저 정한다
왜 장기인가
장기는 가로 9줄, 세로 10줄이다. 초(楚)와 한(漢)이 번갈아 두고, 상대 궁을 잡으면 이긴다. 차, 포, 마, 상, 사, 졸의 움직임이 서로 다르고 궁과 사는 궁성 안에 묶인다. 포는 사이에 포가 아닌 기물 하나를 넘어야 움직이고, 포는 잡지 못한다.
규칙이 이렇게 갈라져 있으면, 판을 통째로 모델에 맡기기 전에 합법수와 평가 함수가 맞는지를 먼저 볼 수 있다. 맥에 있는 구현은 Qt로 된 대국 프로그램, 같은 규칙을 옮긴 웹 대국, 폰 화면을 읽어 그 엔진으로 두는 봇으로 이루어져 있다.
규칙이 곧 환경이다
`src/Game.cpp`가 그 환경이다. 판은 90칸이고, 상차림은 마상상마(내상), 상마마상(외상), 상마상마(좌상), 마상마상(우상) 네 가지다. 초와 한이 각자 고른다.
궁과 사는 자기 궁성 안에서만 움직이고 궁성 대각선도 탄다. 차는 직선으로 막히기 전까지 가고 궁성 대각선도 탄다. 마는 한 칸 옆이 비어야 그 방향으로 뛰어넘고, 상은 그 길의 두 칸이 비어야 한다. 졸과 병은 앞으로 한 칸 또는 옆으로 한 칸이며 뒤로는 가지 않는다. 상대 궁성 대각선에 걸치면 그 대각선 한 칸도 간다.
같은 국면이 기본 세 번이면 그 수는 금지다. 장군이 아닐 때는 한 수를 쉴 수 있다. 빅장은 두 궁이 같은 줄에서 사이에 기물 없이 마주 본 상태다. 빅장을 켜 두었고 지금 장군이 아니면 비김을 선언할 수 있다.
프로젝트 구조
| 파일 | 역할 |
|---|---|
| src/Game.cpp | 상차림, 합법수, 장군, 빅장, 반복 금지, 기권과 무르기를 담당한다. |
| src/Ai.cpp | 깊이 3의 네가맥스 탐색과 기물·자리 평가. 가중치는 파일로 바꿀 수 있다. |
| src/MainWindow.cpp | Qt 대국 창. 사람과 컴퓨터가 둔다. |
| web/lib/game.ts | 같은 규칙을 브라우저용으로 옮긴 판이다. |
| web/lib/ai.ts | 같은 평가와 탐색을 웹 워커에서 돌린다. |
| kakao-bot/bot.py | 모노몹 장기 화면을 읽어 엔진이 고른 수를 누른다. |
| kakao-bot/learn.py | 사람 기보로 평가 가중치를 맞춘다. |
| arena/arena.cpp | 가중치나 깊이가 다른 두 AI의 자가대국이다. |
수를 고르는 방법
기본 기물 값은 차 130, 포 70, 마 50, 상 30, 사 30, 졸 20이다. 졸이 앞으로 나간 칸, 마와 상이 중앙에 가까운 정도, 차가 상대 궁과 같은 줄에 있는 정도를 더한다. 장군을 받는 쪽은 18을 깎고, 한의 평가에는 기본 15가 더해져 있다. 궁 자체는 이 자리 점수에 넣지 않는다. 승패는 궁을 잡은 결과로 탐색 안에서 처리한다.
탐색은 둘 차례 기준으로 점수를 뒤집는 네가맥스다. 깊이 기본값은 3이고, 잡는 수에 가산점을 주어 먼저 본다. 알파와 베타로 이미 나쁜 가지는 끊는다. 웹의 `web/lib/ai.ts`도 이 순서와 같은 점수를 쓴다.
가중치는 `key=value` 파일로 바꿀 수 있다. 궁성 침입 감점, 궁성 안의 차 추가 감점, 사 가점, 탐색 끝에서 잡는 수만 더 보는 연장은 기본이 0이라 꺼져 있다.
사람 기보로 가중치만 맞추기
카카오봇은 폰의 모노몹 장기 화면을 읽어 판을 복원하고, 같은 엔진이 고른 칸을 누른다. 기보는 로컬 SQLite에 남긴다. 그 기보 파일과 화면 캡처는 예제에 넣지 않았다.
`kakao-bot/learn.py`는 각 국면의 합법수마다 둔 뒤 평가 점수를 매기고, 사람이 실제로 둔 수의 확률이 커지도록 가중치를 좌표 탐색으로 조정한다. 기본값에서 멀어지면 벌점을 준다. 네 국면마다 하나는 검증으로 빼 둔다. `arena`는 학습된 가중치와 기본 가중치를 초와 한을 바꿔 가며 두어 세기를 본다.
웹 대국은 진행 중 기보를 SQLite에 남기고, 저장 시점에서 이어 둔다. 상차림과 탐색 깊이, 빅장 사용 여부를 판마다 고른다.
댓글
0개아직 댓글이 없습니다.