Compare commits

...

10 Commits

8 changed files with 79 additions and 20 deletions

View File

@ -55,6 +55,9 @@ func Warningf(f string, args ...any) *Warning {
// IsSeriousError returns whether e is a non-warning error. If e is nil, this // IsSeriousError returns whether e is a non-warning error. If e is nil, this
// returns false. // returns false.
func IsSeriousError(e error) bool { func IsSeriousError(e error) bool {
if e == nil {
return false
}
return !errors.Is(e, AnyWarning) return !errors.Is(e, AnyWarning)
} }

View File

@ -463,3 +463,20 @@ func (p *Player[C]) ReportError(e error) {
func (p *Player[C]) CanAct() bool { func (p *Player[C]) CanAct() bool {
return p.ActionsRemaining > 0 && (len(p.Hand) > 0 || len(p.PermanentActions) > 0) return p.ActionsRemaining > 0 && (len(p.Hand) > 0 || len(p.PermanentActions) > 0)
} }
// Debug adds a message to the player's temporary messages if their debug level
// is at least the level specified.
func (p *Player[C]) Debug(minLevel int, msg Message) {
if p.DebugLevel < minLevel || msg == nil {
return
}
p.TemporaryMessages = append(p.TemporaryMessages, msg)
}
// Emit adds a message to the player's temporary messages.
func (p *Player[C]) Emit(msg Message) {
if msg == nil {
return
}
p.TemporaryMessages = append(p.TemporaryMessages, msg)
}

View File

@ -141,7 +141,7 @@ func (r *RuleCollection[C]) performInsert(k *keyedRule[C]) {
r.rules[k.id] = k r.rules[k.id] = k
s := r.byStep[k.Step()] s := r.byStep[k.Step()]
if s == nil { if len(s) == 0 {
r.steps = nil r.steps = nil
} }
s = append(s, k.id) s = append(s, k.id)
@ -326,7 +326,7 @@ func (r *RuleCollection[C]) Run(p *Player[C]) error {
steps := r.steps steps := r.steps
if steps == nil { if steps == nil {
// Step set changed, recalculate. // Step set changed, recalculate.
steps := make([]int, 0, len(r.byStep)) steps = make([]int, 0, len(r.byStep))
for step := range r.byStep { for step := range r.byStep {
steps = append(steps, step) steps = append(steps, step)
} }
@ -334,18 +334,21 @@ func (r *RuleCollection[C]) Run(p *Player[C]) error {
r.steps = steps r.steps = steps
} }
p.Debug(2, Msgf("Executing steps: %v", steps))
var errs ErrorCollector var errs ErrorCollector
for _, step := range steps { for _, step := range steps {
stepRules := r.byStep[step] stepRules := r.byStep[step]
p.Rand.Shuffle(len(stepRules), func(i, j int) { p.Debug(3, Msgf("Executing step %d; length %d", step, len(stepRules)))
stepRules[i], stepRules[j] = stepRules[j], stepRules[i] ShuffleAll(stepRules, p.Rand)
})
var remove []RuleID var remove []RuleID
halt := false halt := false
for _, id := range stepRules { for _, id := range stepRules {
rule := r.rules[id] rule := r.rules[id]
p.Debug(4, Msgf("Executing rule %x (labeled %q)", id, rule.Label()))
err := rule.Enact(p) err := rule.Enact(p)
if err != nil { if err != nil {
p.Debug(2, Msgf("Rule %x (%q): error: %v", id, rule.Label(), err))
ignore := false ignore := false
if errors.Is(err, ErrDeleteRule) { if errors.Is(err, ErrDeleteRule) {
remove = append(remove, id) remove = append(remove, id)
@ -377,10 +380,14 @@ func (r *RuleCollection[C]) Run(p *Player[C]) error {
} }
} }
if halt { if halt {
return errs.Emit() ret := errs.Emit()
p.Debug(2, Msgf("Rules stopping early. Result: %v", ret))
return ret
} }
} }
return errs.Emit() ret := errs.Emit()
p.Debug(2, Msgf("Rules complete. Result: %v", ret))
return ret
} }
func (r *RuleCollection[C]) applyDelayedUpdates() { func (r *RuleCollection[C]) applyDelayedUpdates() {

View File

@ -8,13 +8,14 @@ import (
) )
func RunSimpleTerminalUI[C StatsCollection](p *Player[C]) error { func RunSimpleTerminalUI[C StatsCollection](p *Player[C]) error {
for {
err := p.StartNextTurn() err := p.StartNextTurn()
if p.DebugLevel < 1 && IsSeriousError(err) { if p.DebugLevel < 1 && IsSeriousError(err) {
return err return err
} }
p.ReportError(err) p.ReportError(err)
for {
for p.CanAct() { for p.CanAct() {
isCard, cardIdx, choiceIdx, err := pickNextAction(p) isCard, cardIdx, choiceIdx, err := pickNextAction(p)
p.ReportError(err) p.ReportError(err)
@ -135,7 +136,7 @@ func pickNextAction[C StatsCollection](p *Player[C]) (isCard bool, cardIdx int,
cls() cls()
displayOnePanel(p, p.InfoPanels[i-1]) displayOnePanel(p, p.InfoPanels[i-1])
wait() wait()
} else if i < handOffset { } else if i <= handOffset {
i = i - actionsOffset - 1 i = i - actionsOffset - 1
option, promptErr := promptCard(p, p.PermanentActions[i]) option, promptErr := promptCard(p, p.PermanentActions[i])
if option >= 0 || IsSeriousError(promptErr) { if option >= 0 || IsSeriousError(promptErr) {
@ -143,7 +144,7 @@ func pickNextAction[C StatsCollection](p *Player[C]) (isCard bool, cardIdx int,
} }
} else { } else {
i = i - handOffset - 1 i = i - handOffset - 1
option, promptErr := promptCard(p, p.Hand[i-handOffset-1]) option, promptErr := promptCard(p, p.Hand[i])
if option >= 0 || IsSeriousError(promptErr) { if option >= 0 || IsSeriousError(promptErr) {
return true, i, option, nil return true, i, option, nil
} }
@ -265,7 +266,7 @@ func promptCard[C StatsCollection](p *Player[C], card Card[C]) (optionIdx int, e
} }
fmt.Println() fmt.Println()
if valid { if valid {
fmt.Printf("Go (B)ack, (Q)uit, or enact a choice (1 - %d)? > ", len(opts)+1) fmt.Printf("Go (B)ack, (Q)uit, or enact a choice (1 - %d)? > ", len(opts))
} else { } else {
fmt.Print("Go (B)ack or (Q)uit? > ") fmt.Print("Go (B)ack or (Q)uit? > ")
} }
@ -374,6 +375,7 @@ func statsMode[C StatsCollection](p *Player[C]) error {
} else if v <= 0 || v > n { } else if v <= 0 || v > n {
fmt.Println("There's no info panel with that index.") fmt.Println("There's no info panel with that index.")
} else { } else {
cls()
err := displayOnePanel(p, p.InfoPanels[v-1]) err := displayOnePanel(p, p.InfoPanels[v-1])
errs.Add(err) errs.Add(err)
if IsSeriousError(err) { if IsSeriousError(err) {
@ -507,7 +509,7 @@ func review[C StatsCollection](p *Player[C]) error {
cls() cls()
displayOnePanel(p, p.InfoPanels[i-1]) displayOnePanel(p, p.InfoPanels[i-1])
wait() wait()
} else if i < handOffset { } else if i <= handOffset {
i = i - actionsOffset - 1 i = i - actionsOffset - 1
_, _, err := displayCard(p, p.PermanentActions[i], false) _, _, err := displayCard(p, p.PermanentActions[i], false)
errs.Add(err) errs.Add(err)

6
go.mod
View File

@ -1,3 +1,9 @@
module cardSimEngine module cardSimEngine
go 1.20 go 1.20
require (
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
)

8
go.sum Normal file
View File

@ -0,0 +1,8 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=

View File

@ -7,7 +7,7 @@ import (
// SmokeTestCollection is a stats collection for the simple test sim. // SmokeTestCollection is a stats collection for the simple test sim.
type SmokeTestCollection struct { type SmokeTestCollection struct {
Number cardsim.Stored[int] Number cardsim.Stored[int]
Total cardsim.Stored[int] Total cardsim.Stored[int64]
Turns cardsim.Invisible[int] Turns cardsim.Invisible[int]
Flavor cardsim.Stored[string] Flavor cardsim.Stored[string]

View File

@ -4,6 +4,8 @@ package main
import ( import (
"cardSimEngine/cardsim" "cardSimEngine/cardsim"
"fmt" "fmt"
"github.com/kr/pretty"
) )
func main() { func main() {
@ -38,6 +40,7 @@ func main() {
Name: cardsim.MsgStr("Stats"), Name: cardsim.MsgStr("Stats"),
Intro: cardsim.MsgStr("Hi! These are the smoke test stats."), Intro: cardsim.MsgStr("Hi! These are the smoke test stats."),
}, },
ruledumper{},
} }
p.Prompt = prompt{} p.Prompt = prompt{}
p.DebugLevel = 5 p.DebugLevel = 5
@ -54,9 +57,22 @@ func main() {
type prompt struct{} type prompt struct{}
func (prompt) Title(p *cardsim.Player[*SmokeTestCollection]) cardsim.Message { func (prompt) Title(p *cardsim.Player[*SmokeTestCollection]) cardsim.Message {
return cardsim.MsgStr("Prompt title -- should not be visible?") return cardsim.MsgStr("Smoke Test")
} }
func (prompt) Info(p *cardsim.Player[*SmokeTestCollection]) ([]cardsim.Message, error) { func (prompt) Info(p *cardsim.Player[*SmokeTestCollection]) ([]cardsim.Message, error) {
return []cardsim.Message{cardsim.MsgStr("Here, have some stuff.")}, nil return []cardsim.Message{
cardsim.MsgStr("Here, have some stuff."),
cardsim.Msgf("It's turn %d according to the player and turn %d according to me.", p.TurnNumber, p.Stats.Turns.Value),
}, nil
}
type ruledumper struct{}
func (ruledumper) Title(p *player) cardsim.Message {
return cardsim.MsgStr("Rule Dumper")
}
func (ruledumper) Info(p *player) ([]cardsim.Message, error) {
return []cardsim.Message{cardsim.Msgf("%# v", pretty.Formatter(p.Rules))}, nil
} }