using System.ComponentModel; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace CheckersSpielBot { public partial class MainWindow : Window { private readonly GameService _game; private readonly BoardRenderer _renderer; private readonly DatabaseService _db = new(); private AiService _ai; private int _humanPlayer = 1; private int _aiPlayer = 2; private bool _gameEnded = false; private int _selectedRow = -1; private int _selectedCol = -1; private bool _hasPieceSelected = false; private bool _isAiThinking = false; public MainWindow() { InitializeComponent(); _game = new GameService(); _renderer = new BoardRenderer(this, _game); _ai = new AiService(_aiPlayer); InitializeNewGame(); } #region Setup private void InitializeNewGame() { // End previous game as draw if it wasn't finished if (PlayerSession.CurrentGameId != -1 && !_gameEnded) _db.EndGame(PlayerSession.CurrentGameId, "draw"); _gameEnded = false; GameModeDialog dialog = new GameModeDialog(); dialog.ShowDialog(); if (dialog.HumanGoesFirst) { _humanPlayer = 1; _aiPlayer = 2; } else { _humanPlayer = 2; _aiPlayer = 1; } _ai = new AiService(_aiPlayer); string wentFirst = dialog.HumanGoesFirst ? "player" : "bot"; PlayerSession.CurrentGameId = _db.StartGame(PlayerSession.PlayerId, wentFirst); _game.InitializeNewGame(); ClearSelection(); RenderBoard(); RefreshStatusBar(); if (_game.ActivePlayer == _aiPlayer) { TriggerAiTurnAsync(); } } #endregion #region Input private void CellClick(object sender, RoutedEventArgs e) { if (_isAiThinking) return; if (_game.ActivePlayer != _humanPlayer) return; if (sender is not Button btn || btn.Tag == null) return; ParseCellTag(btn.Tag.ToString()!, out int row, out int col); if (_game.IsChainJumpActive) { HandleChainJumpClick(row, col); return; } if (_hasPieceSelected) HandleSecondClick(row, col); else HandleFirstClick(row, col); } private void HandleFirstClick(int row, int col) { int piece = _game.Board[row, col]; if (!_game.BelongsToActivePlayer(piece)) return; if (_game.MandatoryCapturePieces.Count > 0 && !_game.MandatoryCapturePieces.Contains((row, col))) return; if (_game.GetLegalMoves(row, col, capturesOnly: false).Count == 0) return; _selectedRow = row; _selectedCol = col; _hasPieceSelected = true; RenderBoard(); } private void HandleSecondClick(int row, int col) { int piece = _game.Board[row, col]; if (_game.BelongsToActivePlayer(piece)) { bool blockedByCapture = _game.MandatoryCapturePieces.Count > 0 && !_game.MandatoryCapturePieces.Contains((row, col)); if (!blockedByCapture && _game.GetLegalMoves(row, col, capturesOnly: false).Count > 0) { _selectedRow = row; _selectedCol = col; RenderBoard(); } return; } var legalMoves = _game.GetLegalMoves(_selectedRow, _selectedCol, capturesOnly: false); var chosen = legalMoves.Find(m => m.DestinationRow == row && m.DestinationCol == col); if (chosen != null) { ProcessMoveResult(_game.ExecuteMove(_selectedRow, _selectedCol, chosen)); } } private void HandleChainJumpClick(int row, int col) { if (row == _game.ChainJumpRow && col == _game.ChainJumpCol) return; var jumps = _game.GetLegalMoves(_game.ChainJumpRow, _game.ChainJumpCol, capturesOnly: true); var chosen = jumps.Find(m => m.DestinationRow == row && m.DestinationCol == col); if (chosen != null) { ProcessMoveResult(_game.ExecuteMove(_game.ChainJumpRow, _game.ChainJumpCol, chosen)); } } #endregion #region MoveResult private void ProcessMoveResult(MoveResult result) { ClearSelection(); switch (result.Type) { case MoveResult.ResultType.ChainJumpRequired: if (_game.ActivePlayer == _aiPlayer) { TriggerAiTurnAsync(); return; } _hasPieceSelected = true; _selectedRow = _game.ChainJumpRow; _selectedCol = _game.ChainJumpCol; RenderBoard(); StatusText.Text = "Chain jump — must continue!"; break; case MoveResult.ResultType.Victory: RenderBoard(); AnnounceWinner(result.Victor); break; case MoveResult.ResultType.TurnAdvanced: RenderBoard(); RefreshStatusBar(); if (_game.ActivePlayer == _aiPlayer) { TriggerAiTurnAsync(); } break; } } #endregion #region AI private async void TriggerAiTurnAsync() { _isAiThinking = true; StatusText.Text = "Bot is thinking..."; int[,] boardSnapshot = (int[,])_game.Board.Clone(); Move? bestMove = await Task.Run(() => _ai.GetBestMove(boardSnapshot)); _isAiThinking = false; if (bestMove == null) { AnnounceWinner(_humanPlayer); return; } int originRow = -1, originCol = -1; for (int r = 0; r < 8 && originRow == -1; r++) { for (int c = 0; c < 8 && originRow == -1; c++) { if (!BoardHelper.BelongsToPlayer(_game.Board[r, c], _aiPlayer)) continue; var moves = _game.GetLegalMoves(r, c, capturesOnly: true); if (moves.Count == 0) { moves = _game.GetLegalMoves(r, c, capturesOnly: false); } if (moves.Exists(m => m.DestinationRow == bestMove.DestinationRow && m.DestinationCol == bestMove.DestinationCol && m.CapturedPieceRow == bestMove.CapturedPieceRow && m.CapturedPieceCol == bestMove.CapturedPieceCol)) { originRow = r; originCol = c; } } } if (originRow == -1) return; ProcessMoveResult(_game.ExecuteMove(originRow, originCol, bestMove)); } #endregion #region UI private void RenderBoard() { _renderer.RenderBoard(_selectedRow, _selectedCol, _hasPieceSelected, _game.IsChainJumpActive); } private void ClearSelection() { _hasPieceSelected = false; _selectedRow = -1; _selectedCol = -1; } private void RefreshStatusBar() { if (_game.ActivePlayer == _aiPlayer) { StatusText.Text = "Bot's turn"; return; } string hint = _game.MandatoryCapturePieces.Count > 0 ? " — Capture is mandatory!" : ""; StatusText.Text = $"Your turn{hint}"; } private void AnnounceWinner(int victor) { _gameEnded = true; string winnerStr = victor == _humanPlayer ? "player" : "bot"; _db.EndGame(PlayerSession.CurrentGameId, winnerStr); string message = victor == _humanPlayer ? "You win!" : "Bot wins!"; StatusText.Text = message; MessageBox.Show(message, "Game Over", MessageBoxButton.OK, MessageBoxImage.None); } private void History_Click(object sender, RoutedEventArgs e) { var dialog = new HistoryDialog(); dialog.ShowDialog(); } private void Window_Closing(object sender, CancelEventArgs e) { if (PlayerSession.CurrentGameId != -1 && !_gameEnded) _db.EndGame(PlayerSession.CurrentGameId, "draw"); } private static void ParseCellTag(string tag, out int row, out int col) { var parts = tag.Split(','); row = int.Parse(parts[0]); col = int.Parse(parts[1]); } private void NewGame(object sender, RoutedEventArgs e) { InitializeNewGame(); } #endregion } }