docs: 온라인 보드게임 사이트 계획서 (공통 14편 + 게임 26종)

- 개요, 조사(BGA·한국 인기 보드게임·UI/UX), 아키텍처, 실시간 프로토콜,
  안정성·보안, 계정, 로비·방, UI/UX, 데이터 모델, 게임 엔진, 테스트,
  배포·운영, 로드맵(M0~M9), 사용자 결정 항목
- 게임별 규칙·엔진 설계·UI·테스트 체크리스트 26종

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
EJClaw
2026-10-04 04:18:29 +09:00
parent b4a3c51201
commit f4d63e1544
43 changed files with 8948 additions and 0 deletions

258
docs/games/janggi.md Normal file
View File

@@ -0,0 +1,258 @@
# 장기 (`janggi`)
> 마일스톤: M2 · 인원: 최소 2 ~ 최대 2명 · 예상 시간: 약 20~40분 · 난이도: 보통
## 1. 개요
- 초(楚)와 한(漢)이 각 16개의 기물로 상대 궁(장)을 외통수로 몰면 이기는 한국 전통 2인 전략 게임. 공원·경로당·가정에서 어르신부터 어린이까지 두는 친숙한 게임이며 장기판 앞의 "장군!" "멍군!"이 상징적이다.
- 인원 근거: 전통 규칙상 초·한 2인. 최소 2, 최대 2.
- 규칙 기준: (사)대한장기연맹(KJF) 규정을 기본으로 하고, 온라인에 맞게 구현 방식을 정했다. 단체마다 다른 부분(빅장 조건, 반복수 처리, 상차림 순서)은 옵션으로 둔다.
- 완전 정보 게임. 단, `pairing=simultaneous` 상차림 옵션을 쓰면 상대 상차림이 확정 전까지 비공개(8.3).
## 2. 모드와 옵션
| 옵션 키 | 설명 | 선택지 | 기본값 |
|---|---|---|---|
| `scoring` | 점수제(판정이 필요한 종료를 기물 점수로 가림) | `true` / `false`(무승부 처리) | `true` |
| `bikjang` | 빅장 규칙 | `off`(궁이 마주봐도 아무 일 없음) / `kjf`(기물이 하나라도 잡힌 다음 수부터 빅장 가능) / `kja30`(양쪽 기물 점수가 모두 30점 미만일 때만 가능) | `kjf` |
| `repetition` | 반복수 처리 (같은 국면 4번째) | `forbid`(4번째를 만드는 수를 둘 수 없음) / `lose`(KJF: 4회 반복한 쪽 실격패) / `scoreEnd`(4번째 국면 발생 시 점수 판정 종료) | `forbid` |
| `allowPass` | 한수쉼 허용 | `true` / `false` | `true` |
| `setupOrder` | 상차림 순서 | `hanFirst`(KJF: 후수 한이 먼저 공개적으로 차리고 초가 나중) / `simultaneous`(동시에 비공개로 고른 뒤 공개) | `hanFirst` |
| `sideAssignment` | 초·한 결정 | `random` / `hostCho` / `hostHan` / `alternate` | `random` |
| `moveLimit` | 수 제한(도달 시 점수 판정) | `0`(없음) / 150 / 200 / 250 (양쪽 합산 수) | `0` |
| `timeControl.kind` | 시간 방식 | `none` / `byoyomi` | `byoyomi` |
| `timeControl.mainSec` / `periodSec` / `periods` | 기본 시간 / 초읽기 1회 / 횟수 | 5분·10분·20분 / 20·30·60초 / 1~5회 | 10분 / 30초 / 3회 |
| `idleLimitSec` | `none`일 때 무응답 한도 | 300 / 600 | 600 |
| `undo` | 무르기 허용 횟수(상대 동의) | `off` / `1` / `3` / `unlimited` | `3` |
| `drawOffer` | 무승부 제안 | `true` / `false` | `true` |
| `hints` | 이동 가능 칸 표시, 장군 위험 경고 | `off` / `moves` / `movesAndThreats` | `moves` |
| `disconnectGraceSec` | 연결 끊김 유예 | 60 / 120 | 60 |
## 3. 구성물
- 판: 가로 9줄 × 세로 10줄, 교차점 90개. 기물은 교차점에 놓는다. 강(river) 없음.
- 궁성: 양쪽 끝 가운데 3×3 영역. 궁성 안에는 X자 대각선이 그려져 있다.
- 기물(각 진영 16개):
| 기물 | 초 / 한 | 수 | 점수 |
|---|---|---|---|
| 궁(장) | 楚 / 漢 | 1 | — |
| 차 | 車 | 2 | 13 |
| 포 | 包 | 2 | 7 |
| 마 | 馬 | 2 | 5 |
| 상 | 象 | 2 | 3 |
| 사 | 士 | 2 | 3 |
| 졸 / 병 | 卒 / 兵 | 5 | 2 |
기물 점수 합 72점. 후수(한)에게 덤 1.5점 → 초 72, 한 73.5로 시작.
- 내부 좌표: `(f, r)`, `f` 0~8(초 기준 왼→오), `r` 0~9(초의 뒷줄 0, 한의 뒷줄 9).
- 초 궁성: `f` 3~5, `r` 0~2, 중심 `(4,1)`. 대각선: `(3,0)–(4,1)–(5,2)`, `(5,0)–(4,1)–(3,2)`.
- 한 궁성: `f` 3~5, `r` 7~9, 중심 `(4,8)`. 대각선: `(3,7)–(4,8)–(5,9)`, `(5,7)–(4,8)–(3,9)`.
## 4. 준비(셋업)
1. `sideAssignment`로 초(선수)·한(후수) 결정. 초가 먼저 둔다.
2. 고정 배치(초 기준, 한은 `r → 9 - r`로 대칭):
- 궁 `(4,1)` (궁성 중심), 사 `(3,0)`, `(5,0)`, 차 `(0,0)`, `(8,0)`, 포 `(1,2)`, `(7,2)`, 졸 `(0,3) (2,3) (4,3) (6,3) (8,3)`.
- 한: 궁 `(4,8)`, 사 `(3,9)` `(5,9)`, 차 `(0,9)` `(8,9)`, 포 `(1,7)` `(7,7)`, 병 `(0,6) (2,6) (4,6) (6,6) (8,6)`.
3. 상차림: 마·상 4개를 뒷줄의 차와 사 사이 두 칸씩(왼쪽 2칸, 오른쪽 2칸)에 놓는다. 각 플레이어가 자기 시점에서 왼쪽→오른쪽으로 4가지 중 선택:
| 선택 | 자기 시점 왼→오 | 별칭 |
|---|---|---|
| `MSSM` | 마 상 · 상 마 | 안상차림, 원앙마 |
| `SMMS` | 상 마 · 마 상 | 바깥상차림, 양귀마 |
| `SMSM` | 상 마 · 상 마 | 왼상차림(귀마) |
| `MSMS` | 마 상 · 마 상 | 오른상차림(귀마) |
- 초의 왼쪽은 `f` 작은 쪽 → 칸 `(1,0) (2,0) (6,0) (7,0)`. 한은 판 반대편에 앉아 있으므로 한의 "왼쪽"은 `f` 큰 쪽 → 칸 `(7,9) (6,9) (2,9) (1,9)` 순서로 대응.
- `setupOrder=hanFirst`: 한이 먼저 선택(즉시 공개) → 초가 보고 선택. `simultaneous`: 둘 다 비공개로 선택, 둘 다 확정되면 동시에 공개.
4. 시계 초기화, 반복 국면 카운트에 초기 국면(초 차례) 1회 기록.
## 5. 진행 규칙
### 5.1 턴
- 초부터 번갈아 한 수씩: 기물 하나 이동(이동 칸에 상대 기물이 있으면 잡음) 또는 한수쉼(`allowPass`, 장군 상태가 아닐 때만).
- 자기 기물이 있는 칸으로는 이동 불가. 판 밖 이동 불가.
- 이동 후 자기 궁이 상대 기물의 공격을 받게 되는 수는 불법(장군을 피하지 않는 수, 스스로 장군에 들어가는 수 모두 포함).
### 5.2 기물별 행마
- 궁, 사: 궁성 안에서만, 궁성의 선을 따라 1칸. 가로·세로 1칸, 그리고 대각선이 그려진 곳(모서리↔중심)에서만 대각 1칸. 즉 모서리 4곳·중심은 대각 가능, 변의 가운데 4곳(`(4,0) (3,1) (5,1) (4,2)` 등)에서는 대각 불가.
- 차: 가로·세로로 막히지 않는 한 몇 칸이든. 궁성 안(양쪽 궁성 모두)에서는 대각선을 따라서도 이동: 모서리→중심→반대 모서리까지(중심이 비어 있어야 반대 모서리로 감), 중심→모서리 1칸.
- 포: 차와 같은 선(가로·세로, 궁성 대각선)을 따라 움직이되, 반드시 정확히 1개의 기물(포다리)을 뛰어넘어야 한다.
- 포다리는 피아 구분 없이 아무 기물이나 되지만 포(어느 편이든)는 포다리가 될 수 없다.
- 포다리 너머 첫 기물 앞까지의 빈칸 어디든 이동 가능, 또는 포다리 너머 첫 기물이 상대 기물이면 잡을 수 있다. 단 포는 포를 잡을 수 없다.
- 포다리 없이는 한 칸도 움직일 수 없다.
- 궁성 대각선: 모서리에 있는 포는 중심에 포가 아닌 기물이 있으면 그것을 넘어 반대 모서리로 갈 수 있다(빈칸이면 이동, 상대 비-포 기물이면 잡기).
- 마: 가로 또는 세로 1칸 간 뒤 그 방향으로 대각선 1칸(목적지 8곳: `(±1,±2) (±2,±1)`). 첫 1칸(멱)에 기물이 있으면 그 방향으로는 갈 수 없다. 궁성 대각선과 무관.
- 상: 가로 또는 세로 1칸 간 뒤 그 방향으로 대각선 2칸(목적지 8곳: `(±2,±3) (±3,±2)`). 경로의 첫 칸(직선 1칸)과 둘째 칸(대각 1칸째)이 모두 비어 있어야 한다. 예: `(f,r)`에서 위로 가는 상 → 멱 `(f,r+1)`, `(f±1,r+2)`, 목적지 `(f±2,r+3)`.
- 졸(초)/병(한): 앞 또는 옆으로 1칸. 뒤로는 못 간다. 상대 궁성 안에서는 앞쪽 대각선을 따라 1칸 추가 가능: 초 졸은 `(3,7)→(4,8)`, `(5,7)→(4,8)`, `(4,8)→(3,9)`, `(4,8)→(5,9)`; 한 병은 `(3,2)→(4,1)`, `(5,2)→(4,1)`, `(4,1)→(3,0)`, `(4,1)→(5,0)`. 마지막 줄에서는 옆으로만 이동.
- 모든 기물은 승급 없음.
### 5.3 장군·멍군·외통
- 장군: 다음 수에 상대 궁을 잡을 수 있는 상태(포다리를 이용한 포의 장군 포함). 장군을 받은 쪽은 반드시 피해야 하며 한수쉼 불가.
- 멍군: 장군을 막아낸 응수(가리기, 공격 기물 잡기, 궁 피하기). UI 표시용 용어이며 규칙상 별도 효과 없음.
- 외통(체크메이트): 장군 상태에서 합법 수가 없음 → 패배. 궁을 실제로 잡지는 않는다.
- 장군 상태가 아닌데 합법 이동이 없으면 한수쉼(옵션이 꺼져 있어도 이 경우 자동 한수쉼 처리).
### 5.4 빅장
- 정의: 두 궁이 같은 세로줄(`f` 같음)에 있고 그 사이에 기물이 하나도 없는 상태. 장군이 아니다.
- 빅장 성립(부르기): 어떤 수의 결과로 빅장 상태가 되고, 그 시점에 `bikjang` 조건이 충족되면 그 수를 둔 쪽이 빅장을 부른 것이다.
- `kjf`: 이전 수들 중 잡기가 한 번 이상 있었다(첫 잡기가 일어난 그 수 자체로는 부를 수 없고, 그 다음 수부터).
- `kja30`: 그 수를 둔 직후 양쪽 기물 점수(덤 제외)가 모두 30점 미만.
- 조건이 충족되지 않으면 궁이 마주봐도 아무 일도 없다(합법이며 빅장 아님).
- 응수: 빅장을 받은 쪽은 한 수를 둔다. 그 수 후에도 두 궁이 여전히 마주보고 있으면(한수쉼, 무관한 수, 잡기만 하고 막지 않은 수 포함) 빅 → 즉시 종료: `scoring=true`면 남은 기물 점수로 판정, `false`면 무승부. 마주보지 않게 만들면(사이에 기물을 넣거나 궁을 옆으로) 대국 계속.
- 빅장을 부르는 수가 동시에 장군이 될 수는 없다(궁끼리는 장군 관계가 아님). 다른 기물로 장군을 건 상태라면 상대는 장군을 먼저 해결해야 하며, 그 응수 후에도 마주보면 빅.
- `bikjang=off`: 마주보기는 아무 효과 없음(카카오 장기 등 온라인 일부 방식).
### 5.5 반복수
- 국면 = 판 배치 + 둘 차례. 같은 국면이 3번까지는 허용, 4번째로 나타나는 경우:
- `forbid`(기본): 4번째 국면을 만드는 수는 둘 수 없다("같은 수를 너무 많이 반복해서 둘 수 없어요"). 예외: 장군을 받았는데 그 수 말고 합법 수가 없으면 허용하고 즉시 점수 판정(`scoring=false`면 무승부).
- `lose`: 4번째 국면을 만든 쪽 실격패(KJF 규정 "4회 반복 시 실격패").
- `scoreEnd`: 4번째 국면이 나오면 점수 판정 종료.
- 한수쉼으로 생긴 국면도 같은 방식으로 센다(차례만 바뀌므로 별도 국면).
### 5.6 한수쉼 연속
- 양쪽이 연속으로 한수쉼 → 종료: `scoring=true`면 점수 판정, 아니면 무승부.
## 6. 승패와 점수 계산
- 완승: 외통, 상대 기권, 상대 시간패, 상대 실격(`repetition=lose`).
- 점수 판정이 일어나는 경우(`scoring=true`): 빅장 성립, 양쪽 연속 한수쉼, 수 제한 도달, `repetition=scoreEnd`, `forbid`의 예외 상황.
- 점수 = 판에 남은 자기 기물 점수 합(차 13, 포 7, 마 5, 상 3, 사 3, 졸·병 2, 궁 0) + 한에게 덤 1.5.
- 덤이 0.5 단위라 동점은 나오지 않는다. 높은 쪽 승.
- 예시: 빅 성립 시 초에 차2·포1·마1·사2·졸3 = 26+7+5+6+6 = 50, 한에 차1·포2·마2·상1·사2·병4 = 13+14+10+3+6+8 = 54 + 1.5 = 55.5 → 한 5.5점 승("한 판정승 55.5 : 50").
- `scoring=false`면 위 판정 상황은 모두 무승부.
- 무승부 합의도 가능.
## 7. 지역 룰 / 하우스 룰 (옵션으로 켜고 끄기)
- 점수제/비점수제 × 빅장 있음/없음의 4가지 대국 방식이 실제로 공존한다. 기본은 정식 대회에서 주로 쓰는 "점수제 + 빅장".
- 빅장 조건: KJF(기물 1개 이상 잡힌 뒤) / 대한장기협회(KJA, 양쪽 30점 미만) / 없음.
- 반복수: 기본은 "막기"(어린이에게 실격패보다 친절), 공식처럼 실격패도 선택 가능.
- 상차림 순서: KJF는 후수(한)가 먼저 차린다. 친선에서는 각자 동시에 정하는 경우가 많아 옵션 제공.
- 접장기(실력 차 보정으로 차·포 등을 떼고 두기), 기물 점수 변형은 v1 미지원(추후 `handicapRemove` 옵션 후보).
## 8. 엔진 설계
### 8.1 상태(State)
```ts
type Side = 'cho' | 'han';
type PieceKind = 'king' | 'cha' | 'po' | 'ma' | 'sang' | 'sa' | 'jol';
type Piece = { side: Side; kind: PieceKind };
type Sq = { f: number; r: number };
type Arrangement = 'MSSM' | 'SMMS' | 'SMSM' | 'MSMS';
interface JanggiState {
options: JanggiOptions;
players: Record<Side, PlayerId>;
phase: 'setup' | 'playing' | 'finished';
setup: { choice: Record<Side, Arrangement | null> }; // simultaneous면 상대 값은 view에서 숨김
board: (Piece | null)[]; // index = r*9 + f
turn: Side;
moves: ({ kind: 'move'; side: Side; from: Sq; to: Sq; captured: PieceKind | null }
| { kind: 'pass'; side: Side })[];
firstCapturePly: number | null; // 첫 잡기가 일어난 수 번호
positionCounts: Record<string, number>; // 배치+차례 -> 횟수
bikjang: { calledBy: Side; ply: number } | null; // 응수 대기 중인 빅장
consecutivePasses: number;
inCheck: Side | null;
clock: { mainMs: Record<Side, number>; periodsLeft: Record<Side, number>; turnStartedAt: number };
undo: { usedBy: Record<Side, number>; pending: { by: Side; plies: 1 | 2 } | null };
draw: { pendingBy: Side | null; lastOfferPly: Record<Side, number> };
result: JanggiResult | null;
rng: RngState;
}
```
### 8.2 액션
| type | payload | 누가 / 언제 | 검증 조건 |
|---|---|---|---|
| `chooseSetup` | `{ arrangement }` | `setup` 단계. `hanFirst`: 한이 먼저, 그다음 초 / `simultaneous`: 둘 다 | 아직 선택 안 함, 순서 맞음. 둘 다 정해지면 배치 확정, `playing` 진입, 초 차례 |
| `move` | `{ from: Sq, to: Sq }` | 차례인 플레이어, `playing` | `from`에 자기 기물, `to`가 5.2 행마로 도달 가능, 자기 기물 없음, 포는 포다리 조건(포 넘기·포 잡기 금지), 이동 후 자기 궁이 공격받지 않음, 반복수 `forbid` 위반 아님. 사유 예: "포는 포를 넘을 수 없어요", "마의 길(멱)이 막혀 있어요", "장군을 피해야 해요", "궁은 궁성 밖으로 나갈 수 없어요" |
| `pass` | `{}` | 차례인 플레이어 | `allowPass` 또는 합법 이동 없음, 장군 상태 아님("장군을 받은 상태에서는 쉴 수 없어요"), 반복수 위반 아님 |
| `requestUndo` / `respondUndo` | `{}` / `{ accept }` | 플레이어 / 상대 | 횟수 남음, 대기 없음. 수락 시 `moves` 재생으로 판·`positionCounts`·`firstCapturePly`·`bikjang` 재구성, 시계 유지 |
| `offerDraw` / `respondDraw` / `cancelOffer` | | 플레이어 | 거절 후 본인 5수 이후 재제안 |
| `resign` | `{}` | 플레이어 | `setup` 중이면 결과 없이 취소 |
| `timeout` | `{}` | 시스템 | 8.5 |
- `move`/`pass` 적용 후 처리 순서:
1. 시계 차감(초읽기 규칙은 바둑과 동일: 기본 시간 → 초읽기 회차, 회차 초과 시 소진, 0이면 시간패로 액션 무효).
2. 판 갱신, 잡기면 `firstCapturePly` 설정(처음일 때).
3. 응수 대기 중인 빅장이 있고 지금 둔 사람이 응수자라면: 여전히 마주봄 → 빅(종료). 아니면 `bikjang = null`.
4. 상대 외통 판정 → 승리.
5. 새로 빅장 상태가 되었고 조건 충족 → `bikjang = { calledBy: 둔 사람 }`, 이벤트 "빅장".
6. 반복 카운트 증가, `lose`/`scoreEnd` 처리, 연속 한수쉼 처리, `moveLimit` 처리.
7. 장군 여부 이벤트(`janggun`), 직전 장군 해소면 `meonggun` 이벤트.
- 공격 판정 함수 `attacks(board, side, sq)`: 차(직선·궁성 대각), 포(포다리 1개, 포 제외), 마·상(멱 확인), 졸(앞·옆·궁성 대각 앞), 사·궁(궁성 안 인접 선) 모두 포함. 궁끼리 마주봄은 공격이 아니다.
### 8.3 공개/비공개 정보 (view)
- 공개: 판, 차례, 기보, 잡은 기물, 현재 점수(덤 포함), 장군 상태, 빅장 응수 대기 여부, 반복 경고(같은 국면 3회째 도달 시 "한 번 더 반복하면 둘 수 없어요"), 시계, `deadline`, 대기 중인 요청, 결과.
- `setup` 단계 + `simultaneous`: 상대 선택값은 "선택 완료"(true/false)만 보이고 배치는 둘 다 확정될 때까지 숨긴다(플레이어·관전자 모두). `hanFirst`에서는 한의 선택이 즉시 공개.
- 차례인 플레이어에게만: `legalMoves`(하이라이트용), 힌트 옵션 시 `threatened`(잡힐 위험 기물).
- 제외: `rng`, 상대의 미공개 상차림.
### 8.4 랜덤 요소 (시드 RNG 사용 지점)
- `sideAssignment=random`의 초·한 결정.
- 상차림 시간 초과 시 자동 선택(4가지 중 균등 무작위).
- 대국 중에는 사용하지 않는다.
### 8.5 타이머·시간 초과·연결 끊김 시 자동 행동
- `deadline(state)`:
- `setup`: 단계 시작 + 60초(선택해야 할 사람 기준).
- `playing` + `byoyomi`: `turnStartedAt + mainMs[turn] + periodsLeft[turn]*periodSec*1000`.
- `playing` + `none`: `turnStartedAt + idleLimitSec*1000`.
- 종료: `null`.
- `activePlayers`: `setup`에서 `hanFirst`면 순서에 맞는 1명, `simultaneous`면 아직 선택 안 한 전원. `playing`이면 차례인 1명.
- `onTimeout(state, player)`:
- `setup`: `{ type: 'timeout' }` → `apply`가 시드 RNG로 4가지 중 하나를 골라 `chooseSetup`과 같은 처리(`onTimeout`은 RNG를 쓰지 않는 순수 함수로 유지).
- `playing`: `{ type: 'timeout' }` → 시간패. 장기는 자동 수를 두지 않는다.
- 연결 끊김: 끊긴 플레이어가 행동해야 할 때 `min(deadline, 끊김 + disconnectGraceSec)`에 `onTimeout`.
### 8.6 종료 조건과 결과(GameResult)
```ts
interface JanggiResult {
ranking: { playerId: PlayerId; rank: 1 | 2 }[]; // 무승부면 둘 다 1, 셋업 중 취소면 빈 배열
winner: Side | null;
reason: 'checkmate' | 'resign' | 'timeLoss' | 'repetitionLoss'
| 'scoreBikjang' | 'scorePasses' | 'scoreMoveLimit' | 'scoreRepetition'
| 'drawBikjang' | 'drawPasses' | 'drawRepetition' | 'drawMoveLimit' | 'drawAgreed' | 'aborted';
scores: { cho: number; han: number }; // 항상 기록(한은 덤 포함)
moveCount: number;
summaryKo: string; // "초 외통승 (63수)", "한 판정승 55.5 : 50 (빅장)"
}
```
## 9. UI/UX
- 판 방향: 내 진영이 아래. 초는 파랑(또는 초록), 한은 빨강. 기물은 팔각형(차·포·마·상 큰 것, 사·졸 작은 것) 한자 + 작은 한글 라벨 옵션("車 차")으로 한자에 익숙하지 않은 어린이 배려.
- 모바일 세로: 판(9×10)이 세로로 길어 세로 화면에 잘 맞는다. 폰 360px 폭에서 교차점 간격 약 38px이므로 기물 터치 영역을 교차점 간격 전체(정사각형)로 잡고, 선택 시 확대 강조.
- 조작: 기물 탭 → 이동 가능 점 표시(잡기 가능 칸은 빨간 링) → 탭. 포는 포다리 기물을 살짝 강조해 이유가 보이게.
- 상차림 화면: 4개 큰 카드(그림 + 이름 "안상차림 / 원앙마"), 한 화면에 미리보기.
- "장군!" "멍군!" "빅장!" 큰 말풍선과 효과음(목소리 옵션). 외통 시 결과 모달.
- 상단에 양측 점수 바(초 72 : 한 73.5) 상시 표시 → 판정 상황 이해 도움.
- 한수쉼 버튼은 확인 모달. 반복 경고 배너.
- 규칙 보기: 기물별 행마 애니메이션, 멱(막힘) 예시, 포다리 예시, 빅장 설명.
## 10. 테스트 체크리스트
- [ ] 초기 배치 4가지 상차림 × 초/한 시점 매핑이 정확(비대칭인 `SMSM` 기준: 초 → `(1,0)` 상, `(2,0)` 마, `(6,0)` 상, `(7,0)` 마 / 한 → `(7,9)` 상, `(6,9)` 마, `(2,9)` 상, `(1,9)` 마).
- [ ] 마·상 멱: 첫 칸이 막히면 해당 방향 2곳 불가, 상은 둘째 칸 막힘도 확인.
- [ ] 포: 포다리 없이 이동 불가, 포를 포다리로 사용 불가, 포를 잡기 불가, 포다리 너머 여러 빈칸 이동 가능, 궁성 대각선 넘기.
- [ ] 차의 궁성 대각 이동: 모서리→반대 모서리(중심 비었을 때), 중심 막혔을 때 불가.
- [ ] 궁·사가 변 가운데 점에서 대각 이동 불가, 궁성 밖 이동 불가.
- [ ] 졸: 뒤로 불가, 상대 궁성 대각 앞 이동 가능, 자기 궁성에서 대각 불가.
- [ ] 스스로 장군에 들어가는 수 거부, 포다리 역할 기물을 치워 포 장군에 노출되는 수 거부(핀).
- [ ] 장군 중 한수쉼 거부, 외통 판정.
- [ ] 빅장(`kjf`): 잡기 전엔 마주봐도 무효, 첫 잡기 수 자체로는 불가, 그 이후 성립. 응수로 막으면 계속, 못 막으면 점수 판정(덤으로 동점 없음).
- [ ] 빅장(`kja30`): 한쪽이라도 30점 이상이면 무효.
- [ ] 반복수 `forbid`: 4번째 국면을 만드는 수 거부, 장군 중 유일한 수면 허용 후 판정 종료. `lose`: 실격패.
- [ ] 연속 한수쉼 → 점수 판정 / 무승부.
- [ ] 초읽기 시간패, 상차림 시간 초과 시 RNG 자동 선택이 같은 시드에서 동일.
- [ ] `simultaneous` 상차림 중 상대·관전자 view에 선택값 누출 없음(선택 완료 여부만).
- [ ] 무르기 수락 시 `firstCapturePly`, `bikjang`, 반복 카운트 복원.
- [ ] view에 `rng` 없음, 관전자에게 `legalMoves` 없음.
## 11. 참고 자료
- (사)대한장기연맹 장기규정/규칙: https://kojf.net/theme/sample30/html/content03.php (점수·덤 1.5, 판정, 빅장은 기물을 하나라도 취한 후 다음 수부터, 반복수 3회 허용·4회 실격패, 후수가 먼저 상차림, 한수쉼)
- 대한장기연맹 장기규칙 개정 공지(2019.12.31): http://kojf.net/post/478
- 대한장기협회(KJA): http://www.kja.or.kr/business/business5.php (점수제 빅장은 양측 30점 미만일 때)
- 위키백과 「장기」: https://ko.wikipedia.org/wiki/%EC%9E%A5%EA%B8%B0
- 나무위키 「장기/기물 및 행마법」: https://namu.wiki/w/%EC%9E%A5%EA%B8%B0/%EA%B8%B0%EB%AC%BC%20%EB%B0%8F%20%ED%96%89%EB%A7%88%EB%B2%95 , 「장기/용어」: https://namu.wiki/w/%EC%9E%A5%EA%B8%B0/%EC%9A%A9%EC%96%B4
- 상차림 명칭(원앙마·양귀마·귀마): https://brunch.co.kr/@1305b8c2c18141c/529 , https://ko.wikibooks.org/wiki/%EC%9E%A5%EA%B8%B0/%EC%B4%88%EB%B0%98_%ED%8F%AC%EC%A7%84%EB%B2%95
- 점수제/빅장 대국 방식 4종 정리: https://gall.dcinside.com/mgallery/board/view/?id=janggi&no=10449
- Janggi 영문 규칙 요약: https://janggionline.com/how-to-play/ , https://en.wikipedia.org/wiki/Janggi
## 12. 메모 (상표·법적 주의 등)
- "장기"는 전통 게임 명칭으로 상표 문제 없음. 기물 그림·폰트는 자체 제작 또는 무료 라이선스 폰트 사용.
- 단체마다 세부 규칙이 달라 "대한장기연맹 규정 기반, 일부 온라인 방식 적용"이라고 규칙 보기에 명시.
- 금전·베팅 요소 없음.