|
@@ -33,61 +33,11 @@ public class Connect4Minimax
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
- private readonly int[][] directions = [
|
|
|
|
- [ 0, 1 ],
|
|
|
|
- [ 1, 0 ],
|
|
|
|
- [ 1, 1 ],
|
|
|
|
- [ -1, 1 ]
|
|
|
|
- ];
|
|
|
|
-
|
|
|
|
- // public int CountSequences(GameBoard board)
|
|
|
|
- // {
|
|
|
|
- // int score = 0;
|
|
|
|
-
|
|
|
|
- // for (int row = 0; row < board.dimensions.Rows; row++)
|
|
|
|
- // {
|
|
|
|
- // for (int col = 0; col < board.dimensions.Columns; col++)
|
|
|
|
- // {
|
|
|
|
- // for (int dir = 0; dir < 4; dir++)
|
|
|
|
- // {
|
|
|
|
- // int rowDir = directions[dir][0];
|
|
|
|
- // int colDir = directions[dir][1];
|
|
|
|
-
|
|
|
|
- // int endRow = row + (3 * rowDir);
|
|
|
|
- // int endCol = col + (3 * colDir);
|
|
|
|
-
|
|
|
|
- // if (endRow >= 0 && endRow < board.dimensions.Rows &&
|
|
|
|
- // endCol >= 0 && endCol < board.dimensions.Columns)
|
|
|
|
- // {
|
|
|
|
- // int aiCount = 0;
|
|
|
|
- // int humanCount = 0;
|
|
|
|
-
|
|
|
|
- // for (int k = 0; k < 4; k++)
|
|
|
|
- // {
|
|
|
|
- // int currentRow = row + (k * rowDir);
|
|
|
|
- // int currentCol = col + (k * colDir);
|
|
|
|
-
|
|
|
|
- // if (board.cells[currentCol][currentRow] == Player.Computer)
|
|
|
|
- // aiCount++;
|
|
|
|
- // else if (board.cells[currentCol][currentRow] != Player.None)
|
|
|
|
- // humanCount++;
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- // score += EvaluateScoreDiff(humanCount, aiCount);
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- // return score;
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- // БЫСТРЕЕ ПРИ ВЫСОКОЙ ЧАСТОТЕ ПРОЦА
|
|
|
|
- public int CountSequences(GameBoard board)
|
|
|
|
|
|
+ public static int CountSequences(GameBoard board)
|
|
{
|
|
{
|
|
int score = 0;
|
|
int score = 0;
|
|
|
|
|
|
|
|
+ // Считаем по горизонтали
|
|
for (int col = 0; col < board.dimensions.Columns; col++)
|
|
for (int col = 0; col < board.dimensions.Columns; col++)
|
|
{
|
|
{
|
|
for (int row = 0; row < board.dimensions.Rows - 3; row++)
|
|
for (int row = 0; row < board.dimensions.Rows - 3; row++)
|
|
@@ -105,6 +55,7 @@ public class Connect4Minimax
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // По вертикали
|
|
for (int col = 0; col < board.dimensions.Columns - 3; col++)
|
|
for (int col = 0; col < board.dimensions.Columns - 3; col++)
|
|
{
|
|
{
|
|
for (int row = 0; row < board.dimensions.Rows; row++)
|
|
for (int row = 0; row < board.dimensions.Rows; row++)
|
|
@@ -122,6 +73,7 @@ public class Connect4Minimax
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // По диагонали
|
|
for (int col = 0; col < board.dimensions.Columns - 3; col++)
|
|
for (int col = 0; col < board.dimensions.Columns - 3; col++)
|
|
{
|
|
{
|
|
for (int row = 0; row < board.dimensions.Rows - 3; row++)
|
|
for (int row = 0; row < board.dimensions.Rows - 3; row++)
|
|
@@ -139,6 +91,7 @@ public class Connect4Minimax
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // По второй диагонали
|
|
for (int col = 3; col < board.dimensions.Columns; col++)
|
|
for (int col = 3; col < board.dimensions.Columns; col++)
|
|
{
|
|
{
|
|
for (int row = 0; row < board.dimensions.Rows - 3; row++)
|
|
for (int row = 0; row < board.dimensions.Rows - 3; row++)
|
|
@@ -159,36 +112,47 @@ public class Connect4Minimax
|
|
return score;
|
|
return score;
|
|
}
|
|
}
|
|
|
|
|
|
- private int EvaluateMinMax(GameBoard board, int depth, bool maxPlayer = false, int alpha = int.MinValue, int beta = int.MaxValue)
|
|
|
|
|
|
+ private static 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.Computer) return (depth + 1) * 100_000;
|
|
if (currentWinner == Player.Computer) return (depth + 1) * 100_000;
|
|
else if (currentWinner == Player.Human) 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);
|
|
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.Computer : Player.Human, i)) continue;
|
|
if (!board.PlaceCoin(maxPlayer ? Player.Computer : Player.Human, i)) continue;
|
|
|
|
|
|
|
|
+ // Итерация оппонента
|
|
int x = EvaluateMinMax(board, depth - 1, !maxPlayer, alpha, beta);
|
|
int x = EvaluateMinMax(board, depth - 1, !maxPlayer, alpha, beta);
|
|
|
|
+ // Выбираем лучший (или худший) исход
|
|
best = maxPlayer ? int.Max(best, x) : int.Min(best, x);
|
|
best = maxPlayer ? int.Max(best, x) : int.Min(best, x);
|
|
|
|
+ // Отменяем сделанный ход
|
|
board.RemoveFromTop(i);
|
|
board.RemoveFromTop(i);
|
|
|
|
|
|
|
|
+ // Обновляем альфа-бета значения
|
|
if (maxPlayer) alpha = int.Max(alpha, best);
|
|
if (maxPlayer) alpha = int.Max(alpha, best);
|
|
else beta = int.Min(beta, best);
|
|
else beta = int.Min(beta, best);
|
|
|
|
|
|
- if (beta <= alpha) i++;
|
|
|
|
|
|
+ // if (beta <= alpha) i++;
|
|
|
|
+ if (beta <= alpha) return best;
|
|
}
|
|
}
|
|
|
|
|
|
return best;
|
|
return best;
|
|
}
|
|
}
|
|
|
|
|
|
- public int GetBestMove(GameBoard board, int depth)
|
|
|
|
|
|
+ public static 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() { MaxDegreeOfParallelism = 1 }, i =>
|
|
// Parallel.For(0, board.dimensions.Columns, new() { MaxDegreeOfParallelism = 1 }, i =>
|
|
@@ -199,6 +163,7 @@ public class Connect4Minimax
|
|
moves.Add((i, EvaluateMinMax(newBoard, depth)));
|
|
moves.Add((i, EvaluateMinMax(newBoard, depth)));
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ // Находим счет с максимальным счетом, с приоритетом на центральные колонки
|
|
return moves.OrderByDescending(m => m.Score).ThenBy(m => int.Abs(m.Column - (board.dimensions.Columns / 2))).First().Column;
|
|
return moves.OrderByDescending(m => m.Score).ThenBy(m => int.Abs(m.Column - (board.dimensions.Columns / 2))).First().Column;
|
|
}
|
|
}
|
|
}
|
|
}
|