MinMaxABFourInARowBoard.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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) || (computer > 0 && player > 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. // БЫСТРЕЕ ПРИ ВЫСОКОЙ ЧАСТОТЕ ПРОЦА
  71. public int CountSequences(GameBoard board)
  72. {
  73. int score = 0;
  74. for (int col = 0; col < board.dimensions.Columns; col++)
  75. {
  76. for (int row = 0; row < board.dimensions.Rows - 3; row++)
  77. {
  78. int c = 0;
  79. int p = 0;
  80. for (int k = 0; k < 4; k++)
  81. {
  82. if (board.cells[col][row + k] == Player.Computer) c++;
  83. else if (board.cells[col][row + k] != Player.None) p++;
  84. }
  85. score += EvaluateScoreDiff(p, c);
  86. }
  87. }
  88. for (int col = 0; col < board.dimensions.Columns - 3; col++)
  89. {
  90. for (int row = 0; row < board.dimensions.Rows; row++)
  91. {
  92. int c = 0;
  93. int p = 0;
  94. for (int k = 0; k < 4; k++)
  95. {
  96. if (board.cells[col + k][row] == Player.Computer) c++;
  97. else if (board.cells[col + k][row] != Player.None) p++;
  98. }
  99. score += EvaluateScoreDiff(p, c);
  100. }
  101. }
  102. for (int col = 0; col < board.dimensions.Columns - 3; col++)
  103. {
  104. for (int row = 0; row < board.dimensions.Rows - 3; row++)
  105. {
  106. int c = 0;
  107. int p = 0;
  108. for (int k = 0; k < 4; k++)
  109. {
  110. if (board.cells[col + k][row + k] == Player.Computer) c++;
  111. else if (board.cells[col + k][row + k] != Player.None) p++;
  112. }
  113. score += EvaluateScoreDiff(p, c);
  114. }
  115. }
  116. for (int col = 3; col < board.dimensions.Columns; col++)
  117. {
  118. for (int row = 0; row < board.dimensions.Rows - 3; row++)
  119. {
  120. int c = 0;
  121. int p = 0;
  122. for (int k = 0; k < 4; k++)
  123. {
  124. if (board.cells[col - k][row + k] == Player.Computer) c++;
  125. else if (board.cells[col - k][row + k] != Player.None) p++;
  126. }
  127. score += EvaluateScoreDiff(p, c);
  128. }
  129. }
  130. return score;
  131. }
  132. private int EvaluateMinMax(GameBoard board, int depth, bool maxPlayer = false, int alpha = int.MinValue, int beta = int.MaxValue)
  133. {
  134. var currentWinner = board.GetWinner();
  135. if (currentWinner == Player.Computer) return (depth + 1) * 100_000;
  136. else if (currentWinner == Player.Human) return -(depth + 1) * 100_000;
  137. if (!board.HasEmptyColumns()) return 0;
  138. if (depth == 0) return CountSequences(board);
  139. int best = maxPlayer ? int.MinValue : int.MaxValue;
  140. for (int i = 0; i < board.dimensions.Columns; i++)
  141. {
  142. if (!board.PlaceCoin(maxPlayer ? Player.Computer : Player.Human, i)) continue;
  143. int x = EvaluateMinMax(board, depth - 1, !maxPlayer, alpha, beta);
  144. best = maxPlayer ? int.Max(best, x) : int.Min(best, x);
  145. board.RemoveFromTop(i);
  146. if (maxPlayer) alpha = int.Max(alpha, best);
  147. else beta = int.Min(beta, best);
  148. if (beta <= alpha) i++;
  149. }
  150. return best;
  151. }
  152. public int GetBestMove(GameBoard board, int depth)
  153. {
  154. List<(int Column, int Score)> moves = [];
  155. // Parallel.For(0, board.dimensions.Columns, new() { MaxDegreeOfParallelism = 1 }, i =>
  156. Parallel.For(0, board.dimensions.Columns, i =>
  157. {
  158. var newBoard = board.Clone();
  159. if (!newBoard.PlaceCoin(Player.Computer, i)) return;
  160. moves.Add((i, EvaluateMinMax(newBoard, depth)));
  161. });
  162. return moves.OrderByDescending(m => m.Score).ThenByDescending(m => int.Abs(m.Column - (board.dimensions.Columns / 2))).First().Column;
  163. }
  164. }