diff options
Diffstat (limited to 'go/game/ai.go')
| -rw-r--r-- | go/game/ai.go | 148 |
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() { +} |
