Files
scrabble-game/ui/src/lib/dict/testdata/sample_ru.gen.json
T
Ilia Denisov c334a9d7b7
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 1m8s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m38s
feat(offline): port DAWG cursor + move generator to TS (parity-pinned)
First engine-first step of PWA offline mode (Phase A): the client-side move
generator — the "robot brain" a local vs_ai game will run on-device — with no
runtime wiring yet (Phase B).

- dawg.ts: add the step-by-step cursor (root/final/next/arcs), a faithful port
  of dafsa traverse.go over the reader's existing bitstream.
- generate.ts: the Appel-Jacobson generator (leftPart/extendRight + cross-sets +
  counts-rack + board transpose + moveKey ranking), reusing the cursor and
  validate.ts evaluate/connected. A cross-set LetterSet is a Uint8Array, so the
  33-letter Russian alphabet (index 32) is exact under JS bit ops.
- validate.ts: export connected for the generator's connectivity filter.
- backend/cmd/movegen: dev tool building small sample dictionaries and emitting
  golden move-generation fixtures from the real Go solver (EN + RU).
- tests: dawg.cursor.test.ts (enumeration bijection vs indexOf) and
  generate.parity.test.ts (7/7 vs the Go solver: empty board, mid-game, blank,
  single-word rule, Russian index-32 cross-set). The committed EN sample also
  unblocks the existing skipped dawg.parity.test.ts once wired with DICT_* in CI.

Pure additive library code; no runtime behavior change.
2026-07-06 01:35:11 +02:00

1272 lines
20 KiB
JSON

{
"ruleset": {
"size": 33,
"cols": 15,
"center": 112,
"rackSize": 7,
"bingo": 50,
"values": [
1,
3,
1,
3,
2,
1,
3,
5,
5,
1,
4,
2,
2,
2,
1,
1,
2,
1,
1,
1,
2,
10,
5,
5,
5,
8,
10,
10,
4,
3,
8,
8,
3
],
"letterMult": [
[
1,
1,
1,
2,
1,
1,
1,
1,
1,
1,
1,
2,
1,
1,
1
],
[
1,
1,
1,
1,
1,
3,
1,
1,
1,
3,
1,
1,
1,
1,
1
],
[
1,
1,
1,
1,
1,
1,
2,
1,
2,
1,
1,
1,
1,
1,
1
],
[
2,
1,
1,
1,
1,
1,
1,
2,
1,
1,
1,
1,
1,
1,
2
],
[
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1
],
[
1,
3,
1,
1,
1,
3,
1,
1,
1,
3,
1,
1,
1,
3,
1
],
[
1,
1,
2,
1,
1,
1,
2,
1,
2,
1,
1,
1,
2,
1,
1
],
[
1,
1,
1,
2,
1,
1,
1,
1,
1,
1,
1,
2,
1,
1,
1
],
[
1,
1,
2,
1,
1,
1,
2,
1,
2,
1,
1,
1,
2,
1,
1
],
[
1,
3,
1,
1,
1,
3,
1,
1,
1,
3,
1,
1,
1,
3,
1
],
[
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1
],
[
2,
1,
1,
1,
1,
1,
1,
2,
1,
1,
1,
1,
1,
1,
2
],
[
1,
1,
1,
1,
1,
1,
2,
1,
2,
1,
1,
1,
1,
1,
1
],
[
1,
1,
1,
1,
1,
3,
1,
1,
1,
3,
1,
1,
1,
1,
1
],
[
1,
1,
1,
2,
1,
1,
1,
1,
1,
1,
1,
2,
1,
1,
1
]
],
"wordMult": [
[
3,
1,
1,
1,
1,
1,
1,
3,
1,
1,
1,
1,
1,
1,
3
],
[
1,
2,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
2,
1
],
[
1,
1,
2,
1,
1,
1,
1,
1,
1,
1,
1,
1,
2,
1,
1
],
[
1,
1,
1,
2,
1,
1,
1,
1,
1,
1,
1,
2,
1,
1,
1
],
[
1,
1,
1,
1,
2,
1,
1,
1,
1,
1,
2,
1,
1,
1,
1
],
[
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1
],
[
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1
],
[
3,
1,
1,
1,
1,
1,
1,
2,
1,
1,
1,
1,
1,
1,
3
],
[
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1
],
[
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1
],
[
1,
1,
1,
1,
2,
1,
1,
1,
1,
1,
2,
1,
1,
1,
1
],
[
1,
1,
1,
2,
1,
1,
1,
1,
1,
1,
1,
2,
1,
1,
1
],
[
1,
1,
2,
1,
1,
1,
1,
1,
1,
1,
1,
1,
2,
1,
1
],
[
1,
2,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
2,
1
],
[
3,
1,
1,
1,
1,
1,
1,
3,
1,
1,
1,
1,
1,
1,
3
]
]
},
"cases": [
{
"name": "empty-yad",
"placed": null,
"rack": {
"letters": [
32,
4,
17,
0,
18,
15
],
"blanks": 0
},
"mode": 0,
"ignoreCrossWords": false,
"moves": [
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 6,
"letter": 32,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 4,
"blank": false
}
],
"score": 10
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 32,
"blank": false
},
{
"row": 7,
"col": 8,
"letter": 4,
"blank": false
}
],
"score": 10
},
{
"dir": 1,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 32,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 4,
"blank": false
}
],
"score": 10
},
{
"dir": 1,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 32,
"blank": false
},
{
"row": 8,
"col": 7,
"letter": 4,
"blank": false
}
],
"score": 10
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 6,
"letter": 32,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 17,
"blank": false
}
],
"score": 8
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 32,
"blank": false
},
{
"row": 7,
"col": 8,
"letter": 17,
"blank": false
}
],
"score": 8
},
{
"dir": 1,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 32,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 17,
"blank": false
}
],
"score": 8
},
{
"dir": 1,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 32,
"blank": false
},
{
"row": 8,
"col": 7,
"letter": 17,
"blank": false
}
],
"score": 8
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 5,
"letter": 15,
"blank": false
},
{
"row": 7,
"col": 6,
"letter": 18,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 0,
"blank": false
}
],
"score": 6
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 6,
"letter": 0,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 4,
"blank": false
}
],
"score": 6
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 6,
"letter": 15,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 18,
"blank": false
},
{
"row": 7,
"col": 8,
"letter": 0,
"blank": false
}
],
"score": 6
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 0,
"blank": false
},
{
"row": 7,
"col": 8,
"letter": 4,
"blank": false
}
],
"score": 6
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 15,
"blank": false
},
{
"row": 7,
"col": 8,
"letter": 18,
"blank": false
},
{
"row": 7,
"col": 9,
"letter": 0,
"blank": false
}
],
"score": 6
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 32,
"blank": false
}
],
"score": 6
},
{
"dir": 1,
"tiles": [
{
"row": 5,
"col": 7,
"letter": 15,
"blank": false
},
{
"row": 6,
"col": 7,
"letter": 18,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 0,
"blank": false
}
],
"score": 6
},
{
"dir": 1,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 0,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 4,
"blank": false
}
],
"score": 6
},
{
"dir": 1,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 15,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 18,
"blank": false
},
{
"row": 8,
"col": 7,
"letter": 0,
"blank": false
}
],
"score": 6
},
{
"dir": 1,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 0,
"blank": false
},
{
"row": 8,
"col": 7,
"letter": 4,
"blank": false
}
],
"score": 6
},
{
"dir": 1,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 15,
"blank": false
},
{
"row": 8,
"col": 7,
"letter": 18,
"blank": false
},
{
"row": 9,
"col": 7,
"letter": 0,
"blank": false
}
],
"score": 6
},
{
"dir": 1,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 32,
"blank": false
}
],
"score": 6
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 6,
"letter": 0,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 17,
"blank": false
}
],
"score": 4
},
{
"dir": 0,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 0,
"blank": false
},
{
"row": 7,
"col": 8,
"letter": 17,
"blank": false
}
],
"score": 4
},
{
"dir": 1,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 0,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 17,
"blank": false
}
],
"score": 4
},
{
"dir": 1,
"tiles": [
{
"row": 7,
"col": 7,
"letter": 0,
"blank": false
},
{
"row": 8,
"col": 7,
"letter": 17,
"blank": false
}
],
"score": 4
}
]
},
{
"name": "two-ply",
"placed": [
{
"row": 7,
"col": 6,
"letter": 32,
"blank": false
},
{
"row": 7,
"col": 7,
"letter": 4,
"blank": false
}
],
"rack": {
"letters": [
32,
4,
17,
0,
18,
15
],
"blanks": 0
},
"mode": 0,
"ignoreCrossWords": false,
"moves": [
{
"dir": 0,
"tiles": [
{
"row": 8,
"col": 5,
"letter": 32,
"blank": false
},
{
"row": 8,
"col": 6,
"letter": 4,
"blank": false
}
],
"score": 14
},
{
"dir": 0,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 32,
"blank": false
},
{
"row": 6,
"col": 8,
"letter": 4,
"blank": false
}
],
"score": 12
},
{
"dir": 0,
"tiles": [
{
"row": 8,
"col": 5,
"letter": 0,
"blank": false
},
{
"row": 8,
"col": 6,
"letter": 4,
"blank": false
}
],
"score": 12
},
{
"dir": 0,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 32,
"blank": false
},
{
"row": 6,
"col": 8,
"letter": 17,
"blank": false
}
],
"score": 10
},
{
"dir": 0,
"tiles": [
{
"row": 8,
"col": 5,
"letter": 32,
"blank": false
},
{
"row": 8,
"col": 6,
"letter": 17,
"blank": false
}
],
"score": 10
},
{
"dir": 0,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 0,
"blank": false
},
{
"row": 6,
"col": 8,
"letter": 4,
"blank": false
}
],
"score": 8
},
{
"dir": 0,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 32,
"blank": false
}
],
"score": 8
},
{
"dir": 0,
"tiles": [
{
"row": 8,
"col": 5,
"letter": 0,
"blank": false
},
{
"row": 8,
"col": 6,
"letter": 17,
"blank": false
}
],
"score": 8
},
{
"dir": 1,
"tiles": [
{
"row": 8,
"col": 6,
"letter": 4,
"blank": false
}
],
"score": 7
},
{
"dir": 0,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 0,
"blank": false
},
{
"row": 6,
"col": 8,
"letter": 17,
"blank": false
}
],
"score": 6
},
{
"dir": 1,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 32,
"blank": false
}
],
"score": 5
},
{
"dir": 1,
"tiles": [
{
"row": 8,
"col": 6,
"letter": 17,
"blank": false
}
],
"score": 5
},
{
"dir": 1,
"tiles": [
{
"row": 6,
"col": 7,
"letter": 0,
"blank": false
}
],
"score": 3
}
]
}
]
}