clone

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

main
puzzletea / game / gamer.go
gamer.go 3.2K
1// Package game defines the plugin interface for puzzle games.2package game34import (5 "context"6 "math/rand/v2"78 "charm.land/bubbles/v2/key"9 tea "charm.land/bubbletea/v2"10 "github.com/FelineStateMachine/puzzletea/difficulty"11 "github.com/FelineStateMachine/puzzletea/puzzle"12)1314// Gamer is the interface that an active game instance must implement.15type Gamer interface {16 GetDebugInfo() string17 GetFullHelp() [][]key.Binding1819 GetSave() ([]byte, error)20 IsSolved() bool21 Reset() Gamer22 SetTitle(string) Gamer2324 Init() tea.Cmd25 View() string26 Update(msg tea.Msg) (Gamer, tea.Cmd)27}2829// Mode describes a game mode for menu display.30type Mode interface {31 Title() string32 Description() string33 FilterValue() string34}3536// Spawner creates a new game instance. Implemented by concrete mode types37// but kept separate from Mode so that Category (which also satisfies Mode)38// does not need a Spawn method.39type Spawner interface {40 Spawn() (Gamer, error)41}4243// SeededSpawner creates a new game instance using a deterministic RNG.44// This enables reproducible puzzle generation for daily puzzles.45type SeededSpawner interface {46 Spawner47 SpawnSeeded(rng *rand.Rand) (Gamer, error)48}4950// CancellableSpawner optionally supports context-aware generation so callers51// can cancel long-running spawn work.52type CancellableSpawner interface {53 SpawnContext(ctx context.Context) (Gamer, error)54}5556// CancellableSeededSpawner optionally supports context-aware deterministic57// generation so callers can cancel long-running seeded spawn work.58type CancellableSeededSpawner interface {59 SeededSpawner60 SpawnSeededContext(ctx context.Context, rng *rand.Rand) (Gamer, error)61}6263// EloSpawner creates a deterministic game instance from seed text and Elo.64type EloSpawner interface {65 SpawnElo(seed string, elo difficulty.Elo) (Gamer, difficulty.Report, error)66}6768// CancellableEloSpawner optionally supports context-aware Elo generation.69type CancellableEloSpawner interface {70 EloSpawner71 SpawnEloContext(ctx context.Context, seed string, elo difficulty.Elo) (Gamer, difficulty.Report, error)72}7374// BaseMode provides the common title/description fields and the three75// Mode-interface methods so that concrete mode structs can embed it76// instead of duplicating boilerplate.77type BaseMode struct {78 title string79 description string80}8182func NewBaseMode(title, description string) BaseMode {83 return BaseMode{title: title, description: description}84}8586func (b BaseMode) Title() string { return b.title }87func (b BaseMode) Description() string { return b.description }88func (b BaseMode) FilterValue() string { return b.title + " " + b.description }8990func AdaptImport[T Gamer](fn func([]byte) (T, error)) func([]byte) (Gamer, error) {91 return func(data []byte) (Gamer, error) {92 return fn(data)93 }94}9596// NormalizeName lowercases and replaces hyphens/underscores with spaces for97// fuzzy matching of CLI arguments to game names and aliases.98func NormalizeName(s string) string {99 return puzzle.NormalizeName(s)100}101102// SpawnCompleteMsg is sent when an async Spawn() call finishes.103type SpawnCompleteMsg struct {104 Game Gamer105 Report difficulty.Report106 Err error107}108109// HelpToggleMsg is sent from the root model to games when the user toggles110// the full help display with Ctrl+H.111type HelpToggleMsg struct{ Show bool }