feat: add configurable speed and expanded grid presets for snake

- Add per-game speed setting with presets (Slow/Normal/Fast/Insane)
- Add speed selector UI in snake lobby
- Expand grid presets with Tiny (15x15) and XL (50x30)
- Auto-calculate cell size based on grid dimensions
- Preserve speed setting in rematch games
This commit is contained in:
Ryan Hamamura
2026-02-04 10:02:40 -10:00
parent f454e0d220
commit e239e948ae
13 changed files with 199 additions and 69 deletions

View File

@@ -13,7 +13,7 @@ import (
const createGame = `-- name: CreateGame :one
INSERT INTO games (id, board, current_turn, status)
VALUES (?, ?, ?, ?)
RETURNING id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score
RETURNING id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed
`
type CreateGameParams struct {
@@ -47,6 +47,7 @@ func (q *Queries) CreateGame(ctx context.Context, arg CreateGameParams) (Game, e
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
)
return i, err
}
@@ -87,7 +88,7 @@ func (q *Queries) DeleteGame(ctx context.Context, id string) error {
}
const getActiveGames = `-- name: GetActiveGames :many
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score FROM games WHERE game_type = 'connect4' AND status < 2
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE game_type = 'connect4' AND status < 2
`
func (q *Queries) GetActiveGames(ctx context.Context) ([]Game, error) {
@@ -115,6 +116,7 @@ func (q *Queries) GetActiveGames(ctx context.Context) ([]Game, error) {
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
); err != nil {
return nil, err
}
@@ -130,7 +132,7 @@ func (q *Queries) GetActiveGames(ctx context.Context) ([]Game, error) {
}
const getGame = `-- name: GetGame :one
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score FROM games WHERE id = ?
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE id = ?
`
func (q *Queries) GetGame(ctx context.Context, id string) (Game, error) {
@@ -152,6 +154,7 @@ func (q *Queries) GetGame(ctx context.Context, id string) (Game, error) {
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
)
return i, err
}
@@ -192,7 +195,7 @@ func (q *Queries) GetGamePlayers(ctx context.Context, gameID string) ([]GamePlay
}
const getGamesByUserID = `-- name: GetGamesByUserID :many
SELECT g.id, g.board, g.current_turn, g.status, g.winner_user_id, g.winning_cells, g.created_at, g.updated_at, g.rematch_game_id, g.game_type, g.grid_width, g.grid_height, g.max_players, g.game_mode, g.score FROM games g
SELECT g.id, g.board, g.current_turn, g.status, g.winner_user_id, g.winning_cells, g.created_at, g.updated_at, g.rematch_game_id, g.game_type, g.grid_width, g.grid_height, g.max_players, g.game_mode, g.score, g.snake_speed FROM games g
JOIN game_players gp ON g.id = gp.game_id
WHERE gp.user_id = ?
ORDER BY g.updated_at DESC
@@ -223,6 +226,7 @@ func (q *Queries) GetGamesByUserID(ctx context.Context, userID sql.NullString) (
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
); err != nil {
return nil, err
}

View File

@@ -24,6 +24,7 @@ type Game struct {
MaxPlayers int64
GameMode int64
Score int64
SnakeSpeed int64
}
type GamePlayer struct {

View File

@@ -11,9 +11,9 @@ import (
)
const createSnakeGame = `-- name: CreateSnakeGame :one
INSERT INTO games (id, board, current_turn, status, game_type, grid_width, grid_height, max_players, game_mode)
VALUES (?, ?, 0, ?, 'snake', ?, ?, 8, ?)
RETURNING id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score
INSERT INTO games (id, board, current_turn, status, game_type, grid_width, grid_height, max_players, game_mode, snake_speed)
VALUES (?, ?, 0, ?, 'snake', ?, ?, 8, ?, ?)
RETURNING id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed
`
type CreateSnakeGameParams struct {
@@ -23,6 +23,7 @@ type CreateSnakeGameParams struct {
GridWidth sql.NullInt64
GridHeight sql.NullInt64
GameMode int64
SnakeSpeed int64
}
func (q *Queries) CreateSnakeGame(ctx context.Context, arg CreateSnakeGameParams) (Game, error) {
@@ -33,6 +34,7 @@ func (q *Queries) CreateSnakeGame(ctx context.Context, arg CreateSnakeGameParams
arg.GridWidth,
arg.GridHeight,
arg.GameMode,
arg.SnakeSpeed,
)
var i Game
err := row.Scan(
@@ -51,6 +53,7 @@ func (q *Queries) CreateSnakeGame(ctx context.Context, arg CreateSnakeGameParams
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
)
return i, err
}
@@ -91,7 +94,7 @@ func (q *Queries) DeleteSnakeGame(ctx context.Context, id string) error {
}
const getActiveSnakeGames = `-- name: GetActiveSnakeGames :many
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score FROM games WHERE game_type = 'snake' AND status < 2 AND game_mode = 0
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE game_type = 'snake' AND status < 2 AND game_mode = 0
`
func (q *Queries) GetActiveSnakeGames(ctx context.Context) ([]Game, error) {
@@ -119,6 +122,7 @@ func (q *Queries) GetActiveSnakeGames(ctx context.Context) ([]Game, error) {
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
); err != nil {
return nil, err
}
@@ -134,7 +138,7 @@ func (q *Queries) GetActiveSnakeGames(ctx context.Context) ([]Game, error) {
}
const getSnakeGame = `-- name: GetSnakeGame :one
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score FROM games WHERE id = ? AND game_type = 'snake'
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE id = ? AND game_type = 'snake'
`
func (q *Queries) GetSnakeGame(ctx context.Context, id string) (Game, error) {
@@ -156,6 +160,7 @@ func (q *Queries) GetSnakeGame(ctx context.Context, id string) (Game, error) {
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
)
return i, err
}