|
@@ -4,15 +4,13 @@ namespace MinMaxAB;
|
|
|
|
|
|
public class MinMaxABFourInARowBoard
|
|
public class MinMaxABFourInARowBoard
|
|
{
|
|
{
|
|
- protected int EvaluateScoreDiff((int player, int computer) scores)
|
|
|
|
|
|
+ protected static int EvaluateScoreDiff(int player, int computer)
|
|
{
|
|
{
|
|
- //Console.WriteLine($"V: {scores.computer} {scores.player}");
|
|
|
|
|
|
+ if (player == 0 && computer == 0) return 0;
|
|
|
|
|
|
- if (scores.player == 0 && scores.computer == 0) return 0;
|
|
|
|
-
|
|
|
|
- if (scores.computer > 0)
|
|
|
|
|
|
+ if (computer > 0)
|
|
{
|
|
{
|
|
- return scores.computer switch
|
|
|
|
|
|
+ return computer switch
|
|
{
|
|
{
|
|
1 => 1,
|
|
1 => 1,
|
|
2 => 10,
|
|
2 => 10,
|
|
@@ -21,9 +19,9 @@ public class MinMaxABFourInARowBoard
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
- if (scores.player > 0)
|
|
|
|
|
|
+ if (player > 0)
|
|
{
|
|
{
|
|
- return scores.player switch
|
|
|
|
|
|
+ return player switch
|
|
{
|
|
{
|
|
1 => -1,
|
|
1 => -1,
|
|
2 => -10,
|
|
2 => -10,
|
|
@@ -35,97 +33,26 @@ public class MinMaxABFourInARowBoard
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
- // public int CountSequences(GameBoard board, Player player)
|
|
|
|
- // {
|
|
|
|
- // int score = 0;
|
|
|
|
- // int hscore = 0;
|
|
|
|
- // int vscore = 0;
|
|
|
|
- // int dscore = 0;
|
|
|
|
- // int rscore = 0;
|
|
|
|
-
|
|
|
|
- // for (int col = 0; col < board.dimensions.Columns; col++)
|
|
|
|
- // {
|
|
|
|
- // for (int row = 0; row < board.dimensions.Rows; row++)
|
|
|
|
- // {
|
|
|
|
- // if (row < board.dimensions.Columns - 4)
|
|
|
|
- // {
|
|
|
|
- // int computer = 0;
|
|
|
|
- // int p = 0;
|
|
|
|
- // int c2 = 0;
|
|
|
|
- // int p2 = 0;
|
|
|
|
-
|
|
|
|
- // for (int k = 0; k < 4; k++)
|
|
|
|
- // {
|
|
|
|
- // if (board.cells[col, row + k] == player) computer++;
|
|
|
|
- // else if (board.cells[col, row + k] != Player.None) p++;
|
|
|
|
-
|
|
|
|
- // if (col < board.dimensions.Rows - 4)
|
|
|
|
- // {
|
|
|
|
- // if (board.cells[col + k, row + k] == player) c2++;
|
|
|
|
- // else if (board.cells[col + k, row + k] != Player.None) p2++;
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- // hscore += EvaluateScoreDiff((p, computer));
|
|
|
|
- // dscore += EvaluateScoreDiff((p2, c2));
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- // if (col < board.dimensions.Rows - 4)
|
|
|
|
- // {
|
|
|
|
- // int computer = 0;
|
|
|
|
- // int p = 0;
|
|
|
|
-
|
|
|
|
- // for (int k = 0; k < 4; k++)
|
|
|
|
- // {
|
|
|
|
- // if (board.cells[col + k, row] == player) computer++;
|
|
|
|
- // else if (board.cells[col + k, row] != Player.None) p++;
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- // vscore += EvaluateScoreDiff((p, computer));
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- // if (col >= 3 && row < board.dimensions.Columns - 4)
|
|
|
|
- // {
|
|
|
|
- // int computer = 0;
|
|
|
|
- // int p = 0;
|
|
|
|
-
|
|
|
|
- // for (int k = 0; k < 4; k++)
|
|
|
|
- // {
|
|
|
|
- // if (board.cells[col - k, row + k] == player) computer++;
|
|
|
|
- // else if (board.cells[col - k, row + k] != Player.None) p++;
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- // rscore += EvaluateScoreDiff((p, computer));
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
- // return score;
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- public int CountSequences(GameBoard board, Player player)
|
|
|
|
|
|
+ public int CountSequences(GameBoard board)
|
|
{
|
|
{
|
|
int score = 0;
|
|
int score = 0;
|
|
|
|
|
|
- // Direction vectors for all 4 directions:
|
|
|
|
- // Horizontal, Vertical, Diagonal, Reverse Diagonal
|
|
|
|
int[,] directions = new int[,] {
|
|
int[,] directions = new int[,] {
|
|
- { 0, 1 }, // Horizontal: right
|
|
|
|
- { 1, 0 }, // Vertical: down
|
|
|
|
- { 1, 1 }, // Diagonal: down-right
|
|
|
|
- { -1, 1 } // Reverse Diagonal: up-right
|
|
|
|
|
|
+ { 0, 1 },
|
|
|
|
+ { 1, 0 },
|
|
|
|
+ { 1, 1 },
|
|
|
|
+ { -1, 1 }
|
|
};
|
|
};
|
|
|
|
|
|
for (int row = 0; row < board.dimensions.Rows; row++)
|
|
for (int row = 0; row < board.dimensions.Rows; row++)
|
|
{
|
|
{
|
|
for (int col = 0; col < board.dimensions.Columns; col++)
|
|
for (int col = 0; col < board.dimensions.Columns; col++)
|
|
{
|
|
{
|
|
- // Check all 4 directions from current position
|
|
|
|
for (int dir = 0; dir < 4; dir++)
|
|
for (int dir = 0; dir < 4; dir++)
|
|
{
|
|
{
|
|
int rowDir = directions[dir, 0];
|
|
int rowDir = directions[dir, 0];
|
|
int colDir = directions[dir, 1];
|
|
int colDir = directions[dir, 1];
|
|
|
|
|
|
- // Check if we can fit a sequence of 4 in this direction
|
|
|
|
int endRow = row + (3 * rowDir);
|
|
int endRow = row + (3 * rowDir);
|
|
int endCol = col + (3 * colDir);
|
|
int endCol = col + (3 * colDir);
|
|
|
|
|
|
@@ -135,20 +62,18 @@ public class MinMaxABFourInARowBoard
|
|
int aiCount = 0;
|
|
int aiCount = 0;
|
|
int humanCount = 0;
|
|
int humanCount = 0;
|
|
|
|
|
|
- // Check the sequence of 4 cells in current direction
|
|
|
|
for (int k = 0; k < 4; k++)
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
{
|
|
int currentRow = row + (k * rowDir);
|
|
int currentRow = row + (k * rowDir);
|
|
int currentCol = col + (k * colDir);
|
|
int currentCol = col + (k * colDir);
|
|
|
|
|
|
- if (board.cells[currentCol, currentRow] == player)
|
|
|
|
|
|
+ if (board.cells[currentCol][currentRow] == Player.Computer)
|
|
aiCount++;
|
|
aiCount++;
|
|
- else if (board.cells[currentCol, currentRow] != Player.None)
|
|
|
|
|
|
+ else if (board.cells[currentCol][currentRow] != Player.None)
|
|
humanCount++;
|
|
humanCount++;
|
|
}
|
|
}
|
|
|
|
|
|
- // Score the sequence
|
|
|
|
- score += EvaluateScoreDiff((humanCount, aiCount));
|
|
|
|
|
|
+ score += EvaluateScoreDiff(humanCount, aiCount);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -157,32 +82,28 @@ public class MinMaxABFourInARowBoard
|
|
return score;
|
|
return score;
|
|
}
|
|
}
|
|
|
|
|
|
- private int EvaluateMinMax(GameBoard board, Player player, int depth, bool maxPlayer = false, int alpha = int.MinValue, int beta = int.MaxValue)
|
|
|
|
|
|
+ private int EvaluateMinMax(GameBoard board, int depth, bool maxPlayer = false, int alpha = int.MinValue, int beta = int.MaxValue)
|
|
{
|
|
{
|
|
var currentWinner = board.GetWinner();
|
|
var currentWinner = board.GetWinner();
|
|
|
|
|
|
- if (currentWinner == player) return (depth + 1) * 100_000;
|
|
|
|
- if (currentWinner! == GameBoard.Opposite(player)) return -(depth + 1) * 100_000;
|
|
|
|
|
|
+ if (currentWinner == Player.Computer) return (depth + 1) * 100_000;
|
|
|
|
+ else if (currentWinner == Player.Human) return -(depth + 1) * 100_000;
|
|
if (!board.HasEmptyColumns()) return 0;
|
|
if (!board.HasEmptyColumns()) return 0;
|
|
|
|
|
|
- //if (depth == 0) return CountSequences(board, player);
|
|
|
|
- if (depth == 0)
|
|
|
|
- {
|
|
|
|
- return CountSequences(board, player); ;
|
|
|
|
- }
|
|
|
|
|
|
+ if (depth == 0) return CountSequences(board);
|
|
|
|
|
|
int best = maxPlayer ? int.MinValue : int.MaxValue;
|
|
int best = maxPlayer ? int.MinValue : int.MaxValue;
|
|
|
|
|
|
for (int i = 0; i < board.dimensions.Columns; i++)
|
|
for (int i = 0; i < board.dimensions.Columns; i++)
|
|
{
|
|
{
|
|
- if (!board.PlaceCoin(maxPlayer ? player : GameBoard.Opposite(player), i)) continue;
|
|
|
|
|
|
+ if (!board.PlaceCoin(maxPlayer ? Player.Computer : Player.Human, i)) continue;
|
|
|
|
|
|
- int x = EvaluateMinMax(board, player, depth - 1, !maxPlayer, alpha, beta);
|
|
|
|
- best = maxPlayer ? Math.Max(best, x) : Math.Min(best, x);
|
|
|
|
|
|
+ int x = EvaluateMinMax(board, depth - 1, !maxPlayer, alpha, beta);
|
|
|
|
+ best = maxPlayer ? int.Max(best, x) : int.Min(best, x);
|
|
board.RemoveFromTop(i);
|
|
board.RemoveFromTop(i);
|
|
|
|
|
|
- if (maxPlayer) alpha = Math.Max(alpha, best);
|
|
|
|
- else beta = Math.Min(beta, best);
|
|
|
|
|
|
+ if (maxPlayer) alpha = int.Max(alpha, best);
|
|
|
|
+ else beta = int.Min(beta, best);
|
|
|
|
|
|
if (beta <= alpha) i++;
|
|
if (beta <= alpha) i++;
|
|
}
|
|
}
|
|
@@ -190,27 +111,16 @@ public class MinMaxABFourInARowBoard
|
|
return best;
|
|
return best;
|
|
}
|
|
}
|
|
|
|
|
|
- public int GetBestMove(GameBoard board, Player player, int depth)
|
|
|
|
|
|
+ public int GetBestMove(GameBoard board, int depth)
|
|
{
|
|
{
|
|
List<(int Column, int Score)> moves = [];
|
|
List<(int Column, int Score)> moves = [];
|
|
- // Parallel.For(0, board.dimensions.Columns, new ParallelOptions() { MaxDegreeOfParallelism = 1 }, i =>
|
|
|
|
Parallel.For(0, board.dimensions.Columns, i =>
|
|
Parallel.For(0, board.dimensions.Columns, i =>
|
|
{
|
|
{
|
|
var newBoard = (board.Clone() as GameBoard)!;
|
|
var newBoard = (board.Clone() as GameBoard)!;
|
|
- if (!newBoard.PlaceCoin(player, i)) return;
|
|
|
|
- moves.Add((i, EvaluateMinMax(newBoard, player, depth)));
|
|
|
|
|
|
+ if (!newBoard.PlaceCoin(Player.Computer, i)) return;
|
|
|
|
+ moves.Add((i, EvaluateMinMax(newBoard, depth)));
|
|
});
|
|
});
|
|
- // for (int i = 0; i < board.dimensions.Columns; i++)
|
|
|
|
- // {
|
|
|
|
- // Console.Write($"\rAI Thinking {(float)i / board.dimensions.Columns:p0}...");
|
|
|
|
- // if (!PlaceCoin(player, i)) continue;
|
|
|
|
- // moves.Add((i, EvaluateMinMax(player, depth, maxPlayer, alpha, beta)));
|
|
|
|
- // RemoveFromTop(i);
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- int maxScore = moves.Max(move => move.Score);
|
|
|
|
- var bestMoves = moves.Where(move => move.Score == maxScore).OrderBy(move => Math.Abs(move.Column - board.dimensions.Columns / 2)).ToArray();
|
|
|
|
- // Console.WriteLine(string.Join(" | ", moves.OrderBy(m => m.Column).Select(m => $"{m.Column}:{m.Score}")));
|
|
|
|
- return bestMoves.ChooseRandom().Column;
|
|
|
|
|
|
+
|
|
|
|
+ return moves.MaxBy(m => m.Score).Column;
|
|
}
|
|
}
|
|
}
|
|
}
|