// Test the protocol components related to the 'board state' message // type. Throws Exception if a 'board state' message is generated // which is inconsistent with the physical board state, or if a // board state defined by a 'board state' message is physically // inconsistent with the desired state. private static void TestBoardStateMessage() { // Create a new empty game board. TzaarBoard board = new TzaarBoard(true); // Create some game pieces of various types. TzaarPiece p1 = new TzaarPiece.Tzaar(TzaarColor.BLACK); TzaarPiece p2 = new TzaarPiece.Tzarra(TzaarColor.BLACK); TzaarPiece p3 = new TzaarPiece.Tott(TzaarColor.BLACK); // Add the pieces we created to the board at the target // position. board.Add(p1, 0, 0); board.Add(p2, 1, 0); board.Add(p3, 2, 0); TzaarMessage.BoardState message = new TzaarMessage.BoardState(board); // Check that the generated 'board state' message matches the // physical state of the board defined above. if (((string)message).CompareTo("BoardState{{BLACK,Tzaar},{},{},{},{},{BLACK,Tzarra},{},{},{},{},{},{BLACK,Tott},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}}") != 0) throw new Exception(); message = new TzaarMessage.BoardState("BoardState{{BLACK,Tzaar},{},{},{},{},{BLACK,Tzarra},{},{},{},{},{},{BLACK,Tott},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}}"); // Check that a board state generated from a 'board state' // message is consistent with the desired state. Stack<TzaarPiece> S = ((TzaarBoard)message.Board).Query(0, 0); if (S == null || S.Count == 0 || S.Peek().GetType() != typeof(TzaarPiece.Tzaar)) throw new Exception(); }
// The goal is to test various representative valid and invalid // moves for correctness. Throws Exception if a validly specified // path is determined to be invalid, or if an invalidly specified // path is determined to be valid. private static void TestBoardMapPathing() { // Create a new empty game board. TzaarBoardMap boardMap = new TzaarBoardMap(); TzaarBoard board = new TzaarBoard(true); // Create some game pieces of various types. TzaarPiece p1 = new TzaarPiece.Tzaar(TzaarColor.BLACK); TzaarPiece p2 = new TzaarPiece.Tzarra(TzaarColor.BLACK); TzaarPiece p3 = new TzaarPiece.Tott(TzaarColor.BLACK); // Operate on this specific position. int col = 8; int row = 0; // Add the pieces we created to the board at the target // position. board.Add(p1, col, row); board.Add(p2, col, row); board.Add(p3, col, row); // Check that a valid move is reported as valid. if (!boardMap.IsValidPath(board, 0, 0, 0, 1)) throw new Exception(); // Check that an invalid move is reported as invalid. if (boardMap.IsValidPath(board, 4, 3, 4, 4)) throw new Exception(); board.Add(p1, 2, 2); board.Add(p2, 2, 3); // Check that an invalid move is reported as invalid. if (!boardMap.IsValidPath(board, 2, 2, 2, 3)) throw new Exception(); // Check that passing through another piece is reported as // invalid. board.Add(p1, 1, 1); if (boardMap.IsValidPath(board, 0, 0, 2, 2)) throw new Exception(); // Remove the obstructing piece and verify that (0, 0) and // (2, 2) are now connected. board.Take(1, 1); if (!boardMap.IsValidPath(board, 0, 0, 2, 2)) throw new Exception(); }
// Return true if there is some valid path from the specified source // position to the specified target position; otherwise return false. public bool IsValidPath(TzaarBoard board, int fromCol, int fromRow, int toCol, int toRow) { if (!board.IsValidPosition(fromCol, fromRow) || !board.IsValidPosition(toCol, toRow)) // Parameters are out of range. return false; Node fromNode = paths[fromCol][fromRow]; Node toNode = paths[toCol][toRow]; if (fromCol == toCol) { if (fromRow == toRow) // We can't move to ourselves! return false; // The target is in the same column as the source. So, we are // either going north or south. if (fromRow < toRow) // Go north! return ExplorePath(board, fromNode, toNode, "n"); else // Go south! return ExplorePath(board, fromNode, toNode, "s"); } else if (toCol > fromCol) { // Go east! // Try northeast first. if (ExplorePath(board, fromNode, toNode, "ne")) return true; else // Try southeast. return ExplorePath(board, fromNode, toNode, "se"); } else { // Go west! // Try northwest first. if (ExplorePath(board, fromNode, toNode, "nw")) return true; else // Try southwest. return ExplorePath(board, fromNode, toNode, "sw"); } }
// Constructor parses a message string into a BoardState object. // Throws an exception if the message string does not represent a // valid BoardState message. public BoardState(string msg) { if (!GameMessage.IsBoardState(msg)) throw new Exception("This is not a BoardState message."); // Remove all whitespace characters from the message string. string msgString = RemoveAllWhiteSpace(msg); this.data = msgString; // Strip packet wrapper. string boardString = GameMessage.GetMessageData(msg); boardString = boardString.Substring(1, boardString.Length - 2); this.board = GetBoardStateFromString(msgString); }
// The goal is to test the board-querying functionality. Throws // Exception if the piece(s) returned from the specified position // do not match those placed at that position. private static void TestQuery() { // Create a new empty game board. TzaarBoard board = new TzaarBoard(true); // Create some game pieces of various types. TzaarPiece p1 = new TzaarPiece.Tzaar(TzaarColor.BLACK); TzaarPiece p2 = new TzaarPiece.Tzarra(TzaarColor.BLACK); TzaarPiece p3 = new TzaarPiece.Tott(TzaarColor.BLACK); // Add the pieces we created to the board at the target // position. board.Add(p1, 0, 0); board.Add(p2, 0, 0); board.Add(p3, 0, 0); // Check that there are 3 pieces on the target position. Stack<TzaarPiece> pieces = board.Query(0, 0); if (pieces.Count != 3) throw new Exception(); }
// Check that stacking pieces works properly. Additionally, it // should be possible to add a piece to an invalid position on the // board without error. Throws Exception if the determined stack // size is not equal to the size of the stack placed at the // specified position. private static void TestAdd() { // Create a new empty game board. TzaarBoard board = new TzaarBoard(true); // Create some game pieces of various types. TzaarPiece p1 = new TzaarPiece.Tzaar(TzaarColor.BLACK); TzaarPiece p2 = new TzaarPiece.Tzarra(TzaarColor.BLACK); TzaarPiece p3 = new TzaarPiece.Tott(TzaarColor.BLACK); board.Add(p1, 0, 0); board.Add(p2, 0, 0); board.Add(p3, 0, 0); Stack<TzaarPiece> S = board.Query(0, 0); if (S.Count != 3) throw new Exception(); if (S.Peek() != p3) throw new Exception(); // Try to add to a spot that doesn't exist. board.Add((TzaarPiece)null, 8, 8); }
// Return a full copy. public override GameBoard Copy() { TzaarBoard b = new TzaarBoard(true); for (int i = 0; i < this.board.Length; i++) { TzaarBoardPosition[] col = this.board[i]; for (int j = 0; j < col.Length; j++) { TzaarBoardPosition n = col[j]; if (n == null) continue; b.Add(n.Query(), i, j); } } return b; }
// A game can be constructed without a parameter (empty board), with a // premade board, with a premade state, or with the bool value 'true' // for a randomly generated board. public TzaarLogic() { this.boardMap = new TzaarBoardMap(); TzaarBoard board = new TzaarBoard(); this.state = new TzaarGameState(board); }
// Parse a message string into a TzaarBoard object. private TzaarBoard GetBoardStateFromString(string msg) { TzaarBoard board = new TzaarBoard(true); // Strip packet wrapper. string boardString = TzaarMessage.GetMessageData(msg); boardString = boardString.Substring(1, boardString.Length - 2); // Split into individual stacks. string[] stacks = Regex.Split(boardString, "},{"); int[] offsets = new int[] { 0, 5, 11, 18, 26, 34, 42, 49, 55 }; string[] aStack; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { Stack<TzaarPiece> S = board.Query(i, j); if (S == null) break; // Split stack into color (aStack[0]) and individual // pieces. aStack = stacks[j + offsets[i]].Split(','); TzaarColor color = (aStack[0].Equals(TzaarColor.BLACK.ToString())) ? TzaarColor.BLACK : TzaarColor.WHITE; // Add each piece to the board. for (int k = aStack.Length - 1; k >= 0; k--) { String s = aStack[k]; if (s.Equals(typeof(TzaarPiece.Tzaar).Name.ToString())) board.Add(new TzaarPiece.Tzaar(color), i, j); else if (s.Equals(typeof(TzaarPiece.Tzarra).Name.ToString())) board.Add(new TzaarPiece.Tzarra(color), i, j); else if (s.Equals(typeof(TzaarPiece.Tott).Name.ToString())) board.Add(new TzaarPiece.Tott(color), i, j); } } } return board; }
// Constructor to instantiate a new BoardState based on the // specified TzaarBoard object. public BoardState(TzaarBoard aBoard) { this.data = GetStringFromBoardState(aBoard); this.board = (TzaarBoard)aBoard.Copy(); }
// Test the functionality related to 'taking' a piece, or stack of // pieces, from a position on the game board. Additionally, it // should be possible to attempt to 'take' a piece from an invalid // position on the game board without error. Throws Exception if // 'taking' the pieces from the board does not result in the // specified position being empty. private static void TestTake() { // Create a new empty game board. TzaarBoard board = new TzaarBoard(true); // Create some game pieces of various types. TzaarPiece p1 = new TzaarPiece.Tzaar(TzaarColor.BLACK); TzaarPiece p2 = new TzaarPiece.Tzarra(TzaarColor.BLACK); TzaarPiece p3 = new TzaarPiece.Tott(TzaarColor.BLACK); // Add the pieces we created to the board at the target // position. board.Add(p1, 0, 0); board.Add(p2, 0, 0); board.Add(p3, 0, 0); // Check that there are 3 pieces on the target position. if (board.Query(0, 0).Count != 3) throw new Exception(); // Take the pieces. if (board.Take(0, 0).Count != 3) throw new Exception(); // Check that there are now 0 pieces on the target position. if (board.Query(0, 0).Count != 0) throw new Exception(); // Try to Take from a spot that doesn't exist. board.Take(-1, 8); }
// Start at the specified sourceNode and travel in the specified // direction. Return true if there is a clear path between the two // specified board positions; otherwise return false. private static bool ExplorePath(TzaarBoard board, Node fromNode, Node toNode, string direction) { Node currentNode = fromNode; while (true) { // Move to the next node in the specified direction. FieldInfo myPropInfo = currentNode.GetType().GetField(direction); Node nextNode = (Node)myPropInfo.GetValue(currentNode); currentNode = nextNode; if (currentNode == null) // We ran off the board. break; if (currentNode == toNode) // We have successfully reached our destination! return true; Stack<TzaarPiece> currentPieceStack = board.Query(currentNode.col, currentNode.row); if (currentPieceStack.Count != 0) // There are pieces on this position, stop here. break; } // We can't get there. return false; }
// Randomize the board state. private static void RandomizeBoardState(TzaarBoard board) { // Store the counts in a 2D array for easy access. int[][] actual = new int[2][]; actual[0] = new int[] { 6, 9, 15 }; actual[1] = new int[] { 6, 9, 15 }; // Keep track of the number of pieces placed so far. int[,] pieces = new int[2, 3]; // Column lengths of the board. int[] colLengths = new int[] { 5, 6, 7, 8, 8, 8, 7, 6, 5 }; Random rand = new Random(); int player, type; bool valid = false; TzaarColor color; // Now cycle through each space of the board and choose a random // piece to place. for (int i = 0; i < colLengths.Length; i++) { for (int j = 0; j < colLengths[i]; j++) { // While a valid piece has not been chosen... while (!valid) { // Randomly choose 0 - black or 1 - white. player = rand.Next(0, 2); // Randomly choose between 0 and 2. This will be one of // the three types of pieces. type = rand.Next(0, 3); // Check that this particular color and type can still // be placed. if (pieces[player, type] < actual[player][type]) { Stack<TzaarPiece> s = new Stack<TzaarPiece>(); if (player == 0) color = TzaarColor.BLACK; else color = TzaarColor.WHITE; switch (type) { case 0: s.Push(new TzaarPiece.Tzaar(color)); break; case 1: s.Push(new TzaarPiece.Tzarra(color)); break; case 2: s.Push(new TzaarPiece.Tott(color)); break; } board.Add(s, i, j); valid = true; pieces[player, type]++; } } valid = false; } } }
// Scan the board and update the counts for each piece still remaining. private static void UpdatePieceCount(TzaarBoard board) { // The entire board is scanned for the update, so destroy the old // count values. board.blackTzaarCount = 0; board.blackTzarraCount = 0; board.blackTottCount = 0; board.whiteTzaarCount = 0; board.whiteTzarraCount = 0; board.whiteTottCount = 0; // Scan each space of the board to update. for (int i = 0; i < 9; i++) for (int j = 0; j < 9; j++) { // If the position is not valid, we have passed the end of // the column. Move to the next column. if (!board.IsValidPosition(i, j)) break; // Otherwise, find the color and number of pieces at the // current position and update accordingly. Stack<TzaarPiece> S = board.board[i][j].Query(); if (S.Count == 0) continue; TzaarPiece topPiece = S.Peek(); if (topPiece.Color == TzaarColor.WHITE) { if (topPiece.GetType() == typeof(TzaarPiece.Tzaar)) board.whiteTzaarCount++; else if (topPiece.GetType() == typeof(TzaarPiece.Tzarra)) board.whiteTzarraCount++; else board.whiteTottCount++; } else { if (topPiece.GetType() == typeof(TzaarPiece.Tzaar)) board.blackTzaarCount++; else if (topPiece.GetType() == typeof(TzaarPiece.Tzarra)) board.blackTzarraCount++; else board.blackTottCount++; } } }
// The goal is to verify that a change to the board state in the // form of a 'capture' or 'stack' operation results in a correct // update to the counts for each piece remaining on the board. // Throws Exception if a piece count is inconsistent with the actual // game state following the specified operation. private static void TestUpdatePieceCount() { TzaarBoard board = new TzaarBoard(true); board.Add(new TzaarPiece.Tzaar(TzaarColor.WHITE), 2, 2); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 2, 3); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 3, 4); TzaarGameState state = new TzaarGameState(board); TzaarLogic game = new TzaarLogic(state); int BlackTzaarCount = ((TzaarBoard)game.GetGameState().Board).BlackTzaarCount; game.Move(2, 2, 2, 3); // A Black Tzaar was 'captured', so the piece count should be // decremented by 1. if (BlackTzaarCount - ((TzaarBoard)game.GetGameState().Board).BlackTzaarCount != 1) throw new Exception(); }
// Test the components which control functionality to 'stack' pieces // on one another. Throws Exception if an attempt to 'stack' pieces // results in a stack size not equal to the total number of pieces // being 'stacked'. private static void TestStack() { TzaarBoard board = new TzaarBoard(true); board.Add(new TzaarPiece.Tzaar(TzaarColor.WHITE), 2, 2); board.Add(new TzaarPiece.Tzaar(TzaarColor.WHITE), 2, 3); TzaarGameState state = new TzaarGameState(board, 4); TzaarLogic game = new TzaarLogic(state); game.Move(2, 2, 2, 3); Stack<TzaarPiece> S = board.Query(2, 3); // Stacked a total of 2 pieces, so stack size should be 2. if (S.Count() != 2) throw new Exception(); // Stacked a Tzaar on top of another Tzaar, so the top piece // should be a Tzaar. if (S.Peek().GetType() != typeof(TzaarPiece.Tzaar)) throw new Exception(); // Stacked a white piece on top of another white piece, so the // top piece should be white. if (S.Peek().Color != TzaarColor.WHITE) throw new Exception(); }
// Test the components which determine an end-game state. Throws // Exception if the actual game state is an end-game state and the // game is not determined to be over. private static void TestGameOver() { // Black has no Totts; black loses. TzaarBoard board = new TzaarBoard(true); board.Add(new TzaarPiece.Tzaar(TzaarColor.WHITE), 0, 0); board.Add(new TzaarPiece.Tzarra(TzaarColor.WHITE), 0, 1); board.Add(new TzaarPiece.Tott(TzaarColor.WHITE), 0, 2); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 1, 1); board.Add(new TzaarPiece.Tzarra(TzaarColor.BLACK), 1, 2); TzaarLogic game = new TzaarLogic(board); if (!game.WhiteCanCapture()) throw new Exception(); if (!game.BlackCanCapture()) throw new Exception(); if (game.WhiteIsOutOfPieces()) throw new Exception(); if (!game.BlackIsOutOfPieces()) throw new Exception(); if (!game.IsGameOver()) throw new Exception(); // Black and White have enough pieces to play, but white can't // make any more moves. board = new TzaarBoard(true); board.Add(new TzaarPiece.Tzaar(TzaarColor.WHITE), 0, 0); board.Add(new TzaarPiece.Tzarra(TzaarColor.WHITE), 0, 1); board.Add(new TzaarPiece.Tott(TzaarColor.WHITE), 0, 2); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 1, 0); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 1, 0); board.Add(new TzaarPiece.Tzarra(TzaarColor.BLACK), 8, 0); board.Add(new TzaarPiece.Tott(TzaarColor.BLACK), 8, 1); game = new TzaarLogic(board); if (game.WhiteCanCapture()) throw new Exception(); if (!game.BlackCanCapture()) throw new Exception(); if (game.WhiteIsOutOfPieces()) throw new Exception(); if (game.BlackIsOutOfPieces()) throw new Exception(); if (!game.IsGameOver()) throw new Exception(); // Neither player can make a move, so we don't know who won; // this is ok, because we can handle this case in the server // based on who made the final move, and which turn of theirs it // was. As far as the game logic is concerned, the game is over, // but neither player won. board = new TzaarBoard(true); board.Add(new TzaarPiece.Tzaar(TzaarColor.WHITE), 0, 0); board.Add(new TzaarPiece.Tzarra(TzaarColor.WHITE), 0, 1); board.Add(new TzaarPiece.Tott(TzaarColor.WHITE), 0, 2); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 8, 0); board.Add(new TzaarPiece.Tzarra(TzaarColor.BLACK), 8, 1); board.Add(new TzaarPiece.Tott(TzaarColor.BLACK), 8, 2); game = new TzaarLogic(board); if (game.WhiteCanCapture()) throw new Exception(); if (game.BlackCanCapture()) throw new Exception(); if (game.WhiteIsOutOfPieces()) throw new Exception(); if (game.BlackIsOutOfPieces()) throw new Exception(); if (!game.IsGameOver()) throw new Exception(); }
// Test components controlling the logic for one player to 'capture' // the piece of another player. Throws Exception if a valid // 'capture' results in an inconsistent game state, if an invalid // capture is allowed, or if a player is allowed to move the // opposite player's game pieces. private static void TestCapture() { // Test a simple, valid capture. TzaarBoard board = new TzaarBoard(true); TzaarPiece whitePiece = new TzaarPiece.Tzaar(TzaarColor.WHITE); board.Add(whitePiece, 2, 2); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 2, 3); TzaarGameState state = new TzaarGameState(board); TzaarLogic game = new TzaarLogic(state); game.Move(2, 2, 2, 3); // The destination position should now contain the white piece // which captured the black piece. if (board.Query(2, 3).Peek() != whitePiece) throw new Exception(); // The origin position should no longer contain any pieces. if (board.Query(2, 2).Count() != 0) throw new Exception(); // Test an illegal capture. The capture is illegal because the // stack in the target position is more powerful than the stack // at the source. board = new TzaarBoard(true); board.Add(new TzaarPiece.Tzaar(TzaarColor.WHITE), 2, 2); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 2, 3); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 2, 3); state = new TzaarGameState(board); game = new TzaarLogic(state); bool passedTest = false; try { game.Move(2, 2, 2, 3); } catch { passedTest = true; } if (!passedTest) { throw new Exception(); } // Now try controlling some of the opponent's pieces. The game // should not allow it! board = new TzaarBoard(true); board.Add(new TzaarPiece.Tzaar(TzaarColor.WHITE), 2, 2); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 2, 3); board.Add(new TzaarPiece.Tzaar(TzaarColor.BLACK), 2, 3); state = new TzaarGameState(board); game = new TzaarLogic(state); passedTest = false; try { game.Move(2, 3, 2, 2); } catch { passedTest = true; } if (!passedTest) { throw new Exception(); } }