aboutsummaryrefslogtreecommitdiff
path: root/go/game/action.go
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2024-12-27 12:40:19 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-01-27 16:43:58 +0100
commit4e2ab55d345fd5e5c5d2d5bc56948611b15564d5 (patch)
treec6998ac54687c442ad0baa541edd0fbf917b1308 /go/game/action.go
parent5aa8e7ddbc41aed55e7918e6afaec0b5c07fa510 (diff)
downloadmuhqs-game-4e2ab55d345fd5e5c5d2d5bc56948611b15564d5.tar.gz
muhqs-game-4e2ab55d345fd5e5c5d2d5bc56948611b15564d5.zip
introduce game.State interface abstraction
This allows to not use the internal game state directly from client code.
Diffstat (limited to 'go/game/action.go')
-rw-r--r--go/game/action.go149
1 files changed, 89 insertions, 60 deletions
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
}