M9: 컴퓨터 상대 — 게임 22종 컴퓨터 전략, 서버 구동(BotDriver), 연습 방

- packages/shared/src/bots.ts: 컴퓨터 id(bot:<n>:<ulid>)·프로필(컴퓨터 n)·BOT_GAMES
  (도블·할리갈리·그림 맞히기·마피아 제외)
- packages/games/src/bots: 전략 계약(자기 view + 합법 수만), decideBotAction(validate 재검사),
  게임별 <게임>/bot.ts 22개 + 행동 테스트, 전 게임 판 끝까지 돌리는 공통 테스트
- 서버: Room.addBot/removeBot(방장·대기실만, 방은 친구만), RoomManager.createPractice
  (POST /api/rooms {practice:true}), BotDriver(사람 같은 지연, 사람과 같은 창이면 더 천천히,
  재시작 후 다시 예약), 연습 게임은 전적 제외(result_json.practice), 사람이 모두 나가면 무효
- 프로토콜 addBot/removeBot, 문서 03·06 §12·08·09 §8

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
EJClaw
2026-10-06 13:32:28 +09:00
parent c27cb0a11a
commit a3f81ded37
67 changed files with 5113 additions and 18 deletions

View File

@@ -120,3 +120,8 @@ export interface GameUI<V> {
- 게임별 불변식(카드 총수 보존, 칩 총합 보존 등)
3. **정보 유출 검사**: 매 단계 각 플레이어 view의 JSON에 남의 비공개 카드 ID가 없는지(게임이 `secretsOf(state, player)`를 테스트용으로 제공).
4. **시간 초과 테스트**: 모든 상태에서 `onTimeout`이 합법 행동을 돌려주는지.
## 8. 컴퓨터 상대 (`packages/games/src/bots/`, 06 §12)
- 게임마다 `<게임>/bot.ts`의 전략 하나: `(입력: { me, view, legal, random }) => 행동`. `view`는 `view(state, me)`, `legal`은 `legalActions(state, me)`. 게임 상태는 넘기지 않는다(비공개 정보 차단).
- `decideBotAction`이 고른 수를 `validate`로 다시 검사하고, 틀리면 다른 합법 수로 바꾼다. 서버는 그래도 실패하면 게임의 `onTimeout` 행동을 쓴다.
- 테스트(`bots/bots.test.ts`): 게임마다 "사람 1명은 시간 초과만, 나머지는 컴퓨터"와 "컴퓨터끼리" 판을 끝까지 돌려, 모든 수가 합법이고 컴퓨터 때문에 멈추지 않으며 한 수가 충분히 빠른지 본다.