Chinese checkers: two to six players, and a greedy jumper guaranteed to finish¶
Chinese checkers is a race for up to six players, one on each arm of a star-shaped board. The obvious way to play is to always make the move that brings your pieces nearest the far point. It works for most of a game, then breaks down near the end: pieces shuffle sideways without getting anywhere, and the last few can take many extra moves to get home. Nothing guarantees it ever finishes. This page shows how the library's grounded pattern (propose, check, then act or degrade) wraps that greedy player in two checks that guarantee it fills its goal in the solo race, and it says exactly what that guarantee does and does not cover.
It also covers the multi-player game: one agent per arm for two, three, four, five or six players, the order in which players finish, and what the guarantee still says when other players' pieces stand in the way.
This page is for anyone who wants to see act-or-degrade on a racing game, and for engineers who want a worked example of a gate whose guarantee comes from a potential: a number every allowed move must lower. No Chinese-checkers experience is needed; the rules are below.
Try it in your browser
The board below plays this game's recorded example with no download; Play live runs this package itself in your browser, in Pyodide (about 7 MB, downloaded once), and the play page has every setting.
Chinese checkers runs here when JavaScript is on; the play page has it too, and the commands below run it in a terminal.
Run it¶
uv run grounded-decision examples games_chinese_checkers # the report below
uv run grounded-decision play chinese_checkers # watch gated vs lookahead
uv run grounded-decision play chinese_checkers --you --guarded # you propose, the gate checks
uv run grounded-decision play chinese_checkers --size 6x6 # a bigger star (side 6)
uv run grounded-decision play chinese_checkers --players 3 --agents gated,lookahead,greedy
uv run grounded-decision play chinese_checkers --players 6 --view history --max-steps 30 --delay 0
uv run grounded-decision play chinese_checkers --players 4 --agents gated --coords
uv run grounded-decision play chinese_checkers --players 3 --agents you,gated,lookahead
--players N takes 1 (the solo race) to 6. --agents names one agent per
seat in turn order; a single name fills every seat, and you takes one
seat (with --agents you alone, the other seats get the default line-up:
gated, lookahead, greedy, expectimax, clairvoyant, random, first N).
--coords draws the row letters and hole numbers used in move names (on
by default when you have a seat).
Source: src/grounded_decision/examples/games/chinese_checkers_rules.py
(board and rules) and chinese_checkers.py (players, gate, report).
Tests: tests/test_games_chinese_checkers.py.
The board and the rules, exactly as implemented¶
The board is a six-pointed star of holes. The standard star has 121 holes: a hexagon of 61 in the middle and six triangular points of 10. The side of the star is the number of holes along a point's edge (4 for the standard board); every side from 1 up works, and a side-\(n\) star has \(6n(n+1)+1\) holes and \(n(n+1)/2\) pieces per player.
In the two-player text board, X starts at the bottom and races to fill
the top point; O does the opposite. Rows are lettered from the top and
holes numbered from the left of each row, so e5 is the fifth hole of
row e.
a O
b O O
c O O O
d O O O O
e . . . . . . . . . . . . .
f . . . . . . . . . . . .
g . . . . . . . . . . .
h . . . . . . . . . .
i . . . . . . . . .
j . . . . . . . . . .
k . . . . . . . . . . .
l . . . . . . . . . . . .
m . . . . . . . . . . . . .
n X X X X
o X X X
p X X
q X
Seats: one player per arm¶
The six points are the arms, named clockwise from the top: top, upper
right, lower right, bottom, lower left, upper left. Each player's pieces
start filling one arm (their home) and must fill the opposite arm
(their goal). Turns go clockwise, starting from the bottom arm.
Two players are X and O; three to six players are A to F in turn
order. These are the seatings the code uses (from the report):
| Players | Home arms, in turn order | Goals empty at the start |
|---|---|---|
| 1 (solo race) | bottom | top |
| 2 | bottom, top | none |
| 3 | bottom, upper left, upper right (every other arm) | all three |
| 4 | bottom, upper left, top, lower right (two opposite pairs) | none |
| 5 (variant) | bottom, lower left, upper left, top, upper right | lower right (C's goal) |
| 6 | all six, clockwise from the bottom | none |
Five players is not a standard seating: the arm nobody starts in is C's
goal, so C (the upper-left player) never has to wait for anybody to
leave. It is offered as a documented variant. Six players start like this
(A at the bottom, then clockwise):
a D
b D D
c D D D
d D D D D
e C C C C . . . . . E E E E
f C C C . . . . . . E E E
g C C . . . . . . . E E
h C . . . . . . . . E
i . . . . . . . . .
j B . . . . . . . . F
k B B . . . . . . . F F
l B B B . . . . . . F F F
m B B B B . . . . . F F F F
n A A A A
o A A A
p A A
q A
The rules¶
| Rule | What the code does |
|---|---|
| Turn order | Clockwise round the board; players who have finished are skipped. |
| Step | Move one piece to an adjacent empty hole (six directions). |
| Jump | Hop over one adjacent piece, of any colour, into the empty hole directly beyond. Nothing is captured. Pieces may pass through and stop in any arm. |
| Jump chains | After a jump the same piece may jump again, and may stop after any jump. A chain never lands on a hole it has already visited, its start included. |
| One move per turn | A step or one chain, never both. A player with no legal move passes. |
| Finishing | Every hole of your goal is occupied, by anyone, and at least one by your own piece (the anti-spoiling rule below). Without spoilers this is the usual "all your pieces home". A finished player keeps their placing, takes no more turns, and their pieces stay on the board (unless --clear-finished). |
| Winning and placings | The first player to finish wins. With three or more players the rest play on for the other placings; the game ends when one player is left (last place), at the no-progress rule, or at the move cap. |
| Ranking | Finishing order first; then, for players who have not finished, more pieces in their goal, then less distance to go. Players equal on all three share a placing. |
| Move cap | Every game has a cap: twice the moves a player gaining one space per move needs, for every player (480 plies for two players, 1440 for six, 240 moves in the solo race). At the cap the game ends: the winner is the first finisher if anybody finished, else the player with strictly the most pieces home, else nobody; the ranking orders everyone. In the solo race the cap means "did not finish". |
| No progress | With two or more players, the game also ends when nobody has set a new personal best distance to go for a whole window of plies (players × 2 × 4 × side: 64 for two players on the standard star, 384 for three on side 16), and is ranked as at the cap. See When the game ends. |
The solo race is the same board with only X: fill the top point in
as few moves as possible. It is the usual benchmark for Chinese-checkers
move choice, because it isolates racing skill from blocking.
The anti-spoiling rule¶
A player can spoil: leave one piece in their own starting point for ever. With two, four or six players that point is somebody's goal, so under the plain "all your pieces home" rule that player can never finish. Published rule sets differ on what to do; anti-spoiling rules say such a peg must not stop the other player from winning (see the rules at gamerules.com).
We implement that principle as the filled-goal convention: you win as soon as every hole of your goal is occupied and at least one holds your own piece. Pieces an opponent leaves in your goal then count towards filling it, so leaving them there can only help you. It is one choice among several (another lets the blocked player swap with the blocker), a house rule rather than a universal one. The same rule applies to every player whatever the number of players, and pieces parked by passers-by count the same way as spoilers. Its advantages here: it needs no extra kind of move, and since a move fills exactly one hole, which lies in at most one goal, a single move finishes at most one player, so the order of finishing is never ambiguous. The tests build a position with a spoiler in the tip of X's goal and check that X wins the moment its nine pieces fill the other holes, and the same with three players (B's piece parked in A's goal).
Measuring progress¶
A hole's distance to goal is the number of spaces between it and the tip of the player's goal point: the fewest steps a lone piece would need on an empty board. Distance to go is the sum over a player's pieces, minus the smallest possible sum, so it is 120 at the start of the standard game and 0 exactly when the goal is filled (the goal point is precisely the holes less than 4 spaces from the tip).
Measuring to the tip, rather than counting rows, matters: by rows, a piece wandering sideways into one of the side points costs nothing, and a greedy player drifts there and must walk back.
When the game ends¶
A game of three or more players can go on long after the race is decided. Weak players may never fill their goals, and pieces can get locked in for good. Four rules end every game.
Finishing with the anti-spoiling rule. A player finishes the moment
their goal is full, even if some holes are filled by other players'
pieces. The finisher's own pieces that are still outside then stay
outside for good. The game says so wherever the finish is shown: in the
outcome, in the placings line and in the player's standings line, for
example finished 1st — goal full (anti-spoiling), 7 pieces outside.
This is common with many players. In twelve six-player games of gated
players on a side-6 star (--seed 0 to --seed 11), the first finisher
had pieces outside in eleven.
Leftover pieces. By default a finished player's pieces stay on the
board. Others can jump over them, but they never move again. If they
stand in someone's goal, they count towards filling it. They can also
wall in another player's last pieces, and a walled-in piece can never
get home. --clear-finished (clear_finished=True) is the opt-in
variant: it takes a player's pieces off the board the moment they
finish. Their placing and pieces-home count do not change.
The move cap (players × 2 × the distance to cover) is a hard limit. On big stars it is very long: 35,904 plies for three players on side 16.
The no-progress rule, like the forty-move rule in checkers. Each
player's personal best is the lowest distance to go they have had so
far. A ply counts as progress if the mover sets a new personal best or
if somebody finishes. If a whole window of plies passes without
progress, the game ends. It is ranked exactly as at the cap: finishers
in order, then more pieces home, then less distance to go. The reason
reads "no progress for N plies". The window is players × 2 × 4 × side.
Here \(4 \times \text{side}\) is the longest distance on the star, from
one tip to the opposite tip. A player waits at most players − 1 plies
between turns, so every player still racing gets at least
\(8 \times \text{side}\) turns in any window. Precisely: the rule never
ends a game while some player still racing sets a new personal best at
least once in every \(8 \times \text{side}\) of their own turns.
--stall-plies N sets the window and --stall-plies 0 turns the rule
off (stall_plies=None in code). The solo race never uses this rule:
there is nobody to rank against, and the gated player's solo guarantee
below is about finishing.
Two real games show what it does. The first is two random players and a greedy one on a side-16 star:
uv run grounded-decision play chinese_checkers --players 3 --size 16x16 --agents random,random,greedy --seed 1 --view none
GAME OVER no progress for 384 plies; placings 1 C (goal full (anti-spoiling), 7 pieces outside) · 2 B · 3 A (winner: C)
moves 13069 · overrides 0 · score 1 / 2 / 129
Greedy (C) fills its goal with 129 of its 136 pieces plus the random
players' leftovers. The two random players then wander until neither has
improved its best for 384 plies, and the game ends after 13,069 plies.
With --stall-plies 0 the same game runs to the cap, 35,904 plies.
Random players keep setting new bests for a long time, because their
pieces slowly spread out of their far-away home arm, so the rule ends
these games at roughly a quarter to a half of the cap, not right after
the winner finishes. On the standard star, random players are often
still improving when the cap is reached.
The second is six gated players on a side-6 star, where leftovers lock two players in:
uv run grounded-decision play chinese_checkers --players 6 --size 6x6 --agents gated --seed 2 --view none
uv run grounded-decision play chinese_checkers --players 6 --size 6x6 --agents gated --seed 2 --view none --clear-finished
GAME OVER no progress for 288 plies; placings 1 F (goal full (anti-spoiling), 3 pieces outside) · 2 A · 3 D · 4 E (goal full (anti-spoiling), 1 piece outside) · 5 B · 6 C (winner: F)
moves 969 · overrides 77 · score 21 / 20 / 19 / 21 / 20 / 18
GAME OVER F finished first; placings 1 F (goal full (anti-spoiling), 3 pieces outside) · 2 D · 3 A · 4 C · 5 E (goal full (anti-spoiling), 1 piece outside) · 6 B (winner: F)
moves 648 · overrides 88 · score 21 / 20 / 21 / 21 / 20 / 18
With the default rules, B and C are left with pieces that never get
home. They shuffle the pieces they can move without improving, and the
no-progress rule ends the game after 969 plies. With --stall-plies 0
the game runs to the cap of 4,368 plies and ends with the same scores
and placings. With
--clear-finished, the finishers' pieces leave the board and five
players finish in 648 plies.
What the rule does not do: it does not decide who is truly stuck. A player whose distance to go goes down very slowly, like a random player, counts as progressing. A player who must back away for more than \(8 \times \text{side}\) turns before it can improve could in principle be cut off. In the tests' games between gated, greedy, lookahead and expectimax players (two to six players), the longest stretch without anybody's new best is 8 plies, against windows of 64 to 192.
The players¶
random- A uniformly random legal move.
greedy- The move that lowers distance to go the most right now. It considers every step and every jump chain of every piece (a breadth-first search over the jumps available to each piece). Ties are broken at random, seeded by the position, so every game can be replayed.
lookahead- Two of its own moves deep: it picks the move maximising "distance gained now plus the best gain of its next move", ignoring the opponent's reply (usual for race heuristics, where interaction is limited). That lets it set up ladders, rows of pieces to jump along, which greedy never does on purpose. It looks at a few hundred positions per move instead of a dozen or so.
gated- Greedy proposes; three checks decide; if any fails, the gate plays a certified move instead.
expectimax- Two of its own moves deep like
lookahead, but in between it averages over the other players' replies under a model: each plays greedy's move, or a uniformly random one 20% of the time. With three or more players the expectation over everyone's replies is estimated from a few seeded sampled lines. clairvoyant- A benchmark, not a real player: the same two-move search, but the engine shows it the actual next move of every other agent (chained in turn order until its own next turn). It measures what not seeing the future costs; see information regimes.
play chinese_checkers --list prints one line per agent, and each agent's
longer description (how it moves, what it guarantees, cost, when to use
it) is in AGENT_DETAILS in chinese_checkers.py.
What the gate checks¶
Two numbers describe how far along a player is:
- \(R\), the distance to go.
- \(\Delta\), the open-hole distance: the fewest steps in which one of the player's pieces could walk into an empty goal hole nearer the tip than itself, through empty holes, never stepping further from the tip.
The gate drives the pair \(\Phi = (R, \Delta)\) down, compared like words in a dictionary: \(R\) first, and \(\Delta\) only to break a tie.
legal: the proposal is one of the legal moves.makes_progress: the move lowers \(\Phi\): it lowers \(R\), or keeps \(R\) and brings a piece closer to an open goal hole. If no legal move can do that (possible in the two-player game, when the opponent's pieces are in the way), the move must be one of those leaving \(\Phi\) lowest.keeps_pack: no piece is left stranded. The gap is how much further from the tip the rearmost piece is than the next one. A piece with no companion within \(B = 2\) of it has nothing to jump over and must crawl one hole at a time. The move passes if afterwards the rearmost piece is on the goal's doorstep (the holes 4 from the tip, where it cannot be stranded), or the gap is at most 2, or the move narrows the gap. If no move passing check 2 can manage that, any move passing check 2 is accepted.
When a check fails the gate degrades to the certified fallback: among the moves that pass all three checks, the one leaving the least distance to go (ties go to the smallest gap). The "if no move…" clauses make that set non-empty in every position, so the fallback always exists and, by construction, always passes the checks.
The report¶
From uv run grounded-decision examples games_chinese_checkers:
== Games: Chinese checkers with progress and no-stranding checks ==
Solo race on the standard star (side 4: 121 holes, 10 pieces each): fill the opposite point.
30 races per agent (seeds 0-29); cap 240 moves.
agent finished min median mean max overrides
random 0/30 - - - - 0
(unfinished races: 0.2 pieces home on average at the cap)
greedy 30/30 54 68.0 68.9 87 0
lookahead 30/30 35 47.0 46.3 58 0
gated 30/30 54 63.0 65.7 82 79
gated_progress_only 30/30 54 67.5 67.6 83 18
Head to head on the same board (6 seeds, both seatings; anti-spoiling rule on):
pairing W D L mean plies overrides
gated vs greedy 7 0 5 115.1 35
gated vs lookahead 0 0 12 88.2 57
lookahead vs greedy 10 0 2 87.3 0
gated vs random 12 0 0 100.2 19
(W/D/L and overrides are for the first-named agent.)
A gate override (solo race, seed 0, move 38):
a X
b . X
c . X .
d . . . .
e . . . . . . . . . . . . .
f . . . . . . . . . . . .
g . . . . . . . . . . .
h . X . . . X . . X X
i . . . . . . . . .
j . X . . . . . . . .
k . . . . . . . . . . .
l . . . . . . . . . . . .
m . . . . . . . . . . . . .
n . . X .
o . . .
p X .
q .
greedy proposed n3-m7 (step, 1 nearer)
[pass] legal: the move is legal
[pass] makes_progress: distance to go 49 -> 48
[FAIL] keeps_pack: the rearmost piece would trail the next by 3 (limit 2) with nothing left near it to jump over
played instead: p1-o1 (step, 1 nearer)
A gate override (solo race, seed 1, move 74):
a X
b X X
c X X X
d X . X X
e . . . . X . . . . . . . .
f . . . . . . . . . . . .
g . . . . . . . . . . .
h . . . . . . . . . .
i . . . . . . . . .
j . . . . . . . . . .
k . . . . . . . . . . .
l . . . . . . . . . . . .
m . . . . . . . . . . . . .
n . . . .
o . . .
p . .
q .
greedy proposed d4-d2 (1 jump, level)
[pass] legal: the move is legal
[FAIL] makes_progress: distance to go 1 -> 1 and no piece gets closer to an open goal hole, although another move would make progress
[pass] keeps_pack: every piece is in or next to the goal; none can be stranded
played instead: d1-d2 (step, level)
Many players: one agent per arm, turns clockwise, the same anti-spoiling rule for everyone.
players home arms, in turn order goals empty at the start
2 bottom, top none
3 bottom, upper left, upper right top, lower right, lower left
4 bottom, upper left, top, lower right none
5 bottom, lower left, upper left, top, upper right lower right (variant)
6 bottom, lower left, upper left, top, upper right, lower right none
Seat-rotation tournaments (every line-up in each of its N rotations; place k of N earns N-k points, ties share):
3 players, 4 seeds x 3 rotations = 12 games:
agent points mean 1st places 1..N mean plies overrides
gated 10 0.83 0 0 10 2 157.7 44
greedy 2 0.17 0 0 2 10 157.7 0
lookahead 24 2.00 12 12 0 0 157.7 0
6 players, 2 seeds x 6 rotations = 12 games:
agent points mean 1st places 1..N mean plies overrides
gated#1 15 1.25 0 0 0 2 3 3 4 241.8 65
greedy#1 23 1.92 1 1 1 1 4 3 2 241.8 0
lookahead#1 48 4.00 5 5 4 2 0 1 0 241.8 0
gated#2 19 1.58 1 1 0 1 4 3 3 241.8 76
greedy#2 26 2.17 1 1 2 3 1 2 3 241.8 0
lookahead#2 49 4.08 4 4 5 3 0 0 0 241.8 0
A six-player game (seed 0; seats A-F: gated, lookahead, greedy, expectimax, gated, greedy):
A finished first; placings 1 A · 2 B · 3 D · 4 E · 5 F · 6 C after 235 plies.
The gated seats made 73 moves: 73 lowered their potential, 0 were forced (no move did), 0 passes; 16 overrides.
How to read it:
- The solo table is 30 races per player on the standard star; each
seed changes only how ties are broken. "Min / median / mean / max" are
the moves taken to fill the goal, over the races that finished.
gated_progress_onlyis the same gate withkeeps_packswitched off, to show what that check adds on its own. - The gate makes greedy a little faster and its worst case better.
Gated greedy takes 65.7 moves on average against greedy's 68.9, and its
slowest race is 82 moves against 87. About 2.6 moves per race are
overridden (79 in 30 races). Most of that comes from
makes_progress(67.6 moves with it alone);keeps_packadds the rest. - The gate does not make a stronger player. The two-move
lookaheadfinishes in 46.3 moves on average, far ahead of both, and beats gated greedy in all 12 head-to-head games. The gate only checks greedy's proposals; it adds no planning. Its value is the guarantee in the next section, which neither greedy nor lookahead has, at almost no cost per move. - Head to head, gated beats plain greedy 7 to 5 and wins every game against random. Each pairing is 6 seeds, each played with both seatings (12 games); "mean plies" counts both players' moves.
- The first showcase is the straggler failure. The piece on
p1is furthest back, and the next one, onn3, is 2 spaces ahead of it. Greedy wants to advancen3, which would leavep13 behind with no piece near enough to jump over. The gate refuses, and bringsp1up instead. - The second showcase is the endgame shuffle. One piece is left
outside the goal (on
e5) and the only empty goal hole isd2. Greedy proposes a sideways jump inside the goal that brings no piece nearer the empty hole. The gate playsd1-d2, which opensd1right in front of the last piece, so the next move,e5-d1, finishes the race.
Two to six players¶
The last part of the report plays the multi-player game.
- The seatings table is the code's own list (the same as the table in "Seats" above). Only the three-player seating starts with every goal empty; only the five-player variant leaves one goal empty.
- Seat-rotation tournaments. Each line-up plays every seed in each of
its \(N\) rotations (entrant \(i\) sits in seat \(i + r\) in rotation
\(r\)), so every agent plays every seat equally often. A match is
scored by placing: place \(k\) of \(N\) earns \(N - k\) points (2, 1
and 0 with three players; 5 down to 0 with six), and tied players
share the points of the places they cover, so each match hands out
\(N(N-1)/2\) points. "places 1..N" counts how often the agent took each
place;
#1and#2tell apart two copies of the same agent. - With three players the gate helps: gated greedy scores 10 points to
plain greedy's 2 and takes second place in 10 of 12 games, while
lookaheadwins all 12. - With six players it does not: in the crowded six-player game the
gated seats score 15 and 19 points against greedy's 23 and 26, and
lookaheadwins 9 of 12 games. The gate's checks are about the gated player's own progress and pack; on a crowded board, holding stragglers back costs places. This is the honest price of the guarantee below, which is about progress, not placing. - The six-player game (
Agated,Blookahead,Cgreedy,Dexpectimax,Egated,Fgreedy) ends after 235 plies with all six placed. Every one of the 73 moves the gated seats made lowered their own potential \(\Phi\): none was forced by blocking, and 16 were overrides of greedy's proposal.
Exactly what the gate guarantees¶
Theorem (solo race). From any position of the solo race that is not
yet won, there is a step that lowers \(\Phi\) and passes keeps_pack
without using its "if no move…" clause. So every move the gated player
makes lowers \(\Phi\) strictly: it never repeats a position, and it fills
its goal within \((R_0 + 1)(H + 1)\) moves, where \(R_0\) is the starting
distance to go and \(H\) the number of holes.
Why, in outline (the full argument is in the module docstring of
chinese_checkers.py):
- A lowering step exists. Take the empty goal hole furthest from the tip. If it is not in the goal's base row, the goal holes just behind it are full, so one of those pieces can step into it, lowering \(R\). If it is in the base row, walk any piece outside the goal straight towards the tip to the doorstep, then along the doorstep to the hole behind the empty one. The last piece on that walk has a clear path of never-outward steps into the hole, so \(\Delta\) is finite, and stepping that piece along the path lowers \(R\) or keeps \(R\) and lowers \(\Delta\).
- It can be made without stranding a piece. If the gap is under 2, one step changes it by at most 1. If the gap is 2 or more, the rearmost piece's nearer neighbour is empty (the next piece is at least 2 nearer), so stepping the rearmost piece forward lowers \(R\) and narrows the gap.
Two facts about the star make these steps possible: every hole more than 4 spaces from the tip has a neighbour one space nearer, and the doorstep is a connected line. The tests check both facts for every star side from 1 to 8. They also check the theorem itself on 400 random placements of the pieces for each side from 1 to 5, and check in full gated races (sides 2 to 5) that \(\Phi\) falls on every move.
What it does not say:
- Not optimal. The guarantee is that the race ends, not that it ends
quickly; the bound is very loose (about 15,000 moves for the standard
board, against the 54 to 82 actually taken), and
lookaheadis much faster. - Solo race only. With other players their pieces can block every move that lowers \(\Phi\). The "if no move…" clauses then apply, and nothing is proved about finishing; the move cap and the no-progress rule end every game. What still holds is the next section.
- Greedy alone also finished every race here. Nothing stops greedy from shuffling without progress for ever (it plays moves that lower neither number, which the tests confirm); these 30 races merely did not get stuck that way. The guarantee rules it out in every position.
What still holds with two to six players¶
The solo theorem is a statement about one player alone. With other players, here is exactly what remains true for the gated player, whatever the others do (the worst-case regime: no assumption about them, not even that they play independently):
- Every move it plays is legal and passes all three checks (the fallback's set is never empty, by the same argument).
- Per player, the theorem's step: whenever some legal move lowers its own \(\Phi\), the move it plays lowers it. It never wastes a turn while progress is available.
- Its distance to go \(R\) never rises on its own move unless every legal move raises it. (Only its own moves change \(R\); other players can change \(\Delta\) only by filling or freeing holes.)
- If the other pieces stop standing in its way (for example once they have all left the region between it and its goal), the solo argument applies again from that position: it finishes within \((R + 1)(H + 1)\) more moves of its own, unless the game ends first. The move cap can end it, and so can the no-progress rule, if nobody sets a new personal best for \(8 \times \text{side}\) turns of every player. The gated player's \(R\) never goes up on its own move unless it is forced, so as long as its \(R\) falls at least once in every \(8 \times \text{side}\) of its turns, the rule cannot fire. But it can make many level moves in a row that only lower \(\Delta\), so this is not proved for every game.
The report counts how often item 2's "progress is available" failed in practice: in the six-player game, none of the gated seats' 73 moves was forced.
The other two regimes mean, with \(N\) players:
expectimax(probabilistic): its scores are expectations under a model in which each other player independently plays greedy's move, or a random one 20% of the time. They say nothing about players the model does not describe.clairvoyant(benchmark): it sees the actual next move of every other predictable player until its own next turn. A human or a second clairvoyant seat cannot be foreseen; then it ignores replies and says so in its notes.
Watching a game: the board in the terminal¶
play draws the star with its true hex shape (each row offset by half a
hole). With colour, each arm is shaded in its owner's colour, pieces are
● in six colours from the Okabe–Ito palette (chosen to stay
distinguishable with the common kinds of colour blindness), and an empty
goal hole is a ring ◦ in the colour of the player who must fill it.
Without colour (NO_COLOR, --color never) each player gets a different
shape instead: ● A, ▲ B, ■ C, ◆ D, ★ E, ✚ F (X and O in
the two-player game are ● and ▲). ∘ marks the hole the last move
left, and the holes a jump chain landed on are drawn as the chain's line
(╱ ╲ ─). In the ASCII style pieces are their players' letters, + an
empty goal hole and , the hole just left.
The panel gives, for every player, a progress bar of the distance covered,
pieces home, distance to go and current placing, with a sparkline of the
last moves. This is the last frame of
uv run grounded-decision play chinese_checkers --players 6 --view history --max-steps 30 --delay 0 --style unicode --color never:
┌──────────────────────────┐ move 30 ✚ F (random): f9-h7 (1 jump, level)
│ ◦ │ moves 30 · acts 5 · overrides 0 · A to go 104 · A home 0 · A place 5 · B to go 103 · …
│ ◦ ◆ │ ● A █▍░░░░░░░░ home 0/10 · to go 104 · 5th ▁▁▃▃▄▄▆▆██
│ ◦ ◆ ◦ │ ▲ B █▍░░░░░░░░ home 0/10 · to go 103 · 4th ▁▂▂▃▃▄▄▆▆█
│ ◆ ◆ ◦ ◆ │ ■ C █▉░░░░░░░░ home 0/10 · to go 98 · 2nd ▁▂▂▂▂▅▅▆▆█
│■ ■ ■ ◦ ★ · ◆ · ★ ◦ ◦ ★ ◦ │ ◆ D █▉░░░░░░░░ home 0/10 · to go 98 · 2nd ▁▂▂▃▃▅▅▆▆█
│ ■ ◦ ◦ ▲ ■ · · · ∘ ★ ★ ★ │ ★ E ██▏░░░░░░░ home 0/10 · to go 95 · 1st ▁▁▂▂▄▄▅▅██
│ ◦ ★ ■ ★ ◆ ■ · ■ ★ ◦ ◆ │ ✚ F █░░░░░░░░░ home 0/10 · to go 108 · 6th ▁▁▂▂▄▄▅▅██
│ ■ · · ◆ · ◆ ✚ · · ★ │
│ ▲ · · · · · · · ✚ │
│ ◦ · · · · · · · · ◦ │
│ ◦ ▲ ● ✚ · ● ▲ · ✚ ◦ ✚ │
│ ◦ ▲ ◦ ▲ ▲ ● ■ · · ◦ ✚ ✚ │
│▲ ▲ ◦ ▲ · · ● · ● ◦ ✚ ✚ ✚ │
│ ● ● ◦ ● │
│ ◦ ◦ ◦ │
│ ● ● │
│ ◦ │
└──────────────────────────┘
STOPPED the move budget ran out
moves 30 · overrides 0 · score 0 / 0 / 0 / 0 / 0 / 0
placings 1 E · 2 C · 2 D · 4 B · 5 A · 6 F
After 30 plies (five moves each) nobody has a piece home yet; E has
covered the most distance, and C and D share second place. Placings
before anyone finishes come from the ranking rule (pieces home, then
distance to go). The blocks and braille styles keep the star's shape
too, at two and eight holes per character.
Performance and bigger boards¶
Neighbours and jumps of every hole are computed once per board size, so move generation never does coordinate arithmetic: for each piece it lists the empty neighbours and runs one breadth-first search over the holes reachable by jumps. The cost of a move grows with the pieces and the holes they can reach, not with a fixed board size. The open-hole distance \(\Delta\) is one breadth-first search backwards from the empty goal holes, and the gate computes it only when a move does not lower \(R\). The tests require a full standard game (gated against greedy) to finish in under two seconds, a game on a side-10 star (661 holes, 55 pieces each) to average under 10 milliseconds per move, and a full six-player game of gated players (6 × 10 pieces on the standard star) to finish within the cap at under 5 milliseconds per move.
Extending it¶
guarded(proposer)wraps any player in the same gate, including a human (--you --guarded) or thelookaheadplayer; the solo-race guarantee holds whatever the proposer suggests, because it rests only on the checks and the fallback. The end rules for several players (the no-progress rule,clear_finished) do not touch the solo race.- A different progress measure needs the two board facts above to hold
for it;
test_the_facts_the_proof_relies_onis the place to check them. - A different anti-spoiling rule is one method,
ChineseCheckers.goal_won, plus its tests. - A different seating is one entry of
SEATINGSinchinese_checkers_rules.py(home arm per seat, in turn order); the tests check every seating's geometry and the proof's two board facts for all six arms. - Tournaments for any number of players are in the engine:
rotation_tournament(game, agents, seeds=...)plays every rotation of a line-up and scores placings withranking_points;round_robindoes that for every group of \(N\) agents from a larger pool. A game ranks players through an optionalranking(state)method; without one, the winner is first and everybody else ties.