Stage 13: alphabet on the wire (UI alphabet-agnostic, TODO-4)
Tests · Go / test (push) Successful in 10s
Tests · Integration / integration (push) Successful in 12s
Tests · UI / test (push) Successful in 19s
Tests · Go / test (pull_request) Successful in 9s
Tests · Integration / integration (pull_request) Successful in 12s
Tests · UI / test (pull_request) Successful in 19s
Tests · Go / test (push) Successful in 10s
Tests · Integration / integration (push) Successful in 12s
Tests · UI / test (push) Successful in 19s
Tests · Go / test (pull_request) Successful in 9s
Tests · Integration / integration (pull_request) Successful in 12s
Tests · UI / test (pull_request) Successful in 19s
Live play now exchanges per-variant alphabet indices instead of concrete letters (rack out; submit-play, evaluate, exchange, word-check in). The client caches each variant's (index, letter, value) table behind StateRequest.include_alphabet and renders the rack and blank chooser from it, dropping the hardcoded value/alphabet tables. History, the durable journal and GCG stay decoded concrete characters (ARCHITECTURE §9.1, unchanged). - pkg/fbs: new AlphabetEntry + PlayTile; StateView.rack -> [ubyte] + alphabet; StateRequest.include_alphabet; SubmitPlay/Eval tiles -> [PlayTile]; Exchange tiles + CheckWord word -> [ubyte] (committed Go + TS regenerated). - engine: AlphabetTable + a cached per-variant codec (LetterForIndex/EncodeRack/ DecodeTiles/DecodeWord) + BlankIndex sentinel; Go parity test. - backend server edge maps index<->letter (new thin game.Service.GameVariant); game.Service domain methods, engine.Game and the robot keep one letter-based play path. The gateway forwards indices verbatim (no alphabet table). - ui: lib/alphabet.ts in-memory cache; codec encodes/decodes indices; premiums.ts is geometry-only; the mock seeds a fixture table; the UI normalises display to upper case (codec + cache), leaving placement/board/checkword unchanged. Parity moved to the Go engine.AlphabetTable test; premiums.ts loses its value tables. Discharges TODO-4.
This commit is contained in:
@@ -198,7 +198,7 @@ func submitPlayHandler(backend *backendclient.Client) Handler {
|
||||
func gameStateHandler(backend *backendclient.Client) Handler {
|
||||
return func(ctx context.Context, req Request) ([]byte, error) {
|
||||
in := fb.GetRootAsStateRequest(req.Payload, 0)
|
||||
st, err := backend.GameState(ctx, req.UserID, string(in.GameId()))
|
||||
st, err := backend.GameState(ctx, req.UserID, string(in.GameId()), in.IncludeAlphabet())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -238,17 +238,17 @@ func chatPostHandler(backend *backendclient.Client) Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// decodeTiles reads the placed tiles from a SubmitPlayRequest.
|
||||
func decodeTiles(in *fb.SubmitPlayRequest) []backendclient.TileJSON {
|
||||
// decodeTiles reads the index-addressed tiles to place from a SubmitPlayRequest (Stage 13).
|
||||
func decodeTiles(in *fb.SubmitPlayRequest) []backendclient.PlayTileJSON {
|
||||
n := in.TilesLength()
|
||||
tiles := make([]backendclient.TileJSON, 0, n)
|
||||
var t fb.TileRecord
|
||||
tiles := make([]backendclient.PlayTileJSON, 0, n)
|
||||
var t fb.PlayTile
|
||||
for i := 0; i < n; i++ {
|
||||
if in.Tiles(&t, i) {
|
||||
tiles = append(tiles, backendclient.TileJSON{
|
||||
tiles = append(tiles, backendclient.PlayTileJSON{
|
||||
Row: int(t.Row()),
|
||||
Col: int(t.Col()),
|
||||
Letter: string(t.Letter()),
|
||||
Letter: int(t.Letter()),
|
||||
Blank: t.Blank(),
|
||||
})
|
||||
}
|
||||
@@ -256,17 +256,17 @@ func decodeTiles(in *fb.SubmitPlayRequest) []backendclient.TileJSON {
|
||||
return tiles
|
||||
}
|
||||
|
||||
// decodeEvalTiles reads the tentative tiles from an EvalRequest.
|
||||
func decodeEvalTiles(in *fb.EvalRequest) []backendclient.TileJSON {
|
||||
// decodeEvalTiles reads the index-addressed tentative tiles from an EvalRequest (Stage 13).
|
||||
func decodeEvalTiles(in *fb.EvalRequest) []backendclient.PlayTileJSON {
|
||||
n := in.TilesLength()
|
||||
tiles := make([]backendclient.TileJSON, 0, n)
|
||||
var t fb.TileRecord
|
||||
tiles := make([]backendclient.PlayTileJSON, 0, n)
|
||||
var t fb.PlayTile
|
||||
for i := 0; i < n; i++ {
|
||||
if in.Tiles(&t, i) {
|
||||
tiles = append(tiles, backendclient.TileJSON{
|
||||
tiles = append(tiles, backendclient.PlayTileJSON{
|
||||
Row: int(t.Row()),
|
||||
Col: int(t.Col()),
|
||||
Letter: string(t.Letter()),
|
||||
Letter: int(t.Letter()),
|
||||
Blank: t.Blank(),
|
||||
})
|
||||
}
|
||||
@@ -274,12 +274,12 @@ func decodeEvalTiles(in *fb.EvalRequest) []backendclient.TileJSON {
|
||||
return tiles
|
||||
}
|
||||
|
||||
// decodeStringVector reads the exchange tiles from an ExchangeRequest.
|
||||
func decodeStringVector(in *fb.ExchangeRequest) []string {
|
||||
n := in.TilesLength()
|
||||
out := make([]string, 0, n)
|
||||
for i := 0; i < n; i++ {
|
||||
out = append(out, string(in.Tiles(i)))
|
||||
// bytesToInts widens a FlatBuffers ubyte vector (an alphabet-index list) to []int for the
|
||||
// backend JSON edge (Stage 13: rack-exchange tiles and the word-check query).
|
||||
func bytesToInts(bs []byte) []int {
|
||||
out := make([]int, len(bs))
|
||||
for i, b := range bs {
|
||||
out[i] = int(b)
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -319,7 +319,7 @@ func resignHandler(backend *backendclient.Client) Handler {
|
||||
func exchangeHandler(backend *backendclient.Client) Handler {
|
||||
return func(ctx context.Context, req Request) ([]byte, error) {
|
||||
in := fb.GetRootAsExchangeRequest(req.Payload, 0)
|
||||
res, err := backend.Exchange(ctx, req.UserID, string(in.GameId()), decodeStringVector(in))
|
||||
res, err := backend.Exchange(ctx, req.UserID, string(in.GameId()), bytesToInts(in.TilesBytes()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -352,7 +352,7 @@ func evaluateHandler(backend *backendclient.Client) Handler {
|
||||
func checkWordHandler(backend *backendclient.Client) Handler {
|
||||
return func(ctx context.Context, req Request) ([]byte, error) {
|
||||
in := fb.GetRootAsCheckWordRequest(req.Payload, 0)
|
||||
res, err := backend.CheckWord(ctx, req.UserID, string(in.GameId()), string(in.Word()))
|
||||
res, err := backend.CheckWord(ctx, req.UserID, string(in.GameId()), bytesToInts(in.WordBytes()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user