Compare commits
11 Commits
2875dc5af8
...
74a2493ef4
Author | SHA1 | Date | |
---|---|---|---|
74a2493ef4 | |||
2c2e68ff93 | |||
d13e04e2f4 | |||
57348f7ebf | |||
9796c2e970 | |||
0f21020647 | |||
e96d81a7b4 | |||
3a7bf9c2fb | |||
00ea284cbc | |||
159f6b6b5f | |||
2480a1631b |
@ -49,7 +49,8 @@ type CardOption[C StatsCollection] interface {
|
|||||||
// a warning, the game crashes.
|
// a warning, the game crashes.
|
||||||
//
|
//
|
||||||
// After an option is enacted, the card is deleted. If a card should be
|
// After an option is enacted, the card is deleted. If a card should be
|
||||||
// repeatable, Enact must return it to the deck (on every option).
|
// repeatable, Enact must return it to the deck (on every option) or
|
||||||
|
// the card needs to reinsert itself with its Then function.
|
||||||
Enact(p *Player[C]) (Message, error)
|
Enact(p *Player[C]) (Message, error)
|
||||||
|
|
||||||
// Enabled returns whether this option can curently be enacted.
|
// Enabled returns whether this option can curently be enacted.
|
||||||
@ -63,7 +64,8 @@ type BasicCard[C StatsCollection] struct {
|
|||||||
IsUrgent bool
|
IsUrgent bool
|
||||||
CardText Message
|
CardText Message
|
||||||
CardOptions []CardOption[C]
|
CardOptions []CardOption[C]
|
||||||
AfterOption func(p *Player[C], option CardOption[C]) error
|
// AfterOption is given the card itself as its first argument.
|
||||||
|
AfterOption func(c Card[C], p *Player[C], option CardOption[C]) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title implements Card.
|
// Title implements Card.
|
||||||
@ -91,7 +93,7 @@ func (b *BasicCard[C]) Then(p *Player[C], option CardOption[C]) error {
|
|||||||
if b.AfterOption == nil {
|
if b.AfterOption == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return b.AfterOption(p, option)
|
return b.AfterOption(b, p, option)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drawn implements Card.
|
// Drawn implements Card.
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,18 +260,34 @@ func (p *Player[C]) StartNextTurn() error {
|
|||||||
return errs.Emit()
|
return errs.Emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw draws a card into the hand, informing the card that it has been drawn.
|
||||||
|
// If more than a million cards refuse to enter the hand, this crashes with
|
||||||
|
// ErrUncooperativeCards. If the deck does not have enough cards, this
|
||||||
|
// returns WarningTooFewCards.
|
||||||
|
func (p *Player[C]) Draw() error {
|
||||||
|
for attempts := 0; attempts < 1000000; attempts++ {
|
||||||
|
if p.Deck.Len() == 0 {
|
||||||
|
return WarningTooFewCards
|
||||||
|
}
|
||||||
|
c := p.Deck.Draw()
|
||||||
|
if c.Drawn(p) {
|
||||||
|
p.Hand = append(p.Hand, c)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ErrUncooperativeCards
|
||||||
|
}
|
||||||
|
|
||||||
// FillHand draws up to the hand limit, informing cards that they have been
|
// FillHand draws up to the hand limit, informing cards that they have been
|
||||||
// drawn. If more than a million cards refuse to enter the hand, this crashes
|
// drawn. If more than a million cards refuse to enter the hand, this crashes
|
||||||
// with ErrUncooperativeCards. If the deck does not have enough cards, this
|
// with ErrUncooperativeCards. If the deck does not have enough cards, this
|
||||||
// returns WarningTooFewCards.
|
// returns WarningTooFewCards.
|
||||||
func (p *Player[C]) FillHand() error {
|
func (p *Player[C]) FillHand() error {
|
||||||
failureLimit := 1000000
|
var lastErr error
|
||||||
for failureLimit > 0 && p.Deck.Len() > 0 && len(p.Hand) < p.HandLimit {
|
for p.Deck.Len() > 0 && len(p.Hand) < p.HandLimit {
|
||||||
c := p.Deck.Draw()
|
lastErr = p.Draw()
|
||||||
if c.Drawn(p) {
|
if IsSeriousError(lastErr) {
|
||||||
p.Hand = append(p.Hand, c)
|
return lastErr
|
||||||
} else {
|
|
||||||
failureLimit--
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,10 +295,6 @@ func (p *Player[C]) FillHand() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if failureLimit <= 0 {
|
|
||||||
return ErrUncooperativeCards
|
|
||||||
}
|
|
||||||
|
|
||||||
return WarningTooFewCards
|
return WarningTooFewCards
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,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)
|
||||||
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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()
|
|
||||||
if p.DebugLevel < 1 && IsSeriousError(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.ReportError(err)
|
|
||||||
|
|
||||||
|
err := p.StartNextTurn()
|
||||||
|
if p.DebugLevel < 1 && IsSeriousError(err) {
|
||||||
|
return 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
6
go.mod
@ -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
8
go.sum
Normal 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=
|
162
smoketest/cards.go
Normal file
162
smoketest/cards.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cardSimEngine/cardsim"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type aliases, unlike distinctly named types, are fully substitutable for
|
||||||
|
// the original type. This trims off some annoying-to-type things.
|
||||||
|
type player = cardsim.Player[*SmokeTestCollection]
|
||||||
|
type card = cardsim.Card[*SmokeTestCollection]
|
||||||
|
type cardOption = cardsim.CardOption[*SmokeTestCollection]
|
||||||
|
|
||||||
|
func makeAdditionCard(amt int) cardsim.Card[*SmokeTestCollection] {
|
||||||
|
c := &cardsim.BasicCard[*SmokeTestCollection]{
|
||||||
|
CardTitle: cardsim.Msgf("Additive %d", amt),
|
||||||
|
CardText: cardsim.Msgf("You can change the Number by %d.", amt),
|
||||||
|
CardOptions: []cardsim.CardOption[*SmokeTestCollection]{
|
||||||
|
&cardsim.BasicOption[*SmokeTestCollection]{
|
||||||
|
Text: cardsim.Msgf("Add %d", amt),
|
||||||
|
Effect: func(p *player) error {
|
||||||
|
p.Stats.Number.Value += amt
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Output: cardsim.MsgStr("Added."),
|
||||||
|
},
|
||||||
|
&cardsim.BasicOption[*SmokeTestCollection]{
|
||||||
|
Text: cardsim.Msgf("Subtract %d", amt),
|
||||||
|
Effect: func(p *player) error {
|
||||||
|
p.Stats.Number.Value -= amt
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Output: cardsim.MsgStr("Subtracted."),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AfterOption: func(c card, p *player, _ cardOption) error {
|
||||||
|
p.Deck.InsertRandomBottom(0.5, c)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeMultiplicationCard(amt int) cardsim.Card[*SmokeTestCollection] {
|
||||||
|
c := &cardsim.BasicCard[*SmokeTestCollection]{
|
||||||
|
CardTitle: cardsim.Msgf("Multiplicative %d", amt),
|
||||||
|
CardText: cardsim.Msgf("You can multiply or divide the Number by %d, or maybe divide the Number by that.", amt),
|
||||||
|
CardOptions: []cardsim.CardOption[*SmokeTestCollection]{
|
||||||
|
&cardsim.BasicOption[*SmokeTestCollection]{
|
||||||
|
Text: cardsim.Msgf("Multiply by %d", amt),
|
||||||
|
Effect: func(p *player) error {
|
||||||
|
p.Stats.Number.Value *= amt
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Output: cardsim.MsgStr("Multiplied."),
|
||||||
|
},
|
||||||
|
&cardsim.BasicOption[*SmokeTestCollection]{
|
||||||
|
Text: cardsim.Msgf("Integer divide by %d", amt),
|
||||||
|
Effect: func(p *player) error {
|
||||||
|
p.Stats.Number.Value /= amt
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Output: cardsim.MsgStr("Divided."),
|
||||||
|
},
|
||||||
|
inverseDivision(amt),
|
||||||
|
},
|
||||||
|
AfterOption: func(c card, p *player, _ cardOption) error {
|
||||||
|
p.Deck.InsertRandomBottom(0.5, c)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type inverseDivision int
|
||||||
|
|
||||||
|
func (i inverseDivision) OptionText(p *player) (cardsim.Message, error) {
|
||||||
|
if p.Stats.Number.Value == 0 {
|
||||||
|
return cardsim.MsgStr("You can't divide by zero!"), nil
|
||||||
|
}
|
||||||
|
return cardsim.Msgf("Divide %d by the Number", int(i)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i inverseDivision) Enact(p *player) (cardsim.Message, error) {
|
||||||
|
if p.Stats.Number.Value == 0 {
|
||||||
|
return nil, errors.New("you can't divide by zero!")
|
||||||
|
}
|
||||||
|
p.Stats.Number.Value = int(i) / p.Stats.Number.Value
|
||||||
|
return cardsim.MsgStr("Inverse divided."), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i inverseDivision) Enabled(p *player) bool {
|
||||||
|
return p.Stats.Number.Value != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func initDeck(d *cardsim.Deck[*SmokeTestCollection]) {
|
||||||
|
addMe := []int{
|
||||||
|
0, 1, 2, 5, 10, 50, 100, 1000, 2500, 500000, 9876543,
|
||||||
|
}
|
||||||
|
for _, n := range addMe {
|
||||||
|
d.Insert(cardsim.BottomOfDeck, makeAdditionCard(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
multiplyMe := []int{
|
||||||
|
2, 4, 8, 16, 32, 64, 128, 512, 1024, 9999, 84720413,
|
||||||
|
}
|
||||||
|
for _, n := range multiplyMe {
|
||||||
|
d.Insert(cardsim.BottomOfDeck, makeMultiplicationCard(n))
|
||||||
|
}
|
||||||
|
if err := d.Shuffle(); cardsim.IsSeriousError(err) {
|
||||||
|
panic(err)
|
||||||
|
} else if err != nil {
|
||||||
|
fmt.Printf("Error shuffling: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func installPermanentActions(pa *[]card) {
|
||||||
|
*pa = []card{
|
||||||
|
&cardsim.BasicCard[*SmokeTestCollection]{
|
||||||
|
CardTitle: cardsim.MsgStr("Reset to 0"),
|
||||||
|
CardText: cardsim.MsgStr("Resets Number to 0."),
|
||||||
|
CardOptions: []cardOption{
|
||||||
|
&cardsim.BasicOption[*SmokeTestCollection]{
|
||||||
|
Text: cardsim.MsgStr("Reset to 0."),
|
||||||
|
Effect: func(p *player) error {
|
||||||
|
p.Stats.Number.Value = 0
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Output: cardsim.MsgStr("Done."),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&cardsim.BasicCard[*SmokeTestCollection]{
|
||||||
|
CardTitle: cardsim.MsgStr("Reset to 1000000"),
|
||||||
|
CardText: cardsim.MsgStr("Resets Number to one million."),
|
||||||
|
CardOptions: []cardOption{
|
||||||
|
&cardsim.BasicOption[*SmokeTestCollection]{
|
||||||
|
Text: cardsim.MsgStr("Reset to 1,000,000"),
|
||||||
|
Effect: func(p *player) error {
|
||||||
|
p.Stats.Number.Value = 1000000
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Output: cardsim.MsgStr("Done."),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&cardsim.BasicCard[*SmokeTestCollection]{
|
||||||
|
CardTitle: cardsim.MsgStr("Draw a card"),
|
||||||
|
CardText: cardsim.MsgStr("Draw an extra card."),
|
||||||
|
CardOptions: []cardOption{
|
||||||
|
&cardsim.BasicOption[*SmokeTestCollection]{
|
||||||
|
Text: cardsim.MsgStr("Draw an extra card."),
|
||||||
|
Effect: func(p *player) error {
|
||||||
|
return p.Draw()
|
||||||
|
},
|
||||||
|
Output: cardsim.MsgStr("Drawn. Probably."),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
25
smoketest/collection.go
Normal file
25
smoketest/collection.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cardSimEngine/cardsim"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SmokeTestCollection is a stats collection for the simple test sim.
|
||||||
|
type SmokeTestCollection struct {
|
||||||
|
Number cardsim.Stored[int]
|
||||||
|
Total cardsim.Stored[int64]
|
||||||
|
Turns cardsim.Invisible[int]
|
||||||
|
|
||||||
|
Flavor cardsim.Stored[string]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmokeTestCollection) Average() float64 {
|
||||||
|
return float64(c.Total.Value) / float64(c.Turns.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmokeTestCollection) Stats() []cardsim.Stat {
|
||||||
|
stats := cardsim.ExtractStats(c)
|
||||||
|
stats = append(stats, cardsim.StatFunc("Average", c.Average))
|
||||||
|
cardsim.SortStats(stats)
|
||||||
|
return stats
|
||||||
|
}
|
78
smoketest/main.go
Normal file
78
smoketest/main.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Binary smoketest runs a very simple cardsim thing.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cardSimEngine/cardsim"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/kr/pretty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
p := cardsim.InitPlayer(
|
||||||
|
&SmokeTestCollection{
|
||||||
|
Number: cardsim.Stored[int]{
|
||||||
|
Name: "Number",
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
Total: cardsim.Stored[int64]{
|
||||||
|
Name: "Total",
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
Turns: cardsim.Invisible[int]{
|
||||||
|
Name: "Turns",
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
Flavor: cardsim.Stored[string]{
|
||||||
|
Name: "Flavor",
|
||||||
|
Value: "Lemon",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
p.Name = "Dave"
|
||||||
|
p.HandLimit = 3
|
||||||
|
p.ActionsPerTurn = 2
|
||||||
|
installRules(p.Rules)
|
||||||
|
initDeck(p.Deck)
|
||||||
|
installPermanentActions(&p.PermanentActions)
|
||||||
|
p.InfoPanels = []cardsim.InfoPanel[*SmokeTestCollection]{
|
||||||
|
&cardsim.BasicStatsPanel[*SmokeTestCollection]{
|
||||||
|
Name: cardsim.MsgStr("Stats"),
|
||||||
|
Intro: cardsim.MsgStr("Hi! These are the smoke test stats."),
|
||||||
|
},
|
||||||
|
ruledumper{},
|
||||||
|
}
|
||||||
|
p.Prompt = prompt{}
|
||||||
|
p.DebugLevel = 5
|
||||||
|
|
||||||
|
err := cardsim.RunSimpleTerminalUI(p)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Terminated with error:")
|
||||||
|
fmt.Println(err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Terminated without error.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type prompt struct{}
|
||||||
|
|
||||||
|
func (prompt) Title(p *cardsim.Player[*SmokeTestCollection]) cardsim.Message {
|
||||||
|
return cardsim.MsgStr("Smoke Test")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prompt) Info(p *cardsim.Player[*SmokeTestCollection]) ([]cardsim.Message, error) {
|
||||||
|
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
|
||||||
|
}
|
31
smoketest/rules.go
Normal file
31
smoketest/rules.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cardSimEngine/cardsim"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
updateTotal = cardsim.RuleFunc[*SmokeTestCollection]{
|
||||||
|
Name: "updateTotal",
|
||||||
|
Seq: 1,
|
||||||
|
F: func(p *cardsim.Player[*SmokeTestCollection]) error {
|
||||||
|
p.Stats.Total.Value += int64(p.Stats.Number.Value)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
countTurn = cardsim.RuleFunc[*SmokeTestCollection]{
|
||||||
|
Name: "countTurn",
|
||||||
|
Seq: math.MinInt,
|
||||||
|
F: func(p *cardsim.Player[*SmokeTestCollection]) error {
|
||||||
|
p.Stats.Turns.Value++
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func installRules(rules *cardsim.RuleCollection[*SmokeTestCollection]) {
|
||||||
|
rules.Insert(&updateTotal)
|
||||||
|
rules.Insert(&countTurn)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user