public BoardState TryApplyMove(ChessMove newMove, out bool putsUserInCheck) { var newBoardState = new BoardState(this, newMove); if (newBoardState.KingIsCapturable()) { putsUserInCheck = true; return this; } else { putsUserInCheck = false; return newBoardState; } }
// Hypothetical board state where the turn color has not changed private BoardState(BoardState previousState, out bool isCheck, out bool isCheckMate) { BoardGrid = new ChessPiece[8, 8]; // Copy elements. for (int row = 0; row < 8; row++) { for (int column = 0; column < 8; column++) { Coordinate coordinate = new Coordinate(row, column); SetPieceAtCoordinate(previousState.GetPieceAtCoordinate(coordinate), coordinate); } } // Copy other board state values. BlackKingsideCastling = previousState.BlackKingsideCastling; BlackKingsideCastling = previousState.BlackQueensideCastling; WhiteKingsideCastling = previousState.WhiteKingsideCastling; WhiteQueensideCastling = previousState.WhiteQueensideCastling; TurnColor = previousState.TurnColor == ChessPieceColor.White ? ChessPieceColor.Black : ChessPieceColor.White; ; FullMove = previousState.FullMove; HalfMove = previousState.HalfMove; EnPassantTarget = new Coordinate(-1, -1); ParseLongAlgebraicNotation(""); InitializeAllPossibleMoves(); isCheck = KingIsCapturable(); isCheckMate = false; if (isCheck) { this.TurnColor = TurnColor == ChessPieceColor.White ? ChessPieceColor.Black : ChessPieceColor.White; InitializeAllPossibleMoves(); isCheckMate = true; for (int row = 0; row < 8 && isCheckMate; row++) { for (int column = 0; column < 8 && isCheckMate; column++) { var moves = PossibleMovesGrid[row, column]; if (moves != null) { foreach (var move in moves) { if (!new BoardState(this, move, false).KingIsCapturable()) { isCheckMate = false; break; } } } } } } }
// Make a new board state by applying a new move to an already existing board state. public BoardState(BoardState previousState, ChessMove newMove, bool lookForCheck = true) { BoardGrid = new ChessPiece[8, 8]; if (!newMove.KingsideCastle && !newMove.QueensideCastle) { HashSet<ChessMove> previousPossibleMoves = previousState.GetPossibleMoves(newMove.From); if (!previousPossibleMoves.Contains(newMove)) { throw new BoardStateException("Illegal move."); } } // Copy elements. for (int row = 0; row < 8; row++) { for (int column = 0; column < 8; column++) { Coordinate coordinate = new Coordinate(row, column); SetPieceAtCoordinate(previousState.GetPieceAtCoordinate(coordinate), coordinate); } } // Copy other board state values. BlackKingsideCastling = previousState.BlackKingsideCastling; BlackQueensideCastling = previousState.BlackQueensideCastling; WhiteKingsideCastling = previousState.WhiteKingsideCastling; WhiteQueensideCastling = previousState.WhiteQueensideCastling; // Turn color will be flipped and fullmove/halfmove will be incremented after move is applied. TurnColor = previousState.TurnColor; FullMove = previousState.FullMove; HalfMove = previousState.HalfMove; // Reset En Passant. EnPassantTarget = new Coordinate(-1, -1); // Castling special case. if (newMove.KingsideCastle || newMove.QueensideCastle) { int row = TurnColor == ChessPieceColor.White ? FirstRowWhite : FirstRowBlack; int rookStartColumn = newMove.KingsideCastle ? KingsideRookColumn : QueensideRookColumn; int kingEndColumn = newMove.KingsideCastle ? KingsideCastledKingColumn : QueensideCastledKingColumn; int rookEndColumn = newMove.KingsideCastle ? KingsideCastledRookColumn : QueensideCastledRookColumn; var kingStart = new Coordinate(row, KingStartColumn); var kingEnd = new Coordinate(row, kingEndColumn); var rookStart = new Coordinate(row, rookStartColumn); var rookEnd = new Coordinate(row, rookEndColumn); SetPieceAtCoordinate(new ChessPiece(ChessPieceColor.None, ChessPieceType.None), kingStart); SetPieceAtCoordinate(new ChessPiece(ChessPieceColor.None, ChessPieceType.None), rookStart); SetPieceAtCoordinate(new ChessPiece(TurnColor, ChessPieceType.King), kingEnd); SetPieceAtCoordinate(new ChessPiece(TurnColor, ChessPieceType.Rook), rookEnd); if (TurnColor == ChessPieceColor.White) { WhiteKingsideCastling = false; WhiteQueensideCastling = false; } else { BlackKingsideCastling = false; BlackQueensideCastling = false; } } // All other move types. else { // If en passant if (newMove.PieceType == ChessPieceType.Pawn) { if (previousState.EnPassantTarget.Equals(newMove.To)) { if (TurnColor == ChessPieceColor.White) { SetPieceAtCoordinate(new ChessPiece(ChessPieceColor.None, ChessPieceType.None), new Coordinate(newMove.To, -1, 0)); } else { SetPieceAtCoordinate(new ChessPiece(ChessPieceColor.None, ChessPieceType.None), new Coordinate(newMove.To, 1, 0)); } } // Mark if the new move triggers the possibilty of an En Passant from the following turn. int pawnDoubleFromRow = TurnColor == ChessPieceColor.White ? 1 : 6; int pawnDoubleToRow = TurnColor == ChessPieceColor.White ? 3 : 4; int enPassantTargetTargetRow = TurnColor == ChessPieceColor.White ? 2 : 5; if (newMove.From.Row == pawnDoubleFromRow && newMove.To.Row == pawnDoubleToRow) { EnPassantTarget = new Coordinate(enPassantTargetTargetRow, newMove.From.Column); } } // King movements disable castling. else if (newMove.PieceType == ChessPieceType.King) { if (TurnColor == ChessPieceColor.White) { WhiteKingsideCastling = false; WhiteQueensideCastling = false; } else { BlackKingsideCastling = false; BlackQueensideCastling = false; } } // Rook movements disable on their side. else if (newMove.PieceType == ChessPieceType.Rook) { if (TurnColor == ChessPieceColor.White) { if (newMove.From.Equals(new Coordinate(FirstRowWhite, KingsideRookColumn))) { WhiteKingsideCastling = false; } else if (newMove.From.Equals(new Coordinate(FirstRowWhite, QueensideRookColumn))) { WhiteQueensideCastling = false; } } else { if (newMove.From.Equals(new Coordinate(FirstRowBlack, KingsideRookColumn))) { BlackKingsideCastling = false; } else if (newMove.From.Equals(new Coordinate(FirstRowBlack, QueensideRookColumn))) { BlackQueensideCastling = false; } } } // Set square that the piece is moving from to empty, and moving to to have the piece. SetPieceAtCoordinate(new ChessPiece(ChessPieceColor.None, ChessPieceType.None), newMove.From); SetPieceAtCoordinate(new ChessPiece(TurnColor, newMove.IsPromotionToQueen ? ChessPieceType.Queen : newMove.PieceType), newMove.To); } // Reset or increment halfMove. if (newMove.IsCapture || newMove.PieceType == ChessPieceType.Pawn) { HalfMove = 0; } else { HalfMove++; } // Set applied move to be the previous move. PreviousMove = newMove; // Increment fullMove after blacks turn; if (TurnColor == ChessPieceColor.Black) { FullMove++; } // Switch turns. TurnColor = previousState.TurnColor == ChessPieceColor.White ? ChessPieceColor.Black : ChessPieceColor.White; bool isCheck = false; bool isCheckMate = false; if (lookForCheck) { new BoardState(this, out isCheck, out isCheckMate); PreviousMove = new ChessMove(PreviousMove.From, PreviousMove.To, PreviousMove.PieceType, PreviousMove.IsCapture, PreviousMove.IsPromotionToQueen, PreviousMove.DrawOfferExtended, isCheck, isCheckMate, PreviousMove.KingsideCastle, PreviousMove.QueensideCastle); } // Finally, determine the list of legal moves. InitializeAllPossibleMoves(); }
public MatchState(PlayerInfo opponent, BoardState boardState, bool selfIsWhite, string identifier) { this.Opponent = opponent; this.BoardState = boardState; this.SelfIsWhite = selfIsWhite; this.Identifier = identifier; }
void RefreshBoard(GameState.MatchState matchState, BoardState.Coordinate? selected) { CurrentMatchState = matchState; StatusText.text = string.Format("{0} ({1})", matchState.IsSelfTurn() ? "Your Turn" : "Their Turn", matchState.BoardState.TurnColor == BoardState.ChessPieceColor.White ? "white" : "black"); for (int r = 0; r < 8; r++) { for (int c = 0; c < 8; c++) { foreach (Transform child in TransformGrid[r, c]) { Destroy(child.gameObject); } } } if (matchState.BoardState.PreviousMove.IsCheckMate) { TitleText.text = "Game Finished"; StatusText.text = string.Format("{0} won!", matchState.IsSelfTurn() ? "They" : "You"); return; } HashSet<BoardState.ChessMove> moves; var moveDestinations = new HashSet<BoardState.Coordinate>(); if (selected.HasValue) { moves = matchState.BoardState.GetPossibleMoves(selected.Value); } else { moves = new HashSet<BoardState.ChessMove>(); } foreach (var move in moves) { moveDestinations.Add(move.To); } // Fill in pieces, except those that are move destinations for (int c = 0; c < 8; c++) { for (int r = 0; r < 8; r++) { var coordinate = new BoardState.Coordinate(r, c); bool isSelectedCoordinate = selected.HasValue && selected.Value.Equals(coordinate); bool isMoveDestination = moveDestinations.Contains(coordinate); BoardState.ChessPiece piece = matchState.BoardState.GetPieceAtCoordinate(coordinate); // pieces that are move destinations are handled later because clicking on them has movement behavior. if (piece.Type != BoardState.ChessPieceType.None && !isMoveDestination) { Color pieceHighlight = isSelectedCoordinate ? Color.green : Color.white; Image pieceImage = prefabToPieceImage(piece, pieceHighlight); pieceImage.transform.SetParent(TransformGrid[r, c], false); // Only allow player to make moves if it is their turn. Games against local // player allow both players to make a move on the same device. if (matchState.IsSelfTurn() || matchState.Opponent.IsLocalOpponent()) { pieceImage.GetComponent<Button>().onClick.AddListener(delegate { if (selected == null) { RefreshBoard(matchState, coordinate); } else if (selected.Value.Equals(coordinate)) { RefreshBoard(matchState, null); } else { RefreshBoard(matchState, coordinate); } }); } } } } // Fill in pieces that are move destinations foreach (var move in moves) { bool putsUserInCheck; var newMatchState = new GameState.MatchState( matchState.Opponent, matchState.BoardState.TryApplyMove(move, out putsUserInCheck), matchState.SelfIsWhite, matchState.Identifier); if (!putsUserInCheck) { bool isOpponentPiece = matchState.BoardState.GetPieceAtCoordinate(move.To).Type != BoardState.ChessPieceType.None; Color pieceHighlight = isOpponentPiece ? Color.red : Color.blue; BoardState.ChessPiece fromPiece = matchState.BoardState.GetPieceAtCoordinate(move.From); Image pieceImage = prefabToPieceImage(fromPiece, pieceHighlight); pieceImage.transform.SetParent(TransformGrid[move.To.Row, move.To.Column], false); // Only allow player to make moves if it is their turn. Games against local // player allow both players to make a move on the same device. if (matchState.IsSelfTurn() || matchState.Opponent.IsLocalOpponent()) { pieceImage.GetComponent<Button>().onClick.AddListener(delegate { // GameManager will handler whether or not we are saving a local or multiplayer // game, and will notify us of the updated state via our // CurrentMatchStateAvailableHandler StatusText.text = "Saving Match..."; GameManager.Instance.UpdateMatchState(newMatchState); }); } } } }
public Image prefabToPieceImage(BoardState.ChessPiece piece, Color color) { Image pieceSpritePrefab = null; switch (piece.Type) { case BoardState.ChessPieceType.Rook: pieceSpritePrefab = piece.Color == BoardState.ChessPieceColor.White ? WhiteRookPrefab : BlackRookPrefab; break; case BoardState.ChessPieceType.Knight: pieceSpritePrefab = piece.Color == BoardState.ChessPieceColor.White ? WhiteKnightPrefab : BlackKnightPrefab; break; case BoardState.ChessPieceType.Bishop: pieceSpritePrefab = piece.Color == BoardState.ChessPieceColor.White ? WhiteBishopPrefab : BlackBishopPrefab; break; case BoardState.ChessPieceType.Queen: pieceSpritePrefab = piece.Color == BoardState.ChessPieceColor.White ? WhiteQueenPrefab : BlackQueenPrefab; break; case BoardState.ChessPieceType.King: pieceSpritePrefab = piece.Color == BoardState.ChessPieceColor.White ? WhiteKingPrefab : BlackKingPrefab; break; case BoardState.ChessPieceType.Pawn: pieceSpritePrefab = piece.Color == BoardState.ChessPieceColor.White ? WhitePawnPrefab : BlackPawnPrefab; break; } var pieceImage = Instantiate(pieceSpritePrefab) as Image; float pieceWScale = 50 / pieceImage.rectTransform.rect.width; float pieceHScale = 50 / pieceImage.rectTransform.rect.height; pieceImage.rectTransform.localScale = new Vector3(pieceWScale, pieceHScale); pieceImage.rectTransform.localPosition = new Vector3(); pieceImage.color = color; return pieceImage; }