anvilsign in

collin/mahjong

1import type { Tile } from '../game/tiles';
2
3/**
4 * A hand's arrangement is the player's own business, and online it stays on
5 * their device: sending every drag over the wire and waiting for it to come
6 * back would put a round-trip inside a gesture. So the shared state carries the
7 * hand as a multiset in whatever order the engine keeps it, and each client
8 * lays its own copy out however its player left it.
9 *
10 * This is the reconciliation: keep the previous arrangement wherever the tiles
11 * are still there, drop what has gone, and append what is new at the end — the
12 * engine appends the drawn tile last, so the tile that just arrived lands in
13 * the drawn slot the UI already marks.
14 */
15export function reconcileOrder(prev: Tile[], hand: Tile[]): Tile[] {
16 const counts = new Map<Tile, number>();
17 for (const t of hand) counts.set(t, (counts.get(t) ?? 0) + 1);
18
19 const out: Tile[] = [];
20 for (const t of prev) {
21 const n = counts.get(t) ?? 0;
22 if (n > 0) {
23 counts.set(t, n - 1);
24 out.push(t);
25 }
26 }
27 // Whatever is left arrived since we last looked, in the engine's own order —
28 // which puts the newest draw at the end, where the 摸 marker expects it.
29 for (const t of hand) {
30 const n = counts.get(t) ?? 0;
31 if (n > 0) {
32 counts.set(t, n - 1);
33 out.push(t);
34 }
35 }
36 return out;
37}