Skip to content

Commit 83cfcc9

Browse files
committed
Enable fsf builtin betza premoves
1 parent 5f05639 commit 83cfcc9

7 files changed

Lines changed: 77 additions & 13 deletions

client/cataloguedPremove.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface CataloguedPremoveMetadata {
88
readonly source?: 'user' | 'fairy-stockfish-builtin';
99
readonly ini?: string;
1010
readonly baseVariant?: string;
11+
readonly betzaPieces?: Readonly<Record<string, string>>;
1112
}
1213

1314
interface CataloguedPremoveDefinition {
@@ -65,6 +66,11 @@ function parsePremoveDefinition(meta: CataloguedPremoveMetadata): CataloguedPrem
6566
const movements = new Map<string, BetzaPremove>();
6667
let kingRoleLetter: string | undefined;
6768

69+
for (const [roleLetter, betza] of Object.entries(meta.betzaPieces ?? {})) {
70+
const normalizedRole = normalRoleLetter(roleLetter);
71+
if (normalizedRole) movements.set(normalizedRole, createBetzaPremove(betza));
72+
}
73+
6874
for (const { key, value } of options) {
6975
const normalizedKey = key.toLowerCase();
7076
if (!(normalizedKey === 'king' || /^custompiece\d+$/i.test(key)) || value === '-') continue;
@@ -85,13 +91,10 @@ function parsePremoveDefinition(meta: CataloguedPremoveMetadata): CataloguedPrem
8591
}
8692

8793
export function registerCataloguedPremove(meta: CataloguedPremoveMetadata): void {
88-
// Site variants and catalogued Fairy-Stockfish built-ins continue to use
89-
// chessgroundx's established hard-coded premoves. The generic Betza path is
90-
// intentionally limited to user-defined variants.
91-
if (!meta.name || meta.source === 'fairy-stockfish-builtin') {
92-
cataloguedPremoveDefinitions.delete(meta.name);
93-
return;
94-
}
94+
// Site variants never register here and keep chessgroundx's established
95+
// premoves. Catalogued variants can supplement their base-variant fallback
96+
// with Betza movement definitions resolved by the server.
97+
if (!meta.name) return;
9598
cataloguedPremoveDefinitions.set(meta.name, parsePremoveDefinition(meta));
9699
}
97100

client/variants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,7 @@ export interface CataloguedVariantClientDocument {
20412041
readonly displayName: string;
20422042
readonly tooltip?: string;
20432043
readonly pieceNames?: Readonly<Record<string, string>>;
2044+
readonly betzaPieces?: Readonly<Record<string, string>>;
20442045
readonly ini: string;
20452046
readonly baseVariant?: string;
20462047
readonly startFen: string;

server/catalogued_betza.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -411,11 +411,9 @@ def _cached_catalogued_betza_diagrams(
411411
return tuple(diagrams)
412412

413413

414-
def catalogued_betza_diagrams(doc: Mapping[str, Any]) -> list[CataloguedBetzaDiagram]:
415-
"""Return inline SVG movement diagrams for custom and known FSF built-in pieces."""
416-
414+
def _piece_definitions_for_doc(doc: Mapping[str, Any]) -> tuple[_PieceDiagramDefinition, ...]:
417415
ini = str(doc.get("rulesIni") or doc.get("ini") or "")
418-
definitions = _cached_piece_diagram_definitions(
416+
return _cached_piece_diagram_definitions(
419417
ini,
420418
_doc_piece_letters_key(doc),
421419
_doc_piece_names_key(doc),
@@ -424,6 +422,18 @@ def catalogued_betza_diagrams(doc: Mapping[str, Any]) -> list[CataloguedBetzaDia
424422
_doc_variant_name(doc.get("baseVariant")),
425423
BETZA_DIAGRAM_RENDERER_VERSION,
426424
)
425+
426+
427+
def catalogued_betza_pieces(doc: Mapping[str, Any]) -> dict[str, str]:
428+
"""Return resolved Betza movement definitions needed by catalogued clients."""
429+
430+
return {definition.piece: definition.betza for definition in _piece_definitions_for_doc(doc)}
431+
432+
433+
def catalogued_betza_diagrams(doc: Mapping[str, Any]) -> list[CataloguedBetzaDiagram]:
434+
"""Return inline SVG movement diagrams for custom and known FSF built-in pieces."""
435+
436+
definitions = _piece_definitions_for_doc(doc)
427437
board_width = _preview_dimension(doc.get("width"))
428438
board_height = _preview_dimension(doc.get("height"))
429439
return list(

server/catalogued_variants.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import aiohttp_session
1818
from aiohttp import web
19-
from catalogued_betza import catalogued_betza_diagrams
19+
from catalogued_betza import catalogued_betza_diagrams, catalogued_betza_pieces
2020
from catalogued_board import catalogued_start_board_preview
2121
from catalogued_rules import catalogued_rule_summary
2222
from compress import MAX_COMPRESSED_BOARD_HEIGHT, MAX_COMPRESSED_BOARD_WIDTH
@@ -1062,6 +1062,7 @@ class CataloguedVariantClientDocument(TypedDict):
10621062
displayName: str
10631063
tooltip: str
10641064
pieceNames: NotRequired[dict[str, str]]
1065+
betzaPieces: NotRequired[dict[str, str]]
10651066
ini: str
10661067
baseVariant: str
10671068
startFen: str
@@ -2744,6 +2745,9 @@ def _client_doc(
27442745
piece_names = parse_catalogued_piece_names(doc.get("pieceNames"))
27452746
if piece_names:
27462747
client_doc["pieceNames"] = piece_names
2748+
betza_pieces = catalogued_betza_pieces(doc)
2749+
if betza_pieces:
2750+
client_doc["betzaPieces"] = betza_pieces
27472751
if _is_fsf_builtin_catalogued_doc(doc):
27482752
client_doc["fsfBuiltinVariant"] = _fsf_builtin_variant_name(doc)
27492753
references = _catalogued_references_for_display(doc)

tests/cataloguedPremove.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@ describe('catalogued variant premoves', () => {
4242
expect(destinations).toEqual(new Set(['d5', 'd6', 'd7', 'd8', 'c6', 'e6']));
4343
});
4444

45+
test('catalogued Fairy-Stockfish built-in pieces use server-resolved Betza movement', () => {
46+
registerCataloguedPremove({
47+
name: 'betza-test',
48+
source: 'fairy-stockfish-builtin',
49+
baseVariant: 'capablanca',
50+
ini: '',
51+
betzaPieces: { j: 'BN' },
52+
});
53+
const state = board(['d4', 'j', 'white']);
54+
const destinations = premoveForVariant('betza-test', false, dimensions)(state, 'd4', false);
55+
const expected = createBetzaPremove('BN')({ origin: [3, 3], color: 'white', board: dimensions }).map(
56+
position => util.pos2key([position[0], position[1]]),
57+
);
58+
59+
expect(new Set(destinations)).toEqual(new Set(expected));
60+
});
61+
4562
test('directional custom movement rotates for black', () => {
4663
registerCataloguedPremove({
4764
name: 'betza-test',

tests/test_catalogued_betza.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from unittest.mock import patch
55

66
import catalogued_betza
7-
from catalogued_betza import catalogued_betza_diagrams
7+
from catalogued_betza import catalogued_betza_diagrams, catalogued_betza_pieces
88

99

1010
class CataloguedBetzaDiagramTestCase(unittest.TestCase):
@@ -84,6 +84,31 @@ def test_custom_king_with_betza_is_rendered_but_bare_king_is_ignored(self):
8484
self.assertEqual(with_custom_king[0]["betza"], "KN")
8585
self.assertEqual(bare_king, [])
8686

87+
def test_resolved_piece_map_includes_fsf_builtin_betza(self):
88+
pieces = catalogued_betza_pieces(
89+
{
90+
"name": "janus",
91+
"fsfBuiltinVariant": "janus",
92+
"baseVariant": "capablanca",
93+
"ini": "",
94+
"pieces": ["p", "n", "b", "r", "q", "k", "j"],
95+
}
96+
)
97+
98+
self.assertEqual(pieces, {"j": "BN"})
99+
100+
def test_custom_piece_overrides_inherited_fsf_builtin_betza(self):
101+
pieces = catalogued_betza_pieces(
102+
{
103+
"name": "custom-janus",
104+
"baseVariant": "janus",
105+
"ini": "[custom-janus:janus]\ncustomPiece1 = j:W",
106+
"pieces": ["p", "n", "b", "r", "q", "k", "j"],
107+
}
108+
)
109+
110+
self.assertEqual(pieces, {"j": "W"})
111+
87112
def test_custom_piece_diagram_list_is_cached_by_ini_and_preview_size(self):
88113
doc = {
89114
"ini": """

tests/test_catalogued_fsf_rules_ini.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ def test_yarishogi_rules_ini_is_documentation_only(self) -> None:
2929
self.assertEqual(client_doc["ini"], "")
3030
self.assertNotIn("rulesIni", client_doc)
3131
self.assertEqual(client_doc["fsfBuiltinVariant"], "yarishogi")
32+
self.assertEqual(
33+
client_doc["betzaPieces"],
34+
{"n": "fRffN", "b": "fFfR", "r": "frlR", "g": "WfFbR", "s": "fKbR"},
35+
)
3236

3337
rule_context = catalogued_variant_rule_context(doc)
3438
self.assertTrue(rule_context["system"])

0 commit comments

Comments
 (0)