Maximum Micro-moves Rule Algorithm
In classic chess, a turn consists of a single move by one player. In our variant of Dice Chess, a turn consists of rolling exactly 3 dice and executing a sequence of up to 3 micro-moves (or up to 2 if castling is played, since castling consumes two dice).
A player is legally required to maximize their turn’s activity by executing the maximum achievable number of micro-moves from their rolled dice. This document describes the formal mathematical rules, edge cases, and recursive filtering algorithm used by the engine to enforce this requirement.
High-Level Execution Flow
Section titled “High-Level Execution Flow”The following flowchart illustrates how the engine evaluates and filters moves under the maximum micro-moves rule:
graph TD
A[Start Turn: Roll 3 Dice] --> B[Generate all pseudo-legal Moves for rolled dice]
B --> C{For each Move: Is it Pawn Promotion?}
C -- Yes --> D[Generate 4 distinct promotion moves: Q, R, B, N]
C -- No --> E[Normal moves]
D --> F[Simulate state after each move]
E --> F
F --> G[Recursively calculate max sequence length for remaining dice]
G --> H[Determine global maximum sequence length L*]
H --> I[Filter moves: Only keep moves that achieve L* or capture the King]
The Core Rule
Section titled “The Core Rule”When three dice are rolled, a player must play a sequence of micro-moves that uses as many of the rolled dice as possible. A first move $m_1$ is legally playable if and only if it is part of at least one sequence of moves $(m_1, m_2, \dots, m_k)$ that achieves the absolute maximum number of sequential micro-moves possible from the initial position.
- E.g., if the maximum possible sequence of moves has a length of 3, all 1-move or 2-move sequences are illegal (the player cannot choose to play a shorter sequence).
- If the maximum possible sequence length is 0 (e.g., all 3 dice rolled are Queens, but the player has no Queens on the board), the player cannot make any moves and must pass their turn.
King-Capture Exemption (Win Condition)
Section titled “King-Capture Exemption (Win Condition)”Since capturing the opponent’s king is the ultimate win condition, any move that directly captures the opponent’s king is immediately legal, even if it terminates the turn early and does not achieve the maximum possible micro-move count. Capturing the king wins the game on the spot.
Mathematical Formulation
Section titled “Mathematical Formulation”Let:
- $S$ be the current
GameState. - $D$ be the multiset (list) of available dice rolled for the turn ($|D| = 3$).
- $\text{generate}(S, d)$ be the set of pseudo-legal moves for piece type $d$ in state $S$ (as generated by
MoveGenerator). - $S.\text{makeMove}(m)$ be the state resulting from applying move $m$ to state $S$, keeping the active color unchanged.
- $\text{isKingCapture}(S, m)$ be a predicate that returns
trueif $m$ captures the opponent’s king in state $S$.
A move $m$ has a dice cost and a die consumption definition:
- Castling Move: $\text{cost}(m) = 2$ and $\text{consumed}(m) = [6, 4]$ (consumes both King and Rook dice).
- Normal Move: $\text{cost}(m) = 1$ and $\text{consumed}(m) = [d]$ (consumes a single die $d$).
We define a predicate $\text{canPay}(D, m)$ to check if the required dice are available:
- If $m$ is castling: $\text{canPay}(D, m) = \text{true}$ if ${6, 4} \subseteq D$.
- If $m$ is a normal move using die $d$: $\text{canPay}(D, m) = \text{true}$ if $d \in D$.
We define the Maximum Achievable Sequence Length $L^*(S, D)$ recursively:
$$ L^*(S, D) = \begin{cases} 0 & \text{if } D = \emptyset \ \max \Big({0} \cup \text{WinningMoves}(S, D) \cup \text{ContinuingMoves}(S, D)\Big) & \text{otherwise} \end{cases} $$
Where:
- $\text{WinningMoves}(S, D) = { \text{cost}(m) \mid d \in D, m \in \text{generate}(S, d), \text{isKingCapture}(S, m), \text{canPay}(D, m) }$
- $\text{ContinuingMoves}(S, D) = { \text{cost}(m) + L^*(S’, D \setminus \text{consumed}(m)) \mid d \in D, m \in \text{generate}(S, d), \neg\text{isKingCapture}(S, m), \text{canPay}(D, m) }$ where $S’ = S.\text{makeMove}(m).\text{copy}(\text{activeColor} = S.\text{activeColor})$ and $D \setminus \text{consumed}(m)$ removes exactly the consumed dice from the multiset $D$.
Move Legality Criterion
Section titled “Move Legality Criterion”A first move $m_1$ generated by die $d_1 \in D$ in state $S_0$ is legal if and only if:
- $\text{isKingCapture}(S_0, m_1) = \text{true}$, OR
- $\text{cost}(m_1) + L^(S_1, D \setminus \text{consumed}(m_1)) = L^(S_0, D)$ where $S_1 = S_0.\text{makeMove}(m_1).\text{copy}(\text{activeColor} = S_0.\text{activeColor})$.
Detailed Edge Cases & Exceptions
Section titled “Detailed Edge Cases & Exceptions”1. King Capture Exemption
Section titled “1. King Capture Exemption”If a move $m$ captures the opponent’s king, it is instantly legal. The game terminates. We do not evaluate subsequent moves for the remaining dice in that path.
2. Multi-Move Piece Relocation
Section titled “2. Multi-Move Piece Relocation”A single physical piece can be moved multiple times in the same turn if the player has multiple appropriate dice. E.g., if a player rolls [1, 1, 1] (Pawn, Pawn, Pawn), they can move the same Pawn three times or three different Pawns.
3. Pawn Promotion Optimization Constraint
Section titled “3. Pawn Promotion Optimization Constraint”When a pawn reaches the 8th rank, it generates 4 separate promotion moves: Queen, Rook, Bishop, Knight. Under the maximum micro-moves rule, the choice of which piece to promote to is strictly governed by whether the promoted piece enables subsequent moves using the remaining dice.
The following Mermaid diagram visualizes the two scenarios:
graph TD
subgraph Example B: No other Knight on board: roll Pawn Pawn Knight
A1[Pawn e7-e8] --> B1{Promote to Knight?}
B1 -- Yes: e7-e8=N --> C1[Knight on board] --> D1[Can play Knight move using '2' die] --> E1[Sequence length = 3 - LEGAL]
B1 -- No: e7-e8=Q --> F1[No Knight on board] --> G1[Cannot play Knight move using '2' die] --> H1[Sequence length = 2 - ILLEGAL]
end
subgraph Example A: Existing Knight on board: roll Pawn Pawn Knight
A2[Pawn e7-e8] --> B2{Promote to Queen?}
B2 -- Yes: e7-e8=Q --> C2[Existing Knight on board] --> D2[Can play Knight move using '2' die] --> E2[Sequence length = 3 - LEGAL]
B2 -- No: e7-e8=N --> F2[Two Knights on board] --> G2[Can play Knight move using '2' die] --> H2[Sequence length = 3 - LEGAL]
end
4. Castling Requires & Consumes Both King and Rook Dice
Section titled “4. Castling Requires & Consumes Both King and Rook Dice”To perform castling, two standard conditions must be met regarding the dice:
- Both the King (
6) and Rook (4) dice must be present in the rolled dice multiset. - Playing castling consumes both the King (
6) and Rook (4) dice simultaneously (cost of 2).
- E.g., if the dice are
[6, 4, 1](King, Rook, Pawn) and the player castles, the remaining dice list becomes[1](Pawn). The player can then make a Pawn move. - E.g., if the dice are
[6, 4, 4](King, Rook, Rook) and the player castles, the remaining dice list becomes[4](Rook), allowing them to move the castled rook (or another rook) again. - E.g., if the dice are
[6, 5, 1](King, Queen, Pawn) and no Rook die is rolled, castling is illegal and cannot be played.
5. Intermediate Checks & King Safety
Section titled “5. Intermediate Checks & King Safety”Unlike classic chess, a player is allowed to move their king through checked squares or leave their king in check during the intermediate micro-moves of their turn. The king is also fully allowed to step into check or stay in check at the end of their turn (for example, to make a highly advantageous capture like eating an enemy Queen). The only constraint is that the king cannot be captured by the opponent. If a king is left in check, the opponent can capture it on their turn if they roll the correct die.
Step-by-Step Worked Examples
Section titled “Step-by-Step Worked Examples”Example 1: Starting Position with [1, 4, 2] (Pawn, Rook, Knight)
Section titled “Example 1: Starting Position with [1, 4, 2] (Pawn, Rook, Knight)”- Initial State ($S_0$): Starting board. All 8 white pawns are on the 2nd rank. White rooks are on $a1$ and $h1$, completely blocked.
- Available Dice ($D$):
[1, 4, 2](Pawn, Rook, Knight). - Evaluation:
- Path A (Pawn
e2-e3):- $S_1 = S_0.\text{makeMove}(\text{e2-e3})$. Rooks are still blocked by $a2, h2, b1, g1$.
- Generating Rook moves in $S_1$ yields $\emptyset$.
- Generating Knight moves in $S_1$ yields
[Nb1-c3]. - $L^*(S_1, [4, 2]) = 1$. Total sequence length = $1 + 1 = 2$.
- Path B (Pawn
a2-a3):- $S_1 = S_0.\text{makeMove}(\text{a2-a3})$. The path for the $a1$ rook is now open.
- Generating Rook moves in $S_1$ yields
[a1-a2]. - If we play Rook
a1-a2to get $S_2$, the remaining dice is[2](Knight). Knight can move toc3. - $L^*(S_1, [4, 2]) = 2$. Total sequence length = $1 + 2 = 3$.
- Conclusion: $L^*(S_0, [1, 4, 2]) = 3$. Move
e2-e3is illegal (length 2 < 3). Movea2-a3is legal (length 3 = 3).
- Path A (Pawn
Example 2: Pawn Promotion with [1, 5, 2] (Pawn, Queen, Knight)
Section titled “Example 2: Pawn Promotion with [1, 5, 2] (Pawn, Queen, Knight)”- Initial State ($S_0$): White pawn on $e7$. White has no queens.
- Available Dice ($D$):
[1, 5, 2](Pawn, Queen, Knight). - Evaluation:
- Since there are no queens on the board, playing Queen first is impossible.
- Playing Pawn first:
e7-e8=Qpromotes the pawn to a Queen. - In the resulting state $S_1$, a Queen is now on $e8$.
- Generating Queen moves in $S_1$ yields multiple legal options.
- $L^*(S_1, [5, 2]) = 2$ (Queen move then Knight move). Total sequence length = $1 + 2 = 3$.
- Conclusion: The pawn promotion move
e7-e8=Qis legal because it allows a subsequent Queen move, maximizing the sequence length to 3.
Example 3: King Capture with [2, 3, 5] (Knight, Bishop, Queen)
Section titled “Example 3: King Capture with [2, 3, 5] (Knight, Bishop, Queen)”- Initial State ($S_0$): White Knight can capture the Black King on $f7$. White Bishop and Queen are completely trapped behind friendly pawns and have no moves.
- Available Dice ($D$):
[2, 3, 5](Knight, Bishop, Queen). - Evaluation:
- The Knight capture
Nxf7#(capturing the King) is a winning move. - Even though the Bishop and Queen are trapped and $L^*(S_1, [3, 5]) = 0$, the Knight capture is immediately legal due to the King-Capture Exemption.
- The Knight capture
Pseudocode Implementation
Section titled “Pseudocode Implementation”Helper: canContinueAfterMove
Section titled “Helper: canContinueAfterMove”Recursively checks if there exists a sequence of moves using the remaining dice that achieves the targeted remaining length.
function canContinueAfterMove(state: GameState, remainingDice: List[Int], targetLength: Int): Boolean = { if (targetLength <= 0) { return true }
for (die <- remainingDice.distinct) { val moves = MoveGenerator.generateMoves(state, die)
for (m <- moves) { if (isKingCapture(state, m)) { return true }
if (m.isCastling) { // Castling requires and consumes BOTH King (6) and Rook (4) dice if (remainingDice.contains(6) && remainingDice.contains(4)) { val remainingAfterCastle = remainingDice.removeFirstOccurrence(6).removeFirstOccurrence(4) val nextState = state.makeMove(m).copy(activeColor = state.activeColor)
if (canContinueAfterMove(nextState, remainingAfterCastle, targetLength - 2)) { return true } } } else { // Normal move consumes a single die val remainingAfterDie = remainingDice.removeFirstOccurrence(die) val nextState = state.makeMove(m).copy(activeColor = state.activeColor)
if (canContinueAfterMove(nextState, remainingAfterDie, targetLength - 1)) { return true } } } }
return false}Main Filter: filterMaximalMoves
Section titled “Main Filter: filterMaximalMoves”Determines the maximum achievable turn length and filters the initial set of moves.
function filterMaximalMoves(state: GameState, dice: List[Int]): List[Move] = { if (dice.isEmpty) { return Nil }
// Step 1: Generate all possible first moves across all available dice val firstMoves = dice.distinct.flatMap(d => MoveGenerator.generateMoves(state, d))
// Step 2: Identify any immediate king captures (instantly legal) val kingCaptures = firstMoves.filter(m => isKingCapture(state, m))
// Step 3: Determine the maximum possible sequence length for all paths var maxSequenceLength = 0 val moveLengths = Map[Move, Int]()
for (m <- firstMoves) { if (isKingCapture(state, m)) { moveLengths(m) = 1 if (1 > maxSequenceLength) { maxSequenceLength = 1 } } else if (m.isCastling) { // Castling must have both 6 and 4 available if (dice.contains(6) && dice.contains(4)) { val remainingDice = dice.removeFirstOccurrence(6).removeFirstOccurrence(4) val nextState = state.makeMove(m).copy(activeColor = state.activeColor)
var length = 2 // Try to see if we can continue with the remaining 1 die if (remainingDice.nonEmpty && canContinueAfterMove(nextState, remainingDice, 1)) { length = 3 }
moveLengths(m) = length if (length > maxSequenceLength) { maxSequenceLength = length } } else { // Castling is impossible without both dice moveLengths(m) = 0 } } else { // Normal move val d = getDieForMove(state, m, dice) val remainingDice = dice.removeFirstOccurrence(d) val nextState = state.makeMove(m).copy(activeColor = state.activeColor)
var length = 1 // See if we can play 1 or 2 more moves if (canContinueAfterMove(nextState, remainingDice, 1)) { length = 2 if (canContinueAfterMove(nextState, remainingDice, 2)) { length = 3 } }
moveLengths(m) = length if (length > maxSequenceLength) { maxSequenceLength = length } } }
// Step 4: Filter moves that achieve the maximum sequence length (or are king captures) val legalMoves = firstMoves.filter { m => isKingCapture(state, m) || (moveLengths(m) == maxSequenceLength && moveLengths(m) > 0) }
return legalMoves}