feat: westworldwithwoman. add elsa
This commit is contained in:
1 parent
2764849943
commit
0e7482e04c
28 files changed
+1946
No files matched your search
@@ -0,0 +1,248 @@
|
||||
package westworldwithwoman
|
||||
|
||||
// State is the Go version of the C++ class template:
|
||||
//
|
||||
// template <class entity_type>
|
||||
// class State
|
||||
// {
|
||||
// public:
|
||||
// virtual void Enter(entity_type*)=0;
|
||||
// virtual void Execute(entity_type*)=0;
|
||||
// virtual void Exit(entity_type*)=0;
|
||||
// virtual ~State(){}
|
||||
// };
|
||||
//
|
||||
// The book turned State into a template so it isn't stuck being a
|
||||
// checklist for Miner only - any entity type can use it. Go's
|
||||
// version of a template is a generic type: the "[T any]" part is a
|
||||
// blank slot (called a type parameter) that gets filled in with a
|
||||
// real type wherever State is used. T stands in for the book's
|
||||
// entity_type. "any" just means "T can be filled in with literally
|
||||
// any type" - no restrictions.
|
||||
//
|
||||
// C++ used "=0" (pure virtual) to say "every real state MUST provide
|
||||
// these three methods, this class itself is just a checklist." Go's
|
||||
// interface type does exactly that job: nothing implements State
|
||||
// automatically - a type only counts as a State[T] once it has all
|
||||
// three methods with these exact signatures for that particular T.
|
||||
//
|
||||
// There's no Go equivalent needed for "virtual ~State(){}" (the
|
||||
// destructor) - Go has a garbage collector that cleans up memory on
|
||||
// its own, so there's nothing to write by hand for that.
|
||||
type State[T any] interface {
|
||||
// Enter runs once, the moment an entity switches into this state.
|
||||
Enter(entity *T)
|
||||
|
||||
// Execute runs every single update step while the entity is in
|
||||
// this state - this is where the actual behavior/logic lives.
|
||||
Execute(entity *T)
|
||||
|
||||
// Exit runs once, the moment the entity leaves this state.
|
||||
Exit(entity *T)
|
||||
}
|
||||
|
||||
// locationType is a temporary stand-in for the C++ enum
|
||||
// "location_type" (things like shack, goldmine, bank, saloon). The
|
||||
// book hasn't shown us that enum yet, so this is a plain number type
|
||||
// for now until you paste the real definition.
|
||||
type locationType int
|
||||
|
||||
// shack, goldmine, bank and saloon are the four locations the miner
|
||||
// visits, matching the four states described in the book (we still
|
||||
// haven't seen the book's actual enum definition, so these are our
|
||||
// own stand-in values - their exact numbers don't matter, we only
|
||||
// ever compare locations for equality).
|
||||
const (
|
||||
shack locationType = iota
|
||||
goldmine
|
||||
bank
|
||||
saloon
|
||||
)
|
||||
|
||||
// Miner is the Go version of:
|
||||
//
|
||||
// class Miner : public BaseGameEntity
|
||||
//
|
||||
// In C++, "Miner : public BaseGameEntity" means a Miner IS a
|
||||
// BaseGameEntity - it inherits all of its fields and methods for
|
||||
// free. Go has no inheritance. Instead we EMBED a *BaseGameEntity
|
||||
// inside Miner: we stick the smaller struct inside the bigger one,
|
||||
// and Go automatically lets Miner "borrow" BaseGameEntity's methods
|
||||
// as if they were its own. That's why Miner gets an ID() method
|
||||
// without us writing one ourselves - it's borrowed.
|
||||
type Miner struct {
|
||||
*BaseGameEntity
|
||||
|
||||
// stateMachine is the Go version of
|
||||
// "StateMachine<Miner>* m_pStateMachine". All the bookkeeping
|
||||
// that used to live directly on Miner (currentState, and the
|
||||
// exit/enter dance in ChangeState) now belongs to this instead -
|
||||
// Miner just delegates to it.
|
||||
stateMachine *StateMachine[Miner]
|
||||
|
||||
// location is where the miner currently is.
|
||||
location locationType
|
||||
|
||||
// goldCarried: how many gold nuggets the miner has in his pockets.
|
||||
goldCarried int
|
||||
|
||||
// moneyInBank: how much money the miner has deposited at the bank.
|
||||
moneyInBank int
|
||||
|
||||
// thirst: the higher this number, the thirstier the miner is.
|
||||
thirst int
|
||||
|
||||
// fatigue: the higher this number, the more tired the miner is.
|
||||
fatigue int
|
||||
}
|
||||
|
||||
// NewMiner is the Go stand-in for the C++ constructor "Miner(int id)".
|
||||
// C++'s member initializer list (the "m_Location(shack), ..." part
|
||||
// before the constructor's { }) is just how C++ sets a struct's
|
||||
// starting field values before the body runs - in Go we set the same
|
||||
// starting values with an ordinary struct literal instead.
|
||||
func NewMiner(id int) *Miner {
|
||||
m := &Miner{
|
||||
BaseGameEntity: NewBaseGameEntity(id),
|
||||
location: shack,
|
||||
goldCarried: 0,
|
||||
moneyInBank: 0,
|
||||
thirst: 0,
|
||||
fatigue: 0,
|
||||
}
|
||||
|
||||
// set up state machine
|
||||
m.stateMachine = NewStateMachine(m)
|
||||
m.stateMachine.SetCurrentState(GoHomeAndSleepTilRested())
|
||||
m.stateMachine.SetGlobalState(MinerGlobalState())
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// FSM returns the miner's state machine. This is the Go version of
|
||||
// "GetFSM()const" - Go convention drops the "Get" prefix that
|
||||
// C++/Java getters use, so it's just FSM().
|
||||
func (m *Miner) FSM() *StateMachine[Miner] {
|
||||
return m.stateMachine
|
||||
}
|
||||
|
||||
// Location returns where the miner currently is. This is the getter
|
||||
// the book's Enter/Execute/Exit methods call to check on a miner
|
||||
// before deciding what to do.
|
||||
func (m *Miner) Location() locationType {
|
||||
return m.location
|
||||
}
|
||||
|
||||
// ChangeLocation moves the miner to a new location. This is the
|
||||
// setter half of the pair - states call this to actually send a
|
||||
// miner somewhere.
|
||||
func (m *Miner) ChangeLocation(loc locationType) {
|
||||
m.location = loc
|
||||
}
|
||||
|
||||
// AddToGoldCarried adds amount to how much gold the miner is
|
||||
// currently carrying in his pockets (separate from what's already
|
||||
// safely deposited at the bank).
|
||||
func (m *Miner) AddToGoldCarried(amount int) {
|
||||
m.goldCarried += amount
|
||||
}
|
||||
|
||||
// IncreaseFatigue bumps the miner's tiredness up by one notch.
|
||||
func (m *Miner) IncreaseFatigue() {
|
||||
m.fatigue++
|
||||
}
|
||||
|
||||
// maxNuggets is a placeholder for a constant the book hasn't shown
|
||||
// us the exact value of yet (something like "const int MaxNuggets").
|
||||
// It's how many gold nuggets a miner's pockets can hold before his
|
||||
// pockets count as "full." Replace this once you paste the real
|
||||
// value from the book.
|
||||
const maxNuggets = 3
|
||||
|
||||
// PocketsFull reports whether the miner is carrying as much gold as
|
||||
// his pockets can hold.
|
||||
func (m *Miner) PocketsFull() bool {
|
||||
return m.goldCarried >= maxNuggets
|
||||
}
|
||||
|
||||
// thirstThreshold is a placeholder for a constant the book hasn't
|
||||
// shown us the exact value of yet (it's called something like
|
||||
// "ComfortLevel" there). It's the point past which the miner's
|
||||
// thirst counts as "too thirsty." Replace this once you paste the
|
||||
// real value from the book.
|
||||
const thirstThreshold = 5
|
||||
|
||||
// Thirsty reports whether the miner's thirst has crossed the
|
||||
// threshold where he needs a drink.
|
||||
func (m *Miner) Thirsty() bool {
|
||||
return m.thirst > thirstThreshold
|
||||
}
|
||||
|
||||
// BuyAndDrinkAWhiskey simulates buying and drinking a whiskey:
|
||||
// thirst drops straight back to zero. We're inventing this method
|
||||
// ourselves (the book hasn't shown the QuenchThirst state's real
|
||||
// Execute body to us), based purely on the chapter's written
|
||||
// description: "he visits the saloon in order to buy a whiskey. When
|
||||
// his thirst is quenched, ...". Named differently from the
|
||||
// QuenchThirst state itself (below) to avoid confusing the two.
|
||||
func (m *Miner) BuyAndDrinkAWhiskey() {
|
||||
m.thirst = 0
|
||||
}
|
||||
|
||||
// TransferGoldToBank moves everything the miner is carrying into his
|
||||
// bank balance, and empties his pockets back out.
|
||||
func (m *Miner) TransferGoldToBank() {
|
||||
m.moneyInBank += m.goldCarried
|
||||
m.goldCarried = 0
|
||||
}
|
||||
|
||||
// comfortableWealth is our own placeholder for how much money in the
|
||||
// bank counts as "wealthy enough" to head home and rest, per the
|
||||
// book's description of VisitBankAndDepositGold. The book hasn't
|
||||
// given us a real number for this, so this is a guess - swap it out
|
||||
// if you find the book's actual value.
|
||||
const comfortableWealth = 6
|
||||
|
||||
// RichEnough reports whether the miner has banked enough money to
|
||||
// feel comfortable stopping work for a while.
|
||||
func (m *Miner) RichEnough() bool {
|
||||
return m.moneyInBank >= comfortableWealth
|
||||
}
|
||||
|
||||
// DecreaseFatigue lowers the miner's tiredness by one notch, without
|
||||
// letting it go below zero.
|
||||
func (m *Miner) DecreaseFatigue() {
|
||||
if m.fatigue > 0 {
|
||||
m.fatigue--
|
||||
}
|
||||
}
|
||||
|
||||
// fatigueThreshold is our own placeholder for the "acceptable level"
|
||||
// of fatigue mentioned in GoHomeAndSleepTilRested's description. The
|
||||
// book hasn't given us a real number for this either - another
|
||||
// guess, swap it out once you know the real one.
|
||||
const fatigueThreshold = 5
|
||||
|
||||
// Fatigued reports whether the miner is still too tired to go back
|
||||
// to work.
|
||||
func (m *Miner) Fatigued() bool {
|
||||
return m.fatigue > fatigueThreshold
|
||||
}
|
||||
|
||||
// Update is the real body now - the book finally showed it. Every
|
||||
// tick the miner gets a little thirstier, then the state machine is
|
||||
// told to run (which executes the global state, then the current
|
||||
// state - see StateMachine.Update).
|
||||
func (m *Miner) Update() {
|
||||
m.thirst++
|
||||
m.stateMachine.Update()
|
||||
}
|
||||
|
||||
// ChangeState no longer does the exit/enter/swap work itself - that
|
||||
// job now belongs entirely to StateMachine.ChangeState. Miner just
|
||||
// passes the request along. This is exactly the cleanup the book
|
||||
// promised: the agent doesn't manage state transitions by hand
|
||||
// anymore, it delegates.
|
||||
func (m *Miner) ChangeState(newState State[Miner]) {
|
||||
m.stateMachine.ChangeState(newState)
|
||||
}
|
||||
Reference in new issue
Block a user