The full game: castling with every condition, en passant including the
case where taking would expose your own king, promotion to any of the
four, pins, checkmate, stalemate, insufficient material, the fifty-move
rule and threefold repetition.
Move generation is checked against the published perft counts rather
than against hand-written cases — the opening array to depth four
(197,281 positions) plus four standard test positions chosen precisely
because they break generators. Those counts are exact, so matching them
leaves very little room for a rule to be subtly wrong.
The opponent is a negamax search with alpha-beta, iterative deepening
and a capture-only quiescence tail, at three strengths. The quiescence
tail is the part that stops it hanging pieces: a fixed-depth search that
stops mid-exchange scores the position as though the recapture never
comes.
Two supporting changes:
The AI now thinks off the caller's thread. A card AI answers instantly,
but a chess search runs for a second or more, and the match controller's
scope belongs to the screen — searching there would freeze the board it
is drawn on.
Offline setup can seat the local player somewhere other than seat 0,
which is what lets you play Black. Seat 0 is White by definition, so
choosing a colour has to choose a seat.
FEN is read and written, so a table can start from a set position.
203 engine tests pass locally.
Three more card games, engine to screen.
Kaiser: 32-card prairie trick-taking with bidding, the five of hearts
worth five and the three of spades costing three. Bidding is capped at
ten because ten is all a hand can be worth, and a losing score gives the
game a floor — without one a table that keeps bidding and going down
never reaches a result.
Crazy 8s: eights are wild and name the suit in force, which is why the
table states that suit in words rather than leaving it to be read off
the pile. Blocked rounds are scored rather than hanging.
Wizard: the 60-card deck, one more card dealt each round until the pack
runs out, bid your tricks exactly. The shared card model gains WIZARD
and JESTER ranks, so every ordinary deck now builds from Rank.standard
rather than walking the whole enum.
Also fixes a lobby bug this would have hit: hosting seated the
catalogue minimum rather than the table the host had just set up, so a
six-handed game would have been rejected by its own rules engine.
164 engine tests pass locally.
- An 8-card variant, laid out as two rows of four. The existing column
scoring covers it without change.
- Opening cards are now chosen rather than dealt face up. Nothing starts
revealed; each player in turn turns over their allowance, and the hole
begins once everyone has picked. This needed a new SETUP phase in the
engine, since it is a real decision and has to be validated like any
other move rather than being decided at deal time.
- "Line up the final putt", off by default: throw a drawn card away
without turning your last face-down card over, so you are not forced to
close the hole on a card you did not choose. Only offered on the last
face-down card. Because a whole table could otherwise decline forever,
two full laps of nothing but thrown cards calls the hole and scores it
where it stands.
- The end of a hole now shows a score card — every player's strokes for
the hole and their running total, lowest first — and the next hole is
not dealt until it is dismissed. MatchController gained a general
awaitsConfirmation hook for this, so any game wanting a scoreboard
between rounds can stop the table the same way. Only the host waits;
clients render what they are sent.
Nine more Golf tests, covering the 8-card layout, the opening choice,
when the putt option is and is not offered, and that a table refusing to
putt out still finishes.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qjfMiPUDshdKEUa6vxqzN
Two more games on the existing framework — no changes were needed to the
lobby, networking, settings or update layers, which is what the shared
GameRules/GameAi contract was for.
President
- Threes low, twos high. Lead any number of one rank; follow with the
same count of a higher rank, or pass. When everyone has passed the
pile comes down and the last player to play leads again.
- Toggles: player count, rounds, twos clear the pile, four of a kind
bombs, and the between-round card exchange.
- Plays are offered as whole sets rather than by selecting cards. A set
is the unit of the game, so choosing from the legal sets is both
easier to operate and impossible to get wrong.
Golf
- 4, 6 or 9 card grids. Draw from stock or discard, then either swap the
card in or — only if it came off the stock — throw it and turn one of
your own over. A card taken from the discard pile has to be used.
- Matching columns cancel to nothing; on the 3x3 board rows do too, but
not on the shallower grids where three cards are not a line.
- Face-down cards are hidden from everyone including their owner, which
is the point of the game: viewFor masks them with a fixed placeholder
so the real card never reaches the device that has not turned it over.
Both engines are pure Kotlin and unit tested — 71 tests now, covering
ranking, legal moves, pile and hole resolution, scoring, redaction, wire
round-trips, and full random and AI playthroughs. The Golf test also
checks the AI actually beats random play rather than merely being legal.
Also widened the setup screen's option chips to wrap: the reveal-count
row carries up to ten, and a chip off the edge of the screen is a
setting that cannot be changed — the same failure as the Euchre Pass
button.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qjfMiPUDshdKEUa6vxqzN
A release listed all eleven games with nine of them marked "Coming
soon", so most of the menu was a dead end for anyone installing from the
production channel. Production now lists only what can be played; dev
builds still show everything, which is where seeing the roadmap is
useful.
The menu reads the installed APK's own version name, so this follows the
build rather than the update channel selected for the future — a dev
build shows the full list even if its owner has switched back to
production for updates.
CatalogTest pins both views, including that filtering never empties a
category and strands its heading.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qjfMiPUDshdKEUa6vxqzN
The real cause of being forced to pick up the turn card was the AI, not
the Pass button. Measured over 2000 deals, the computer ordered up on
100% of them and the dealer was dragged into picking up on 97.2% —
bidding never once reached the second round, so a human dealer never got
a say. Average hand scored about 11 against a bid threshold of 9, so
essentially every hand bid.
- Voids no longer count towards a hand unless there is trump left to ruff
with, which was inflating precisely the weak hands that should pass.
- Thresholds recalibrated against the actual score distribution.
- EuchreBiddingRateTest pins the result so this cannot drift back: now
32.8% ordered up in round one, dealer forced to pick up on 12.8%, and
round two reached on 67%.
Finished tricks are now held and swept, for this and any later
trick-taking game:
- The engine kept a trick only until it resolved, so the fourth card was
never visible — it vanished the instant it landed. Completed tricks are
now retained in state until the next card is played.
- The table holds them for a second, names the winner, then slides all
four towards that player's edge and fades them out. The hold scales
with the animation speed setting.
- MatchController gains a general holdBeforeNextMove hook so the AI waits
out the animation rather than playing over it.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qjfMiPUDshdKEUa6vxqzN
First vertical slice: the whole shell plus two games playable end to end,
so every layer is exercised before the other nine games are built on it.
Games
- Euchre: bowers, ordering up, dealer discard, going alone, stick the
dealer, and full scoring. Rule variants (deck size, points to win,
stick the dealer, going alone) are toggles in game setup.
- Sequence: two-eyed and one-eyed jacks, free corners, locked sequences,
the shared-chip limit, and dead-card exchange. Teams, players per team
and the sequence target are toggles.
- The board layout in SequenceBoard is generated, not the commercial
board — it holds every structural property the rules need, but does not
match a physical board. Documented in docs/RULES-sequence.md.
Multiplayer
- One transport abstraction over newline-delimited JSON, implemented
twice: LAN via NSD discovery and TCP, Bluetooth via Classic RFCOMM.
A lobby advertises on both at once, so a table can mix them.
- Host-authoritative: clients send move intents, the host re-validates
every one against the rules, and each client receives a state redacted
for its own seat so hidden information never crosses the wire.
- Seats start as AI and are handed to people as they join, which is what
makes any mix of humans and AI work without a separate code path.
Shell
- Main menu lists all eleven games; the nine without engines are marked
coming soon and are driven entirely by GameCatalog.
- Settings: sound, animation speed (one multiplier drives card movement
and AI thinking time), and OTA updates from the latest GitHub release.
- Layouts measure available space rather than assuming an orientation.
The game package is free of Android types, so the rules engines and AI
run under plain JUnit; 43 tests cover ranking, following suit, scoring,
sequence detection, redaction, wire round-trips and full random and AI
playthroughs.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qjfMiPUDshdKEUa6vxqzN