refactor: rename game package to connect4, drop Game prefix from types
All checks were successful
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped

Rename game/ -> connect4/ to avoid c4/game stutter. Drop redundant
Game prefix from exported types (GameStore -> Store, GameInstance ->
Instance, GameStatus -> Status). Rename NATS subjects from game.{id}
to connect4.{id}. URL routes unchanged.
This commit is contained in:
Ryan Hamamura
2026-03-02 20:31:00 -10:00
parent f71acfc73e
commit 38eb9ee398
14 changed files with 125 additions and 127 deletions

126
connect4/persist.go Normal file
View File

@@ -0,0 +1,126 @@
package connect4
import (
"context"
"github.com/ryanhamamura/c4/db/repository"
"github.com/ryanhamamura/c4/player"
"github.com/rs/zerolog/log"
)
func (gi *Instance) save() error {
err := saveGame(gi.queries, gi.game)
if err != nil {
log.Error().Err(err).Str("game_id", gi.game.ID).Msg("failed to save game")
}
return err
}
func (gi *Instance) savePlayer(p *Player, slot int) error {
err := saveGamePlayer(gi.queries, gi.game.ID, p, slot)
if err != nil {
log.Error().Err(err).Str("game_id", gi.game.ID).Int("slot", slot).Msg("failed to save game player")
}
return err
}
// saveGame persists the game state via upsert.
func saveGame(queries *repository.Queries, g *Game) error {
var winnerUserID *string
if g.Winner != nil && g.Winner.UserID != nil {
winnerUserID = g.Winner.UserID
}
var winningCells *string
if wc := g.WinningCellsToJSON(); wc != "" {
winningCells = &wc
}
return queries.UpsertGame(context.Background(), repository.UpsertGameParams{
ID: g.ID,
Board: g.BoardToJSON(),
CurrentTurn: int64(g.CurrentTurn),
Status: int64(g.Status),
WinnerUserID: winnerUserID,
WinningCells: winningCells,
RematchGameID: g.RematchGameID,
})
}
func saveGamePlayer(queries *repository.Queries, gameID string, p *Player, slot int) error {
var userID, guestPlayerID *string
if p.UserID != nil {
userID = p.UserID
} else {
id := string(p.ID)
guestPlayerID = &id
}
return queries.CreateGamePlayer(context.Background(), repository.CreateGamePlayerParams{
GameID: gameID,
UserID: userID,
GuestPlayerID: guestPlayerID,
Nickname: p.Nickname,
Color: int64(p.Color),
Slot: int64(slot),
})
}
func loadGame(queries *repository.Queries, id string) (*Game, error) {
row, err := queries.GetGame(context.Background(), id)
if err != nil {
return nil, err
}
return gameFromRow(row)
}
func loadGamePlayers(queries *repository.Queries, id string) ([]*Player, error) {
rows, err := queries.GetGamePlayers(context.Background(), id)
if err != nil {
return nil, err
}
return playersFromRows(rows), nil
}
func gameFromRow(row *repository.Game) (*Game, error) {
g := &Game{
ID: row.ID,
CurrentTurn: int(row.CurrentTurn),
Status: Status(row.Status),
}
if err := g.BoardFromJSON(row.Board); err != nil {
return nil, err
}
if row.WinningCells != nil {
_ = g.WinningCellsFromJSON(*row.WinningCells)
}
if row.RematchGameID != nil {
g.RematchGameID = row.RematchGameID
}
return g, nil
}
func playersFromRows(rows []*repository.GamePlayer) []*Player {
players := make([]*Player, 0, len(rows))
for _, row := range rows {
p := &Player{
Nickname: row.Nickname,
Color: int(row.Color),
}
if row.UserID != nil {
p.UserID = row.UserID
p.ID = player.ID(*row.UserID)
} else if row.GuestPlayerID != nil {
p.ID = player.ID(*row.GuestPlayerID)
}
players = append(players, p)
}
return players
}