From 4e2ab55d345fd5e5c5d2d5bc56948611b15564d5 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Fri, 27 Dec 2024 12:40:19 +0100 Subject: introduce game.State interface abstraction This allows to not use the internal game state directly from client code. --- go/game/action.go | 149 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 89 insertions(+), 60 deletions(-) (limited to 'go/game/action.go') diff --git a/go/game/action.go b/go/game/action.go index 5a8fc35b..715f39a3 100644 --- a/go/game/action.go +++ b/go/game/action.go @@ -5,55 +5,73 @@ import ( "log" ) +type ActionSpeed int + +const ( + slow ActionSpeed = iota + fast +) + +var ActionSpeeds = struct { + Slow ActionSpeed + Fast ActionSpeed +}{ + Slow: slow, + Fast: fast, +} + type ( - ActionCostFunc func(*State) bool - ActionResolveFunc func(*State) + ActionCostFunc func(*LocalState) bool + ActionResolveFunc func(*LocalState) ActionFuncPrototype func(Action) ActionResolveFunc ) type Action interface { Source() interface{} Controller() *Player - GameState() *State + Speed() ActionSpeed + GameState() *LocalState Targets() *Targets Target() *Target // Shortcut for to select the first target NeedsActionCostChoice() bool - CheckTargets(*State) error - PayCosts(s *State) bool - resolve(s *State) + CheckTargets(*LocalState) error + PayCosts(s *LocalState) bool + resolve(s *LocalState) String() string } type PassPriority struct{ player *Player } -func (a *PassPriority) Source() interface{} { return a.player } -func (a *PassPriority) Controller() *Player { return a.player } -func (a *PassPriority) GameState() *State { return a.player.gameState } -func (*PassPriority) Targets() *Targets { return nil } -func (*PassPriority) Target() *Target { return nil } -func (*PassPriority) NeedsActionCostChoice() bool { return false } -func (*PassPriority) CheckTargets(*State) error { return nil } -func (*PassPriority) PayCosts(*State) bool { return true } -func (*PassPriority) resolve(*State) {} -func (*PassPriority) String() string { return "pass" } -func NewPassPriority(p *Player) Action { return &PassPriority{p} } +func (a *PassPriority) Source() interface{} { return a.player } +func (a *PassPriority) Controller() *Player { return a.player } +func (a *PassPriority) Speed() ActionSpeed { return fast } +func (a *PassPriority) GameState() *LocalState { return a.player.gameState } +func (*PassPriority) Targets() *Targets { return nil } +func (*PassPriority) Target() *Target { return nil } +func (*PassPriority) NeedsActionCostChoice() bool { return false } +func (*PassPriority) CheckTargets(*LocalState) error { return nil } +func (*PassPriority) PayCosts(*LocalState) bool { return true } +func (*PassPriority) resolve(*LocalState) {} +func (*PassPriority) String() string { return "pass" } +func NewPassPriority(p *Player) Action { return &PassPriority{p} } type TargetSelection struct { player *Player targets *Targets } -func (sel *TargetSelection) Source() interface{} { return sel.player } -func (sel *TargetSelection) Controller() *Player { return sel.player } -func (sel *TargetSelection) GameState() *State { return sel.player.gameState } -func (sel *TargetSelection) Targets() *Targets { return sel.targets } -func (sel *TargetSelection) Target() *Target { return sel.targets.ts[0] } -func (*TargetSelection) NeedsActionCostChoice() bool { return false } -func (sel *TargetSelection) CheckTargets(s *State) error { return sel.targets.CheckTargets(s) } -func (*TargetSelection) PayCosts(*State) bool { return true } -func (*TargetSelection) resolve(*State) {} -func (*TargetSelection) String() string { return "target selection" } +func (sel *TargetSelection) Source() interface{} { return sel.player } +func (sel *TargetSelection) Controller() *Player { return sel.player } +func (sel *TargetSelection) Speed() ActionSpeed { return fast } +func (sel *TargetSelection) GameState() *LocalState { return sel.player.gameState } +func (sel *TargetSelection) Targets() *Targets { return sel.targets } +func (sel *TargetSelection) Target() *Target { return sel.targets.ts[0] } +func (*TargetSelection) NeedsActionCostChoice() bool { return false } +func (sel *TargetSelection) CheckTargets(s *LocalState) error { return sel.targets.CheckTargets(s) } +func (*TargetSelection) PayCosts(*LocalState) bool { return true } +func (*TargetSelection) resolve(*LocalState) {} +func (*TargetSelection) String() string { return "target selection" } func newTargetSelection(player *Player, targets *Targets) Action { return &TargetSelection{player, targets} @@ -86,9 +104,10 @@ func (a *ActionBase) Controller() *Player { return nil } } -func (a *ActionBase) GameState() *State { return a.Controller().gameState } -func (a *ActionBase) resolve(s *State) { a.resolveFunc(s) } -func (a *ActionBase) PayCosts(s *State) bool { +func (a *ActionBase) Speed() ActionSpeed { return slow } +func (a *ActionBase) GameState() *LocalState { return a.Controller().gameState } +func (a *ActionBase) resolve(s *LocalState) { a.resolveFunc(s) } +func (a *ActionBase) PayCosts(s *LocalState) bool { if a.costFunc != nil { return a.costFunc(s) } @@ -96,7 +115,7 @@ func (a *ActionBase) PayCosts(s *State) bool { } func (a *ActionBase) Targets() *Targets { return a.targets } func (a *ActionBase) Target() *Target { return a.targets.ts[0] } -func (a *ActionBase) CheckTargets(s *State) error { +func (a *ActionBase) CheckTargets(s *LocalState) error { if a.targets == nil { return nil } @@ -131,16 +150,17 @@ type DeclareTriggeredActionsAction struct { actions []*TriggeredAction } -func (sel *DeclareTriggeredActionsAction) Source() interface{} { return nil } -func (sel *DeclareTriggeredActionsAction) Controller() *Player { return nil } -func (sel *DeclareTriggeredActionsAction) GameState() *State { return nil } -func (sel *DeclareTriggeredActionsAction) Targets() *Targets { return nil } -func (sel *DeclareTriggeredActionsAction) Target() *Target { return nil } -func (*DeclareTriggeredActionsAction) NeedsActionCostChoice() bool { return false } -func (sel *DeclareTriggeredActionsAction) CheckTargets(s *State) error { return nil } -func (*DeclareTriggeredActionsAction) PayCosts(*State) bool { return true } -func (*DeclareTriggeredActionsAction) resolve(*State) {} -func (*DeclareTriggeredActionsAction) String() string { return "declared triggered actions" } +func (sel *DeclareTriggeredActionsAction) Source() interface{} { return nil } +func (sel *DeclareTriggeredActionsAction) Controller() *Player { return nil } +func (sel *DeclareTriggeredActionsAction) Speed() ActionSpeed { return fast } +func (sel *DeclareTriggeredActionsAction) GameState() *LocalState { return nil } +func (sel *DeclareTriggeredActionsAction) Targets() *Targets { return nil } +func (sel *DeclareTriggeredActionsAction) Target() *Target { return nil } +func (*DeclareTriggeredActionsAction) NeedsActionCostChoice() bool { return false } +func (sel *DeclareTriggeredActionsAction) CheckTargets(s *LocalState) error { return nil } +func (*DeclareTriggeredActionsAction) PayCosts(*LocalState) bool { return true } +func (*DeclareTriggeredActionsAction) resolve(*LocalState) {} +func (*DeclareTriggeredActionsAction) String() string { return "declared triggered actions" } func NewDeclareTriggeredActionsAction(t []*TriggeredAction) *DeclareTriggeredActionsAction { return &DeclareTriggeredActionsAction{t} @@ -166,7 +186,7 @@ func (a *PileDropAction) String() string { return s[:len(s)-1] + " ]" } -func (*PileDropAction) PayCosts(*State) bool { return true } +func (*PileDropAction) PayCosts(*LocalState) bool { return true } func newPileDropAction(perm Permanent, tile *Tile, pile []Permanent) *PileDropAction { a := &PileDropAction{ @@ -176,7 +196,7 @@ func newPileDropAction(perm Permanent, tile *Tile, pile []Permanent) *PileDropAc pile, } - a.resolveFunc = func(s *State) { + a.resolveFunc = func(s *LocalState) { for i, p := range pile { p.onDrop(p) p.setContainingPerm(nil) @@ -201,7 +221,7 @@ var ( ) func NewPlayActionCostFunc(player *Player, cost int) ActionCostFunc { - return func(*State) bool { + return func(*LocalState) bool { if player.Resource < cost { return false } @@ -242,7 +262,7 @@ func NewPlayAction(p *Player, c *Card, args ...interface{}) *PlayAction { a.targets = newTargets() } - a.resolveFunc = func(s *State) { + a.resolveFunc = func(s *LocalState) { s.resolvePlay(a) } @@ -255,6 +275,13 @@ func NewPlayActionVariadicCosts(p *Player, c *Card, variadicCosts int) *PlayActi return a } +func (a *PlayAction) Speed() ActionSpeed { + if a.Card.IsPermanent() { + return slow + } + return fast +} + func (a *PlayAction) String() string { p := a.source.(*Player) if a.targets.RequireSelection() { @@ -292,7 +319,7 @@ func NewMoveAction(u *Unit) *MoveAction { a.targets = newTargets(newTarget(u.Controller().gameState, moveActionTargetDesc, a)) - a.resolveFunc = func(s *State) { + a.resolveFunc = func(s *LocalState) { t := targetTile(a.Target()) movePermanent(u, t) } @@ -313,7 +340,7 @@ func (a *MoveAction) String() string { } func genBaseActionCost(u *Unit, attack, move int) ActionCostFunc { - return func(*State) bool { + return func(*LocalState) bool { if u.AvailAttackActions < attack || u.AvailMoveActions < move { return false } @@ -332,7 +359,7 @@ func genMoveActionCost(u *Unit) ActionCostFunc { } func genFullActionCost(u *Unit) ActionCostFunc { - return func(*State) bool { + return func(*LocalState) bool { if u.AvailAttackActions < 1 || u.AvailMoveActions < 1 { return false } @@ -356,7 +383,7 @@ func NewEquipAction(u *Unit, e *Equipment) *EquipAction { e, } - a.resolveFunc = func(s *State) { + a.resolveFunc = func(s *LocalState) { u.equip(e) } @@ -382,7 +409,7 @@ func NewAttackAction(u *Unit) *AttackAction { a.targets = newTargets(newTarget(u.Controller().gameState, attackActionTargetDesc, a)) - a.resolveFunc = func(s *State) { + a.resolveFunc = func(s *LocalState) { s.fight(u, a.Target().sel[0].(Permanent)) } @@ -415,7 +442,7 @@ func newArtifactSwitchAction(u *Unit, artifact Permanent) *ArtifactSwitchAction artifact, } - a.resolveFunc = func(s *State) { + a.resolveFunc = func(s *LocalState) { s.switchPermanents(a.Source().(*Unit), a.Artifact) } @@ -444,7 +471,7 @@ func newArtifactMoveAction(u *Unit, artifact Permanent) *ArtifactMoveAction { a.targets = newArtifactMoveTargets(u.Controller().gameState, a) - a.resolveFunc = func(s *State) { + a.resolveFunc = func(s *LocalState) { tile := targetTile(a.Targets().ts[0]) movePermanent(u, tile) @@ -483,7 +510,7 @@ func newFullAction(u *Unit, proto ActionFuncPrototype, desc string) *FullAction desc: desc, } - a.resolveFunc = func(s *State) { + a.resolveFunc = func(s *LocalState) { rf := proto(a) rf(s) } @@ -518,7 +545,7 @@ func NewFreeAction(p Permanent, resolveProto ActionFuncPrototype, costFunc Actio desc, } - a.resolveFunc = func(s *State) { + a.resolveFunc = func(s *LocalState) { rf := resolveProto(a) rf(s) } @@ -526,6 +553,8 @@ func NewFreeAction(p Permanent, resolveProto ActionFuncPrototype, costFunc Actio return a } +func (a *FreeAction) Speed() ActionSpeed { return fast } + func (a *FreeAction) String() string { p := a.source.(Permanent) if a.targets.RequireSelection() { @@ -543,7 +572,7 @@ func (a *BuyAction) card() *Card { return a.Target().sel[0].(*Card) } -func (a *BuyAction) CheckTargets(s *State) error { +func (a *BuyAction) CheckTargets(s *LocalState) error { err := a.targets.CheckTargets(s) if err != nil { return err @@ -551,11 +580,11 @@ func (a *BuyAction) CheckTargets(s *State) error { return s.isValidBuy(a) } -func (a *BuyAction) resolve(s *State) { +func (a *BuyAction) resolve(s *LocalState) { a.player.Store.MoveCard(a.card(), a.player.DiscardPile) } -func (a *BuyAction) PayCosts(s *State) bool { +func (a *BuyAction) PayCosts(s *LocalState) bool { cost := a.card().BuyCosts.Costs(s) if cost < 0 || cost > a.player.Resource { return false @@ -585,7 +614,7 @@ type UpkeepAction struct { TargetSelection } -func (a *UpkeepAction) PayCosts(*State) bool { +func (a *UpkeepAction) PayCosts(*LocalState) bool { costs := 0 for _, t := range a.Target().sel { u := t.(*Unit) @@ -600,7 +629,7 @@ func (a *UpkeepAction) PayCosts(*State) bool { return true } -func (a *UpkeepAction) resolve(*State) { +func (a *UpkeepAction) resolve(*LocalState) { p := a.player s := p.gameState for _, i := range a.Target().sel { @@ -609,7 +638,7 @@ func (a *UpkeepAction) resolve(*State) { } // Keep and pay for the rest - for _, u := range s.Units { + for _, u := range s.units { if u.Controller() != p { continue } -- cgit v1.2.3