public ScoringMove MTD(AIBoard board) { int gamma, guess = globalGuess; ScoringMove scoringMove = null; maximumExploredDepth = 0; string output = ""; for (byte i = 0; i < MAX_ITERATIONS; i++) { gamma = guess; scoringMove = Test(board, 0, gamma - 1); guess = scoringMove.score; if (guess == gamma) { globalGuess = guess; output += "guess encontrado en iteracion " + i; //MTDPathTest.text = output; return(scoringMove); } } output += "guess no encontrado"; globalGuess = guess; //MTDPathTest.text = output; return(scoringMove); }
void ObserveBoard() { board = new AIBoard(physicBoard); for (byte row = 0; row < 9; row++) { int maxColumn = board.supBoard.cells[row].Length; for (byte column = 0; column < maxColumn; column++) { //Text spaceText = buttonList[row, column]; if (board.supBoard.cells[row][column].Marble == null) { board.spaces[row][column] = PLAYER_COLOR.NONE; } else { if (board.supBoard.cells[row][column].Marble.isWhite) { board.spaces[row][column] = PLAYER_COLOR.WHITE; } else { board.spaces[row][column] = PLAYER_COLOR.BLACK; } } } } board.activePlayer = this.activePlayer; board.zobristKeys = this.zobristKeys; board.calculateHashValue(); }
/// <summary> /// Makes a list of all the moves player of code 'code' can make'. /// </summary> // XXX: TODO - optimize this. private static IEnumerable <Move> _findMoves(AIBoard board, int code) { int ocode = code == 1 ? 2 : 1; // Go through all possible moves of each piece of code 'code' on the board foreach (var gp in board.GetPieces(code)) { for (int i = 0; i < 16; i++) { int xto = gp.Item1 + positioncheck[i][0], yto = gp.Item2 + positioncheck[i][1]; if (xto >= Board.SIZE_X || xto < 0 || yto >= Board.SIZE_Y || yto < 0) { continue; } if (board[xto, yto] != 0) { continue; } // Count conversions int gain = board.Gain(xto, yto, ocode); if (i < 8) { gain += 1; } yield return(new Move(gp.Item1, gp.Item2, xto, yto, i >= 8, gain)); //yield return new Move(gp.x, gp.y, xto, yto, i >= 8, 0); // Conversion count is never used, so 0 is used as a placeholder } } }
private List <float> GetHardWeights(AIBoard board) { float p1spWeight = 1f; float p2spWeight = 1f; //Variables used in weight calculations. int playerOneSP = BoardAnalysis.FindShortestPath(board, true); int playerTwoSP = BoardAnalysis.FindShortestPath(board, true); int playerTwoNumWalls = CurrentBoard.GetPlayerTwoNumWalls(); int distanceBetweenPlayers = BoardAnalysis.FindDistanceBetween(CurrentBoard, CurrentBoard.GetPlayerOnePos(), CurrentBoard.GetPlayerTwoPos()); if (playerOneSP - playerTwoSP > 2) { p1spWeight = 5; } else if (playerOneSP < 5) { p1spWeight = 2; } List <float> w = new List <float> { p1spWeight, p2spWeight }; return(w); }
/// <summary> /// Add or update a entry in the hashtable. /// </summary> private void _updateOrAddHash(AIBoard cur, int code, int depth, StateInfo.ValueType flag, int value, Move best) { if (!this.transTable.ContainsKey(cur)) { this.transTable[cur] = new StateInfo() { CodeToMove = code, Depth = depth, Flag = flag, Value = value, Best = best }; return; } var entry = this.transTable[cur]; if (depth < entry.Depth) // Don't overwrite w/ shallower entries. { return; } entry.CodeToMove = code; entry.Depth = depth; entry.Flag = flag; entry.Value = value; if (best != null || entry.Best == null) { entry.Best = best; } }
void NewMove(int x, int y, GameObject piece, Board previousGameBoard) { Tile potentialMoveTile = tiles[x, y]; PotentialMove newMove = new PotentialMove(); newMove.tile = potentialMoveTile; newMove.piece = piece; AIBoard potentialNewBoard = new AIBoard(); Board potentialBoard = new Board(); foreach (GameObject whiteAdd in previousGameBoard.white) { potentialBoard.white.Add(whiteAdd); } foreach (GameObject blackAdd in previousGameBoard.black) { potentialBoard.black.Add(blackAdd); } if (newMove.tile.piece != null) { if (newMove.tile.piece.GetComponent <UnitInfo>().colour == 0) { potentialBoard.white.Remove(newMove.tile.piece); } else { potentialBoard.black.Remove(newMove.tile.piece); } } potentialBoard.GetBoardValue(currentTurn); newMove.ResultingBoard = potentialBoard; potentialMoves.Add(newMove); }
public WallDiffNode(AIBoard copy) { Board = new AIBoard(copy); Depth = 0; MoveMade = "rootnode"; Diff = BoardAnalysis.FindShortestPath(Board, true) - BoardAnalysis.FindShortestPath(Board, false); }
//Used for creating children public WallDiffNode(WallDiffNode n, string move) { MoveMade = move; Board = new AIBoard(n.Board); Board.MakeMove(move); Depth = n.Depth + 1; PreviousDiff = n.Diff; }
public AIBoard GenerateNewBoardFromMove(List <MoveMarble> move) { AIBoard newBoard = this.DuplicateBoard(); newBoard.Move(move, activePlayer); newBoard.activePlayer = Opponent(newBoard.activePlayer); return(newBoard); }
public ShotResult PlayPlayerMove(string userShotKey) { var shotResult = AIBoard.ShootAt(userShotKey); PlayerBoard.AddShotResult(shotResult); return(shotResult); }
public ShotResult PlayAIMove(GameField gameField = null) { gameField ??= _aiPlayer.GetGameFieldToShoot(); var shotResult = PlayerBoard.ShootAt(gameField); AIBoard.AddShotResult(shotResult); return(shotResult); }
public WinnerType GetWinner() { if (AIBoard.AllShipsAreSunk()) { return(WinnerType.Player); } if (PlayerBoard.AllShipsAreSunk()) { return(WinnerType.Computer); } return(WinnerType.None); }
//Heuristic utility used to check if a direct path to the end of the board exists. public static bool HasDirectPath(AIBoard board, bool isPlayerOne, string space) { bool pathExists = true; string currentPoint = space; HashSet <Move> badMoves = board.GetInvalidPawnMoves(); if (isPlayerOne) { string nextPoint = new string(new char[] { currentPoint[0], (char)(currentPoint[1] + 1) }); int i = 0; while (i < 10) { i++; if (badMoves.Contains(new Move(currentPoint, nextPoint))) { pathExists = false; break; } else { currentPoint = nextPoint; nextPoint = new string(new char[] { currentPoint[0], (char)(currentPoint[1] + 1) }); } } } else { string nextPoint = new string(new char[] { currentPoint[0], (char)(currentPoint[1] - 1) }); int i = 0; while (i < 10) { i++; if (badMoves.Contains(new Move(currentPoint, nextPoint))) { pathExists = false; break; } else { currentPoint = nextPoint; nextPoint = new string(new char[] { currentPoint[0], (char)(currentPoint[1] - 1) }); } } } return(pathExists); }
//This returns all walls adjacent to the wall move made in public List <WallDiffNode> GetChildren() { List <WallDiffNode> children = new List <WallDiffNode>(); List <string> adjacentWalls = DictionaryLookup.PerformWallsOfInterestLookup(MoveMade); foreach (string wall in adjacentWalls) { AIBoard tempBoard = new AIBoard(Board); tempBoard.MakeMove(wall); if (BoardAnalysis.CheckPathExists(tempBoard, true) && BoardAnalysis.CheckPathExists(tempBoard, false)) { children.Add(new WallDiffNode(this, wall)); } } return(children); }
//If risk is very high will return 1, low risk is higher return value. private int GetMoveRisk(string move) { int riskValue = 100; AIBoard tempBoard = new AIBoard(CurrentBoard); tempBoard.MakeMove(move); WallDiffNode rootNode = new WallDiffNode(tempBoard); List <string> wallPlacements = tempBoard.GetWallMoves(); foreach (string wall in wallPlacements) { riskValue = Math.Min(riskValue, RiskIterate(new WallDiffNode(rootNode, wall), 0)); } return(riskValue); }
public AIBoard DuplicateBoard() { AIBoard newBoard = new AIBoard(this.supBoard); for (byte row = 0; row < rows; row++) { int maxcolumn = spaces[row].Length; for (byte column = 0; column < maxcolumn; column++) { newBoard.spaces[row][column] = this.spaces[row][column]; } } newBoard.activePlayer = this.activePlayer; return(newBoard); }
//Uses a depth first search to find any path that reaches the end goal public static bool CheckPathExists(AIBoard board, bool isPlayerOne) { //Moves to be visited is used to prevent the revisiting of nodes by another branch. HashSet <string> movesToBeVisited = new HashSet <string>(); Stack <SearchNode> spaces = new Stack <SearchNode>(); bool result = false; //Adds the appropriate starting node depending on specified player. if (isPlayerOne) { spaces.Push(new SearchNode(board.GetPlayerOnePos())); } else { spaces.Push(new SearchNode(board.GetPlayerTwoPos())); } while (spaces.Count != 0 && !result) { SearchNode currentNode = spaces.Pop(); //Check the win conditions of the appropriate player. if (HasDirectPath(board, isPlayerOne, currentNode.space)) { result = true; break; } //Get the possible moves from the space of the current node. List <string> movesFromSpace = board.GetAdjacentMoves(currentNode.space); //Adds the appropriate nodes to the stack to be searched. foreach (string move in movesFromSpace) { if (!movesToBeVisited.Contains(move)) { spaces.Push(new SearchNode(move, currentNode.depth)); movesToBeVisited.Add(move); } } } return(result); }
//Finds shortest path for the chosen player to the end of the board and returns it. public static int FindShortestPath(AIBoard board, bool isPlayerOne) { Queue <SearchNode> spaces = new Queue <SearchNode>(); HashSet <string> movesToBeVisited = new HashSet <string>(); int result = -1; if (isPlayerOne) { spaces.Enqueue(new SearchNode(board.GetPlayerOnePos())); } else { spaces.Enqueue(new SearchNode(board.GetPlayerTwoPos())); } movesToBeVisited.Add(spaces.Peek().space); //Will exit after all paths return without finding end or //break out of loop when first path to finish is found. while (spaces.Count != 0 && result == -1) { SearchNode currentNode = spaces.Dequeue(); //Checks for easy direct path if (HasDirectPath(board, isPlayerOne, currentNode.space)) { result = currentNode.depth + FindDirectDistance(currentNode.space, isPlayerOne); break; } //Get a list of moves from the current node location. //If the node has not already been visited by this branch it is added to the queue. List <string> movesFromSpace = board.GetAdjacentMoves(currentNode.space); foreach (string move in movesFromSpace) { if (!movesToBeVisited.Contains(move)) { spaces.Enqueue(new SearchNode(move, currentNode.depth)); movesToBeVisited.Add(move); } } } return(result); }
//Constructs a list of treenodes that result from every move made that is possible. //Pruning function (SetNodesOfInterest) will cut of many nodes that are not of interest. public List <TreeNode> GetChildren() { List <TreeNode> children = new List <TreeNode>(); foreach (string move in Board.GetPawnMoves()) { //This checks to make sure walls are valid AIBoard tempBoard = new AIBoard(Board); tempBoard.MakeMove(move); children.Add(new TreeNode(tempBoard, move)); } //This gets only valid walls but is done here so that it will only check walls of interest. //This avoids checking if a ton of walls are valid that we don't care about. //This is why we do not just call GetWallMoves() if ((Board.GetIsPlayerOneTurn() && Board.GetPlayerOneNumWalls() == 0) || (!Board.GetIsPlayerOneTurn() && Board.GetPlayerTwoNumWalls() == 0)) { } else { HashSet <string> wallMoves = Board.GetAllValidWalls(); SetNodesOfInterest(ref wallMoves); foreach (string wall in wallMoves) { //This checks to make sure walls are valid AIBoard tempBoard = new AIBoard(Board); tempBoard.MakeMove(wall); if (BoardAnalysis.CheckPathExists(tempBoard, true) && BoardAnalysis.CheckPathExists(tempBoard, false)) { children.Add(new TreeNode(tempBoard, wall)); } } } //end of wall selection return(children); }
/// <summary> /// Heuristic evaluation of a board state for 'goodness'. /// </summary> private static int _score(AIBoard b, int code, int othercode) { // If b is in a game over state, return a score that represents a win or loss if (b.GameOver) { if (b.Count(code) > b.Count(othercode)) { return(int.MaxValue - 10); } else { return(-(int.MaxValue - 10)); } } // There are 3 major components to a good board state: // - A small perimeter (not many spaces next to friendly pieces, which indicates a good structure) // - The number of pieces within capturing range of an enemy // - The friendly piece count int perimeter = 0, pieces = b.Count(code), vulnpieces = 0; // First find all friendly pieces foreach (var gp in b.GetPieces(code)) { // For each space next to this piece empty, add to the perimeter sum. perimeter += b.EmptyAdjacent(gp.Item1, gp.Item2); // If piece can be captured, add to vulnerable pieces. if (b.IsVulnerable(gp.Item1, gp.Item2, othercode)) { vulnpieces++; } } // Bring together the scores and apply math to generate the best representative score of the board return(25 * pieces - 8 * vulnpieces - 2 * perimeter); // Mess around with coefficients until ai works }
ScoringMove Test(AIBoard board, byte depth, int gamma) { // Devuelve el score del tablero y la jugada con la que se llega a él. List <MoveMarble> bestMove = new List <MoveMarble>(); int bestScore = 0; ScoringMove scoringMove; // score, movimiento AIBoard newBoard; Record record; if (depth > maximumExploredDepth) { maximumExploredDepth = depth; } record = transpositionTable.GetRecord(board.hashValue); if (record != null) { if (record.depth > MAX_DEPTH - depth) { if (record.minScore > gamma) { scoringMove = new ScoringMove(record.minScore, record.bestMove); return(scoringMove); } if (record.maxScore < gamma) { scoringMove = new ScoringMove(record.maxScore, record.bestMove); return(scoringMove); } } } else { record = new Record(); record.hashValue = board.hashValue; record.depth = MAX_DEPTH - depth; record.minScore = MINUS_INFINITE; record.maxScore = INFINITE; } // Comprobar si hemos terminado de hacer recursión if (board.IsEndOfGame() || depth == MAX_DEPTH) { if (depth % 2 == 0) { record.maxScore = board.Evaluate(activePlayer); } else { record.maxScore = -board.Evaluate(activePlayer); } record.minScore = record.maxScore; transpositionTable.SaveRecord(record); scoringMove = new ScoringMove(record.maxScore); } else { bestScore = MINUS_INFINITE; //modificar AIBoard.possibleMoves() para que devuelva los posibles movimientos List <MoveMarble>[] possibleMoves; possibleMoves = board.PossibleMoves(); foreach (List <MoveMarble> move in possibleMoves) { //newBoard = board.GenerateNewBoardFromMove(move); newBoard = board.HashGenerateNewBoardFromMove(move);//cambiar // Recursividad scoringMove = Test(newBoard, (byte)(depth + 1), -gamma); int invertedScore = -scoringMove.score; // Actualizar mejor score if (invertedScore > bestScore) { bestScore = invertedScore; bestMove = move;//cambiar record.bestMove = move; } if (bestScore < gamma) { record.maxScore = bestScore; } else { record.minScore = bestScore; } } transpositionTable.SaveRecord(record); scoringMove = new ScoringMove(bestScore, bestMove); } return(scoringMove); }
// Makes the AI choose and execute a move public override void MakeMove() { transTable.Clear(); repetitionCheck.Clear(); moveNum++; // Set up root of tree. this.gameTree.Root = new Tree <Move, AIBoard> .Node(); this.workingBoard = new AIBoard(board); // TODO - int best = int.MinValue; int adjusted_depth = SEARCH_DEPTH; Move move = null; startT = DateTime.Now; for (int d = 1; d <= adjusted_depth; d++) { // Initialize state. List <Move> pv = new List <Move>(); depthStart = DateTime.Now; #if DEBUG nodeCnt = 0; transHits = 0; transCuts = 0; evalCalls = 0; #endif // Perform bounded depth-first search for the best move score. best = -_search(this.gameTree.Root, d, -int.MaxValue, int.MaxValue, true, pv); // If we finished our last ply fast enough (<MAX_PLY_TIME), and we're not in mid-game, go for another. // If the game's over, though, don't bother. if (d == adjusted_depth && moveNum > 25 && (DateTime.Now - depthStart).TotalSeconds < MAX_PLY_TIME && best < 10000 && best > -10000) { adjusted_depth++; } // WE GOT CALLED TO MAKE A MOVE BUT HAVE NO MOVES TO MAKE. WAT. if (this.gameTree.Root.Count == 0) { return; } if (pv.Count > 0) { move = pv[0]; } #if DEBUG _trace("[SEARCH] d={0}, n={1}, tHit={2} ({3}%), tCut={4} ({5}%), e={6} ({7}%)", d, nodeCnt, transHits, Math.Round((double)transHits / nodeCnt, 2) * 100, transCuts, Math.Round((double)transCuts / nodeCnt, 2) * 100, evalCalls, Math.Round((double)evalCalls / nodeCnt, 2) * 100); _trace(" t={0}s ({1} n/s), PV={2}", Math.Round((DateTime.Now - depthStart).TotalMilliseconds / 1000, 3), Math.Round(nodeCnt / ((DateTime.Now - depthStart).TotalMilliseconds / 1000), 0), pv.Aggregate("", new Func <string, Move, string>((a, b) => a + b + " ")).Trim()); #endif } Debug.Assert(move != null, "No move! Endgame condition without our notification?"); #if DEBUG _trace("[EXEC] {0}", move); #endif _executeMove(this.board, move, aicode, notaicode); }
//Used for creating a rootnode. //This will determine who the max player is for this node and all future children. //Max is equal to whose turn it is at this point. public TreeNode(AIBoard copy) { Board = new AIBoard(copy); MoveMade = "rootnode"; value = 0; }
//Used in the creating of children of a treenode. public TreeNode(AIBoard copy, string move) { Board = new AIBoard(copy); MoveMade = move; value = 0; }
public HardAI() { CurrentBoard = new AIBoard(); }
public AI() { CurrentBoard = new AIBoard(); timer = new Stopwatch(); }