public static Evaluation CalculateBoardScore(Con4Board board, int depth) { int score = 0; BoardState bs = BoardState.ongoing; score += CalulateRowScores(board, ref bs); score += CalculateColumnScores(board, ref bs); score += CalculateDiagonalScores(board, ref bs); if (bs == BoardState.playerWon) { score = winScore - depth; } else if (bs == BoardState.enemyWon) { score = -winScore + depth; } else if (IsDraw(board)) { bs = BoardState.draw; score = 0; } return(new Evaluation() { boardState = bs, score = score }); }
private static int CalculateDiagonalScores(Con4Board board, ref BoardState bs) { int score = 0; List <int> seq; // Positive line slopes for (int i = 0; i < 4; i++) { seq = GenerateDiagonalSequence(board, i, boardHeight - 1, true); score += CalculateSequenceScore(seq, ref bs); } seq = GenerateDiagonalSequence(board, 0, boardHeight - 2, true); score += CalculateSequenceScore(seq, ref bs); seq = GenerateDiagonalSequence(board, 0, boardHeight - 3, true); score += CalculateSequenceScore(seq, ref bs); // Negative line slopes for (int i = 0; i < 4; i++) { seq = GenerateDiagonalSequence(board, i, 0, false); score += CalculateSequenceScore(seq, ref bs); } seq = GenerateDiagonalSequence(board, 0, 1, false); score += CalculateSequenceScore(seq, ref bs); seq = GenerateDiagonalSequence(board, 0, 2, false); score += CalculateSequenceScore(seq, ref bs); return(score); }
// Returns best score found for current player private static int MiniMaxSearch(BoardNode currentNode, int player, int depth, int a, int b) { if (MainClass.timesUp) { return(0); // Will not be used as this whole iteration is aborted } // End game if max depth reached or game is over if (depth == maxDepth || currentNode.eval.boardState != BoardState.ongoing) { return(currentNode.eval.score); } // Create and evaluate children List <BoardNode> boardNodes = new List <BoardNode>(); foreach (int move in currentNode.board.GetPossibleMoves()) { Con4Board nextBoard = currentNode.board.SimulateMove(player, move); Evaluation nextEval = CalculateBoardScore(nextBoard, depth + 1); boardNodes.Add(new BoardNode { board = nextBoard, eval = nextEval }); } // Orden them, most promising first if (player == MAXIMIZING) { boardNodes = boardNodes.OrderByDescending(x => x.eval.score).ToList(); } else { boardNodes = boardNodes.OrderBy(x => x.eval.score).ToList(); } int searchScore = player == MAXIMIZING ? int.MinValue : int.MaxValue; foreach (BoardNode boardNode in boardNodes) { if (player == MAXIMIZING) { searchScore = Math.Max(searchScore, MiniMaxSearch(boardNode, MINIMIZING, depth + 1, a, b)); a = Math.Max(a, searchScore); if (a >= b) { break; } } else { searchScore = Math.Min(searchScore, MiniMaxSearch(boardNode, MAXIMIZING, depth + 1, a, b)); b = Math.Min(b, searchScore); if (b <= a) { break; } } } return(searchScore); }
private static bool IsDraw(Con4Board board) { for (int x = 0; x < boardWidth; x++) { if (board.grid[x, 0] == 0) { return(false); } } return(true); }
private static List <int> GenerateDiagonalSequence(Con4Board board, int x, int y, bool positiveSlope) { List <int> seq = new List <int>(); while (LegalPos(x, y)) { seq.Add(board.grid[x, y]); x++; y += positiveSlope ? -1 : 1; } return(seq); }
private static int CalculateColumnScores(Con4Board board, ref BoardState bs) { int score = 0; for (int x = 0; x < boardWidth; x++) { List <int> seq = new List <int>(boardHeight); for (int y = 0; y < boardHeight; y++) { seq.Add(board.grid[x, y]); } score += CalculateSequenceScore(seq, ref bs); } return(score); }
public override bool Equals(object obj) { /*Con4Board other = obj as Con4Board; if (other == null) return false; */ if (obj.GetType() != GetType()) return false; Con4Board other = obj as Con4Board; /*for (int x = 0; x < WIDTH; x++) for (int y = 0; y < HEIGHT; y++) if (other.grid[x, y] != grid[x, y]) return false;*/ // Iteration order to find differences early for(int y = HEIGHT-1; y >= 0; y--) for(int x = 0; x < WIDTH; x++) if (other.grid[x, y] != grid[x, y]) return false; return true; }
public Con4Board SimulateMove(int player, int move) { simulations++; Con4Board deepCopy = new Con4Board(grid); deepCopy.PlayMove(player, move); return deepCopy; }
// Calls minimax for each possible move and returns best move public static int FindBestMove(Connect4Game connect4, Con4Board startBoard, int maxDepth) { MiniMaxAlphaBetaHeuristic.maxDepth = maxDepth; MiniMaxAlphaBetaHeuristic.connect4 = connect4; int player = MAXIMIZING; int a = int.MinValue; int b = int.MaxValue; List <MoveBoardNode> moveBoardNodes = new List <MoveBoardNode>(); foreach (int move in startBoard.GetPossibleMoves()) { Con4Board nextBoard = startBoard.SimulateMove(player, move); Evaluation nextEval = CalculateBoardScore(nextBoard, 1); BoardNode boardNode = new BoardNode { board = nextBoard, eval = nextEval }; moveBoardNodes.Add(new MoveBoardNode { move = move, boardNode = boardNode }); } moveBoardNodes = moveBoardNodes.OrderByDescending(x => x.boardNode.eval.score).ToList(); // player is maximizing Console.WriteLine("\tMove scores {move, score}: "); Console.Write("\t"); List <MoveScore> results = new List <MoveScore>(); foreach (MoveBoardNode moveBoardNode in moveBoardNodes) { int searchScore = MiniMaxSearch(moveBoardNode.boardNode, MINIMIZING, 1, a, b); a = Math.Max(a, searchScore); if (a >= b) { break; } Console.Write("{" + moveBoardNode.move + ", " + searchScore + "} , "); results.Add(new MoveScore() { move = moveBoardNode.move, score = searchScore }); } int maxScore = results.Max(x => x.score); if (Math.Abs(maxScore) > 900) // Either confident in win or in loss, don't search deeper { MainClass.gameOutcomeFinal = true; } if (results.Count(x => x.score > -900) == 1) // Only 1 viable option, just do it! { MainClass.gameOutcomeFinal = true; } int bestMove = results.Find(x => x.score == maxScore).move; //int decidedMove = DecideMove(results); Console.WriteLine("\tBest move: " + bestMove); Console.WriteLine("\tSimulations: " + Con4Board.simulations); Con4Board.simulations = 0; return(bestMove); }