/// <summary> /// Min max searching algorithm with defined cutoff depth. /// </summary> /// <returns>The column that will be moved played in.</returns> private int MinimaxCutoffSearch(BitBoard board) { int maxDepth = 7; var openMoveValue = 0.0m; var movedColumn = -1; var alpha = decimal.MinValue; var beta = decimal.MaxValue; var nodeCounter = new NodeCounter(); var stopWatch = new Stopwatch(); stopWatch.Start(); if (AiColor == DiscColor.Red) { (openMoveValue, movedColumn) = MinValue(board, maxDepth, alpha, beta, nodeCounter, AiColor); } else { (openMoveValue, movedColumn) = MaxValue(board, maxDepth, alpha, beta, nodeCounter, AiColor); } stopWatch.Stop(); var elapsed = stopWatch.Elapsed; var elapsedTime = String.Format("Searched for {0:00}.{1:0000} seconds", elapsed.Seconds, elapsed.Milliseconds); Console.WriteLine(elapsedTime); Console.WriteLine($"Column {movedColumn} was chosen."); Console.WriteLine($"{nodeCounter.TotalNodes} were explored."); return(movedColumn); }
public void UpdateBoard(BitBoard board) { GameBoard = board; }
private (decimal BoardScore, int Column) MinValue( BitBoard board, int depth, decimal alpha, decimal beta, NodeCounter nodeCounter, DiscColor movingColor) { nodeCounter.Increment(); var openColumns = GetOpenColumns(in board); // TODO I think these columns are safe but not certain // I didn't see any problems with this during some test games, may require more vetting though // Drawn game if (openColumns.Count == 0) { return(0.0m, -1); } if (depth == 0 || CheckVictory(in board) != DiscColor.None) { return(EvaluateBoardState(in board, movingColor), -1); } // Win and return immediately if possible var winningMove = FindKillerMove(in board, movingColor); if (winningMove.HasWinner && winningMove.Winner == movingColor) { var winningBoard = BitBoardMove(in board, winningMove.Column, movingColor); var winningScore = EvaluateBoardState(in winningBoard, movingColor); return(winningScore, winningMove.Column); } // Stop the opponent from winning if possible var oppWinningMove = FindKillerMove(in board, OpponentColor); if (oppWinningMove.HasWinner) { var stopWinningBoard = BitBoardMove(in board, oppWinningMove.Column, AiColor); var stopWinningScore = EvaluateBoardState(in stopWinningBoard, movingColor); return(stopWinningScore, oppWinningMove.Column); } decimal minimumMoveValue = decimal.MaxValue; int movedColumn = -1; foreach (int openMove in openColumns) { var newState = BitBoardMove(in board, openMove, movingColor); var childMaxValue = MaxValue(newState, depth - 1, alpha, beta, nodeCounter, ChangeTurnColor(movingColor)).BoardScore; if (childMaxValue < minimumMoveValue) { minimumMoveValue = childMaxValue; movedColumn = openMove; } if (minimumMoveValue <= alpha) { return(minimumMoveValue, openMove); } beta = Math.Min(beta, minimumMoveValue); } return(minimumMoveValue, movedColumn); }