Files
joke-app/docs/games/yacht.md
EJClaw f4d63e1544 docs: 온라인 보드게임 사이트 계획서 (공통 14편 + 게임 26종)
- 개요, 조사(BGA·한국 인기 보드게임·UI/UX), 아키텍처, 실시간 프로토콜,
  안정성·보안, 계정, 로비·방, UI/UX, 데이터 모델, 게임 엔진, 테스트,
  배포·운영, 로드맵(M0~M9), 사용자 결정 항목
- 게임별 규칙·엔진 설계·UI·테스트 체크리스트 26종

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 04:18:29 +09:00

15 KiB
Raw Permalink Blame History

요트 다이스 (yacht)

마일스톤: M4 · 인원: 최소 1 ~ 최대 8명 · 예상 시간: 1인당 약 58분(4명 약 2030분) · 난이도: 쉬움

1. 개요

  • 주사위 5개를 한 차례에 최대 3번 굴리며(원하는 주사위는 고정), 12개 족보 칸 중 하나에 점수를 기록한다. 12라운드 후 총점이 가장 높은 사람이 이긴다.
  • 한국에서는 닌텐도 스위치 "세계의 아소비 대전 51"(영문 Clubhouse Games: 51 Worldwide Games)에 수록된 "요트"로 크게 알려졌고, 인터넷 방송과 웹 게임으로 "요트 다이스"라는 이름이 널리 쓰인다. 이 문서의 기본 규칙은 그 버전을 따른다. 미국식 상용 변형(야찌 방식 13칸)은 옵션으로 둔다.
  • 인원 근거: 요트는 전통적으로 인원 제한이 없는 주사위 게임이며 혼자서 점수 도전도 가능하다(최소 1명). 상한은 원작에 없으므로 점수표 가독성과 대기 시간을 고려해 8명으로 정한다.

2. 모드와 옵션

옵션 키 설명 선택지 기본값
scoring 점수 규칙 yacht51(12칸, 51 수록판), yahtzee(13칸, 미국식 변형) yacht51
fullHouseAllowsYacht yacht51에서 5개가 모두 같은 눈을 풀하우스로 인정할지 true, false true
yahtzeeJoker yahtzee에서 추가 요트(5개 같은 눈) 처리 forced(공식 강제 조커), free(자유 선택 조커) forced
turnSeconds 한 차례 제한 시간 30, 60, 90, null 60
showPreview 아직 안 채운 칸에 "지금 넣으면 몇 점"을 미리 표시 true, false true

3. 구성물

  • 육면체 주사위 5개(공용).
  • 플레이어별 점수표(아래 6장 칸 구성).

4. 준비(셋업)

  1. 좌석 순서를 RNG로 섞어 차례 순서를 정한다(방장이 "입장 순서 유지"를 선택하면 섞지 않음 — UI 설정, 엔진 옵션 아님. 기본은 섞음).
  2. 모든 칸 비움, 라운드 1, 첫 플레이어 차례, rollsUsed = 0, 고정(hold) 없음.

5. 진행 규칙

  • 게임은 칸 수만큼의 라운드(yacht51 12라운드, yahtzee 13라운드)로 진행된다. 한 라운드에 모든 플레이어가 차례 순서대로 한 번씩 차례를 가진다.
  • 한 차례:
    1. 첫 굴림: 주사위 5개를 모두 굴린다(ROLL). 첫 굴림 전에는 고정할 수 없고 점수를 기록할 수 없다.
    2. 두 번째·세 번째 굴림(선택): 원하는 주사위를 고정(SET_HOLD)하고 나머지만 다시 굴린다. 고정은 굴림마다 자유롭게 바꿀 수 있다(이전에 고정한 주사위를 풀어서 다시 굴려도 됨). 5개 모두 고정한 상태로는 굴릴 수 없다.
    3. 기록: 1~3번 굴린 뒤 언제든 비어 있는 칸 하나를 골라 현재 주사위 5개로 점수를 기록한다(SCORE). 조건을 만족하지 않는 칸에 넣으면 0점. 3번 굴린 뒤에는 반드시 기록해야 한다.
    4. 기록하면 차례가 끝나고 다음 사람으로 넘어간다.
  • 한 번 기록한 칸은 바꿀 수 없다. 모든 칸이 차면 그 플레이어는 끝.

6. 승패와 점수 계산

6.1 yacht51 (기본, 12칸)

칸 조건 점수 최대
에이스(1) 없음 눈 1인 주사위 눈의 합 5
듀스(2) 없음 눈 2인 주사위 눈의 합 10
트레이(3) 없음 눈 3의 합 15
포(4) 없음 눈 4의 합 20
파이브(5) 없음 눈 5의 합 25
식스(6) 없음 눈 6의 합 30
상단 보너스 위 6칸 합계가 63 이상 35 (자동) 35
초이스 없음 주사위 5개 눈의 합 30
포 카인드 같은 눈이 4개 이상 주사위 5개 눈의 합 30
풀 하우스 같은 눈 3개 + 다른 같은 눈 2개 (fullHouseAllowsYacht=true면 5개 모두 같은 눈도 인정) 주사위 5개 눈의 합 30 (인정 안 하면 28)
스몰 스트레이트 연속된 4개 눈 포함(1-2-3-4, 2-3-4-5, 3-4-5-6) 15 15
라지 스트레이트 연속된 5개 눈(1-2-3-4-5, 2-3-4-5-6) 30 30
요트 5개 모두 같은 눈 50 50
  • 총점 = 상단 6칸 합 + 상단 보너스 + 하단 6칸 합. 이론상 최대 325점(풀하우스에 요트 인정 시), 323점(불인정 시).
  • 상단 보너스는 63점(각 눈 3개씩 = 3+6+9+12+15+18) 기준이며, 상단 칸이 다 차기 전이라도 63에 도달하는 순간 확정 표시해도 된다(점수는 동일).
  • 라지 스트레이트는 스몰 스트레이트 조건도 만족한다(스몰 칸에 넣으면 15점).
  • 요트는 포 카인드 조건도 만족한다(포 카인드 칸에 넣으면 5개 합).

예시: 주사위 [6,6,6,6,2]

  • 식스 24, 듀스 2, 초이스 26, 포 카인드 26, 풀 하우스 0, 스트레이트 0, 요트 0. 예시: [3,3,5,5,5] → 풀 하우스 21, 초이스 21, 트레이 6, 파이브 15. 예시: [4,4,4,4,4] → 요트 50, 포 카인드 20, 풀 하우스 20(true일 때) 또는 0, 포 20.

6.2 yahtzee (옵션, 13칸)

칸 조건 점수
1~6 없음 해당 눈의 합
상단 보너스 상단 합 63 이상 35
쓰리 카인드 같은 눈 3개 이상 5개 합
포 카인드 같은 눈 4개 이상 5개 합
풀 하우스 3개 + 2개(서로 다른 눈) 25
스몰 스트레이트 연속 4개 포함 30
라지 스트레이트 연속 5개 40
요트(5개 같은 눈) 5개 모두 같음 50
찬스 없음 5개 합
  • 요트 보너스: 요트 칸에 이미 50점이 기록된 상태에서 다시 5개 같은 눈이 나와 어딘가에 기록하면 +100(횟수 제한 없음). 요트 칸이 0점이면 보너스 없음.
  • 조커 규칙(요트 칸이 이미 채워진 상태(50 또는 0)에서 5개 같은 눈을 기록할 때):
    • forced(공식): (1) 그 눈에 해당하는 상단 칸이 비어 있으면 반드시 거기에 기록. (2) 상단 칸이 이미 차 있으면 비어 있는 하단 칸 아무 곳에나 기록할 수 있고, 이때 풀 하우스 25, 스몰 30, 라지 40을 조건 무관하게 인정(쓰리/포 카인드·찬스는 5개 합). (3) 하단도 모두 차 있으면 비어 있는 상단 칸 하나에 0점.
    • free: 아무 빈 칸이나 고를 수 있다. 단 조커(풀 하우스·스트레이트 고정 점수 인정)는 해당 눈의 상단 칸이 이미 차 있을 때만 적용.
  • 총점 = 상단 합 + 보너스 35 + 하단 합 + 요트 보너스 합계.

6.3 순위와 동점

  • 총점 높은 순. 동점이면 공동 순위(1등 동점이면 공동 우승). 원작에 별도 동점 규칙이 없으므로 추가 판정은 하지 않는다.
  • 혼자 하는 경우 결과는 "총점"과 "개인 최고 기록 대비"만 표시(기록 저장은 SQLite에 선택적으로).

7. 지역 룰 / 하우스 룰 (옵션으로 켜고 끄기)

  • 풀 하우스에 요트 인정 여부(fullHouseAllowsYacht): 51 수록판 설명 기준으로 기본 인정. 전통 요트 규칙(풀 하우스는 서로 다른 두 눈)을 원하면 끈다.
  • 전통 요트 규칙의 포 카인드는 "같은 4개의 합"만 세는 경우가 있다(최대 24점). 수요가 있으면 fourKindScore: 'allFive' | 'fourOnly' 옵션으로 추가할 수 있다(현재 범위 밖).
  • 야찌 방식 13칸과 조커 규칙: scoring = yahtzee, yahtzeeJoker.

8. 엔진 설계

8.1 상태(State)

type Category =
  | 'ones' | 'twos' | 'threes' | 'fours' | 'fives' | 'sixes'
  | 'choice' | 'threeKind' | 'fourKind' | 'fullHouse'
  | 'smallStraight' | 'largeStraight' | 'yacht';
// yacht51 사용 칸: threeKind 제외 12칸 / yahtzee 사용 칸: 13칸 (choice = 찬스)

interface YachtState {
  options: YachtOptions;
  rng: RngState;
  order: PlayerId[];                   // 차례 순서
  current: number;                     // order 인덱스
  round: number;                       // 1부터
  totalRounds: 12 | 13;
  dice: [number, number, number, number, number] | null; // 첫 굴림 전 null
  held: [boolean, boolean, boolean, boolean, boolean];
  rollsUsed: 0 | 1 | 2 | 3;
  sheets: Record<PlayerId, {
    scores: Partial<Record<Category, number>>;
    yahtzeeBonus: number;              // yahtzee 모드 요트 보너스 누적(100 단위)
  }>;
  left: PlayerId[];                    // 기권·퇴장한 사람(남은 칸은 0점 처리)
  phase: 'playing' | 'finished';
  turnSeq: number;
  deadlineAt: number | null;
}

8.2 액션

type payload 누가 / 언제 검증 조건
ROLL 없음 현재 차례 사람, rollsUsed < 3 rollsUsed = 0이면 고정 무시(모두 굴림). 그 외 고정 안 된 주사위가 1개 이상. 위반: "이번 차례에는 더 굴릴 수 없어요." / "굴릴 주사위를 하나 이상 풀어 주세요."
SET_HOLD { held: boolean[5] } 현재 차례 사람, 1 ≤ rollsUsed < 3 길이 5. rollsUsed = 3이면 의미 없으므로 거부. (실시간으로 다른 사람 화면에도 고정 상태가 보이도록 별도 액션)
SCORE { category: Category } 현재 차례 사람, rollsUsed ≥ 1 현재 모드에서 쓰는 칸이고 비어 있음. yahtzee + forced 조커 상황이면 6.2의 강제 순서를 만족하는 칸만 허용("이 요트는 ○ 칸에 먼저 넣어야 해요.").
CONCEDE 없음 참가자 누구나 남은 칸 모두 0점 확정, 차례 순서에서 제외
  • 점수 계산은 순수 함수 scoreFor(dice, category, sheet, options) → { points, joker, yahtzeeBonus }로 분리해 클라이언트 미리보기와 서버가 같은 코드를 공유한다.
  • 이벤트: rolled{dice,held}, holdChanged, scored{player,category,points,bonus}, upperBonus{player}, turnChanged, roundChanged, gameOver.

8.3 공개/비공개 정보 (view)

  • 숨겨진 정보 없음. 모든 참가자·관전자에게 같은 View: 주사위, 고정 상태, 남은 굴림 수, 모든 점수표, 현재 차례, 남은 시간.
  • 현재 차례 사람(및 showPreview=true면 모두)에게 preview: Partial<Record<Category, number>>(빈 칸에 지금 넣을 때 점수)를 넣는다.
  • rng 상태는 View에 넣지 않는다.

8.4 랜덤 요소 (시드 RNG 사용 지점)

  • 셋업: 차례 순서 섞기(Fisher–Yates).
  • ROLL: 고정되지 않은 주사위마다 1 + floor(rng() * 6), 인덱스 0→4 순서로 호출(리플레이 일관성).

8.5 타이머·시간 초과·연결 끊김 시 자동 행동

  • deadline: 차례 시작 시 now + turnSeconds(차례 전체에 대한 시간, 굴릴 때마다 초기화하지 않음).
  • onTimeout:
    • rollsUsed = 0 → ROLL (그 다음 즉시 다시 onTimeout이 호출되어 기록).
    • rollsUsed ≥ 1 → SCORE: 허용된 빈 칸 중 점수가 가장 높은 칸. 동점이면 6.1/6.2 표의 위쪽 칸 우선. 모든 칸이 0점이면 희생 순서 ones → yacht → largeStraight → smallStraight → fullHouse → fourKind → threeKind → twos → threes → fours → fives → sixes → choice 중 첫 빈 칸.
  • 끊긴 사람: 차례가 오면 10초 유예 후 위 자동 행동. 3번 연속 자동이면 "자리 비움" 표시.

8.6 종료 조건과 결과(GameResult)

  • 마지막 라운드의 마지막 사람이 기록하면 종료(기권자는 건너뜀). 모든 사람이 기권하면 즉시 종료.
interface YachtResult {
  ranking: { rank: number; player: PlayerId; total: number; upper: number; upperBonus: number; lower: number; yahtzeeBonus: number }[];
  winners: PlayerId[];
  sheets: Record<PlayerId, Partial<Record<Category, number>>>;
  summary: string; // 예: "민수 승리! 245점 (요트 1회)"
}

9. UI/UX

  • 모바일 세로: 위에 주사위 5개(각 64px 이상, 고정된 주사위는 아래로 내려가며 자물쇠 표시), 그 아래 큰 "굴리기 (남은 2번)" 버튼, 아래 절반은 점수표. 점수표는 내 열을 크게, 다른 사람은 가로 스크롤 열 또는 탭.
  • 조작: 주사위를 누르면 고정/해제. 점수표의 빈 칸에 미리보기 점수가 회색으로 보이고, 누르면 "식스에 24점 기록할까요?" 확인 후 기록(실수 방지, 설정에서 끌 수 있음).
  • 하이라이트: 조건을 만족하는 칸은 초록색, 0점이 되는 칸은 흐리게. 상단 보너스 진행률("63까지 12점 남음") 막대.
  • 애니메이션: 주사위 굴림 0.6초(서버 결과 수신 후 재생). 요트 달성 시 큰 축하 효과.
  • 초보자 도움말: "규칙 보기"에 칸별 조건과 예시 그림. 힌트 옵션: 추천 고정(간단한 휴리스틱: 가장 많은 같은 눈 유지)을 연하게 표시.
  • 다른 사람 차례에도 주사위와 고정 상태가 실시간으로 보인다(관전 재미).

10. 테스트 체크리스트

  • yacht51 점수: [6,6,6,6,2] 각 칸 값이 6.1 예시와 일치. [3,3,5,5,5] 풀 하우스 21. [1,2,3,4,6] 스몰 15, 라지 0. [2,3,4,5,6] 스몰 15, 라지 30.
  • fullHouseAllowsYacht: [4,4,4,4,4] 풀 하우스 = 20(true) / 0(false).
  • 상단 보너스: 상단 합 62 → 0, 63 → 35.
  • 첫 굴림 전 SCORE, SET_HOLD 거부. 3번 굴린 뒤 ROLL 거부. 5개 모두 고정 후 ROLL 거부.
  • 고정 해제 후 재굴림: 2번째에 고정한 주사위를 3번째에 풀면 그 주사위도 굴려진다.
  • 이미 채운 칸 SCORE 거부. 조건 불충족 칸은 0점 기록 허용.
  • yahtzee 요트 보너스: 요트 50 기록 후 두 번째 요트 → +100. 요트 칸 0점이면 보너스 0.
  • forced 조커: 요트 칸 채움 + [5,5,5,5,5] + 파이브 칸 비어 있음 → 파이브 이외 SCORE 거부. 파이브 차 있음 → 라지 스트레이트 40 인정. 하단 모두 차 있음 → 빈 상단 칸 0점만 허용.
  • 12라운드 × 3명 진행 후 자동 종료, 동점자는 같은 rank.
  • 시간 초과: 굴리지 않은 상태 → 자동 굴림 후 최고 점수 칸 기록. 모든 칸 0점 → 희생 순서대로 기록.
  • 연결 끊김 사용자 차례가 유예 후 자동 진행되고, 다른 사람 진행이 막히지 않음.
  • 기권: 남은 칸 0점, 이후 차례에서 제외, 결과에 포함.
  • 정보 유출: View에 rng 없음. 같은 시드·액션 열 리플레이 시 같은 주사위.
  • 1인 플레이: 12라운드 연속 진행, 결과 정상.

11. 참고 자료

12. 메모 (상표·법적 주의 등)

  • "Yahtzee"(야찌)는 Hasbro의 등록 상표다. 화면·옵션 이름에 쓰지 말고 "13칸 변형", "미국식 규칙" 등으로 표기한다. "요트(Yacht)"는 오래된 공용 게임 이름이라 사용 가능하다.
  • "세계의 아소비 대전 51", "Clubhouse Games"는 닌텐도 상표이므로 화면에 "○○ 규칙"처럼 쓰지 않고 내부 문서에서만 출처로 언급한다. 표시 이름 후보: "요트 다이스", "주사위 요트".
  • 돈·포인트를 걸지 않는다. 점수는 게임 안 점수일 뿐이다.