public moveData generateTree(int maxDepth, string[] newGameBoard, int piece, Piece[] currentPieces) { totalGamestates = 0; Node newNode = new Node(); newNode.gameBoard = newGameBoard; newNode.pieceToPlay = piece; root = newNode; Node currentNode = root; Node parentNode; Node sibling = null; parentNode = currentNode; currentNode.sibling = null; currentNode.pieces = currentPieces; generateChildrenGamestate(currentNode, parentNode, piece, sibling, maxDepth, 0); winningMove move = searchForBestPlay(currentNode, 0, maxDepth, 0); Console.Write("Total moves generated: "); Console.WriteLine(totalGamestates); printBoard(move.winningNode); // This is bad but it works. string pieceOnDeck = move.winningNode.pieceToPlay == NULLPIECE? NULLPIECE.ToString() : move.winningNode.pieces[move.winningNode.pieceToPlay].piece; return(new moveData { lastMoveOnBoard = move.winningNode.pieces[move.winningNode.moveOnBoard].piece, pieceToPlay = pieceOnDeck }); }
// Only selects a random piece and position when there is no plays better than the one originally selected public static winningMove RandomPlay(winningMove winChoice) { int rand; var rnd = new Random(); List <int> playablePieces; List <int> playablePositions; if (winChoice.heuristicValue == 0) { playablePositions = AIFunctions.makePlayablePositionList(winChoice.winningNode.gameBoard); if (playablePositions.Count() == 0) { return(winChoice); } else if (playablePositions.Count() - 1 == 0) { rand = 0; } else { rand = rnd.Next(0, playablePositions.Count() - 1); } winChoice.winningNode.moveOnBoard = playablePositions[rand]; playablePieces = AIFunctions.makePlayablePiecesOnly(winChoice.winningNode.pieces); if (playablePieces.Count() == 0) { return(winChoice); } else if (playablePieces.Count() - 1 == 0) { rand = 0; } else { rand = rnd.Next(0, playablePieces.Count() - 1); } winChoice.winningNode.pieceToPlay = playablePieces[rand]; } return(winChoice); }
// Searches tree generated for the best play. Selects the best move from the move given by the opponent. public static winningMove searchForBestPlay(QuartoSearchTree.Node currentNode, int depth, int minimax, int alpha, int beta, bool maxPlayer) { int j = 0; string pieceString; int boardPosition = 0; int minimaxCompare; winningMove winChoice = new winningMove(); winningMove winChoice2 = new winningMove(); winChoice.winningNode = new QuartoSearchTree.Node(); // If only root in tree if (currentNode.parent == null && currentNode.children[0] == null) { } // Detects for win if (currentNode.parent == null) { for (int i = 0; i < QuartoSearchTree.MAXGAMEBOARD && currentNode.children[i] != null; i++) { if (currentNode.pieceToPlay == QuartoSearchTree.NULLPIECE) { pieceString = null; } else { pieceString = currentNode.pieces[currentNode.pieceToPlay].piece; } while (currentNode.gameBoard[boardPosition] != null) { boardPosition++; } bool win = Heuristic.isWin(currentNode.gameBoard, pieceString, boardPosition); if (win) { winChoice.winningNode = currentNode.children[i]; winChoice.heuristicValue = 0; return(winChoice); } boardPosition++; } } // If it is the bottom of the tree if (currentNode.children[0] == null) { int value; for (int i = 0; i < QuartoSearchTree.MAXGAMEBOARD && currentNode.parent.children[i] != null; i++) { if (currentNode.parent.children[i].pieceToPlay == QuartoSearchTree.NULLPIECE) { pieceString = null; } else { pieceString = currentNode.parent.children[i].pieces[currentNode.parent.children[i].pieceToPlay].piece; } minimaxCompare = Heuristic.calculateHeuristic(currentNode.parent.children[i].gameBoard, pieceString); if (i == 0) { minimax = Heuristic.calculateHeuristic(currentNode.parent.children[i].gameBoard, pieceString); winChoice.winningNode = currentNode.parent.children[i]; winChoice.heuristicValue = minimax; totalSearch++; } else if ((minimaxCompare > minimax && maxPlayer == true) || (minimaxCompare < minimax && maxPlayer == false)) { minimax = minimaxCompare; winChoice.winningNode = currentNode.parent.children[i]; winChoice.heuristicValue = minimax; totalSearch++; } //prunes the rest of children if they can be pruned if (maxPlayer) { value = -INFINITY; if (winChoice.heuristicValue > value) { value = winChoice.heuristicValue; } if (value > alpha) { alpha = value; } if (alpha >= beta) { break; } } else { value = INFINITY; if (winChoice.heuristicValue < value) { value = winChoice.heuristicValue; } if (value < beta) { beta = value; } if (alpha >= beta) { break; } } j++; } } // Iterates through all children nodes else { int value; while (j < QuartoSearchTree.MAXGAMEBOARD && currentNode.children[j] != null) { if (j == 0) { winChoice = searchForBestPlay(currentNode.children[j], depth, minimax, alpha, beta, !maxPlayer); if (currentNode.parent == null || currentNode.parent.parent == null) { } // When it is evaluating the next move from the root else if (currentNode.parent.parent.parent == null) { winChoice.winningNode = currentNode; totalSearch++; } } else { winChoice2 = searchForBestPlay(currentNode.children[j], depth, minimax, alpha, beta, !maxPlayer); if ((winChoice2.heuristicValue > winChoice.heuristicValue && maxPlayer == true) || (winChoice2.heuristicValue < winChoice.heuristicValue && maxPlayer == false)) { winChoice.heuristicValue = winChoice2.heuristicValue; if (currentNode.parent == null) { winChoice.winningNode = winChoice2.winningNode; totalSearch++; } else if (currentNode.parent.parent == null) { winChoice.heuristicValue = winChoice2.heuristicValue; winChoice.winningNode = winChoice2.winningNode; totalSearch++; } // When it is evaluating the next move from the root else if (currentNode.parent.parent.parent == null) { winChoice.winningNode = currentNode; winChoice.heuristicValue = winChoice2.heuristicValue; totalSearch++; } } } //prunes the rest of children if they can be pruned if (maxPlayer) { value = -INFINITY; if (winChoice.heuristicValue > value) { value = winChoice.heuristicValue; } if (value > alpha) { alpha = value; } if (alpha >= beta) { break; } } else { value = INFINITY; if (winChoice.heuristicValue < value) { value = winChoice.heuristicValue; } if (value < beta) { beta = value; } if (alpha >= beta) { break; } } j++; } } if (currentNode.parent == null) { RandomPlay(winChoice); } return(winChoice); }
public moveData generateTree(string[] newGameBoard, int piece, Piece[] currentPieces, int difficulty) { int piecesOnBoard; int maxDepth; int positionToBlock; bool boardBlockable = false; string moveToSend; string pieceOnDeck; Node newNode = new Node(); newNode.gameBoard = newGameBoard; newNode.pieceToPlay = piece; root = newNode; Node currentNode = root; Node parentNode; parentNode = currentNode; currentNode.pieces = currentPieces; positionToBlock = AIFunctions.findWinningPositionToBlock(newGameBoard, currentPieces[piece].piece); if (difficulty == 3 && positionToBlock != -1 && Heuristic.calculateHeuristic(newGameBoard, currentPieces[piece].piece) > 0) { boardBlockable = true; } // Sets tree depth according to how many pieces are on the board piecesOnBoard = AIFunctions.countPiecesOnBoard(newGameBoard); maxDepth = AIFunctions.setTreeDepth(piecesOnBoard, difficulty); // Generates game tree generateChildrenGamestate(currentNode, parentNode, piece, maxDepth, 0); // Finds the best move in the game tree based on a heuristic. winningMove move = NegaMax.searchForBestPlay(currentNode, maxDepth, 0, -MAXGAMEBOARD, MAXGAMEBOARD, true); if (boardBlockable && !move.isWin && piecesOnBoard != MAXGAMEBOARD - 1) { // Erases old winning move then adds new move to board. // Also sets old piece to playable again. move.winningNode.gameBoard[move.winningNode.moveOnBoard] = null; move.winningNode.gameBoard[positionToBlock] = currentPieces[piece].piece; move.winningNode.pieces[move.winningNode.pieceToPlay].setPlayable(true); move.winningNode = AIFunctions.checkForOpponentWin(move.winningNode, currentPieces[piece].piece); pieceOnDeck = move.winningNode.pieceToPlay == NULLPIECE? NULLPIECE.ToString() : move.winningNode.pieces[move.winningNode.pieceToPlay].piece; moveToSend = move.winningNode.pieces[positionToBlock].piece; } else { if (piecesOnBoard != 0 && move.winningNode.pieceToPlay != NULLPIECE) { move.winningNode = AIFunctions.checkForOpponentWin(move.winningNode, null); } moveToSend = move.winningNode.pieces[move.winningNode.moveOnBoard].piece; //Checks for win by opponent, given the piece chosen //If win it makes it equal to the next child and so on pieceOnDeck = move.winningNode.pieceToPlay == NULLPIECE? NULLPIECE.ToString() : move.winningNode.pieces[move.winningNode.pieceToPlay].piece; } return(new moveData { lastMoveOnBoard = moveToSend, pieceToPlay = pieceOnDeck }); }
public winningMove searchForBestPlay(Node currentNode, int depthCounter, int depth, int negaMax) { int j = 0; string pieceString; int boardPosition = 0; int negaMaxCompare; winningMove winChoice = new winningMove(); winningMove winChoice2 = new winningMove(); winChoice.heuristicValue = -1; winChoice.winningNode = new Node(); // If only root in tree if (currentNode.parent == null && currentNode.children[0] == null) { Console.WriteLine("Search Failed: Tree only contains root"); } // Detects for win if (currentNode.parent == null) { for (int i = 0; i < MAXGAMEBOARD && currentNode.children[i] != null; i++) { if (currentNode.pieceToPlay == NULLPIECE) { pieceString = null; } else { pieceString = currentNode.pieces[currentNode.pieceToPlay].piece; } while (currentNode.gameBoard[boardPosition] != null) { boardPosition++; } bool win = Heuristic.isWin(currentNode.gameBoard, pieceString, boardPosition); if (win) { winChoice.winningNode = currentNode.children[i]; winChoice.heuristicValue = 0; return(winChoice); } boardPosition++; } } // If it is the bottom of the tree if (currentNode.children[0] == null) { for (int i = 0; i < MAXGAMEBOARD && currentNode.parent.children[i] != null; i++) { if (currentNode.parent.children[i].pieceToPlay == NULLPIECE) { pieceString = null; } else { pieceString = currentNode.parent.children[i].pieces[currentNode.parent.children[i].pieceToPlay].piece; } negaMaxCompare = Heuristic.calculateHeuristic(currentNode.parent.children[i].gameBoard, pieceString); if (i == 0) { negaMax = Heuristic.calculateHeuristic(currentNode.parent.children[i].gameBoard, pieceString); winChoice.winningNode = currentNode.parent.children[i]; winChoice.heuristicValue = negaMax; } else if (-negaMaxCompare > -negaMax) { negaMax = -negaMaxCompare; winChoice.winningNode = currentNode.parent.children[i]; winChoice.heuristicValue = negaMax; } } } // Iterates through all children nodes else { while (j < MAXGAMEBOARD && currentNode.children[j] != null) { if (j == 0) { winChoice = searchForBestPlay(currentNode.children[j], depthCounter++, depth, negaMax); if (currentNode.parent == null || currentNode.parent.parent == null) { } else if (currentNode.parent.parent.parent == null) { winChoice.winningNode = currentNode; } } else { winChoice2 = searchForBestPlay(currentNode.children[j], depthCounter++, depth, negaMax); if (-winChoice2.heuristicValue > -winChoice.heuristicValue) { winChoice.heuristicValue = -winChoice2.heuristicValue; if (currentNode.parent == null) { } else if (currentNode.parent.parent == null) { winChoice.heuristicValue = winChoice2.heuristicValue; } else if (currentNode.parent.parent.parent == null) { winChoice.winningNode = currentNode; winChoice.heuristicValue = winChoice2.heuristicValue; } } } j++; } } //printBoard(winChoice.winningNode); Console.WriteLine(winChoice.heuristicValue); return(winChoice); }
public moveData generateTree(string[] newGameBoard, int piece, Piece[] currentPieces, int difficulty) { // hashing function would go here // Followed by a lookup in the transposition table //HashFunction.ZobristHash zash = new HashFunction.ZobristHash(); //zash.init_zobristHash(); totalGamestates = 0; int piecesOnBoard; int maxDepth; int positionToBlock; bool boardBlockable = false; string moveToSend; positionToBlock = AIFunctions.findWinningPositionToBlock(newGameBoard, currentPieces[piece].piece); if (difficulty == 3 && positionToBlock != -1 && Heuristic.calculateHeuristic(newGameBoard, currentPieces[piece].piece) > 0) { boardBlockable = true; } Node newNode = new Node(); newNode.gameBoard = newGameBoard; newNode.pieceToPlay = piece; root = newNode; Node currentNode = root; Node parentNode; parentNode = currentNode; currentNode.pieces = currentPieces; // Sets tree depth according to how many pieces are on the board piecesOnBoard = AIFunctions.countPiecesOnBoard(newGameBoard); maxDepth = AIFunctions.setTreeDepth(piecesOnBoard, difficulty); generateChildrenGamestate(currentNode, parentNode, piece, maxDepth, 0); winningMove move = NegaMax.searchForBestPlay(currentNode, maxDepth, 0, -MAXGAMEBOARD, MAXGAMEBOARD, true); //Checks for win by opponent, given the piece chosen //If win it makes it equal to the next child and so on if (piecesOnBoard != 0 && move.winningNode.pieceToPlay != NULLPIECE && difficulty > 1) { move.winningNode = AIFunctions.checkForOpponentWin(move.winningNode); } // This is bad but it works. string pieceOnDeck = move.winningNode.pieceToPlay == NULLPIECE? NULLPIECE.ToString() : move.winningNode.pieces[move.winningNode.pieceToPlay].piece; if (boardBlockable) { moveToSend = move.winningNode.pieces[positionToBlock].piece; } else { moveToSend = move.winningNode.pieces[move.winningNode.moveOnBoard].piece; } return(new moveData { lastMoveOnBoard = moveToSend, pieceToPlay = pieceOnDeck }); }