Files
super-auto-pets-board-game/internal/game/log.go
T

73 lines
2.7 KiB
Go

package game
import (
"fmt"
"strings"
)
// LogEntry is one human-readable line in the game's running event log. The
// log is public — both players see the same entries across every phase — and
// explains not just what happened but why (e.g. which pet granted an apple).
type LogEntry struct {
Seq int `json:"seq"` // stable, monotonically increasing id
Round int `json:"round"` // round the entry belongs to
Phase Phase `json:"phase"`
Seat int `json:"seat"` // acting seat, or -1 when none
Icon string `json:"icon,omitempty"` // leading emoji
Kind string `json:"kind,omitempty"` // structured tag; see constants below
Text string `json:"text"` // the sentence itself
Source string `json:"source,omitempty"` // card id that caused a spawn
Spawn string `json:"spawn,omitempty"` // "apple" | "bee" for spawn entries
// The fields below add machine-readable copies of facts the Text already
// states publicly, so observers (the AI player included) don't have to
// parse English. They must never carry information the text doesn't.
Count int `json:"count,omitempty"` // e.g. apples gained
CardName string `json:"cardName,omitempty"` // named card, when public
Cards []string `json:"cards,omitempty"` // card ids involved, when public
}
// Structured LogEntry.Kind tags. Only "result" affects the client; the rest
// exist so observers can follow the public action stream structurally.
const (
LogResult = "result" // battle outcome (client holds it until the replay ends)
LogBuy = "buy" // Seat bought Source/CardName from the shop row
LogSell = "sell" // Seat sold Source/CardName (it became an apple)
LogTrade = "trade" // Seat traded in Cards for a next-tier pick
LogTradePick = "tradePick" // Seat took their pick; CardName set when revealed
)
// addLog appends an entry, stamping it with the next sequence number and the
// current round/phase. Callers set Seat/Icon/Text (and Source/Spawn when the
// entry represents something spawning off a card).
func (g *Game) addLog(e LogEntry) {
g.LogSeq++
e.Seq = g.LogSeq
e.Round = g.Round
e.Phase = g.Phase
g.Log = append(g.Log, e)
}
// logf is the common case: an entry with just a seat, icon, and message.
func (g *Game) logf(seat int, icon, format string, args ...any) {
g.addLog(LogEntry{Seat: seat, Icon: icon, Text: fmt.Sprintf(format, args...)})
}
// article returns "a" or "an" to suit the following word.
func article(word string) string {
if word == "" {
return "a"
}
if strings.ContainsRune("aeiouAEIOU", rune(word[0])) {
return "an"
}
return "a"
}
// plural returns "s" when n is not 1, for simple "N apple(s)" phrasing.
func plural(n int) string {
if n == 1 {
return ""
}
return "s"
}