feat(stats): best-move word, moves & hint-share, and a hint-count fix (#81)
The statistics screen gains real depth, plus a hint-count bug fix found along the way. - Best move per variant: the screen shows the actual best-move word (drawn as game tiles; a wildcard shows its letter but no value), broken down by game variant, empty variants omitted. New account_best_move table, written at game finish. - Moves & hint share: two new lifetime tiles — the player's play count and the share of plays that used a hint — from summed account_stats counters (moves, hints_used). Honest-AI games are excluded, like the rest of the stats. - Hint-count fix: the in-game hint badge no longer goes stale across games. The global wallet now rides the wire apart from the per-game allowance (wallet_balance on StateView/HintResult/StatsView), so the client reads the live wallet rather than a per-game snapshot; game_players.hints_used now counts every hint (allowance + wallet), its true per-game total. - Account merge: sums the new moves/hints_used counters and merges the per-variant best moves (higher score kept), which it previously dropped. - Admin: the user card shows Moves and Hints used. - UI polish: tab/label wording, game-over text, and e2e selectors hardened against label changes. All wire additions are trailing (backward-compatible). Docs (ARCHITECTURE, FUNCTIONAL +ru, UISN_DESIGN) updated in step.
This commit was merged in pull request #81.
This commit is contained in:
+18
-1
@@ -11,6 +11,8 @@ import type {
|
||||
AccountRef,
|
||||
Banner,
|
||||
BannerCampaign,
|
||||
BestMove,
|
||||
BestMoveTile,
|
||||
BlockStatus,
|
||||
ChatMessage,
|
||||
EvalResult,
|
||||
@@ -381,6 +383,7 @@ function decodeStateViewTable(v: fb.StateView): StateView {
|
||||
rack,
|
||||
bagLen: v.bagLen(),
|
||||
hintsRemaining: v.hintsRemaining(),
|
||||
walletBalance: v.walletBalance(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -407,7 +410,7 @@ export function decodeMoveResult(buf: Uint8Array): MoveResult {
|
||||
export function decodeHintResult(buf: Uint8Array): HintResult {
|
||||
const r = fb.HintResult.getRootAsHintResult(new ByteBuffer(buf));
|
||||
const m = r.move();
|
||||
return { move: m ? decodeMove(m) : emptyMove(), hintsRemaining: r.hintsRemaining() };
|
||||
return { move: m ? decodeMove(m) : emptyMove(), hintsRemaining: r.hintsRemaining(), walletBalance: r.walletBalance() };
|
||||
}
|
||||
|
||||
export function decodeEvalResult(buf: Uint8Array): EvalResult {
|
||||
@@ -742,12 +745,26 @@ export function decodeRedeemResult(buf: Uint8Array): AccountRef {
|
||||
|
||||
export function decodeStats(buf: Uint8Array): Stats {
|
||||
const v = fb.StatsView.getRootAsStatsView(new ByteBuffer(buf));
|
||||
const bestMoves: BestMove[] = [];
|
||||
for (let i = 0; i < v.bestMovesLength(); i++) {
|
||||
const m = v.bestMoves(i);
|
||||
if (!m) continue;
|
||||
const word: BestMoveTile[] = [];
|
||||
for (let j = 0; j < m.wordLength(); j++) {
|
||||
const t = m.word(j);
|
||||
if (t) word.push({ letter: s(t.letter()), value: t.value(), blank: t.blank() });
|
||||
}
|
||||
bestMoves.push({ variant: s(m.variant()) as Variant, score: m.score(), word });
|
||||
}
|
||||
return {
|
||||
wins: v.wins(),
|
||||
losses: v.losses(),
|
||||
draws: v.draws(),
|
||||
maxGamePoints: v.maxGamePoints(),
|
||||
maxWordPoints: v.maxWordPoints(),
|
||||
moves: v.moves(),
|
||||
hintsUsed: v.hintsUsed(),
|
||||
bestMoves,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user