public void TestPathLength1() { HexGame hexGame = new HexGame(7); Assert.IsTrue(hexGame.HasWon() == Occupied.Empty); int xPathLength = hexGame.PlayerScore(true); int yPathLength = hexGame.PlayerScore(false); // clean the path, get cells on the path List<Location> xPath = hexGame.GetCleanPath(true); List<Location> yPath = hexGame.GetCleanPath(false); Assert.AreEqual(xPath.Count, yPath.Count); Assert.AreEqual(xPath.Count, hexGame.Board.Size * hexGame.Board.Size); Assert.AreEqual(xPathLength, yPathLength); for (int x = 0; x < hexGame.Board.Size; x++) { for (int y = 0; y < hexGame.Board.Size; y++) { Location loc = new Location(x, y); Assert.IsTrue(xPath.Contains(loc)); Assert.IsTrue(yPath.Contains(loc)); } } }
public void EquivalentCellsHaveSameHashCodes() { Location loc1 = new Location(2, 1); Location loc2 = new Location(2, 1); Assert.AreEqual(loc1.GetHashCode(), loc2.GetHashCode()); }
public void EqualityTest() { Location loc1 = new Location(2, 3); Location loc2 = new Location(3, 2); Location loc1Dup = new Location(2, 3); Assert.AreNotEqual(loc1, loc2); Assert.AreEqual(loc1, loc1); Assert.AreEqual(loc2, loc2); Assert.AreEqual(loc1, loc1Dup); Assert.IsTrue(loc1 == loc1Dup); Assert.IsFalse(loc1 == loc2); Assert.IsTrue(loc1.Equals(loc1)); Assert.IsTrue(loc1.Equals(loc1Dup)); Assert.IsFalse(loc1.Equals(loc2)); Assert.IsFalse(loc1.Equals(null)); Assert.IsFalse(loc1.Equals(3)); Assert.IsFalse(loc1.Equals("hello")); Assert.IsFalse(loc1 != loc1Dup); Assert.IsTrue(loc1 != loc2); }
public bool IsOnPath(Location loc) { if (this.onPathData != null) { return this.onPathData[loc.X, loc.Y]; } return true; }
public Cell[] BetweenEdge(Location loc, bool playerX) { if (playerX) { return this.playerXBetweenEdge[loc.X, loc.Y]; } return this.playerYBetweenEdge[loc.X, loc.Y]; }
public void CopyMakesEqualObject() { Location location = new Location(3, 4); Location copiedLocation = location; Assert.AreEqual(3, copiedLocation.X); Assert.AreEqual(4, copiedLocation.Y); Assert.AreEqual(location, copiedLocation); }
public void BetweenEdgeTest() { HexBoard hexBoard = new HexBoard(BoardSize); for (int x = 0; x < hexBoard.Size; x++) { for (int y = 0; y < hexBoard.Size; y++) { Location testLoc = new Location(x, y); Cell[] resultX = hexBoard.BetweenEdge(testLoc, true); if (y == 1) { // second or second-last row if (x < hexBoard.Size - 1) { Assert.AreEqual(2, resultX.Length, testLoc.ToString()); } } else if (y == hexBoard.Size - 2) { // second or second-last row if (x > 0) { Assert.AreEqual(2, resultX.Length, testLoc.ToString()); } } else { Assert.AreEqual(0, resultX.Length, testLoc.ToString()); } Cell[] resultY = hexBoard.BetweenEdge(testLoc, false); if (x == 1) { // second or second-last row if (y < hexBoard.Size - 1) { Assert.AreEqual(2, resultY.Length, testLoc.ToString()); } } else if (x == hexBoard.Size - 2) { // second or second-last row if (y > 0) { Assert.AreEqual(2, resultY.Length, testLoc.ToString()); } } else { Assert.AreEqual(0, resultY.Length, testLoc.ToString()); } } } }
public void DifferentCellsHaveDifferentHashCodes() { Location loc1 = new Location(1, 1); Location loc2 = new Location(1, 2); Location loc3 = new Location(2, 2); Assert.AreNotEqual(loc1.GetHashCode(), loc2.GetHashCode()); Assert.AreNotEqual(loc1.GetHashCode(), loc3.GetHashCode()); Assert.AreNotEqual(loc2.GetHashCode(), loc3.GetHashCode()); }
public void NeighboursNearOriginTest() { HexBoardNeighbours testBoard = new HexBoardNeighbours(10); Location inValue = new Location(1, 1); Location[][] outValue = testBoard.Neighbours2(inValue); Assert.IsNotNull(outValue); Assert.AreEqual(4, outValue.Length); TestNeighbours(testBoard, inValue, outValue); }
public void NeighboursMiddleTest() { HexBoardNeighbours testBoard = new HexBoardNeighbours(10); Location inValue = new Location(5, 5); Location[][] outValue = testBoard.Neighbours2(inValue); Assert.IsNotNull(outValue); Assert.AreEqual(6, outValue.Length); TestNeighbours(testBoard, inValue, outValue); }
public void NeighboursFarTest() { HexBoardNeighbours testBoard = new HexBoardNeighbours(10); Location inValue = new Location(9, 9); Location[] outValue = testBoard.Neighbours(inValue); Assert.IsNotNull(outValue); Assert.AreEqual(2, outValue.Length); Assert.AreEqual(2, testBoard.NeighbourCount(inValue)); TestNeighbours(testBoard, inValue, outValue); }
public void TestCalculateMove3PlayerX() { HexBoard board = new HexBoard(5); PlayFourMoves(board); Minimax minimax = MakeMinimaxForBoard(board); MinimaxResult firstPlayerResult = minimax.DoMinimax(4, true); Location firstPlayerExpectedMove = new Location(2, 1); Assert.AreEqual(firstPlayerExpectedMove, firstPlayerResult.Move, "Wrong first player location"); }
public void GoodMoveAtLevel3() { HexBoard board = new HexBoard(6); PlayToWinInThreeMoves(board); Minimax minimax = MakeMinimaxForBoard(board); MinimaxResult bestMove = minimax.DoMinimax(3, true); Location win = new Location(1, 3); Assert.AreEqual(win, bestMove.Move, "Wrong play location"); }
public void TestPathLength2() { HexGame hexGame = new HexGame(7); /* player1 has played at 3, 3 thier shortest path has narrowed to ones passing through this */ hexGame.Play(3, 3); /* These points are on player X's shortest path */ Location[] xPath = new Location[19]; xPath[0] = new Location(0, 6); xPath[1] = new Location(1, 5); xPath[2] = new Location(1, 6); xPath[3] = new Location(2, 4); xPath[4] = new Location(2, 5); xPath[5] = new Location(2, 6); xPath[6] = new Location(3, 0); xPath[7] = new Location(3, 1); xPath[8] = new Location(3, 2); xPath[9] = new Location(3, 3); xPath[10] = new Location(3, 4); xPath[11] = new Location(3, 5); xPath[12] = new Location(3, 6); xPath[13] = new Location(4, 0); xPath[14] = new Location(4, 1); xPath[15] = new Location(4, 2); xPath[16] = new Location(5, 0); xPath[17] = new Location(5, 1); xPath[18] = new Location(6, 0); Assert.IsTrue(hexGame.HasWon() == Occupied.Empty); int xPathLength = hexGame.PlayerScore(true); int yPathLength = hexGame.PlayerScore(false); // clean the path, get cells on the path List<Location> xPathActual = hexGame.GetCleanPath(true); Assert.IsTrue(xPathLength < yPathLength); for (int x = 0; x < hexGame.Board.Size; x++) { for (int y = 0; y < hexGame.Board.Size; y++) { Cell cell = hexGame.Board.GetCellAt(x, y); bool xOnPath = cell.Location.IsInList(xPath); bool xOnPathActual = xPathActual.Contains(cell.Location); Assert.AreEqual(xOnPath, xOnPathActual, "X Path at " + cell.Location); } } }
public void TestCalculateMove2MinimaxPlayerY() { HexBoard board = new HexBoard(3); PlayTwoMoves(board); Minimax minimax = MakeMinimaxForBoard(board); MinimaxResult secondPlayerResult = minimax.DoMinimax(2, false); Location expectedPlay = new Location(0, 2); Assert.AreEqual(expectedPlay, secondPlayerResult.Move, "Wrong play location"); Assert.IsFalse(MoveScoreConverter.IsWin(secondPlayerResult.Score)); }
/// <summary> /// get candidate moves - /// this list is comprehensive - it will contain all empty cells on the board /// and sorted - good moves come first /// the list returned may be longer than the valid cells /// but then will have a null location at the end /// Actual length is (BoardSize ^ 2) - number of moves already played /// </summary> /// <param name="board">the board to read</param> /// <param name="lookaheadDepth">the current depth of lookahead</param> /// <returns>the locations</returns> public IEnumerable<Location> CandidateMoves(HexBoard board, int lookaheadDepth) { // No potential good moves? return them all then if (this.goodMoves.GetCount(lookaheadDepth) == 0) { return board.EmptyCells(); } int maxListLength = (board.Size * board.Size) - this.cellsPlayedCount; // enough space for all the possible moves Location[] result = new Location[maxListLength]; int resultIndex = 0; // mask out the ones that have been used - intialised to false bool[,] maskCellSelected = new bool[board.Size, board.Size]; // good moves are found first Location[] myGoodMoves = this.goodMoves.GetGoodMoves(lookaheadDepth); // copy in the good moves foreach (Location goodMoveLoc in myGoodMoves) { if (board.GetCellAt(goodMoveLoc).IsEmpty() && (!maskCellSelected[goodMoveLoc.X, goodMoveLoc.Y])) { result[resultIndex] = goodMoveLoc; resultIndex++; maskCellSelected[goodMoveLoc.X, goodMoveLoc.Y] = true; } } // copy in all moves where the cell is empty; // and not already in by virtue of being a good move foreach (Cell testCell in board.GetCells()) { if (testCell.IsEmpty() && (!maskCellSelected[testCell.X, testCell.Y])) { result[resultIndex] = testCell.Location; resultIndex++; } } // null marker at the end if (resultIndex < maxListLength) { result[resultIndex] = Location.Null; } return result; }
public void GameLevel5Test() { HexGame game = new HexGame(6); SetupGame(game); // get next move for red Location expectedBestMove = new Location(2, 5); // level 5 failed // choses 0,0. // proably failing to pick a near win over a distant one // a-b cutoff failure? TestBestMove(game, 5, expectedBestMove); }
private static void TestBestMove(HexGame game, int level, Location expectedBestMove) { Minimax hexPlayer = new Minimax(game.Board, game.GoodMoves, new CandidateMovesAll()); MinimaxResult bestMove = hexPlayer.DoMinimax(level, true); // test the location of the move Assert.AreEqual(expectedBestMove, bestMove.Move, "Wrong move at level " + level); // test the expected score if (level >= 3) { Assert.AreEqual(Occupied.PlayerX, MoveScoreConverter.Winner(bestMove.Score)); } }
public void ClearTest() { HexBoard hexBoard = new HexBoard(BoardSize); // set a cell hexBoard.PlayMove(1, 1, true); Assert.AreEqual(Occupied.PlayerX, hexBoard.GetCellOccupiedAt(1, 1)); Location loc11 = new Location(1, 1); Assert.AreEqual(Occupied.PlayerX, hexBoard.GetCellOccupiedAt(1, 1)); Assert.AreEqual(Occupied.PlayerX, hexBoard.GetCellOccupiedAt(loc11)); // reset it hexBoard.Clear(); Assert.AreEqual(Occupied.Empty, hexBoard.GetCellOccupiedAt(1, 1)); Assert.AreEqual(Occupied.Empty, hexBoard.GetCellOccupiedAt(loc11)); }
/// <summary> /// Store a good move at the front of the list for the given depth /// Move the rest up /// If the good move was in the list but not that the front, /// bring it to the front /// If there are enough good moves already, one falls off the end /// else the count goes up /// </summary> /// <param name="moveDepth">the depth at which to add</param> /// <param name="insertLoc">the locaiton to add</param> public void AddGoodMove(int moveDepth, Location insertLoc) { if (insertLoc.IsNull()) { return; } if (moveDepth >= this.Depth) { this.SetDepth(moveDepth + 1); } Location[] myDepthLocs = this.moves[moveDepth]; Location currentLoc = insertLoc; bool foundInList = false; int max = this.count[moveDepth]; if (max >= GoodMovesCount) { max = GoodMovesCount - 1; } for (int index = 0; index <= max; index++) { Location tempLoc = myDepthLocs[index]; // insert and move the next move up one myDepthLocs[index] = currentLoc; currentLoc = tempLoc; // if the original inserted loc was in the list, stop moving up if ((index < max) && insertLoc.Equals(currentLoc)) { foundInList = true; break; } } // has the stored move count increased? if ((!foundInList) && (this.count[moveDepth] < GoodMovesCount)) { this.count[moveDepth]++; } }
public bool AreNeighbours(Location location1, Location location2) { if (!this.IsOnBoard(location1) || !this.IsOnBoard(location2)) { return false; } int xDif = location1.X - location2.X; int yDif = location1.Y - location2.Y; // too far away if (Math.Abs(xDif) > 1) { return false; } if (Math.Abs(yDif) > 1) { return false; } // 9 cells have a diff of 1 or less (3 *3 square) // two of these are not hex-neighbours, and one is the same cell // the other three are neighbours // same cell if ((xDif == 0) && (yDif == 0)) { return false; } // not neighbours if ((xDif == 1) && (yDif == 1)) { return false; } if ((xDif == -1) && (yDif == -1)) { return false; } return true; }
public void TestDataMove() { for (int i = 0; i < 10; i++) { var insertLoc = new Location(i, i); this.goodMoves.AddGoodMove(0, insertLoc); Location[] insertOutMoves = this.goodMoves.GetGoodMoves(0); // length should be the same as the number inserted Assert.IsTrue(this.goodMoves.GetCount(0) == (i + 1), "Failed count at " + i); Assert.IsTrue(insertOutMoves.Length == (i + 1)); // new element at the start Assert.IsTrue(insertLoc.Equals(insertOutMoves[0])); // first element at the end Assert.IsTrue(insertOutMoves[i].Equals(0, 0)); } Location[] outMoves = this.goodMoves.GetGoodMoves(0); Assert.IsTrue(outMoves.Length == 10); Assert.IsTrue(outMoves[0].Equals(9, 9)); // bring to front this.goodMoves.AddGoodMove(0, new Location(5, 5)); outMoves = this.goodMoves.GetGoodMoves(0); Assert.IsTrue(outMoves.Length == 10); Assert.IsTrue(outMoves[0].Equals(5, 5)); // bring various to front, test all are still present for (int i = 9; i >= 0; i--) { this.goodMoves.AddGoodMove(0, new Location(i, i)); outMoves = this.goodMoves.GetGoodMoves(0); Assert.IsTrue(outMoves.Length == 10); Assert.IsTrue(outMoves[0].Equals(i, i)); IsAllPresentOnce(outMoves); } }
public void TestCalculateMove3PlayerY() { HexBoard board = new HexBoard(5); PlayFourMoves(board); Minimax minimax = MakeMinimaxForBoard(board); // test score at this point PathLengthLoop pathLength = new PathLengthLoop(board); int playerScore = pathLength.PlayerScore(true); Assert.AreEqual(3, playerScore); playerScore = pathLength.PlayerScore(false); Assert.AreEqual(3, playerScore); MinimaxResult secondPlayerResult = minimax.DoMinimax(4, false); Location secondPlayerExpectedMove = new Location(1, 2); Assert.AreEqual(secondPlayerExpectedMove, secondPlayerResult.Move, "Wrong second player location"); }
public void GameIterateTest() { HexGame game = new HexGame(6); SetupGame(game); // get next move for red Location[] expectedBestMove = new Location[7]; expectedBestMove[1] = new Location(2, 5); expectedBestMove[2] = new Location(2, 5); expectedBestMove[3] = new Location(2, 5); expectedBestMove[4] = new Location(2, 5); expectedBestMove[5] = new Location(2, 5); expectedBestMove[6] = new Location(2, 5); // test levels 1-6 for (int level = 1; level < 7; level++) { TestBestMove(game, level, expectedBestMove[level]); } }
public void NeighboursOffTest() { HexBoardNeighbours testBoard = new HexBoardNeighbours(5); Location inValue = new Location(5, 0); Location[][] outValue = testBoard.Neighbours2(inValue); Assert.IsNotNull(outValue); Assert.AreEqual(0, outValue.Length); }
private static void TestOnBoard(HexBoardNeighbours testBoard, Location neighbour) { Assert.IsTrue(testBoard.IsOnBoard(neighbour)); Assert.GreaterOrEqual(neighbour.X, 0); Assert.GreaterOrEqual(neighbour.Y, 0); Assert.Less(neighbour.X, testBoard.BoardSize); Assert.Less(neighbour.Y, testBoard.BoardSize); }
private static void TestNeighbours(HexBoardNeighbours testBoard, Location testLoc, IEnumerable<Location[]> neighbourGroups) { TestOnBoard(testBoard, testLoc); foreach (Location[] neighbours in neighbourGroups) { Location neighbour2 = neighbours[0]; Location between1 = neighbours[1]; Location between2 = neighbours[2]; TestOnBoard(testBoard, neighbour2); TestOnBoard(testBoard, between1); TestOnBoard(testBoard, between2); // that the betweens are neighbours of start, end eand each other Assert.IsTrue(testBoard.AreNeighbours(between1, between2)); Assert.IsTrue(testBoard.AreNeighbours(testLoc, between1)); Assert.IsTrue(testBoard.AreNeighbours(testLoc, between2)); Assert.IsTrue(testBoard.AreNeighbours(neighbour2, between1)); Assert.IsTrue(testBoard.AreNeighbours(neighbour2, between2)); // but not neighbours of each other Assert.IsFalse(testBoard.AreNeighbours(testLoc, neighbour2)); } }
private Location RandomMiddle() { int midPoint = (this.BoardSize / 2) - 1; Location midLocation = new Location(midPoint, midPoint); if ((this.BoardSize > 6) && this.RandomBool()) { return this.RandomNeighbour(midLocation); } return midLocation; }
private Location RandomElement(Location[] locations) { int max = locations.Length; int selection = this.randomNumbers.Next(max); return locations[selection]; }
private Location RandomNeighbour(Location loc) { HexBoardNeighbours neighbourFinder = new HexBoardNeighbours(this.BoardSize); Location[] neighbours = neighbourFinder.Neighbours(loc); return this.RandomElement(neighbours); }