Files
LEA2/CheckersSpielBot/AiService.cs
T
abdelaziz bc4118704e logic
2026-03-30 07:49:37 +02:00

175 lines
6.4 KiB
C#

namespace CheckersSpielBot
{
public class AiService
{
private const int MaxDepth = 7;
private const int WinScore = 100000;
private const int LoseScore = -100000;
private readonly int _aiPlayer;
private readonly int _humanPlayer;
public AiService(int aiPlayer)
{
_aiPlayer = aiPlayer;
_humanPlayer = aiPlayer == 1 ? 2 : 1;
}
public Move? GetBestMove(int[,] board)
{
Move? bestMove = null;
int bestScore = int.MinValue;
var allMoves = GetAllMovesForPlayer(board, _aiPlayer);
if (allMoves.Count == 0) return null;
foreach (var (originRow, originCol, move) in allMoves)
{
int[,] newBoard = ApplyMove(board, originRow, originCol, move);
int score = Minimax(newBoard, MaxDepth - 1, int.MinValue, int.MaxValue, false, move.IsCapture, move.DestinationRow, move.DestinationCol);
if (score > bestScore)
{
bestScore = score;
bestMove = move;
}
}
return bestMove;
}
// Minimax with Alpha-Beta Pruning
private int Minimax(int[,] board, int depth, int alpha, int beta, bool isMaximizing, bool lastMoveWasCapture, int lastMoveRow, int lastMoveCol)
{
int currentPlayer = isMaximizing ? _aiPlayer : _humanPlayer;
// Chain jump
if (lastMoveWasCapture)
{
var chainJumps = new MoveGeneratorService(board).GetLegalMoves(lastMoveRow, lastMoveCol, capturesOnly: true);
if (chainJumps.Count > 0)
{
// Same player continue jumping / don't switch turn
if (isMaximizing)
{
int best = int.MinValue;
foreach (var jump in chainJumps)
{
int[,] newBoard = ApplyMove(board, lastMoveRow, lastMoveCol, jump);
int score = Minimax(newBoard, depth, alpha, beta, true, true, jump.DestinationRow, jump.DestinationCol);
best = Math.Max(best, score);
alpha = Math.Max(alpha, best);
if (beta <= alpha) break;
}
return best;
}
else
{
int best = int.MaxValue;
foreach (var jump in chainJumps)
{
int[,] newBoard = ApplyMove(board, lastMoveRow, lastMoveCol, jump);
int score = Minimax(newBoard, depth, alpha, beta, false, true, jump.DestinationRow, jump.DestinationCol);
best = Math.Min(best, score);
beta = Math.Min(beta, best);
if (beta <= alpha) break;
}
return best;
}
}
}
// Terminal / depth check
var allMoves = GetAllMovesForPlayer(board, currentPlayer);
if (allMoves.Count == 0) { return isMaximizing ? LoseScore : WinScore; }
if (depth == 0) { return BoardEvaluator.Evaluate(board, _aiPlayer); }
if (isMaximizing)
{
int best = int.MinValue;
foreach (var (oRow, oCol, move) in allMoves)
{
int[,] newBoard = ApplyMove(board, oRow, oCol, move);
int score = Minimax(newBoard, depth - 1, alpha, beta, false, move.IsCapture, move.DestinationRow, move.DestinationCol);
best = Math.Max(best, score);
alpha = Math.Max(alpha, best);
if (beta <= alpha) { break; }
}
return best;
}
else
{
int best = int.MaxValue;
foreach (var (oRow, oCol, move) in allMoves)
{
int[,] newBoard = ApplyMove(board, oRow, oCol, move);
int score = Minimax(newBoard, depth - 1, alpha, beta, true, move.IsCapture, move.DestinationRow, move.DestinationCol);
best = Math.Min(best, score);
beta = Math.Min(beta, best);
if (beta <= alpha) { break; }
}
return best;
}
}
#region helpers
private static List<(int, int, Move)> GetAllMovesForPlayer(int[,] board, int player)
{
var moveGen = new MoveGeneratorService(board);
var captures = new List<(int, int, Move)>();
var normals = new List<(int, int, Move)>();
for (int r = 0; r < 8; r++)
{
for (int c = 0; c < 8; c++)
{
if (!BoardHelper.BelongsToPlayer(board[r, c], player)) continue;
var jumps = moveGen.GetLegalMoves(r, c, capturesOnly: true);
if (jumps.Count > 0)
{
foreach (var j in jumps) captures.Add((r, c, j));
continue;
}
var moves = moveGen.GetLegalMoves(r, c, capturesOnly: false);
foreach (var m in moves)
{
normals.Add((r, c, m));
}
}
}
return captures.Count > 0 ? captures : normals;
}
private static int[,] ApplyMove(int[,] board, int originRow, int originCol, Move move)
{
int[,] newBoard = (int[,])board.Clone();
int piece = newBoard[originRow, originCol];
newBoard[move.DestinationRow, move.DestinationCol] = piece;
newBoard[originRow, originCol] = 0;
if (move.IsCapture) { newBoard[move.CapturedPieceRow, move.CapturedPieceCol] = 0; }
// Promotion
if (newBoard[move.DestinationRow, move.DestinationCol] == 1 && move.DestinationRow == 0) { newBoard[move.DestinationRow, move.DestinationCol] = 3; }
if (newBoard[move.DestinationRow, move.DestinationCol] == 2 && move.DestinationRow == 7) { newBoard[move.DestinationRow, move.DestinationCol] = 4; }
return newBoard;
}
#endregion
}
}