Port the finite state machine from "Programming Game AI by Example" chapter 2: BaseGameEntity, a generic State[T]/StateMachine[T] pair (Go generics standing in for the book's C++ templates), and a Miner that cycles through digging, banking, sleeping, and drinking states. VisitBankAndDepositGold, GoHomeAndSleepTilRested, and QuenchThirst are original implementations based on the book's written behavior description, since their C++ source wasn't available. Includes a runnable demo in main.go.
62 lines
2.0 KiB
Go
62 lines
2.0 KiB
Go
package westworld
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
// visitBankAndDepositGold is the Go version of the (not yet pasted)
|
|
// singleton class VisitBankAndDepositGold from the book. Same
|
|
// pattern as enterMineAndDigForNugget: unexported type, one shared
|
|
// instance, reached only through the function below.
|
|
type visitBankAndDepositGold struct{}
|
|
|
|
var (
|
|
visitBankAndDepositGoldInstance *visitBankAndDepositGold
|
|
visitBankAndDepositGoldOnce sync.Once
|
|
)
|
|
|
|
// VisitBankAndDepositGold is the Go stand-in for
|
|
// "VisitBankAndDepositGold::Instance()".
|
|
func VisitBankAndDepositGold() *visitBankAndDepositGold {
|
|
visitBankAndDepositGoldOnce.Do(func() {
|
|
visitBankAndDepositGoldInstance = &visitBankAndDepositGold{}
|
|
})
|
|
return visitBankAndDepositGoldInstance
|
|
}
|
|
|
|
var _ State[Miner] = (*visitBankAndDepositGold)(nil)
|
|
|
|
// The book hasn't shown us this state's real code, so this is our
|
|
// own implementation based purely on its written description: "the
|
|
// miner will walk to the bank and deposit any nuggets he is
|
|
// carrying. If he then considers himself wealthy enough, he will
|
|
// change state to GoHomeAndSleepTilRested. Otherwise he will change
|
|
// state to EnterMineAndDigForNugget."
|
|
|
|
// Enter sends the miner to the bank, if he isn't there already.
|
|
func (s *visitBankAndDepositGold) Enter(m *Miner) {
|
|
if m.Location() != bank {
|
|
fmt.Printf("\n%s: Goin' to the bank. Yes siree", GetNameOfEntity(m.ID()))
|
|
m.ChangeLocation(bank)
|
|
}
|
|
}
|
|
|
|
// Execute deposits whatever gold the miner is carrying, then decides
|
|
// where to go next based on how wealthy that leaves him.
|
|
func (s *visitBankAndDepositGold) Execute(m *Miner) {
|
|
m.TransferGoldToBank()
|
|
fmt.Printf("\n%s: Depositing gold. Total savings now: %d", GetNameOfEntity(m.ID()), m.moneyInBank)
|
|
|
|
if m.RichEnough() {
|
|
m.ChangeState(GoHomeAndSleepTilRested())
|
|
} else {
|
|
m.ChangeState(EnterMineAndDigForNugget())
|
|
}
|
|
}
|
|
|
|
// Exit announces that the miner is leaving the bank.
|
|
func (s *visitBankAndDepositGold) Exit(m *Miner) {
|
|
fmt.Printf("\n%s: Leavin' the bank", GetNameOfEntity(m.ID()))
|
|
}
|