feat(game): official first-move tile draw + admin step-by-step replay
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
Decide who moves first by the official rule: each seated player draws one tile, the one closest to "A" leads (a blank beats every letter), ties re-drawing until a single leader remains. Each draw uses honest per-draw crypto/rand entropy (not the deterministic bag seed), so the recorded draw — not a seed — is the only account of the outcome. The leader takes seat 0, so the engine and journal replay are unchanged. The draw is recorded with the game (game_setup_draws, migration 00013) for future tournaments, designed as a discrete "player N draws a tile" step. Friend/AI games draw at creation. Auto-match draws when the game opens, against a synthetic uuid.Nil opponent whose draw rows (NULL account_id) are back-filled to the real opponent on join — so the opener's seat is fixed up front and the existing open-game pre-move is preserved with no reseating. Admin /_gm/games/:id gains the recorded draw list and a simple step-by-step board replay (game.ReplayTimeline + a vanilla-JS stepper): a board with A-O/1-15 headers and highlighted premium squares, placed letters with their tile value as a subscript, rack panels around the board (seat 0 top, 1 bottom, 2 left, 3 right) with the current player highlighted, and a per-move log with the tiles drawn and the bag remainder. Docs: ARCHITECTURE §6/§9, FUNCTIONAL (+_ru), PRERELEASE (FM row), design spec.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GameSetupDraws struct {
|
||||
GameID uuid.UUID `sql:"primary_key"`
|
||||
Round int16 `sql:"primary_key"`
|
||||
PickNo int16 `sql:"primary_key"`
|
||||
AccountID *uuid.UUID
|
||||
Letter string
|
||||
IsBlank bool
|
||||
DrawRank int16
|
||||
CreatedAt time.Time
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/postgres"
|
||||
)
|
||||
|
||||
var GameSetupDraws = newGameSetupDrawsTable("backend", "game_setup_draws", "")
|
||||
|
||||
type gameSetupDrawsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
GameID postgres.ColumnString
|
||||
Round postgres.ColumnInteger
|
||||
PickNo postgres.ColumnInteger
|
||||
AccountID postgres.ColumnString
|
||||
Letter postgres.ColumnString
|
||||
IsBlank postgres.ColumnBool
|
||||
DrawRank postgres.ColumnInteger
|
||||
CreatedAt postgres.ColumnTimestampz
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
DefaultColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type GameSetupDrawsTable struct {
|
||||
gameSetupDrawsTable
|
||||
|
||||
EXCLUDED gameSetupDrawsTable
|
||||
}
|
||||
|
||||
// AS creates new GameSetupDrawsTable with assigned alias
|
||||
func (a GameSetupDrawsTable) AS(alias string) *GameSetupDrawsTable {
|
||||
return newGameSetupDrawsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new GameSetupDrawsTable with assigned schema name
|
||||
func (a GameSetupDrawsTable) FromSchema(schemaName string) *GameSetupDrawsTable {
|
||||
return newGameSetupDrawsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new GameSetupDrawsTable with assigned table prefix
|
||||
func (a GameSetupDrawsTable) WithPrefix(prefix string) *GameSetupDrawsTable {
|
||||
return newGameSetupDrawsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new GameSetupDrawsTable with assigned table suffix
|
||||
func (a GameSetupDrawsTable) WithSuffix(suffix string) *GameSetupDrawsTable {
|
||||
return newGameSetupDrawsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newGameSetupDrawsTable(schemaName, tableName, alias string) *GameSetupDrawsTable {
|
||||
return &GameSetupDrawsTable{
|
||||
gameSetupDrawsTable: newGameSetupDrawsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newGameSetupDrawsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newGameSetupDrawsTableImpl(schemaName, tableName, alias string) gameSetupDrawsTable {
|
||||
var (
|
||||
GameIDColumn = postgres.StringColumn("game_id")
|
||||
RoundColumn = postgres.IntegerColumn("round")
|
||||
PickNoColumn = postgres.IntegerColumn("pick_no")
|
||||
AccountIDColumn = postgres.StringColumn("account_id")
|
||||
LetterColumn = postgres.StringColumn("letter")
|
||||
IsBlankColumn = postgres.BoolColumn("is_blank")
|
||||
DrawRankColumn = postgres.IntegerColumn("draw_rank")
|
||||
CreatedAtColumn = postgres.TimestampzColumn("created_at")
|
||||
allColumns = postgres.ColumnList{GameIDColumn, RoundColumn, PickNoColumn, AccountIDColumn, LetterColumn, IsBlankColumn, DrawRankColumn, CreatedAtColumn}
|
||||
mutableColumns = postgres.ColumnList{AccountIDColumn, LetterColumn, IsBlankColumn, DrawRankColumn, CreatedAtColumn}
|
||||
defaultColumns = postgres.ColumnList{IsBlankColumn, CreatedAtColumn}
|
||||
)
|
||||
|
||||
return gameSetupDrawsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
GameID: GameIDColumn,
|
||||
Round: RoundColumn,
|
||||
PickNo: PickNoColumn,
|
||||
AccountID: AccountIDColumn,
|
||||
Letter: LetterColumn,
|
||||
IsBlank: IsBlankColumn,
|
||||
DrawRank: DrawRankColumn,
|
||||
CreatedAt: CreatedAtColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
DefaultColumns: defaultColumns,
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ func UseSchema(schema string) {
|
||||
GameInvitations = GameInvitations.FromSchema(schema)
|
||||
GameMoves = GameMoves.FromSchema(schema)
|
||||
GamePlayers = GamePlayers.FromSchema(schema)
|
||||
GameSetupDraws = GameSetupDraws.FromSchema(schema)
|
||||
Games = Games.FromSchema(schema)
|
||||
Identities = Identities.FromSchema(schema)
|
||||
Sessions = Sessions.FromSchema(schema)
|
||||
|
||||
Reference in New Issue
Block a user