aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go/TODO2
-rw-r--r--go/game/ai.go63
-rw-r--r--go/game/ai_test.go61
-rw-r--r--go/game/unit_test.go2
4 files changed, 114 insertions, 14 deletions
diff --git a/go/TODO b/go/TODO
index 0f82a126..ede55fc0 100644
--- a/go/TODO
+++ b/go/TODO
@@ -8,4 +8,4 @@
* implement draft
* implement game log
* finish AIs
- * shy ai
+ * tyrant
diff --git a/go/game/ai.go b/go/game/ai.go
index ec535182..415f01e0 100644
--- a/go/game/ai.go
+++ b/go/game/ai.go
@@ -258,6 +258,43 @@ func moveToRandomTile(ai *UnitAI) Action {
return a
}
+func moveAwayFromEnemies(ai *UnitAI) Action {
+ if ai.u.AvailMoveActions == 0 {
+ return nil
+ }
+
+ graph := ai.s.Map().generateMapGraphFor(ai.u)
+ var farest *Tile
+ var farestEnemyDistance int64
+
+ for _, availableTile := range ai.u.MoveRangeTiles(ai.s.Map()) {
+ var nearestEnemy int64 = -1
+
+ for _, enemyUnit := range ai.s.EnemyUnits(ai.u.Controller()) {
+ p, err := findPathTo(graph, enemyUnit, availableTile.Position)
+ if err != nil {
+ continue
+ }
+ if nearestEnemy == -1 || p.Distance < nearestEnemy {
+ nearestEnemy = p.Distance
+ }
+ }
+
+ if farest == nil || farestEnemyDistance < nearestEnemy {
+ farest = availableTile
+ farestEnemyDistance = nearestEnemy
+ }
+ }
+
+ if farest == nil {
+ return nil
+ }
+
+ a := NewMoveAction(ai.u)
+ a.Target().AddSelection(farest)
+ return a
+}
+
func actionFromPaths(ai *UnitAI, graph *dijkstra.Graph, paths []*dijkstra.BestPath) Action {
nearest := 0
for i, p := range paths {
@@ -332,7 +369,7 @@ func attackAttackableEnemyPerm(ai *UnitAI) Action {
return a
}
-func fullAction(ai *UnitAI) {
+func fullAction(ai *UnitAI) Action {
fullActions := ai.u.FullActions
idx := 0
if len(fullActions) > 1 {
@@ -340,10 +377,14 @@ func fullAction(ai *UnitAI) {
}
a := fullActions[idx]
- err := selectRandomTargets(ai.s.Rand, a.Targets())
- if err == nil {
- ai.actions <- a
+ if a.Targets() != nil && a.Targets().RequireSelection() {
+ err := selectRandomTargets(ai.s.Rand, a.Targets())
+ if err == nil {
+ return nil
+ }
}
+
+ return a
}
// 1. If enemy Unit in attack Range then
@@ -399,13 +440,17 @@ out:
}
}
if !inEnemyAttackRange && ai.u.HasFullAction() {
- fullAction(ai)
+ ai.actions <- fullAction(ai)
+ return
}
// 2.
- // TODO: implement path finding
- // if u.AvailMoveActions > 0 {
- // }
+ if ai.u.AvailMoveActions > 0 {
+ a := moveAwayFromEnemies(ai)
+ if !ai.PostAndContinue(a) {
+ return
+ }
+ }
// 3.
if a := attackAttackableEnemyPerm(ai); a != nil {
@@ -452,7 +497,7 @@ func WanderingAI(ai *UnitAI, x int) {
func TargetOrientedAI(ai *UnitAI) {
// 1.
if ai.u.HasFullAction() {
- fullAction(ai)
+ ai.actions <- fullAction(ai)
return
}
diff --git a/go/game/ai_test.go b/go/game/ai_test.go
index 880c1efc..2d3a6a32 100644
--- a/go/game/ai_test.go
+++ b/go/game/ai_test.go
@@ -65,6 +65,62 @@ symbols:
}
}
+func TestClowFish(t *testing.T) {
+ mapDef := `map: |1-
+ WWWWW
+ WWWWW
+ WWWWW
+ WWWWW
+ WWWWW
+symbols:
+ W: deep water
+`
+ r := strings.NewReader(mapDef)
+ m, _ := readMap(r)
+ s := NewLocalState()
+ s.SetMap(m)
+ p1 := s.AddNewPlayer("kraken", NewDeck(), nil)
+ p2 := s.AddNewPlayer("player", NewDeck(), nil)
+ c := s.addNewUnit(NewCard("kraken/clownfish"), Position{2, 2}, p1)
+ s.addNewUnit(NewCard("kraken/sailfish"), Position{1, 1}, p2)
+ s.addNewUnit(NewCard("kraken/sailfish"), Position{3, 2}, p2)
+
+ ai := NewUnitAI(s, c)
+ if ai == nil {
+ t.Fatal("No AI available for Bauernfeind")
+ }
+
+ ai.promptAction()
+ a := <-ai.actions
+ if a == nil {
+ t.Fatal("No action reported")
+ }
+
+ var ma *MoveAction
+ var ok bool
+ if ma, ok = a.(*MoveAction); !ok {
+ t.Fatal("AI did not move flee from enemies")
+ }
+
+ if tile, ok := ma.targets.Cur().sel[0].(*Tile); ok {
+ if tile != m.TileAt(Position{1, 4}) {
+ t.Fatalf("Unexpected clownfish move: expected tile(1,4), got: %s", tile)
+ }
+ } else {
+ t.Fatal("Target is not a Tile")
+ }
+ ma.resolve(s)
+
+ ai = NewUnitAI(s, c)
+ ai.promptAction()
+ a = <-ai.actions
+ if a == nil {
+ t.Fatal("No action reported")
+ }
+ if _, ok := a.(*FullAction); !ok {
+ t.Fatal("Unthreatend AI did not use a full action")
+ }
+}
func TestBauernFeind(t *testing.T) {
mapDef := `map: |1-
@@ -72,7 +128,6 @@ func TestBauernFeind(t *testing.T) {
W
WWW
-
symbols:
W: wall
`
@@ -91,7 +146,7 @@ symbols:
}
bAi.promptAction()
- a := <- bAi.actions
+ a := <-bAi.actions
if a == nil {
t.Fatal("No action reported")
}
@@ -103,7 +158,7 @@ symbols:
}
if tile, ok := ma.targets.Cur().sel[0].(*Tile); ok {
- if tile != m.TileAt(Position{3,2}) {
+ if tile != m.TileAt(Position{3, 2}) {
t.Fatalf("Unexpected Bauernfeind move: expected tile(3,2), got: %s", tile)
}
} else {
diff --git a/go/game/unit_test.go b/go/game/unit_test.go
index fd9b95b4..68fadea4 100644
--- a/go/game/unit_test.go
+++ b/go/game/unit_test.go
@@ -19,7 +19,7 @@ symbols:
`
s := NewLocalState()
r := strings.NewReader(mapDef)
- m, _ := readMap(r)
+ m, _ := readMap(r)
s.SetMap(m)
s.AddNewPlayer("player", NewDeck(), nil)