package game import ( "strings" "testing" ) func TestGenerateGraph(t *testing.T) { mapDef := `map: |+1 HST HSF TST symbols: T: tower H: house F: farm S: street ` player := NewMockPlayer() m, _ := readMap(strings.NewReader(mapDef)) a := NewUnit(NewCard("base/archer"), m.TileAt(Position{0, 0}), player) f := NewUnit(NewCard("misc/farmer"), m.TileAt(Position{2, 2}), player) graph := m.generateMapGraphFor(a) p, err := findPathTo(graph, a, Position{1, 1}) if err != nil { t.Fatal(err) } if p.Distance != 3 { t.Fatalf("Wrong distance %d", p.Distance) } paths := findPathsToUnits(graph, m, a, []*Unit{f}) if len(paths) == 0 || paths[0] == nil { t.Fatal("No path found") } if paths[0].Distance != 3 { t.Fatalf("Wrong distance %d", p.Distance) } } func TestAngrySquidDiagonal(t *testing.T) { mapDef := `map: |+1 WWWWWWW W W WW WW WWWWWWW WWWWWWW W W symbols: W: deep water ` m, err := readMap(strings.NewReader(mapDef)) if m == nil || err != nil { t.Fatal("failed to parse map", m, err) } player := NewMockPlayer() s := NewUnit(NewCard("kraken/angry_squid"), m.TileAt(Position{5, 0}), player) f := NewUnit(NewCard("misc/farmer"), m.TileAt(Position{6, 5}), player) graph := m.generateMapGraphFor(s) paths := findPathsToUnits(graph, m, s, []*Unit{f}) if len(paths) == 0 || paths[0] == nil { t.Fatal("No path found") } } func TestClowFish(t *testing.T) { mapDef := `map: |+1 WWWWW WWWWW WWWWW WWWWW WWWWW symbols: W: deep water ` m, _ := readMap(strings.NewReader(mapDef)) s := NewLocalState() s.SetMap(m) p1 := s.AddNewPlayer("kraken", NewDeck()) p2 := s.AddNewPlayer("player", NewDeck()) 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 Clownfish") } 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 WWW W WWW symbols: W: wall ` m, _ := readMap(strings.NewReader(mapDef)) s := NewLocalState() s.SetMap(m) p1 := s.AddNewPlayer("tyrant", NewDeck()) p2 := s.AddNewPlayer("player", NewDeck()) b := s.addNewUnit(NewCard("tyrant/bauernfeind"), Position{1, 1}, p1) s.addNewUnit(NewCard("misc/farmer"), Position{1, 3}, p2) bAi := NewUnitAI(s, b) if bAi == nil { t.Fatal("No AI available for Bauernfeind") } bAi.promptAction() a := <-bAi.actions if a == nil { t.Fatal("No action reported") } var ma *MoveAction var ok bool if ma, ok = a.(*MoveAction); !ok { t.Fatal("AI not moving towards target") } if tile, ok := ma.targets.Cur().sel[0].(*Tile); ok { if tile != m.TileAt(Position{3, 2}) { t.Fatalf("Unexpected Bauernfeind move: expected tile(3,2), got: %s", tile) } } else { t.Fatal("Target is not a Tile") } } func TestSuggestUnitAi(t *testing.T) { mapDef := `map: |+1 HST HSF TST symbols: T: tower H: house F: farm S: street ` m, _ := readMap(strings.NewReader(mapDef)) player := NewMockPlayer() a := NewUnit(NewCard("base/archer"), m.TileAt(Position{0, 0}), player) if SuggestUnitAI(a) != "aggressive" { t.Fatal("expected aggressive") } f := NewUnit(NewCard("misc/farmer"), m.TileAt(Position{1, 1}), player) exp := "target-oriented farm tile" is := SuggestUnitAI(f) if exp != is { t.Fatal(exp, " != ", is) } k := NewUnit(NewCard("misc/king"), m.TileAt(Position{0, 1}), player) if SuggestUnitAI(k) != "shy" { t.Fatal("expected shy") } tc := NewUnit(NewCard("base/tax_collector"), m.TileAt(Position{2, 2}), player) if SuggestUnitAI(tc) != "shy" { t.Fatal("expected shy") } } func TestUnitAIFromDesc(t *testing.T) { mapDef := `map: |+1 HST HSF TST symbols: T: tower H: house F: farm S: street ` m, _ := readMap(strings.NewReader(mapDef)) player := NewMockPlayer() k := NewUnit(NewCard("misc/king"), m.TileAt(Position{0, 0}), player) ai := NewUnitAIFromDesc(nil, k, "shy") if ai == nil { t.Fatal("ai is nil") } } func TestShyUnitAI(t *testing.T) { mapDef := `map: |+1 HST HSF TST symbols: T: tower H: house F: farm S: street ` s, _, p, o := newMockState() m, _ := readMap(strings.NewReader(mapDef)) s.SetMap(m) s.addNewUnit(NewCard("base/knight"), Position{0, 0}, p) king := s.addNewUnit(NewCard("misc/king"), Position{1, 1}, o) ai := NewUnitAI(s, king) if ai == nil { t.Fatal("ai is nil") } ai.promptAction() a, _ := ai.NextAction() if a == nil { t.Fatal("Nil action received from shy AI") } s.ResolveAction(a) pos := Position{2, 2} if king.Tile().Position != pos { t.Fatal("king not moving to most distant pos from enemy units", king.Tile().Position) } ai.promptAction() _, ok := ai.NextAction() if ok { t.Fatal("ShyAI did not close its channel") } } func TestUnthreatendShyUnitAI(t *testing.T) { mapDef := `map: |+1 HST H H F W symbols: T: tower H: house F: farm S: street W: deep water ` s := NewLocalState() m, _ := readMap(strings.NewReader(mapDef)) s.SetMap(m) p := s.AddNewPlayer("p", NewDeck()) r := s.addNewUnit(NewCard("base/recruiter"), Position{1, 1}, p) ai := NewUnitAI(s, r) if ai == nil { t.Fatal("ai is nil") } ai.promptAction() a, more := ai.NextAction() if a == nil { t.Fatal("Nil action received from shy AI") } if _, ok := a.(*FullAction); !ok { t.Fatal("Not FullAction received from uncontested recruiter") } if !more { t.Fatal("Ai action channel already closed") } _, more = ai.NextAction() if more { t.Fatal("AI did not finish after issuing a FullAction") } } func TestBlockedPathUnitAI(t *testing.T) { mapDef := `map: |+1 WSW WSW WSW SSS symbols: T: tower H: house F: farm S: street W: deep water ` s, _, p, o := newMockState() m, _ := readMap(strings.NewReader(mapDef)) s.SetMap(m) s.addNewUnit(NewCard("base/archer"), Position{1, 3}, p) f1 := s.addNewUnit(NewCard("base/fighter"), Position{1, 1}, o) f2 := s.addNewUnit(NewCard("base/fighter"), Position{1, 0}, o) // Move f1 ai := NewUnitAI(s, f1) if ai == nil { t.Fatal("ai is nil") } ai.promptAction() a, _ := ai.NextAction() if a == nil { t.Fatal("Nil action received from aggressive AI") } s.ResolveAction(a) // Move f2 ai = NewUnitAI(s, f2) if ai == nil { t.Fatal("ai is nil") } ai.promptAction() a, _ = ai.NextAction() if a == nil { t.Fatal("Nil action received from aggressive AI") } s.ResolveAction(a) } func TestAggressiveFlexAttackUnitAI(t *testing.T) { mapDef := `map: |+1 SSSS symbols: S: street ` s, _, p, o := newMockState() m, _ := readMap(strings.NewReader(mapDef)) s.SetMap(m) pm := s.addNewUnit(NewCard("base/pikeman"), Position{0, 0}, p) c := s.addNewUnit(NewCard("base/cavalry"), Position{3, 0}, o) ai := NewUnitAI(s, pm) if ai == nil { t.Fatal("ai is nil") } // Move p ai.promptAction() a, _ := ai.NextAction() if a == nil { t.Fatal("Nil action received from aggressive AI") } if _, ok := a.(*MoveAction); !ok { t.Fatal("Received unexpected action", a) } s.ResolveAction(a) // Attack c ai.promptAction() a, _ = ai.NextAction() if a == nil { t.Fatal("Nil action received from aggressive AI") } s.ResolveAction(a) if c.Damage() != 1 { t.Fatal("Cavlary not destroyed") } }