Skip to content

Add Phase 10 game#230

Open
odium1963 wants to merge 11 commits intoXGDevGroup:mainfrom
odium1963:feature/phase10
Open

Add Phase 10 game#230
odium1963 wants to merge 11 commits intoXGDevGroup:mainfrom
odium1963:feature/phase10

Conversation

@odium1963
Copy link
Copy Markdown
Contributor

@odium1963 odium1963 commented Apr 13, 2026

Summary

Adds a full implementation of Phase 10 for PlayPalace.

What's included

  • Game engine (games/phase10/): full turn loop, lay-down mode, hit mode, skip flow, wild placement on runs, tiebreaker, fixed-hands variant, even-phases variant
  • Evaluator (evaluator.py): set/run/color validation, can_hit_group, resolve_run_order for wild placement and group display
  • Bot (bot.py): phase-aware discard strategy, hit targeting, wild placement, skip targeting
  • State (state.py): all 10 phase definitions, player state, all four game options (winning phase, turn timer, even phases, fixed hands)
  • Localization (locales/en/phase10.ftl): complete English locale file
  • Rules document (documents/shared/phase10_rules/en.md): full player-facing rules covering card types, all phases, turn structure, keyboard shortcuts, and strategy tips
  • Tests (tests/test_phase10.py, tests/test_phase10_evaluator.py)

Keybinds

Space (draw deck), Shift+D (draw discard), N (lay down), Enter on card (hit), H (hit browse-first), J (discard focused card), F (confirm group), Escape (cancel), D (read discard), C (read table), P (phase status), E (card counts), Shift+C/H (sort hand), S (scores)

Test plan

  • CLI simulation completes a full game with bots: cd server && uv run cli.py ...
  • Serialization round-trip: --serialize flag passes without errors
  • uv run pytest server/tests/test_phase10.py server/tests/test_phase10_evaluator.py
  • All 10 phases can be laid down and validated correctly
  • Hit mode: sets, runs (including wild placement), and color groups all accept/reject correctly
  • Skip flow: target selection, once-per-round restriction
  • Even-phases and fixed-hands variants reach correct end conditions
  • Tiebreaker triggers and resolves correctly

🤖 Generated with Claude Code

odium1963 and others added 10 commits April 18, 2026 12:20
…ged label

- Prevent a card already committed to a prior lay-down group from being
  selected again in a subsequent group; speaks an error message instead.
- _get_card_label now shows "(staged)" for committed cards so they are
  visually distinct from unselected cards in the menu.
- Added phase10-card-label-staged and phase10-lay-down-card-already-staged
  locale keys.
- Rename game directory from phase_10 to phase10 (no underscore) to match
  project conventions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…flow

Evaluator:
- resolve_run_order: assign values to run cards (wilds fill gaps then extend low)
- can_hit_group: naturals must extend run at ends only; wilds rejected if run
  already spans 1-12

Game:
- Positional card action IDs (card_1..N on sorted hand) replacing card_{card.id}
- _sorted_hand_for_menu: sorts with staged cards at bottom during lay-down
- hit_wild_group_idx flow: when a wild hits a run, prompt for low/high placement
- discard_pending_card_id: two-step discard (select card, then confirm via J)
- hand_sort field + sort-by-color / sort-by-number keybinds (Shift+C / Shift+H)
- check_requirement action during lay-down (reads current group spec aloud)
- skip_targets_this_hand renamed to skip_targets_this_round
- _format_group_summary: table groups show run range / set rank / color count
- _suppress_keybind_rebuild for read-only info actions
- draw/lay-down/hit actions hidden from turn menu (keybind-only)
- Humans seated before bots on first hand
- Skip target labels include phase number
- game_winner_id tracked for build_game_result

Bot:
- Uses _card_action_id for positional card selection
- Wild placement: resolve_run_order decides low vs high
- _choose_discard avoids feeding table groups; sets discard_pending_card_id directly

FTL: wild placement prompts, group summaries, discard action labels, deck count,
     check-requirement, skip-target label, various wording cleanups

Tests: test_phase10.py, test_phase10_evaluator.py (new)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers card types, all 10 phases, scoring, turn structure, lay-down
and hit mechanics, discard flow, winning conditions, all game options,
keyboard shortcuts, and strategy tips. Modelled on the backgammon
rules document.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ction

Two corrections verified against game.py:

1. Hitting: pressing Enter on a hand card when phase is laid down and
   groups exist enters hit mode directly (no H required). H is an
   alternative that lets you enter hit mode first, then choose a card.

2. Discarding: _action_do_discard falls back to the focused menu item
   when no discard_pending_card_id is set, so J discards the focused
   card without a prior Enter selection step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The global escape → cancel keybind shadowed the platform's escape →
show_actions keybind for the entire game, making the actions menu
unreachable via backspace.

Cancel actions are already the first turn-menu item on mode entry
(rebuild_player_menu always passes position=1), so screen reader focus
lands on cancel immediately. Cancel is also listed in the actions menu.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…und scoring

Instead of scheduling timed broadcasts (which race against each player's
screen reader speech rate), show each player a personalised status box
with the full round summary. Players read at their own pace and dismiss
when ready; the next round starts independently via next_round_wait_ticks.

Also removes the scheduled-broadcast test that tested the now-removed
platform feature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Action.skip_menu_rebuild (default False). When every action fired
by a keybind opts in, the post-keybind rebuild_all_menus call is
skipped. Applied to check_scores and check_scores_detailed so pressing
S no longer resets menu cursor focus.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… actions; simplify score format

- C (read table groups) and shift+S (detailed scores) now open a status
  box instead of speaking a joined one-liner
- E (read counts) and other read-only info actions speak inline as
  one-liners, consistent with the rest of the codebase
- Score entry drops verbose "penalty points" suffix: now just
  "Player: Phase N, 123"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace hardcoded " and " with Localization.format_list_and in _commit_lay_down
- Suppress keybind rebuild in _action_read_table when table groups are present
- Route _handle_turn_timeout through self.bot_think for consistent dispatch
- Collapse Skip hit branches into single phase10-hit-invalid-skip reason key
- Remove always-true second condition from _pick_set list comprehension
- Remove dead prev_rank variable from _pick_run
- Add phase10-hit-invalid-skip FTL key; remove five orphaned FTL keys

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… stray PDF

- Guard game_active = True in _end_round with `if not game_over` so
  finish_game()'s game_active = False is not immediately overridden;
  game loop now exits cleanly instead of running to max_ticks
- _action_read_hand now uses _sorted_hand_for_menu so spoken card
  order matches what the player sees in the menu
- Remove Phase-10-rules-printable.pdf from repo root (rules already
  live in server/documents/shared/phase10_rules/en.md)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant