- 개요, 조사(BGA·한국 인기 보드게임·UI/UX), 아키텍처, 실시간 프로토콜, 안정성·보안, 계정, 로비·방, UI/UX, 데이터 모델, 게임 엔진, 테스트, 배포·운영, 로드맵(M0~M9), 사용자 결정 항목 - 게임별 규칙·엔진 설계·UI·테스트 체크리스트 26종 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
191 lines
12 KiB
Markdown
191 lines
12 KiB
Markdown
# 바퀴벌레 포커 (`cockroach-poker`)
|
||
|
||
> 마일스톤: M7 · 인원: 최소 2 ~ 최대 6명 · 예상 시간: 약 15~20분 · 난이도: 쉬움
|
||
|
||
## 1. 개요
|
||
- 해충 카드를 뒤집어 건네며 "이건 쥐야"라고 말하면, 받은 사람이 진실/거짓을 맞히거나 몰래 보고 다른 사람에게 떠넘기는 블러핑 게임. 같은 해충 4장을 앞에 모으는 사람이 진다(패배자 1명을 정하는 게임).
|
||
- 한국에서는 보드게임 카페 입문용 파티 게임으로 인기가 높고, 규칙이 단순해 어린이·어른이 함께 하기 좋다.
|
||
- 인원 근거: 원작(Drei Magier Spiele, 2004, 자크 젤리거) 박스 표기 2~6인. 2인은 원작의 별도 2인 규칙을 따른다.
|
||
|
||
## 2. 모드와 옵션
|
||
| 옵션 키 | 설명 | 선택지 | 기본값 |
|
||
|---|---|---|---|
|
||
| `loseCount` | 같은 종류가 몇 장 쌓이면 패배 | 3 / 4 / 5 | 3~6인 4, 2인 5(원작) |
|
||
| `turnSeconds` | 카드 건네기/판단 제한 시간 | 15 / 30 / 60 | 30 |
|
||
| `showSeenBy` | 현재 카드를 이미 본 사람 표시 | on 고정 | on |
|
||
| `claimHistory` | 이번 라운드의 주장 이력(누가 무엇이라 했는지) 표시 | on / off | on |
|
||
| `quickChat` | 정해진 문구 버튼("정말이야!", "의심스러운데?") | on / off | on |
|
||
|
||
## 3. 구성물
|
||
- 카드 64장: 해충 8종 × 8장.
|
||
|
||
| 종류 키 | 표시 이름 |
|
||
|---|---|
|
||
| `cockroach` | 바퀴벌레 |
|
||
| `bat` | 박쥐 |
|
||
| `fly` | 파리 |
|
||
| `toad` | 두꺼비 |
|
||
| `rat` | 쥐 |
|
||
| `scorpion` | 전갈 |
|
||
| `spider` | 거미 |
|
||
| `stinkbug` | 노린재 |
|
||
|
||
어린이 대상 서비스이므로 그림은 귀여운 캐릭터풍 자체 제작 아트를 쓴다(원작 아트 사용 금지).
|
||
|
||
## 4. 준비(셋업)
|
||
1. 64장을 RNG로 섞는다.
|
||
2. 2인: 섞은 뒤 10장을 보지 않고 제거(공개하지 않음). 남은 54장을 27장씩.
|
||
3. 3~6인: 전부 나눈다. 좌석 순(첫 플레이어부터)으로 1장씩 돌려 나누므로 앞 좌석이 1장 더 받을 수 있다.
|
||
|
||
| 인원 | 손패 장수 |
|
||
|---|---|
|
||
| 2 | 27, 27 (10장 제거) |
|
||
| 3 | 22, 21, 21 |
|
||
| 4 | 16 × 4 |
|
||
| 5 | 13, 13, 13, 13, 12 |
|
||
| 6 | 11, 11, 11, 11, 10, 10 |
|
||
|
||
4. 첫 플레이어는 RNG로 정하고, 좌석은 그 사람부터 시작하게 배치한다(첫 플레이어가 가장 많이 받음).
|
||
5. 각자 앞의 "앞마당"(공개 카드 영역)은 비어 있다.
|
||
|
||
## 5. 진행 규칙
|
||
### 5.1 라운드 시작
|
||
- 라운드 시작 플레이어(시작자)가 손패 1장을 골라 뒷면으로 다른 플레이어 1명에게 건네며 해충 종류 1개를 주장한다. 주장은 진실이어도 거짓이어도 된다.
|
||
- 시작자의 손패가 0장이면 그 즉시 시작자가 진다(6장).
|
||
|
||
### 5.2 카드를 받은 사람의 선택
|
||
받은 사람(수신자)은 둘 중 하나를 고른다.
|
||
1. **판정**: "진실이다" 또는 "거짓이다"라고 말하고 카드를 공개한다.
|
||
- 맞히면: 카드를 건넨 사람(직전 전달자)이 그 카드를 자기 앞마당에 공개해 놓는다.
|
||
- 틀리면: 수신자가 자기 앞마당에 놓는다.
|
||
2. **떠넘기기**: 카드를 몰래 확인한 뒤, 이 카드를 아직 보지 않은 다른 플레이어에게 건네며 다시 주장한다(같은 주장이든 다른 주장이든 자유).
|
||
- "본 사람" = 원래 건넨 시작자 + 이 카드를 본 모든 수신자. 이 사람들에게는 건넬 수 없다.
|
||
- 건넬 수 있는 사람이 없으면(나머지 전원이 이미 봄) 떠넘기기 불가, 반드시 판정.
|
||
- 손패가 0장인 사람에게도 건넬 수 있다(손패와 무관).
|
||
- 2인 규칙: 떠넘기기 없음. 항상 판정.
|
||
|
||
### 5.3 라운드 종료와 다음 시작자
|
||
- 앞마당에 카드를 받은 사람이 다음 라운드 시작자.
|
||
- 판정 대상은 언제나 **가장 마지막 주장**이다.
|
||
|
||
### 5.4 예시(4인 A·B·C·D)
|
||
1. A가 실제 "거미"를 B에게 건네며 "파리"라고 주장.
|
||
2. B가 몰래 보고(거미) C에게 "거미"라고 주장(진실). 본 사람 = {A, B}.
|
||
3. C가 "거짓이다" 판정 → 공개하니 거미 → 주장은 진실이었으므로 C가 틀림 → C의 앞마당에 거미. C가 다음 시작자.
|
||
4. 만약 C가 떠넘기기를 골랐다면 보고 나서 D에게만 건넬 수 있다. D는 떠넘길 사람이 없으므로 반드시 판정.
|
||
|
||
## 6. 승패와 점수 계산
|
||
- 패배 조건(먼저 성립하는 즉시 종료):
|
||
1. 어떤 플레이어의 앞마당에 같은 종류가 `loseCount`장(기본 4, 2인 5) 모였다.
|
||
2. 라운드 시작자가 되어야 하는데 손패가 0장이다.
|
||
- 패배자 1명, 나머지 전원 공동 승리.
|
||
- 순위(방 기록용 보조 표시): 패배자 꼴찌. 나머지는 "앞마당 최다 동일 종류 장수"가 적은 순 → 앞마당 총 장수가 적은 순 → 그래도 같으면 공동 순위.
|
||
- 점수: 승리자 각 1점, 패배자 0점.
|
||
|
||
## 7. 지역 룰 / 하우스 룰 (옵션으로 켜고 끄기)
|
||
| 옵션 | 내용 | 기본값 |
|
||
|---|---|---|
|
||
| `loseCount=3` | 짧은 게임(어린이 모드 추천) | off |
|
||
| `eightKindsRule` | 앞마당에 8종을 모두 1장 이상 모아도 패배(일부 하우스 룰) | off |
|
||
| `twoPlayerPass` | 2인에서도 수신자가 보고 되돌려 줄 수 있음(원작에 없음) | off |
|
||
|
||
## 8. 엔진 설계
|
||
### 8.1 상태(State)
|
||
```ts
|
||
type Kind = 'cockroach' | 'bat' | 'fly' | 'toad' | 'rat' | 'scorpion' | 'spider' | 'stinkbug';
|
||
interface Card { id: number; kind: Kind } // id 0..63
|
||
|
||
interface CockroachState {
|
||
options: CockroachOptions;
|
||
rng: RngState;
|
||
order: PlayerId[]; // 좌석
|
||
hands: Record<PlayerId, number[]>;
|
||
fronts: Record<PlayerId, number[]>; // 공개 앞마당
|
||
removed: number[]; // 2인 제거 10장(비공개)
|
||
round: {
|
||
starter: PlayerId;
|
||
card: number | null; // 건네는 중인 카드
|
||
holder: PlayerId | null; // 현재 수신자(판단할 사람). null이면 시작자가 고르는 중
|
||
lastPasser: PlayerId | null;
|
||
claim: Kind | null; // 마지막 주장
|
||
seen: PlayerId[]; // 이 카드를 본 사람
|
||
claims: { from: PlayerId; to: PlayerId; claim: Kind }[];
|
||
deadline: number;
|
||
};
|
||
loser: PlayerId | null;
|
||
lossReason: 'four' | 'emptyHand' | null;
|
||
lastReveal?: { card: number; claim: Kind; call: 'true' | 'false'; caller: PlayerId; correct: boolean; takenBy: PlayerId };
|
||
}
|
||
```
|
||
|
||
### 8.2 액션
|
||
| type | payload | 누가 / 언제 | 검증 조건 |
|
||
|---|---|---|---|
|
||
| `offer` | `{ cardId; to; claim }` | 시작자, `card == null` | 손패에 있음, `to` != 본인, `claim`이 8종 중 하나 |
|
||
| `call` | `{ call: 'true' \| 'false' }` | 현재 holder, 아직 peek 안 함 | — |
|
||
| `peek` | `{}` | 현재 holder, 아직 안 봄 | 2인이 아님(`twoPlayerPass` 제외), 떠넘길 대상(`seen`에 없고 본인 아닌 사람)이 1명 이상. 이후 holder는 `seen`에 추가되고 view로 카드 내용을 받으며 `call` 불가 |
|
||
| `pass` | `{ to; claim }` | peek 완료한 holder | `to`가 `seen`에 없고 본인 아님, `claim`이 8종 중 하나 |
|
||
|
||
클라이언트는 서버가 카드 내용을 보내기 전에는 카드를 알 수 없으므로 "보기"와 "넘기기"를 두 액션으로 나눈다. 원작에서 "보고 나면 반드시 넘겨야 한다"는 규칙을 그대로 강제한다(peek 후 call 거부, 사유 "카드를 이미 봤으니 다른 사람에게 넘겨야 해요").
|
||
|
||
### 8.3 공개/비공개 정보 (view)
|
||
- 모두: 좌석, 각자 손패 장수, 모든 앞마당, 시작자, 현재 holder, 현재 주장, 주장 이력, `seen` 목록, 공개된 판정 결과.
|
||
- 본인: 자기 손패 내용. 건네는 중인 카드 내용은 `seen`에 있는 사람(시작자와 peek한 사람)에게만.
|
||
- 비공개: 다른 사람 손패, 건네는 중인 카드(미확인자·관전자), 2인 제거 카드(진행 중 비공개, 게임 종료 후 전체 손패와 함께 공개).
|
||
- 관전자: 공개 정보만.
|
||
|
||
### 8.4 랜덤 요소 (시드 RNG 사용 지점)
|
||
덱 셔플, 2인 제거 카드(셔플 결과의 앞 10장), 첫 시작자, 시간 초과 시 무작위 선택.
|
||
|
||
### 8.5 타이머·시간 초과·연결 끊김 시 자동 행동
|
||
- `activePlayers`: card 없으면 시작자, 있으면 holder.
|
||
- `onTimeout`:
|
||
- 시작자 → RNG로 손패 1장, 다음 좌석 플레이어에게, 주장은 RNG 종류.
|
||
- holder(미확인) → `call` 중 RNG(true/false).
|
||
- holder(peek 완료) → `seen`에 없는 첫 좌석 플레이어에게 "실제 종류"로 `pass`.
|
||
- 연결이 90초 이상 끊긴 플레이어는 차례가 오면 즉시 onTimeout 처리. 수신 대상으로는 계속 지정 가능(지정 시 바로 자동 판정).
|
||
|
||
### 8.6 종료 조건과 결과(GameResult)
|
||
```ts
|
||
interface CockroachResult {
|
||
loser: PlayerId;
|
||
reason: 'four' | 'emptyHand';
|
||
losingKind?: Kind;
|
||
ranking: PlayerId[][]; // 공동 순위 그룹, 마지막 그룹 = [loser]
|
||
fronts: Record<PlayerId, Record<Kind, number>>;
|
||
summary: string; // 예: "민수님 앞에 쥐 4장! 민수님 패배, 나머지 모두 승리"
|
||
}
|
||
```
|
||
|
||
## 9. UI/UX
|
||
- 모바일 세로: 상단 상대 플레이어 카드(아바타 + 손패 장수 + 앞마당 종류별 개수 배지, 3장째부터 노란색 경고, 위험 종류는 빨간색). 가운데 "건네지는 카드" 애니메이션과 말풍선 주장("이건 쥐야!"). 하단 내 손패(종류별 묶음 + 개수 배지, 탭으로 선택).
|
||
- 건네기 흐름: 카드 선택 → 받을 사람 탭(건넬 수 없는 사람은 회색) → 8종 그림 버튼 중 주장 선택(그림 + 글자, 각 64px 이상) → 확인.
|
||
- 판정 화면: 큰 버튼 두 개 "진짜!"(초록), "거짓말!"(빨강) + "몰래 보고 넘기기"(파랑, 넘길 사람이 없으면 비활성 + 이유).
|
||
- 공개 애니메이션: 카드 뒤집기 후 "맞혔어요!/틀렸어요!"와 앞마당으로 날아가는 연출.
|
||
- 초보자 도움말: "같은 벌레 4장 모이면 져요", 위험 경고 툴팁.
|
||
|
||
## 10. 테스트 체크리스트
|
||
- [ ] 인원별 손패 장수가 4장 표와 일치, 2인은 10장 제거 후 27장씩, 카드 총합 보존.
|
||
- [ ] 판정 정답(진실 주장에 "진실") → 전달자 앞마당, 오답 → 수신자 앞마당, 받은 사람이 다음 시작자.
|
||
- [ ] 떠넘기기 시 `seen`에 있는 사람(시작자 포함)에게 pass 거부.
|
||
- [ ] 나머지 전원이 본 상태의 holder는 peek 거부, call만 가능.
|
||
- [ ] peek 후 call 거부.
|
||
- [ ] 2인: peek 거부, 같은 종류 5장에서 패배(4장에서는 계속).
|
||
- [ ] 같은 종류 4장 도달 즉시 종료(다른 종류 4장은 무관).
|
||
- [ ] 다음 시작자의 손패가 0장 → 즉시 패배(reason emptyHand).
|
||
- [ ] 손패 0장인 사람에게 카드 건네기 허용, 그가 떠넘기기/판정 정상 동작.
|
||
- [ ] 시간 초과: 시작자 무응답 → 자동 offer, holder 무응답 → 자동 call, peek 후 무응답 → 자동 pass.
|
||
- [ ] 비공개 누출 검사: 미확인 holder·관전자·다른 플레이어 view에 건네는 카드의 kind가 없는지, 다른 사람 손패 내용이 없는지, 2인 제거 카드가 진행 중에 없는지.
|
||
- [ ] 같은 시드 + 액션 로그 재생 시 동일 결과.
|
||
|
||
## 11. 참고 자료
|
||
- 원작 규칙(영문) 요약, UltraBoardGames "How to play Cockroach Poker" — https://www.ultraboardgames.com/cockroach-poker/game-rules.php
|
||
- Wikipedia "Cockroach Poker" — https://en.wikipedia.org/wiki/Cockroach_Poker
|
||
- BoardGameGeek "Cockroach Poker" — https://boardgamegeek.com/boardgame/11971/cockroach-poker
|
||
- University of Waterloo 빠른 규칙 요약(PDF) — https://uwaterloo.ca/childrens-communication-lab/sites/default/files/uploads/documents/cockroach-poker.pdf
|
||
|
||
## 12. 메모 (상표·법적 주의 등)
|
||
- "Cockroach Poker / Kakerlakenpoker / 바퀴벌레 포커"는 Drei Magier Spiele(현 Schmidt Spiele)의 제품명이다. 규칙은 보호 대상이 아니지만 이름과 아트는 사용하지 않는다. 표시 이름 후보: "벌레 떠넘기기", "거짓말 벌레", "벌레 블러핑".
|
||
- 이름에 "포커"가 들어가지만 칩·베팅이 전혀 없는 게임이므로 도박 관련 이슈 없음. 다만 표시 이름에서 "포커"를 빼는 편이 어린이 서비스 인상에 좋다.
|
||
- 벌레 그림을 싫어하는 이용자를 위해 "동물 테마" 스킨(종류 키는 동일, 그림만 교체) 검토.
|