Stage 7 polish: game rework + board zoom + tests (Parts D/E/F/I)
- Board: fixed-viewport transform-scale zoom (animated) with counter-scaled cqw labels, corner letters, bonus-label modes (boardlabels), contrasting grid lines
- Game: Screen shell + game tab-bar (Draw/Skip/Hint/Shuffle) via HoldConfirm popovers; MakeMove 🏁 + compact popup; rack collapses used slots; hint places tiles on board (placementFromHint) + no_hint_available toast; Scores:N replaces Hints; history slide-down (swipe/click, scroll-locked); check-word alphabet/length limit + in-memory cache + 5s throttle
- backend: no_hint_available result code split + test
- vitest: banner rotator + linkify, resultBadge, boardlabels, placementFromHint (29 tests); Playwright smoke updated; prod bundle ~74 KB gzip
This commit is contained in:
+20
-1
@@ -4,7 +4,7 @@
|
||||
// payload. It is board-agnostic (the gateway/engine does full legality validation at
|
||||
// submit), which keeps it trivially unit-testable.
|
||||
|
||||
import type { Direction } from './model';
|
||||
import type { Direction, Tile } from './model';
|
||||
import type { PlacedTile } from './client';
|
||||
|
||||
export interface PendingTile {
|
||||
@@ -36,6 +36,25 @@ export function newPlacement(rack: string[]): Placement {
|
||||
return { rack: [...rack], pending: [] };
|
||||
}
|
||||
|
||||
/**
|
||||
* placementFromHint turns a hint move's tiles into a pending placement by matching each
|
||||
* tile to a rack slot (a blank "?" for blank tiles, else the matching letter), so the
|
||||
* player sees the suggested move laid out and decides whether to commit it.
|
||||
*/
|
||||
export function placementFromHint(tiles: Tile[], rack: string[]): Placement {
|
||||
const used = new Set<number>();
|
||||
const pending: PendingTile[] = [];
|
||||
const take = (pred: (letter: string, i: number) => boolean) => rack.findIndex((l, i) => !used.has(i) && pred(l, i));
|
||||
for (const t of tiles) {
|
||||
let idx = t.blank ? take((l) => l === BLANK) : take((l) => l === t.letter.toUpperCase());
|
||||
if (idx < 0) idx = take((l) => l === BLANK); // fall back to a blank
|
||||
if (idx < 0) continue;
|
||||
used.add(idx);
|
||||
pending.push({ rackIndex: idx, row: t.row, col: t.col, letter: t.letter.toUpperCase(), blank: rack[idx] === BLANK });
|
||||
}
|
||||
return { rack: [...rack], pending };
|
||||
}
|
||||
|
||||
function usedIndexes(p: Placement): Set<number> {
|
||||
return new Set(p.pending.map((t) => t.rackIndex));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user