mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 14:27:27 +00:00
Hangman joins Snake as a built-in, leaderboard-backed TUI game (PRD §5.1). Endless mode: each solved word banks points (longer words and unused guesses score more) and deals a fresh word with full lives; the run ends when one word exhausts all six wrong guesses, persisting the total for members under the "hangman" score key. Guests play without persisting, same as Snake. Generalize the leaderboard board to take a game name and split the single "Leaderboard" row into per-game "Leaderboard — Snake" / "Leaderboard — Hangman" entries. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
1.3 KiB
Go
61 lines
1.3 KiB
Go
package arcade
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
"github.com/profullstack/agentbbs/internal/plugin"
|
|
"github.com/profullstack/agentbbs/internal/store"
|
|
"github.com/profullstack/agentbbs/internal/ui"
|
|
)
|
|
|
|
// board renders the global top scores for one game (PRD §5.1 leaderboards).
|
|
type board struct {
|
|
ctx plugin.Context
|
|
game string
|
|
scores []store.Score
|
|
err error
|
|
}
|
|
|
|
func newBoard(ctx plugin.Context, game string) *board {
|
|
return &board{ctx: ctx, game: game}
|
|
}
|
|
|
|
func (b *board) Init() tea.Cmd {
|
|
return func() tea.Msg {
|
|
scores, err := b.ctx.Store.TopScores(b.game, 10)
|
|
return boardMsg{scores: scores, err: err}
|
|
}
|
|
}
|
|
|
|
type boardMsg struct {
|
|
scores []store.Score
|
|
err error
|
|
}
|
|
|
|
func (b *board) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case boardMsg:
|
|
b.scores, b.err = msg.scores, msg.err
|
|
case tea.KeyMsg:
|
|
return b, back
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
func (b *board) View() string {
|
|
s := theme.Title("Leaderboard — "+b.game) + "\n\n"
|
|
switch {
|
|
case b.err != nil:
|
|
s += ui.Danger.Render("error: " + b.err.Error())
|
|
case len(b.scores) == 0:
|
|
s += ui.Dim.Render("no scores yet — be the first")
|
|
default:
|
|
for i, sc := range b.scores {
|
|
s += fmt.Sprintf("%2d. %-20s %6d\n", i+1, sc.User, sc.Score)
|
|
}
|
|
}
|
|
s += "\n" + ui.KeyBar("any-key return to menu")
|
|
return ui.Frame.Render(s)
|
|
}
|