123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- using FourInARow;
- namespace MinMaxAB;
- public class MinMaxABFourInARowBoard
- {
- protected static int EvaluateScoreDiff(int player, int computer)
- {
- if ((player == 0 && computer == 0) || (computer > 0 && player > 0)) return 0;
- if (computer > 0)
- {
- return computer switch
- {
- 1 => 1,
- 2 => 10,
- 3 => 100,
- _ => 0
- };
- }
- if (player > 0)
- {
- return player switch
- {
- 1 => -1,
- 2 => -10,
- 3 => -100,
- _ => 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)
- {
- int score = 0;
- for (int col = 0; col < board.dimensions.Columns; col++)
- {
- for (int row = 0; row < board.dimensions.Rows - 3; row++)
- {
- int c = 0;
- int p = 0;
- for (int k = 0; k < 4; k++)
- {
- if (board.cells[col][row + k] == Player.Computer) c++;
- else if (board.cells[col][row + k] != Player.None) p++;
- }
- score += EvaluateScoreDiff(p, c);
- }
- }
- for (int col = 0; col < board.dimensions.Columns - 3; col++)
- {
- for (int row = 0; row < board.dimensions.Rows; row++)
- {
- int c = 0;
- int p = 0;
- for (int k = 0; k < 4; k++)
- {
- if (board.cells[col + k][row] == Player.Computer) c++;
- else if (board.cells[col + k][row] != Player.None) p++;
- }
- score += EvaluateScoreDiff(p, c);
- }
- }
- for (int col = 0; col < board.dimensions.Columns - 3; col++)
- {
- for (int row = 0; row < board.dimensions.Rows - 3; row++)
- {
- int c = 0;
- int p = 0;
- for (int k = 0; k < 4; k++)
- {
- if (board.cells[col + k][row + k] == Player.Computer) c++;
- else if (board.cells[col + k][row + k] != Player.None) p++;
- }
- score += EvaluateScoreDiff(p, c);
- }
- }
- for (int col = 3; col < board.dimensions.Columns; col++)
- {
- for (int row = 0; row < board.dimensions.Rows - 3; row++)
- {
- int c = 0;
- int p = 0;
- for (int k = 0; k < 4; k++)
- {
- if (board.cells[col - k][row + k] == Player.Computer) c++;
- else if (board.cells[col - k][row + k] != Player.None) p++;
- }
- score += EvaluateScoreDiff(p, c);
- }
- }
- return score;
- }
- private int EvaluateMinMax(GameBoard board, int depth, bool maxPlayer = false, int alpha = int.MinValue, int beta = int.MaxValue)
- {
- var currentWinner = board.GetWinner();
- 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 (depth == 0) return CountSequences(board);
- int best = maxPlayer ? int.MinValue : int.MaxValue;
- for (int i = 0; i < board.dimensions.Columns; i++)
- {
- if (!board.PlaceCoin(maxPlayer ? Player.Computer : Player.Human, i)) continue;
- int x = EvaluateMinMax(board, depth - 1, !maxPlayer, alpha, beta);
- best = maxPlayer ? int.Max(best, x) : int.Min(best, x);
- board.RemoveFromTop(i);
- if (maxPlayer) alpha = int.Max(alpha, best);
- else beta = int.Min(beta, best);
- if (beta <= alpha) i++;
- }
- return best;
- }
- public int GetBestMove(GameBoard board, int depth)
- {
- List<(int Column, int Score)> moves = [];
- // Parallel.For(0, board.dimensions.Columns, new() { MaxDegreeOfParallelism = 1 }, i =>
- Parallel.For(0, board.dimensions.Columns, i =>
- {
- var newBoard = board.Clone();
- if (!newBoard.PlaceCoin(Player.Computer, i)) return;
- moves.Add((i, EvaluateMinMax(newBoard, depth)));
- });
- return moves.OrderByDescending(m => m.Score).ThenByDescending(m => int.Abs(m.Column - (board.dimensions.Columns / 2))).First().Column;
- }
- }
|