@fortemate/dicechess-engine-wasm 0.12.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,7 +27,7 @@ so importing both in the same application loads the shared half once.
27
27
 
28
28
  | Import | What it gives you | What it loads |
29
29
  | :--- | :--- | :--- |
30
- | `@fortemate/dicechess-engine` | everything: `DiceChess`, `EngineFacade`, the bots, `getBestMove`, equity, doubling and draw decisions | 1,308,539 B (191,262 B gzipped) |
30
+ | `@fortemate/dicechess-engine` | everything: `DiceChess`, `EngineFacade`, the legal turn tree, the bots, `getBestMove`, equity, doubling and draw decisions | 1,308,539 B (191,262 B gzipped) |
31
31
  | `@fortemate/dicechess-engine/rules` | the rules only: `getLegalUciMoves`, `generateMoves`, `applyMove`, `endTurn`, `perft`, `getPieceFromDice`, `canonicalKey` | 891,712 B (138,394 B gzipped) |
32
32
 
33
33
  Use `/rules` wherever the application validates, applies and renders moves but never asks the engine
@@ -68,44 +68,61 @@ const legalMoves = DiceChess.getLegalUciMoves(dfen);
68
68
  console.log("Legal moves in this turn:", legalMoves);
69
69
  // e.g. ["e2e3", "e2e4", "b1c3", "b1a3", ...]
70
70
 
71
- // 2. Apply a micro-move. Note that applyMove preserves the active color
72
- // (White) since a Dice Chess turn may consist of multiple micro-moves.
71
+ // 2. Get every legal turn as a prefix tree of UCI micro-moves. A node without children is a
72
+ // complete turn. getLegalUciMoves judges each position in isolation, so a client that follows
73
+ // a turn one action at a time walks this tree instead of calling getLegalUciMoves again.
74
+ const turns = DiceChess.getLegalTurnTree(dfen);
75
+ console.log("Continuations of e2e4:", Object.keys(turns["e2e4"]));
76
+ // e.g. ["b1a3", "b1c3", "g1e2", "g1f3", "g1h3"]
77
+
78
+ // The dice a legal turn can still spend: pass the rolled DFEN and the moves played since, not the
79
+ // DFEN after them, because the turn is judged as a whole. A client can dim every other die.
80
+ console.log("Playable after e2e4:", DiceChess.getPlayableDice(dfen, ["e2e4"]));
81
+ // "N"
82
+
83
+ // 3. Apply a micro-move. applyMove preserves the active color (White), since a Dice Chess turn
84
+ // may consist of multiple micro-moves, and keeps the dice the move did not spend ("N" here).
73
85
  // Arguments: (dfen, fromSquare, toSquare, optionalPromotionPiece)
74
86
  const nextDfen = DiceChess.applyMove(dfen, "e2", "e4");
75
87
  console.log("DFEN after micro-move:", nextDfen);
76
88
 
77
- // 3. Explicitly end the turn when the player has exhausted their dice.
78
- // This toggles the active color to Black, increments the move counter,
79
- // and clears any stale en-passant targets from the previous turn.
80
- const finalDfen = DiceChess.endTurn(nextDfen);
89
+ // Spend the knight die too: the turn ends at a leaf of the tree, here after e2e4 then g1f3.
90
+ const turnDfen = DiceChess.applyMove(nextDfen, "g1", "f3");
91
+
92
+ // 4. Explicitly end the turn when the player has exhausted their dice.
93
+ // This toggles the active color to Black, clears the dice pool (the full-move number advances
94
+ // only after Black's turn), and clears any stale en-passant targets from the previous turn.
95
+ const finalDfen = DiceChess.endTurn(turnDfen);
81
96
  console.log("DFEN after ending turn:", finalDfen);
82
97
 
83
- // 4. Discover available bots (search algorithms)
98
+ // 5. Discover available bots (search algorithms)
84
99
  const bots = DiceChess.getAvailableBots();
85
100
  console.log("Available bots:", bots);
86
101
  // e.g. [ { id: 'random', name: 'Random', description: '...', difficulty: 1, isExperimental: false }, ... ]
87
102
 
88
- // 5. Discover the built-in time-management policies.
103
+ // 6. Discover the built-in time-management policies.
89
104
  const timePolicies = DiceChess.getAvailableTimePolicies();
90
105
  console.log("Available time policies:", timePolicies);
91
106
  // [ "empirical-v1", "legacy-linear-v1" ]
92
107
 
93
- // 6. Compute the best sequence of micro-moves using the greedy bot search
108
+ // 7. Compute the best sequence of micro-moves using the greedy bot search.
109
+ // The bot needs Black's roll: append the dice (lowercase for Black), here a Pawn and a Knight.
94
110
  // Arguments: (dfen, optionalOptions)
95
- const botResult = DiceChess.getBestMove(finalDfen, { algorithm: "greedy" });
111
+ const rolledDfen = `${finalDfen} pn`;
112
+ const botResult = DiceChess.getBestMove(rolledDfen, { algorithm: "greedy" });
96
113
  console.log("Bot moves:", botResult.moves);
97
- // e.g. [ { from: "g8", to: "f6" } ]
114
+ // e.g. [ { from: "b8", to: "c6" }, { from: "a7", to: "a5" } ]
98
115
 
99
116
  // Clock-aware searches use empirical-v1 by default. Pass legacy-linear-v1 to
100
117
  // reproduce the original allocation for rollback or an A/B comparison.
101
- const timedResult = DiceChess.getBestMove(finalDfen, {
118
+ const timedResult = DiceChess.getBestMove(rolledDfen, {
102
119
  algorithm: "monte-carlo",
103
120
  clock: { remainingMs: 180_000, incrementMs: 2_000, moveNumber: 8 },
104
121
  timePolicy: "empirical-v1",
105
122
  });
106
123
  console.log("Effective budget (ms):", timedResult.budgetMs);
107
124
 
108
- // 7. Doubling Cube & Draw Offers (New)
125
+ // 8. Doubling Cube & Draw Offers (New)
109
126
  // Check if the bot wants to offer a double before its turn:
110
127
  const shouldDouble = DiceChess.shouldBotOfferDouble(finalDfen, 1);
111
128
 
@@ -133,7 +150,7 @@ const acceptDraw = DiceChess.shouldBotAcceptDraw(finalDfen);
133
150
 
134
151
  ## API Reference & Documentation
135
152
 
136
- * **[Interactive User & Developer Guide](https://fortemate.com/dicechess-engine/)**: Complete rules, architectural documentation, and live interactive visual catalogs.
153
+ * **[Interactive User & Developer Guide](https://fortemate.github.io/dicechess-engine/)**: Complete rules, architectural documentation, and live interactive visual catalogs.
137
154
  * **[GitHub Repository](https://github.com/fortemate/dicechess-engine)**: Source code, issue tracker, and contribution guidelines.
138
155
 
139
156
  ---
@@ -13,7 +13,10 @@ export interface EngineFacadeApi {
13
13
  getPieceTypeAt(dfen: string, square: string): number | undefined;
14
14
 
15
15
  /**
16
- * Applies a move to the given DFEN and returns the resulting state.
16
+ * Applies a move to the given DFEN and returns the resulting state, keeping the dice the move
17
+ * did not spend (castling spends the king and the rook die) and, when the DFEN carries dice,
18
+ * refusing a move no die allows.
19
+ * Same as `DiceChess.applyMove`.
17
20
  */
18
21
  applyMove(dfen: string, from: string, to: string, promotion?: string): string | undefined;
19
22
 
@@ -27,6 +30,20 @@ export interface EngineFacadeApi {
27
30
 
28
31
  export const EngineFacade: EngineFacadeApi;
29
32
 
33
+ /**
34
+ * Every legal turn of a rolled position as a prefix tree of UCI micro-moves, in the shape of
35
+ * dicechess-play-api's `MoveTree`, children in UCI order:
36
+ *
37
+ * { "e2e3": { "e3e4": {} }, "e2e4": { "e4e5": {} } }
38
+ *
39
+ * A node with no children is a complete legal turn, and every complete legal turn is such a leaf:
40
+ * a turn that captures the king ends there, and every other turn spends the most dice the roll
41
+ * allows. An empty object means the roll has no legal move.
42
+ */
43
+ export interface MoveTree {
44
+ [uci: string]: MoveTree;
45
+ }
46
+
30
47
  export type TimePolicyId = "empirical-v1" | "legacy-linear-v1";
31
48
 
32
49
  export interface ClockStateOptions {
@@ -63,9 +80,44 @@ export interface DiceChessApi {
63
80
 
64
81
  /**
65
82
  * Returns all legal moves as a flat array of UCI strings (e.g., ["e2e4", "e7e8q"]).
83
+ * They are the legal first actions of a turn from this position, judged in isolation: asked
84
+ * again after each micro-move, the answer can admit actions the whole turn does not allow.
85
+ * Use `getLegalTurnTree` to follow a turn.
66
86
  */
67
87
  getLegalUciMoves(dfen: string): string[];
68
88
 
89
+ /**
90
+ * Returns every legal turn of the rolled position as a prefix tree of UCI micro-moves.
91
+ * Its first level equals `getLegalUciMoves(dfen)`. Deeper levels can be narrower than
92
+ * `getLegalUciMoves` asked again after each micro-move, so a client that follows a turn one
93
+ * action at a time walks this tree. Empty for an invalid DFEN, a DFEN without dice, or a roll
94
+ * with no legal move.
95
+ */
96
+ getLegalTurnTree(dfen: string): MoveTree;
97
+
98
+ /**
99
+ * Returns the dice that a legal turn can still spend once `moves` have been played: those that
100
+ * at least one legal turn beginning with `moves` spends after them. A client can dim the other
101
+ * dice, which no turn left can use.
102
+ *
103
+ * The turn is judged as a whole, so pass the rolled DFEN at the start of the turn, as for
104
+ * `getLegalTurnTree`, and the UCI micro-moves played since, not the DFEN after them. `moves`
105
+ * default to none. In the start position with `QRN` this returns `"NR"`: only a knight can
106
+ * move first, but the rook can follow it.
107
+ *
108
+ * The dice come as the DFEN dice field writes them: ascending by face, in the case of the side
109
+ * to move, and a face repeated as often as the most dice showing it that one legal turn spends.
110
+ * The result is `""` when no legal turn continues: after a complete turn, a king capture
111
+ * included, and for a roll with no legal move or a DFEN without dice. It is `undefined` for an
112
+ * invalid DFEN, when `moves` is not an array of UCI strings, and when it is not the beginning
113
+ * of a legal turn.
114
+ *
115
+ * A client holding the tree needs the call only when the current node has children but no path
116
+ * below it has as many actions as there are dice left: an empty node means no die is playable,
117
+ * and a path as long as the dice left means every one of them is.
118
+ */
119
+ getPlayableDice(dfen: string, moves?: string[]): string | undefined;
120
+
69
121
  /**
70
122
  * Executes perft (performance test) counting leaf nodes at given depth.
71
123
  * @param dfen The position in DFEN notation.
@@ -119,6 +171,11 @@ export interface DiceChessApi {
119
171
 
120
172
  /**
121
173
  * Applies a move to the given DFEN and returns the resulting state.
174
+ * The result keeps the dice the move did not spend; castling spends the king and the rook die.
175
+ * Pass it back unchanged: while dice remain, appending dice to it yields an eight-field DFEN,
176
+ * which is rejected. `undefined` when the move is not pseudo-legal, when no die in the pool
177
+ * allows it, or when an argument is invalid. A position without dice, including the one
178
+ * returned once the last die is spent, accepts any pseudo-legal move.
122
179
  * @param dfen The starting board state in DiceChess FEN notation.
123
180
  * @param from The algebraic notation of the starting square.
124
181
  * @param to The algebraic notation of the target square.