aboutsummaryrefslogtreecommitdiff
path: root/go/game/ai.go
diff options
context:
space:
mode:
Diffstat (limited to 'go/game/ai.go')
-rw-r--r--go/game/ai.go148
1 files changed, 146 insertions, 2 deletions
diff --git a/go/game/ai.go b/go/game/ai.go
index 4860a13d..bd5a5e86 100644
--- a/go/game/ai.go
+++ b/go/game/ai.go
@@ -59,6 +59,10 @@ func NewUnitAI(s *LocalState, u *Unit) *UnitAI {
return nil
}
+ return NewUnitAIFromDesc(s, u, aiDesc)
+}
+
+func NewUnitAIFromDesc(s *LocalState, u *Unit, aiDesc string) *UnitAI {
c := make(chan Action)
wg := new(sync.WaitGroup)
wg.Add(1)
@@ -224,7 +228,7 @@ func findPathsToUnits(graph *dijkstra.Graph, m *Map, u *Unit, units []*Unit) []*
return findPathsToPermanents(graph, m, u, perms)
}
-func findPathsToInterface(graph *dijkstra.Graph, m *Map, u *Unit, options []interface{}) []*dijkstra.BestPath {
+func findPathsToInterface(graph *dijkstra.Graph, m *Map, u *Unit, options []any) []*dijkstra.BestPath {
paths := []*dijkstra.BestPath{}
for _, option := range options {
var bestPath *dijkstra.BestPath
@@ -347,7 +351,7 @@ func moveTowardsNearestEnemyUnit(ai *UnitAI) Action {
return actionFromPaths(ai, graph, paths)
}
-func moveTowardsNearestTargetOption(ai *UnitAI, options []interface{}) Action {
+func moveTowardsNearestTargetOption(ai *UnitAI, options []any) Action {
if ai.u.AvailMoveActions == 0 {
return nil
}
@@ -522,3 +526,143 @@ func TargetOrientedAI(ai *UnitAI) {
WanderingAI(ai, 3)
}
}
+
+// SuggestUnitAI returns a suggested UnitAI variant for the specified card.
+// If there is no special case for a unit, a rough heuristic is used to determinw the AI.
+// Units with full actions are shy and everything else is aggresive.
+func SuggestUnitAI(unit *Unit) string {
+ log.Println(unit.card.Name)
+ switch unit.card.Name {
+ case "King":
+ return "shy"
+ case "Farmer":
+ return "target-oriented farm tile"
+ default:
+ if unit.HasFullAction() {
+ return "shy"
+ } else {
+ return "aggressive"
+ }
+ }
+}
+
+// SimpleAiControl implements a simple state-based AI.
+type SimpleAiControl struct {
+ p *Player
+ last PlayerNotification
+ uAis map[*Unit]*UnitAI
+}
+
+func NewSimpleAiControl(p *Player) *SimpleAiControl {
+ aiCtrl := &SimpleAiControl{
+ p: p,
+ uAis: make(map[*Unit]*UnitAI),
+ }
+ return aiCtrl
+}
+
+func (ai *SimpleAiControl) Player() *Player {
+ return ai.p
+}
+
+// RecvAction implements the actual AI's logic.
+func (ai *SimpleAiControl) RecvAction() (Action, error) {
+ switch ai.last.Notification {
+ case PriorityNotification:
+ return ai.handlePriority(), nil
+ case TargetSelectionPrompt:
+ return ai.handleTargetSelection(), nil
+ }
+ return NewPassPriority(ai.p), nil
+}
+
+func (ai *SimpleAiControl) handlePriority() Action {
+ s := ai.p.gameState
+ active := s.ActivePlayer() == ai.p
+
+ if !active {
+ spells := ai.p.Hand.FilterCards(func(c *Card) bool { return c.Type == CardTypes.Spell })
+ // s.FilterPermanents(func(p Permanent) { return p.HasFreeAction()
+ if len(spells) != 0 {
+ }
+
+ freeActions := []Action{}
+ if len(freeActions) != 0 {
+ }
+ } else {
+ switch s.ActivePhase() {
+ case Phases.UpkeepPhase:
+ // Select units to disband
+ case Phases.ActionPhase:
+ // Slow Actions
+ if s.stack.IsEmpty() {
+ for _, u := range s.OwnUnits(ai.p) {
+ if len(u.AvailSlowActions()) == 0 {
+ continue
+ }
+
+ var uAi *UnitAI
+ var ok bool
+ if uAi, ok = ai.uAis[u]; !ok {
+ uAi = NewUnitAI(s, u)
+ ai.uAis[u] = uAi
+ }
+ uAi.promptAction()
+ return uAi.NextAction()
+ }
+ }
+ case Phases.BuyPhase:
+ // TODO: support buying stuff
+ case Phases.DiscardStep:
+ // TODO: support "smarter" discarding
+ t := ai.last.Context.(*Targets)
+ for _, c := range ai.p.Hand.Cards() {
+ t.AddSelection(c)
+ }
+ return newTargetSelection(ai.p, t)
+ }
+ }
+ return NewPassPriority(ai.p)
+}
+
+func (ai *SimpleAiControl) handleTargetSelection() Action {
+ s := ai.p.gameState
+ ctx := ai.last.Context.(TargetSelectionCtx)
+ t := ctx.Action.Targets()
+ switch s.ActivePhase() {
+ case Phases.UpkeepPhase:
+ // Select units to disband
+ case Phases.ActionPhase:
+ case Phases.BuyPhase:
+ // TODO: support buying stuff
+ case Phases.DiscardStep:
+ // TODO: support "smarter" discarding
+ for _, c := range ai.p.Hand.Cards() {
+ t.AddSelection(c)
+ }
+ return ctx.Action
+ }
+
+ err := selectRandomTargets(s.Rand, t)
+ if err != nil {
+ return nil
+ }
+ return ctx.Action
+}
+
+func (*SimpleAiControl) SendAction(Action) error {
+ return nil
+}
+
+// SendNotification simply stores the last notification sent by the game.
+func (ai *SimpleAiControl) SendNotification(n PlayerNotification) error {
+ ai.last = n
+ return nil
+}
+
+func (ai *SimpleAiControl) RecvNotification() (n PlayerNotification, err error) {
+ return
+}
+
+func (ai *SimpleAiControl) Close() {
+}