clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

bug/architecture-test-trim-path
puzzletea / app / spawn.go
spawn.go 2.5K
1package app23import (4 "context"5 "math/rand/v2"6 "time"78 "github.com/FelineStateMachine/puzzletea/difficulty"9 "github.com/FelineStateMachine/puzzletea/game"10 "github.com/FelineStateMachine/puzzletea/store"1112 tea "charm.land/bubbletea/v2"13)1415const spawnTimeout = 10 * time.Second1617type spawnCompleteMsg struct {18 jobID int6419 result game.SpawnCompleteMsg20}2122func spawnEloCmd(spawner game.EloSpawner, seed string, elo difficulty.Elo, ctx context.Context, jobID int64) tea.Cmd {23 return func() tea.Msg {24 var (25 g game.Gamer26 report difficulty.Report27 err error28 )29 if cancellable, ok := spawner.(game.CancellableEloSpawner); ok {30 g, report, err = cancellable.SpawnEloContext(ctx, seed, elo)31 } else {32 g, report, err = spawner.SpawnElo(seed, elo)33 }34 return spawnCompleteMsg{35 jobID: jobID,36 result: game.SpawnCompleteMsg{Game: g, Report: report, Err: err},37 }38 }39}4041// spawnCmd returns a tea.Cmd that runs Spawn() off the main goroutine.42func spawnCmd(spawner game.Spawner, ctx context.Context, jobID int64) tea.Cmd {43 return func() tea.Msg {44 var (45 g game.Gamer46 err error47 )48 if cancellable, ok := spawner.(game.CancellableSpawner); ok {49 g, err = cancellable.SpawnContext(ctx)50 } else {51 g, err = spawner.Spawn()52 }53 return spawnCompleteMsg{54 jobID: jobID,55 result: game.SpawnCompleteMsg{Game: g, Err: err},56 }57 }58}5960// spawnSeededCmd returns a tea.Cmd that runs SpawnSeeded() off the main goroutine.61// The caller must not use rng after this call — *rand.Rand is not goroutine-safe62// and ownership is transferred to the spawned goroutine.63func spawnSeededCmd(spawner game.SeededSpawner, rng *rand.Rand, ctx context.Context, jobID int64) tea.Cmd {64 return func() tea.Msg {65 var (66 g game.Gamer67 err error68 )69 if cancellable, ok := spawner.(game.CancellableSeededSpawner); ok {70 g, err = cancellable.SpawnSeededContext(ctx, rng)71 } else {72 g, err = spawner.SpawnSeeded(rng)73 }74 return spawnCompleteMsg{75 jobID: jobID,76 result: game.SpawnCompleteMsg{Game: g, Err: err},77 }78 }79}8081func (m *model) cancelActiveSpawn() {82 newSessionController(m).cancelActiveSpawn()83}8485func (m model) handleSpawnComplete(jobID int64, msg game.SpawnCompleteMsg) (tea.Model, tea.Cmd) {86 cmd := newSessionController(&m).handleSpawnComplete(jobID, msg)87 return m, cmd88}8990// saveCurrentGame saves the current game state to the DB if a game is active.91func saveCurrentGame(m model, status store.GameStatus) model {92 newSessionController(&m).saveCurrentGame(status)93 return m94}