games: UNO becomes a table you sit at, and the pot that pays whoever goes out first
Phase D backend: UNO is now a session like hold'em, not a single stake. You sit
with a buy-in stack, ante into a pot each hand, and leave with what's in front of
you. The engine lost its `You` constant and its measured multiples: ApplyMove
takes the acting seat, New takes a seat list, a Tier carries an ante instead of a
Base, and a hand settles by moving the pot to the winner (less rake, and never
when a bot takes it) rather than paying a multiple. A mercy kill puts a seat out
of the hand, not out of the game — the last one standing takes the pot.
The redaction moved to the web layer, where hold'em's already lives: the engine
now stamps every seat's hand onto its events, and viewUno/viewUnoEvents strip
everything that isn't the viewer's own. TestUnoViewNeverLeaksAnotherSeatsCards is
the wall. unoTable implements tableGame; /uno/{sit,move,leave,tables,stream,chat,
say} mirror hold'em, with stream/chat/say now shared game-agnostic handlers.
The frontend is not done: uno.js still calls the retired solo endpoint, so the
page renders but is not yet playable. All engine and web tests are green.
Claude-Session: https://claude.ai/code/session_013M5nD7PgUboJXoDcYHzpuJ
This commit is contained in:
@@ -290,11 +290,33 @@ func (s *Server) table(user string) (tableView, error) {
|
||||
tv := viewTrivia(g, time.Now())
|
||||
v.Trivia = &tv
|
||||
case gameUno:
|
||||
// A seated UNO player's cards are in game_tables, not here — this row is only
|
||||
// their occupancy claim. Load the table and render it as their own seat sees it.
|
||||
if live.TableID == "" {
|
||||
return s.dropUnreadable(user, v, fmt.Errorf("uno row with no table"))
|
||||
}
|
||||
t, tableSeats, err := storage.LoadTable(live.TableID)
|
||||
if errors.Is(err, storage.ErrNoSuchTable) {
|
||||
return s.dropUnreadable(user, v, fmt.Errorf("uno table %s gone", live.TableID))
|
||||
}
|
||||
if err != nil {
|
||||
return tableView{}, err
|
||||
}
|
||||
_, seat, err := storage.PlayerSeat(user)
|
||||
if err != nil {
|
||||
return tableView{}, err
|
||||
}
|
||||
var g uno.State
|
||||
if err := json.Unmarshal(live.State, &g); err != nil {
|
||||
if err := json.Unmarshal(t.State, &g); err != nil {
|
||||
return s.dropUnreadable(user, v, err)
|
||||
}
|
||||
uv := viewUno(g)
|
||||
uv := viewUno(g, seat)
|
||||
for _, ts := range tableSeats {
|
||||
if ts.Seat == seat {
|
||||
uv.BoughtIn = ts.Staked
|
||||
break
|
||||
}
|
||||
}
|
||||
v.Uno = &uv
|
||||
case gameHoldem:
|
||||
// A seated hold'em player's cards are in game_tables, not here — this row is
|
||||
|
||||
Reference in New Issue
Block a user