From f6f3bbbc5b7741ad0db3c88a398cfc3943988529 Mon Sep 17 00:00:00 2001 From: "Ramaekers,Aldrik A.N" Date: Wed, 16 Sep 2020 09:42:04 +0200 Subject: work --- ChessBoard.cs | 618 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 455 insertions(+), 163 deletions(-) (limited to 'ChessBoard.cs') diff --git a/ChessBoard.cs b/ChessBoard.cs index 487ec29..d594d22 100644 --- a/ChessBoard.cs +++ b/ChessBoard.cs @@ -1,163 +1,455 @@ -using Chess.Pieces; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace Chess -{ - public class ChessBoard - { - const int boardSize = 8; - - private float tileWidth; - private float tileHeight; - - internal BoardTile[,] tiles; - private PictureBox container; - - private BoardTile selectedTile = null; - - private bool screenInvalidated = true; - - public ChessBoard(PictureBox container) - { - this.container = container; - - GenerateBoard(); - GeneratePieces(); - - DrawGame(); - } - - public void HandleResize() - { - DrawGame(); - } - - public void GeneratePieces() - { - tiles[0, 0].OccupyingPiece = new Rook(false); - tiles[0, 1].OccupyingPiece = new Knight(false); - tiles[0, 2].OccupyingPiece = new Bishop(false); - tiles[4,4].OccupyingPiece = new Queen(false); - tiles[0, 4].OccupyingPiece = new King(false); - tiles[0, 5].OccupyingPiece = new Bishop(false); - tiles[0, 6].OccupyingPiece = new Knight(false); - tiles[0, 7].OccupyingPiece = new Rook(false); - - for (int x = 0; x < boardSize; x++) - tiles[1, x].OccupyingPiece = new Pawn(false); - - for (int x = 0; x < boardSize; x++) - tiles[6, x].OccupyingPiece = new Pawn(true); - - tiles[7, 0].OccupyingPiece = new Rook(true); - tiles[7, 1].OccupyingPiece = new Knight(true); - tiles[7, 2].OccupyingPiece = new Bishop(true); - tiles[4,5].OccupyingPiece = new Queen(true); - tiles[3, 3].OccupyingPiece = new King(true); - tiles[7, 5].OccupyingPiece = new Bishop(true); - tiles[7, 6].OccupyingPiece = new Knight(true); - tiles[7, 7].OccupyingPiece = new Rook(true); - } - - public ChessPiece PieceAt(int x, int y) - { - if (x >= 0 && y >= 0 && x < boardSize && y < boardSize) - { - return tiles[y, x].OccupyingPiece; - } - - return null; - } - - public void GenerateBoard() - { - tiles = new BoardTile[boardSize, boardSize]; - - for (int y = 0; y < boardSize; y++) - { - for (int x = 0; x < boardSize; x++) - { - tiles[y, x] = new BoardTile(x, y); - } - } - } - - public void DrawTile(Point point) - { - if (point.X >= 0 && point.X < boardSize && point.Y >= 0 && point.Y < boardSize) - { - var tile = tiles[point.Y, point.X]; - - using (Graphics g = (screenInvalidated ? Graphics.FromImage(container.Image) : container.CreateGraphics())) - { - tile.Draw(g, tileWidth, tileHeight, (selectedTile != null && selectedTile.CanMoveTo(this, tile))); - } - } - } - - public void DrawGame() - { - screenInvalidated = true; - - if (container.Image != null) - container.Image.Dispose(); - - container.Image = new Bitmap(container.Size.Width, container.Size.Height); - - tileWidth = container.Size.Width / (float)boardSize; - tileHeight = container.Size.Height / (float)boardSize; - - for (int y = 0; y < boardSize; y++) - { - for (int x = 0; x < boardSize; x++) - { - DrawTile(new Point(x, y)); - } - } - - screenInvalidated = false; - } - - public Point MouseToTilePosition(int x, int y) - { - return new Point((int)(x / tileWidth), (int)(y / tileHeight)); - } - - private void MoveSelectedPieceTo(BoardTile tile) - { - if (selectedTile.CanMoveTo(this, tile)) - { - tile.OccupyingPiece = selectedTile.OccupyingPiece; - selectedTile.OccupyingPiece = null; - } - } - - public void SelectTile(Point point) - { - var clickedTile = tiles[point.Y, point.X]; - - if (selectedTile != clickedTile) - { - if (selectedTile != null) - { - MoveSelectedPieceTo(clickedTile); - } - - selectedTile = clickedTile; - } - else - { - selectedTile = null; - } - - DrawGame(); - } - } -} +using Chess.Events; +using Chess.Pieces; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Chess +{ + public class ChessBoard + { + public const int BoardSize = 8; + + private float tileWidth; + private float tileHeight; + + private MainForm container = null; + private bool screenInvalidated = true; + private List latestTiles = new List(); + private RoundFinishedEvent currentRountData = new RoundFinishedEvent(1); + private int movesMadeWithoutProgress = 60; + + public BoardTile[,] Tiles; + public BoardTile SelectedTile = null; + + /// + /// Event that is evoked when the game has ended. + /// + public event EventHandler OnGameFinished; + + /// + /// Event that is evoked after drawing the board. + /// + public event EventHandler OnDraw; + + /// + /// Event that is evoked after a round has been played. + /// + public event EventHandler OnRoundFinished; + + public ChessBoard(MainForm container) + { + this.container = container; + + GenerateBoard(); + GeneratePieces(); + + DrawGame(); + } + + /// + /// Handle the window resize event. + /// + public void HandleResize() + { + DrawGame(); + } + + /// + /// Generate the pieces. + /// + public void GeneratePieces() + { +#if false + Tiles[0, 0].OccupyingPiece = new Rook(false); + Tiles[0, 1].OccupyingPiece = new Knight(false); + Tiles[0, 2].OccupyingPiece = new Bishop(false); + Tiles[0, 3].OccupyingPiece = new Queen(false); + Tiles[0, 4].OccupyingPiece = new King(false); + Tiles[0, 5].OccupyingPiece = new Bishop(false); + Tiles[0, 6].OccupyingPiece = new Knight(false); + Tiles[0, 7].OccupyingPiece = new Rook(false); + + for (int x = 0; x < BoardSize; x++) + Tiles[1, x].OccupyingPiece = new Pawn(false); + + for (int x = 0; x < BoardSize; x++) + Tiles[6, x].OccupyingPiece = new Pawn(true); + + Tiles[7, 0].OccupyingPiece = new Rook(true); + Tiles[7, 1].OccupyingPiece = new Knight(true); + Tiles[7, 2].OccupyingPiece = new Bishop(true); + Tiles[7, 3].OccupyingPiece = new Queen(true); + Tiles[7, 4].OccupyingPiece = new King(true); + Tiles[7, 5].OccupyingPiece = new Bishop(true); + Tiles[7, 6].OccupyingPiece = new Knight(true); + Tiles[7, 7].OccupyingPiece = new Rook(true); +#endif +#if false + Tiles[0, 7].OccupyingPiece = new King(false); + + Tiles[1, 4].OccupyingPiece = new Queen(true); + Tiles[2, 6].OccupyingPiece = new King(true); +#endif +#if true + Tiles[0, 4].OccupyingPiece = new King(false); + + Tiles[1, 1].OccupyingPiece = new Pawn(true); + Tiles[7, 4].OccupyingPiece = new King(true); +#endif + } + + /// + /// Get the piece at the given tile location. + /// + /// + /// + public ChessPiece PieceAt(Point point) + { + return PieceAt(point.X, point.Y); + } + + /// + /// Get the piece at the given tile location. + /// + /// + /// + /// + public ChessPiece PieceAt(int x, int y) + { + if (x >= 0 && y >= 0 && x < BoardSize && y < BoardSize) + { + return Tiles[y, x].OccupyingPiece; + } + + return null; + } + + /// + /// Get tiles by the given type of the occupying type and color. + /// + /// + /// + /// + public List TilesByPieceType(Type piece, bool isWhite) + { + List result = new List(); + + for (int y = 0; y < ChessBoard.BoardSize; y++) + { + for (int x = 0; x < ChessBoard.BoardSize; x++) + { + var found = PieceAt(x, y); + + if (found != null && found.GetType() == piece && found.IsWhite == isWhite) + { + result.Add(Tiles[y, x]); + } + } + } + + return result; + } + + /// + /// Get a piece by the given type and color. + /// + /// + /// + /// + public ChessPiece PieceByType(Type piece, bool isWhite) + { + for (int y = 0; y < ChessBoard.BoardSize; y++) + { + for (int x = 0; x < ChessBoard.BoardSize; x++) + { + var found = PieceAt(x, y); + if (found != null && found.GetType() == piece && found.IsWhite == isWhite) + { + return found; + } + } + } + + return null; + } + + /// + /// Generate the chess board. + /// + public void GenerateBoard() + { + Tiles = new BoardTile[BoardSize, BoardSize]; + + for (int y = 0; y < BoardSize; y++) + { + for (int x = 0; x < BoardSize; x++) + { + Tiles[y, x] = new BoardTile(x, y); + } + } + } + + /// + /// Draw the tile at the given point. + /// + /// + public void DrawTile(Point point) + { + if (point.X >= 0 && point.X < BoardSize && point.Y >= 0 && point.Y < BoardSize) + { + var tile = Tiles[point.Y, point.X]; + + using (Graphics g = (screenInvalidated ? Graphics.FromImage(container.chessBoardBitmap.Image) : container.chessBoardBitmap.CreateGraphics())) + { + tile.State = TileState.Normal; + + if (latestTiles.Contains(tile)) + tile.State |= TileState.Latest; + + if (SelectedTile != null && SelectedTile.CanMoveTo(this, tile)) + tile.State |= TileState.Targeted; + + if (tile == SelectedTile) + tile.State |= TileState.Selected; + + tile.Draw(g, tileWidth, tileHeight); + } + } + } + + /// + /// Invalidate the current frame and draw + display a new frame. + /// + public void DrawGame() + { + screenInvalidated = true; + + if (container.chessBoardBitmap.Image != null) + container.chessBoardBitmap.Image.Dispose(); + + container.chessBoardBitmap.Image = new Bitmap(container.chessBoardBitmap.Size.Width, container.chessBoardBitmap.Size.Height); + + tileWidth = container.chessBoardBitmap.Size.Width / (float)BoardSize; + tileHeight = container.chessBoardBitmap.Size.Height / (float)BoardSize; + + for (int y = 0; y < BoardSize; y++) + { + for (int x = 0; x < BoardSize; x++) + { + DrawTile(new Point(x, y)); + } + } + + OnDraw?.Invoke(this, null); + + screenInvalidated = false; + } + + /// + /// Convert mouse position relative to window to tile coordinates. + /// + /// Mouse X + /// Mouse Y + /// + public Point MouseToTilePosition(int x, int y) + { + return new Point((int)(x / tileWidth), (int)(y / tileHeight)); + } + + /// + /// Checks whether the given piece can move to any spot. + /// + /// + /// + private bool CanMoveToAnything(BoardTile tile) + { + for (int y = 0; y < BoardSize; y++) + { + for (int x = 0; x < BoardSize; x++) + { + if (tile.OccupyingPiece != null && tile.CanMoveTo(this, Tiles[y, x])) + { + return true; + } + } + } + + return false; + } + + private bool IsCheckmate(Player player) + { + bool kingUnderAttack = false; + bool canMove = false; + var king = TilesByPieceType(typeof(King), !player.IsWhite).FirstOrDefault(); + + for (int y = 0; y < BoardSize; y++) + { + for (int x = 0; x < BoardSize; x++) + { + if (Tiles[y, x].OccupyingPiece?.IsWhite == player.IsWhite) + { + if (Tiles[y, x].OccupyingPiece != null && Tiles[y, x].CanMoveTo(this, king)) + { + kingUnderAttack = true; + } + } + else if (CanMoveToAnything(Tiles[y, x])) canMove = true; + } + } + + return kingUnderAttack && !canMove; + } + + private bool IsStalemate(bool moveMadeByWhite) + { + if (IsKingAlone(!moveMadeByWhite)) + { + if (!CanMoveToAnything(TilesByPieceType(typeof(King), !moveMadeByWhite).FirstOrDefault())) return true; + } + + return false; + } + + private bool IsKingAlone(bool isWhite) + { + if (PieceByType(typeof(Pawn), isWhite) != null) return false; + if (PieceByType(typeof(Rook), isWhite) != null) return false; + if (PieceByType(typeof(Knight), isWhite) != null) return false; + if (PieceByType(typeof(Bishop), isWhite) != null) return false; + if (PieceByType(typeof(Queen), isWhite) != null) return false; + + return true; + } + + private bool EnoughMaterialOnBoard() + { + if (IsKingAlone(true) && IsKingAlone(false)) return false; + + return true; + } + + /// + /// Try to move the currently selected piece to the given board tile. + /// + /// + /// Returns true if movement was successfull. + private bool MoveSelectedPieceTo(BoardTile tile, Player player) + { + if (SelectedTile.CanMoveTo(this, tile)) + { + // Check if current move is a castling move + if (SelectedTile.OccupyingPiece != null && SelectedTile.OccupyingPiece.GetType() == typeof(King) && + tile.OccupyingPiece != null && tile.OccupyingPiece.GetType() == typeof(Rook) && + tile.OccupyingPiece.IsWhite == SelectedTile.OccupyingPiece.IsWhite) + { + var rookDestination = ((Rook)tile.OccupyingPiece).GetCastleLocation(this, tile); + rookDestination.OccupyingPiece = tile.OccupyingPiece; + tile.OccupyingPiece = null; + + tile = ((King)SelectedTile.OccupyingPiece).GetCastleLocation(this, SelectedTile, tile); + } + + currentRountData.AddMove(new PlayerMove(player, SelectedTile, tile)); + + latestTiles.Clear(); + latestTiles.Add(tile); + latestTiles.Add(SelectedTile); + + if (tile.OccupyingPiece == null && SelectedTile.OccupyingPiece.GetType() != typeof(Pawn)) + { + movesMadeWithoutProgress++; + } + else + { + movesMadeWithoutProgress = 0; + } + + tile.OccupyingPiece = SelectedTile.OccupyingPiece; + SelectedTile.OccupyingPiece.PostMovementEvent(tile); + SelectedTile.OccupyingPiece = null; + + if (IsCheckmate(player)) + { + OnGameFinished?.Invoke(this, new GameFinishedEvent(player, GameFinishedReason.CheckMate)); + } + else if (!EnoughMaterialOnBoard()) + { + OnGameFinished?.Invoke(this, new GameFinishedEvent(null, GameFinishedReason.InsufficientMaterial)); + } + else if (IsStalemate(player.IsWhite)) + { + OnGameFinished?.Invoke(this, new GameFinishedEvent(null, GameFinishedReason.Stalemate)); + } + else if (movesMadeWithoutProgress >= 75) + { + OnGameFinished?.Invoke(this, new GameFinishedEvent(null, GameFinishedReason.Move75)); + } + + if (currentRountData.IsDone) + { + OnRoundFinished?.Invoke(this, currentRountData); + currentRountData = new RoundFinishedEvent(currentRountData.Round + 1); + } + + return true; + } + + return false; + } + + /// + /// Select a tile. + /// + /// + /// Returns true if the turn should be ended for current player. + public bool SelectTile(Point point, Player player) + { + bool result = false; + + var clickedTile = Tiles[point.Y, point.X]; + + if (SelectedTile != clickedTile) + { + if (SelectedTile != null) + { + if (MoveSelectedPieceTo(clickedTile, player)) + { + SelectedTile = null; + result = true; + } + else + { + // Only allow selection if player's own pieces. + if (player.IsWhite == clickedTile.OccupyingPiece?.IsWhite) + SelectedTile = clickedTile; + else + SelectedTile = null; + } + } + else + { + // Only allow selection if player's own pieces. + if (player.IsWhite == clickedTile.OccupyingPiece?.IsWhite) + SelectedTile = clickedTile; + else + SelectedTile = null; + } + } + else + { + SelectedTile = null; + } + + DrawGame(); + + return result; + } + } +} -- cgit v1.2.3-70-g09d2