public HexBoard GetBoard() { lock (this.locker) { HexBoard result; if (this.available.Count == 0) { // no boards available, so make a new one result = new HexBoard(this.BoardSize); } else { // return any available board - less jiggering to take the end one int boardIndex = this.available.Count - 1; result = this.available[boardIndex]; this.available.RemoveAt(boardIndex); if (this.available.Count == 0) { Task.Factory.StartNew(() => this.available.Add(new HexBoard(this.BoardSize))); } } // board is now in use this.inUse.Add(result); return result; } }
public HexBoard GetBoard() { lock (this.locker) { HexBoard result; if (this.available.Count == 0) { // no boards available, so make a new one result = new HexBoard(this.BoardSize); } else { // return any available board - less jiggering to take the end one int boardIndex = this.available.Count - 1; result = this.available[boardIndex]; this.available.RemoveAt(boardIndex); if (this.available.Count == 0) { Task.Factory.StartNew(() => this.available.Add(new HexBoard(this.BoardSize))); } } // board is now in use this.inUse.Add(result); return(result); } }
public void GetAgainDoesNotIncreaseCount() { BoardCache boardCache = new BoardCache(10); const int TestSize = 10; HexBoard[] usedBoards = new HexBoard[TestSize]; // add some boards for (int i = 0; i < TestSize; i++) { usedBoards[i] = boardCache.GetBoard(); } // remove them for (int i = 0; i < TestSize; i++) { boardCache.Release(usedBoards[i]); } // get again, count should not change since there are now boards ready to use for (int i = 0; i < TestSize; i++) { usedBoards[i] = boardCache.GetBoard(); Assert.IsNotNull(usedBoards[i]); Assert.AreEqual(TestSize, boardCache.BoardCount); } }
public void ConstructorSetsProperties() { HexBoard hexBoard = new HexBoard(BoardSize); Assert.IsNotNull(hexBoard); Assert.AreEqual(BoardSize, hexBoard.Size); Assert.AreEqual(0, hexBoard.MovesPlayedCount); }
/// <summary> /// Initializes a new instance of the HexBoard class from an existing HexBoard /// </summary> /// <param name="originalBoard">the board to copy</param> public HexBoard(HexBoard originalBoard) { if (originalBoard != null) { this.InitCells(originalBoard.Size); this.CopyStateFrom(originalBoard); } }
/// <summary> /// board is no longer in use /// So put in on the in-use list /// </summary> /// <param name="board">the board to release</param> public void Release(HexBoard board) { lock (this.locker) { this.inUse.Remove(board); this.available.Add(board); } }
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 Minimax(HexBoard board, GoodMoves goodMoves, ICandidateMoves candidateMovesFinder) { this.actualBoard = board; this.goodMoves = goodMoves; this.candidateMovesFinder = candidateMovesFinder; this.boardCache = new BoardCache(board.Size); this.pathLengthFactory = new PathLengthAStarFactory(); }
public void CountEmptyTest() { CandidateMovesAll allMoves = new CandidateMovesAll(); HexBoard testBoard = new HexBoard(BoardSize); Location[] moves = allMoves.CandidateMoves(testBoard, 0).ToArray(); Assert.AreEqual(BoardCellCount, moves.Length); }
/// <summary> /// Initializes a new instance of the HexGame class, with a board size /// </summary> /// <param name="boardSize">size of the board</param> public HexGame(int boardSize) { this.board = new HexBoard(boardSize); IPathLengthFactory pathLengthFactory = new PathLengthAStarFactory(); this.xPathLength = pathLengthFactory.CreatePathLength(this.board); this.yPathLength = pathLengthFactory.CreatePathLength(this.board); this.goodMoves = new GoodMoves(); this.goodMoves.DefaultGoodMoves(boardSize, 5); }
public void BoardCopyTest() { HexBoard hexBoard = new HexBoard(BoardSize); HexBoard copyBoard = new HexBoard(hexBoard); Assert.IsTrue(copyBoard.Equals(hexBoard)); copyBoard.PlayMove(0, 0, true); Assert.IsFalse(copyBoard.Equals(hexBoard)); }
public void CountOneMoveTest() { CandidateMovesAll allMoves = new CandidateMovesAll(); HexBoard testBoard = new HexBoard(BoardSize); testBoard.PlayMove(5, 5, true); IEnumerable<Location> moves = allMoves.CandidateMoves(testBoard, 0); Assert.AreEqual(BoardCellCount - 1, moves.Count()); }
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 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)); }
public void CountOneMoveTest() { GoodMoves goods = new GoodMoves(); CandidateMovesSelective moveFinder = new CandidateMovesSelective(goods, 0); HexBoard testBoard = new HexBoard(BoardSize); testBoard.PlayMove(5, 5, true); IEnumerable<Location> moves = moveFinder.CandidateMoves(testBoard, 0); Assert.Greater(BoardCellCount - 1, moves.Count()); }
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"); }
/// <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 BoardLayoutTest() { HexBoard hexBoard = new HexBoard(BoardSize); for (int x = 0; x < hexBoard.Size; x++) { for (int y = 0; y < hexBoard.Size; y++) { Cell cell = hexBoard.GetCellAt(x, y); Assert.IsNotNull(cell); Assert.IsTrue(cell.X == x); Assert.IsTrue(cell.Y == y); } } }
public void AddBoardIncreasesCount() { BoardCache boardCache = new BoardCache(10); const int TestSize = 10; HexBoard[] usedBoards = new HexBoard[TestSize]; // add some boards for (int i = 0; i < TestSize; i++) { usedBoards[i] = boardCache.GetBoard(); Assert.IsNotNull(usedBoards[i]); Assert.AreEqual(i + 1, boardCache.BoardCount); } }
public void RightMoveAtLevel5PlayerY() { HexBoard board = new HexBoard(6); PlayToWinInThreeMoves(board); Minimax minimax = MakeMinimaxForBoard(board); MinimaxResult bestMove = minimax.DoMinimax(5, false); List<Location> playerYWinningLocations = new List<Location> { new Location(1, 3), new Location(2, 2) }; Assert.IsTrue(playerYWinningLocations.Contains(bestMove.Move), "Wrong play location"); }
public void CopyStateFrom(HexBoard otherBoard) { if (otherBoard != null) { if (otherBoard.Size != this.Size) { throw new Exception("Board sizes do not match"); } foreach (Cell cell in this.cells) { cell.IsOccupied = otherBoard.GetCellAt(cell.Location).IsOccupied; } this.movesPlayedCount = otherBoard.MovesPlayedCount; } }
public void SpeedTest() { const int RandSeed = 24; const int BoardCount = 100; const int BoardSize = 12; Random rands = new Random(RandSeed); HexBoard[] boards = new HexBoard[BoardCount]; // make some boards for (int loopIndex = 0; loopIndex < BoardCount; loopIndex++) { boards[loopIndex] = RandomBoard(BoardSize, 10 + loopIndex, rands); } // time it the old way DateTime oldStart = DateTime.Now; foreach (HexBoard board in boards) { PathLengthLoop oldPath = new PathLengthLoop(board); oldPath.PlayerScore(true); oldPath.PlayerScore(false); } DateTime oldEnd = DateTime.Now; // and the new way DateTime newStart = DateTime.Now; foreach (HexBoard board in boards) { PathLengthAStar newPath = new PathLengthAStar(board); newPath.PlayerScore(true); newPath.PlayerScore(false); } DateTime newEnd = DateTime.Now; TimeSpan oldDuration = oldEnd - oldStart; TimeSpan newDuration = newEnd - newStart; double ratio = newDuration.Milliseconds / (double)oldDuration.Milliseconds; Assert.IsTrue(ratio < 1.0); }
public void TestCalculateMove2Situation() { HexBoard board = new HexBoard(3); PlayTwoMoves(board); // test score at this point PathLengthLoop pathLength = new PathLengthLoop(board); int playerScoreX = pathLength.PlayerScore(true); Assert.AreEqual(2, playerScoreX, "playerScoreX"); int playerScoreY = pathLength.PlayerScore(false); Assert.AreEqual(2, playerScoreY, "playerScoreY"); // no advantage int moveScore = pathLength.SituationScore(); Assert.AreEqual(0, moveScore, "moveScore"); }
public void TestCalculateMove3PathLength() { HexBoard board = new HexBoard(5); PlayFourMoves(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); // no advantage int moveScore = pathLength.SituationScore(); Assert.AreEqual(moveScore, 0); }
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)); }
public void TestMinimax3() { HexBoard board = new HexBoard(5); Minimax minimax = MakeMinimaxForBoard(board); /* on a 5 * 5 board, red(playerx) has 3, 0 and 1, 4 needs to play 2,2 to win - should know this at look ahead 5 */ board.PlayMove(3, 0, true); board.PlayMove(1, 4, true); MinimaxResult bestMove = minimax.DoMinimax(5, true); Location expectedMove = new Location(2, 2); Assert.IsTrue(MoveScoreConverter.IsWin(bestMove.Score), "No win " + bestMove.Score); Assert.AreEqual(expectedMove, bestMove.Move, "Wrong expected move"); }
public void CountDownTest() { CandidateMovesAll allMoves = new CandidateMovesAll(); HexBoard testBoard = new HexBoard(BoardSize); int emptyCellCount = BoardSize * BoardSize; for (int x = 0; x < BoardSize; x++) { for (int y = 0; y < BoardSize; y++) { // as cells are played, less empty cells are left testBoard.PlayMove(x, y, true); emptyCellCount--; IEnumerable<Location> moves = allMoves.CandidateMoves(testBoard, 0); Assert.AreEqual(emptyCellCount, moves.Count()); } } }
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"); }
/// <summary> /// // get the candidate moves /// </summary> /// <param name="board">the board to get moves from</param> /// <param name="lookaheadDepth">the lookahead depth</param> /// <returns>the candiate move locations</returns> public IEnumerable<Location> CandidateMoves(HexBoard board, int lookaheadDepth) { int maxListLength = (board.Size * board.Size) - this.cellsPlayedCount; // enough space for all the possible moves List<Location> result = new List<Location>(maxListLength); // mask out the ones that have been used - intialised to false bool[,] maskCellSelected = new bool[board.Size, board.Size]; if (lookaheadDepth < this.goodMoves.Depth) { // 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.Add(goodMoveLoc); 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])) { if (IsIncluded(testCell, board) || HasFilledNeighbour(testCell, board)) { result.Add(testCell.Location); } } } return result.ToArray(); }
/// <summary> /// are two boards the same, ie same state in all cells /// </summary> /// <param name="otherBoard">the other board</param> /// <returns>the equality boolean</returns> public bool Equals(HexBoard otherBoard) { if (otherBoard == null) { return(false); } if (otherBoard.Size != this.Size) { return(false); } foreach (Cell cell in this.cells) { if (cell.IsOccupied != otherBoard.GetCellAt(cell.Location).IsOccupied) { return(false); } } return(true); }
public void ReleaseBoardIncreasesCount() { BoardCache boardCache = new BoardCache(10); const int TestSize = 10; HexBoard[] usedBoards = new HexBoard[TestSize]; // add some boards for (int i = 0; i < TestSize; i++) { usedBoards[i] = boardCache.GetBoard(); } // remove them for (int i = 0; i < TestSize; i++) { boardCache.Release(usedBoards[i]); // count doesn't go down Assert.AreEqual(TestSize, boardCache.BoardCount); Assert.AreEqual(i + 1, boardCache.AvailableCount); } }
private static void PlayerScoreAlmostBarricaded(IPathLengthFactory pathLengthFactory) { HexBoard hexBoard = new HexBoard(BoardSize); PathLengthBase pathLength = pathLengthFactory.CreatePathLength(hexBoard); // almost barricated board for (int y = 0; y < hexBoard.Size; y++) { if (y != 3) { hexBoard.PlayMove(2, y, true); } } int xScore = pathLength.PlayerScore(true); Assert.IsTrue(xScore > 0); int yScore = pathLength.PlayerScore(false); Assert.IsTrue(yScore > xScore); // strong advantage to player 1 int advantageMoveScore = pathLength.SituationScore(); Assert.IsTrue(advantageMoveScore > 0); }
private static void PlayerScoreBaricaded(IPathLengthFactory pathLengthFactory) { HexBoard hexBoard = new HexBoard(BoardSize); PathLengthBase pathLength = pathLengthFactory.CreatePathLength(hexBoard); // baricaded board for (int y = 0; y < hexBoard.Size; y++) { hexBoard.PlayMove(2, y, true); } int xScore = pathLength.PlayerScore(true); Assert.AreEqual(xScore, 0); int yScore = pathLength.PlayerScore(false); Assert.IsTrue(yScore > hexBoard.Size); // winning advantage to player 1 int winMoveScore = pathLength.SituationScore(); AssertWinner(winMoveScore, Occupied.PlayerX); }
private static void PlayerScoreZigZag(IPathLengthFactory pathLengthFactory) { HexBoard hexBoard = new HexBoard(BoardSize); PathLengthBase pathLength = pathLengthFactory.CreatePathLength(hexBoard); for (int y = 0; y < hexBoard.Size - 1; y++) { hexBoard.PlayMove(2, y, true); hexBoard.PlayMove(5, hexBoard.Size - (1 + y), true); int xScore = pathLength.PlayerScore(true); Assert.IsTrue(xScore > 0); int yScore = pathLength.PlayerScore(false); Assert.IsTrue(yScore >= hexBoard.Size); if (y > (hexBoard.Size / 2)) { Assert.IsTrue(yScore > xScore); } // some advantage to player 1 int advantageMoveScore = pathLength.SituationScore(); Assert.IsTrue(advantageMoveScore >= y); } }