MinMaxABFourInARowBoard.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. using FourInARow;
  2. namespace MinMaxAB;
  3. public class MinMaxABFourInARowBoard
  4. {
  5. protected static int EvaluateScoreDiff(int player, int computer)
  6. {
  7. if (player == 0 && computer == 0) return 0;
  8. if (computer > 0)
  9. {
  10. return computer switch
  11. {
  12. 1 => 1,
  13. 2 => 10,
  14. 3 => 100,
  15. _ => 0
  16. };
  17. }
  18. if (player > 0)
  19. {
  20. return player switch
  21. {
  22. 1 => -1,
  23. 2 => -10,
  24. 3 => -100,
  25. _ => 0
  26. }; ;
  27. }
  28. return 0;
  29. }
  30. private readonly int[][] directions = [
  31. [ 0, 1 ],
  32. [ 1, 0 ],
  33. [ 1, 1 ],
  34. [ -1, 1 ]
  35. ];
  36. public int CountSequences(GameBoard board)
  37. {
  38. int score = 0;
  39. for (int row = 0; row < board.dimensions.Rows; row++)
  40. {
  41. for (int col = 0; col < board.dimensions.Columns; col++)
  42. {
  43. for (int dir = 0; dir < 4; dir++)
  44. {
  45. int rowDir = directions[dir][0];
  46. int colDir = directions[dir][1];
  47. int endRow = row + (3 * rowDir);
  48. int endCol = col + (3 * colDir);
  49. if (endRow >= 0 && endRow < board.dimensions.Rows &&
  50. endCol >= 0 && endCol < board.dimensions.Columns)
  51. {
  52. int aiCount = 0;
  53. int humanCount = 0;
  54. for (int k = 0; k < 4; k++)
  55. {
  56. int currentRow = row + (k * rowDir);
  57. int currentCol = col + (k * colDir);
  58. if (board.cells[currentCol][currentRow] == Player.Computer)
  59. aiCount++;
  60. else if (board.cells[currentCol][currentRow] != Player.None)
  61. humanCount++;
  62. }
  63. score += EvaluateScoreDiff(humanCount, aiCount);
  64. }
  65. }
  66. }
  67. }
  68. return score;
  69. }
  70. private int EvaluateMinMax(GameBoard board, int depth, bool maxPlayer = false, int alpha = int.MinValue, int beta = int.MaxValue)
  71. {
  72. var currentWinner = board.GetWinner();
  73. if (currentWinner == Player.Computer) return (depth + 1) * 100_000;
  74. else if (currentWinner == Player.Human) return -(depth + 1) * 100_000;
  75. if (!board.HasEmptyColumns()) return 0;
  76. if (depth == 0) return CountSequences(board);
  77. int best = maxPlayer ? int.MinValue : int.MaxValue;
  78. for (int i = 0; i < board.dimensions.Columns; i++)
  79. {
  80. if (!board.PlaceCoin(maxPlayer ? Player.Computer : Player.Human, i)) continue;
  81. int x = EvaluateMinMax(board, depth - 1, !maxPlayer, alpha, beta);
  82. best = maxPlayer ? int.Max(best, x) : int.Min(best, x);
  83. board.RemoveFromTop(i);
  84. if (maxPlayer) alpha = int.Max(alpha, best);
  85. else beta = int.Min(beta, best);
  86. if (beta <= alpha) i++;
  87. }
  88. return best;
  89. }
  90. public int GetBestMove(GameBoard board, int depth)
  91. {
  92. List<(int Column, int Score)> moves = [];
  93. // Parallel.For(0, board.dimensions.Columns, new() { MaxDegreeOfParallelism = 1 }, i =>
  94. Parallel.For(0, board.dimensions.Columns, i =>
  95. {
  96. var newBoard = board.Clone();
  97. if (!newBoard.PlaceCoin(Player.Computer, i)) return;
  98. moves.Add((i, EvaluateMinMax(newBoard, depth)));
  99. });
  100. return moves.MaxBy(m => m.Score).Column;
  101. }
  102. }