using System; using System.Collections.Generic; using System.Linq; using Godot; using GoDotTest; using Xunit; namespace GodotComponentTest.tests; public class HexGridPathFindingTests : TestClass { private HexGrid _hexGrid; private HexCell _hexCell; private readonly HexCell _positionA = new(new Vector2(2, 0)); private readonly HexCell _positionB = new(new Vector2(4, 2)); private readonly HexCell _positionC = new(new Vector2(7, 0)); private readonly HexCell _positionD = new(new Vector2(5, 0)); private HexCell _positionE = new(new Vector2(2, 2)); private HexCell _positionF = new(new Vector2(1, 3)); private readonly HexCell _positionG = new(new Vector2(1, 0)); private readonly Vector2[] _obstacles = { new(2, 1), new(3, 1), new(4, 1), new(1, 2), new(3, 2), new(1, 3), new(2, 3) }; public HexGridPathFindingTests(Node testScene) : base(testScene) { } [Setup] public void Setup() { _hexGrid = new HexGrid(); _hexCell = new HexCell(); _hexGrid.SetBounds(new Vector2(0, 0), new Vector2(7, 4)); foreach (Vector2 obstacle in _obstacles) { _hexGrid.AddObstacle(new HexCell(obstacle)); } } [Test] public void TestBounds() { Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(0, 0))); Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(0, 4))); Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(7, 0))); Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(7, 4))); Assert.Equal(0, _hexGrid.GetHexCost(new Vector2(8, 2))); Assert.Equal(0, _hexGrid.GetHexCost(new Vector2(6, 5))); Assert.Equal(0, _hexGrid.GetHexCost(new Vector2(-1, 2))); Assert.Equal(0, _hexGrid.GetHexCost(new Vector2(6, -1))); } [Test] public void TestNegativeBounds() { HexGrid grid = new(); grid.SetBounds(new Vector2(-5, -5), new Vector2(-2, -2)); Assert.Equal(grid.PathCostDefault, grid.GetHexCost(new Vector2(-2, -2))); Assert.Equal(grid.PathCostDefault, grid.GetHexCost(new Vector2(-5, -5))); Assert.Equal(0, grid.GetHexCost(new Vector2(0, 0))); Assert.Equal(0, grid.GetHexCost(new Vector2(-6, -3))); Assert.Equal(0, grid.GetHexCost(new Vector2(-3, -1))); } [Test] public void TestNegativeBoundsAlt() { HexGrid grid = new(); grid.SetBounds(new Vector2(-3, -3), new Vector2(2, 2)); Assert.Equal(grid.PathCostDefault, grid.GetHexCost(new Vector2(-3, -3))); Assert.Equal(grid.PathCostDefault, grid.GetHexCost(new Vector2(2, 2))); Assert.Equal(grid.PathCostDefault, grid.GetHexCost(new Vector2(0, 0))); Assert.Equal(0, grid.GetHexCost(new Vector2(-4, 0))); Assert.Equal(0, grid.GetHexCost(new Vector2(0, 3))); } [Test] public void TestGridObstacles() { Assert.Equal(_obstacles.Length, _hexGrid.Obstacles.Count); // Adding an obstacle _hexGrid.AddObstacle(new HexCell(new Vector2(0, 0))); Assert.Equal(0, _hexGrid.Obstacles[new Vector2(0, 0)]); // Replacing obstacle _hexGrid.AddObstacle(new HexCell(new Vector2(0, 0)), 2); Assert.Equal(2, _hexGrid.Obstacles[new Vector2(0, 0)]); // Removing obstacle _hexGrid.RemoveObstacle(new HexCell(new Vector2(0, 0))); Assert.DoesNotContain(new Vector2(0, 0), _hexGrid.Obstacles); // Removing invalid does not cause error _hexGrid.RemoveObstacle(new HexCell(new Vector2(0, 0))); } [Test] public void TestHexCost() { Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(1, 1))); Assert.Equal(0, _hexGrid.GetHexCost(new HexCell(new Vector3(2, 1, -3)))); _hexGrid.AddObstacle(new HexCell(1, 1), 1.337f); Assert.Equal(1.337f, _hexGrid.GetHexCost(new Vector2(1, 1))); } [Test] public void TestMoveCost() { Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetMoveCost(new Vector2(0, 0), HexCell.DIR_N)); } [Test] public void TestMovieCostCumulative() { _hexGrid.AddObstacle(new Vector2(0, 0), 1); _hexGrid.AddObstacle(new Vector2(0, 1), 2); _hexGrid.AddBarrier(new Vector2(0, 0), HexCell.DIR_N, 4); Assert.Single(_hexGrid.Barriers); _hexGrid.AddBarrier(new Vector2(0, 1), HexCell.DIR_S, 8); Assert.Equal(2, _hexGrid.Barriers.Count); Assert.Equal(14, _hexGrid.GetMoveCost(new Vector2(0, 0), HexCell.DIR_N)); } private void ComparePath(List expected, List path) { Assert.Equal(expected.Count, path.Count()); foreach (int i in Enumerable.Range(0, Math.Min(expected.Count, path.Count()))) { HexCell pathCell = path[i]; HexCell expectedCell = expected[i]; Assert.Equal(expectedCell.AxialCoords, pathCell.AxialCoords); } } [Test] public void TestStraightLine() { List expectedPath = new() { _positionA, new HexCell(new Vector2(3, 0)), new HexCell(new Vector2(4, 0)), new HexCell(new Vector2(5, 0)), new HexCell(new Vector2(6, 0)), _positionC }; ComparePath(expectedPath, _hexGrid.FindPath(expectedPath.First(), expectedPath.Last())); } // TODO: verify what the issue is here // [Test] // public void TestWonkyLine() // { // List expectedPath = new List() // { // _positionB, // new HexCell(new Vector2(5, 1)), // new HexCell(new Vector2(5, 2)), // new HexCell(new Vector2(6, 0)), // new HexCell(new Vector2(6, 1)), // _positionC // }; // // ComparePath(expectedPath, _hexGrid.FindPath(expectedPath.First(), expectedPath.Last())); // } [Test] public void TestObstacle() { List expectedPath = new() { _positionA, new HexCell(new Vector2(3, 0)), new HexCell(new Vector2(4, 0)), new HexCell(new Vector2(5, 0)), new HexCell(new Vector2(5, 1)), _positionB }; ComparePath(expectedPath, _hexGrid.FindPath(expectedPath.First(), expectedPath.Last())); } [Test] public void TestWalls() { Vector3[] walls = { HexCell.DIR_N, HexCell.DIR_NE, HexCell.DIR_SE, HexCell.DIR_S, // DIR_SE is open HexCell.DIR_NW }; foreach (Vector3 wall in walls) { _hexGrid.AddBarrier(_positionG, wall); } List expectedPath = new() { _positionA, new HexCell(new Vector2(1, 1)), new HexCell(new Vector2(0, 1)), new HexCell(new Vector2(0, 0)), _positionG }; ComparePath(expectedPath, _hexGrid.FindPath(expectedPath.First(), expectedPath.Last())); } [Test] public void TestSlopes() { _hexGrid.AddBarrier(_positionG, HexCell.DIR_NE, 3); _hexGrid.AddBarrier(_positionG, HexCell.DIR_N, _hexGrid.PathCostDefault - 0.1f); List expectedPath = new() { _positionA, new HexCell(new Vector2(1, 1)), _positionG }; ComparePath(expectedPath, _hexGrid.FindPath(expectedPath.First(), expectedPath.Last())); } [Test] public void TestRoughTerrain() { List shortPath = new() { _positionA, new HexCell(new Vector2(3, 0)), new HexCell(new Vector2(4, 0)), _positionD, new HexCell(new Vector2(5, 1)), _positionB }; List longPath = new() { _positionA, new HexCell(new Vector2(1, 1)), new HexCell(new Vector2(0, 2)), new HexCell(new Vector2(0, 3)), new HexCell(new Vector2(0, 4)), new HexCell(new Vector2(1, 4)), new HexCell(new Vector2(2, 4)), new HexCell(new Vector2(3, 3)), _positionB }; _hexGrid.PathCostDefault = 1f; ComparePath(shortPath, _hexGrid.FindPath(shortPath.First(), shortPath.Last())); _hexGrid.PathCostDefault = 2f; ComparePath(shortPath, _hexGrid.FindPath(shortPath.First(), shortPath.Last())); _hexGrid.PathCostDefault = 3.9f; ComparePath(shortPath, _hexGrid.FindPath(shortPath.First(), shortPath.Last())); // TODO: check what causes the difference here // _hexGrid.PathCostDefault = 4.1f; // ComparePath(longPath, _hexGrid.FindPath(longPath.First(), longPath.Last())); // // _hexGrid.PathCostDefault = 51f; // ComparePath(longPath, _hexGrid.FindPath(longPath.First(), longPath.Last())); // // _hexGrid.PathCostDefault = 0f; // ComparePath(longPath, _hexGrid.FindPath(longPath.First(), longPath.Last())); } }