private OperationResult verifyMove(GameState gameState, int piecePosition, int newPiecePosition) { var oldSquare = gameState.Squares.GetSquare(piecePosition); var attacks = gameState.Attacks.GetPositionAttacksOnPosition(piecePosition, newPiecePosition); if (!attacks.Any()) { return(OperationResult.Fail($"Can't find an attack by this piece ({ oldSquare.Index } : { oldSquare.Piece.PieceType }) on this position ({ newPiecePosition }).")); } var badPawnAttacks = attacks.Where(a => a.AttackingSquare.Index == piecePosition && a.Index == newPiecePosition && a.Piece == null && a.MayOnlyMoveHereIfOccupiedByEnemy ); if (badPawnAttacks.Any()) { if ( badPawnAttacks.Count() > 1 || gameState.EnPassantTargetSquare == "-" || newPiecePosition != NotationEngine.CoordinateToPosition(gameState.EnPassantTargetSquare) ) { return(OperationResult.Fail($"This piece can only move here if the new square is occupied. ({ oldSquare.Index } : { oldSquare.Piece.PieceType }) on this position ({ newPiecePosition }).")); } } return(OperationResult.Ok()); }
public OperationResult <GameState> MakeMove(GameState gameState, string beginning, string destination, PieceType?piecePromotionType = null) { var pos1 = NotationEngine.CoordinateToPosition(beginning); var pos2 = NotationEngine.CoordinateToPosition(destination); return(this.MakeMove(gameState, pos1, pos2, piecePromotionType)); }
public void SetGameStateSnapshot(GameState oldGameState, GameState newGameState, StateInfo stateInfo, int piecePosition, int newPiecePosition) { var fenPosition = getFENPosition(newGameState.Squares); var castlingAvailability = getCastlingAvailability(newGameState, newGameState.CastlingAvailability, piecePosition, newPiecePosition); var enPassantCoord = getEnPassantCoord(newGameState.Squares, newGameState.ActiveColor, piecePosition, newPiecePosition); var newHalfmoveClock = getHalfmoveClock(oldGameState.Squares, oldGameState.HalfmoveClock, piecePosition, newPiecePosition); var activeColor = newGameState.ActiveColor.Reverse(); newGameState.PiecePlacement = fenPosition; newGameState.ActiveColor = activeColor; newGameState.CastlingAvailability = castlingAvailability; newGameState.EnPassantTargetSquare = enPassantCoord; if (enPassantCoord != "-") { newGameState.EnPassantTargetPosition = NotationEngine.CoordinateToPosition(enPassantCoord); } newGameState.HalfmoveClock = newHalfmoveClock; //better to calculate this value after setting the ActiveColor var fullmoveNumber = getFullmoveNumber(newGameState.FullmoveNumber, newGameState.ActiveColor); newGameState.FullmoveNumber = fullmoveNumber; var pgnMove = stateInfo.IsPawnPromotion ? _pgnService.SquarePairToPGNMove(oldGameState, oldGameState.ActiveColor, piecePosition, newPiecePosition, stateInfo.PawnPromotedTo) : _pgnService.SquarePairToPGNMove(oldGameState, oldGameState.ActiveColor, piecePosition, newPiecePosition); newGameState.PGNMoves.Add(pgnMove); newGameState.PGN = getUpdatedPGN(newGameState, oldGameState.ActiveColor, fullmoveNumber, pgnMove); }
private string squarePairToPGNMove(GameState gameState, Color playerColor, string startSquare, string endSquare) { var startPos = NotationEngine.CoordinateToPosition(startSquare); var endPos = NotationEngine.CoordinateToPosition(endSquare); return(squarePairToPGNMove(gameState, playerColor, startPos, endPos)); }
private (int position, char promotedPiece, bool isCastle) getPositionFromPGNMove(string pgnMove, Color playerColor, string enPassantTargetSquare) { var promotedPiece = '-'; var isCastle = castlePattern.IsMatch(pgnMove); if (isCastle) { var castleResult = getCastlePositionFromPGNMove(pgnMove, playerColor, promotedPiece); return(castleResult.position, castleResult.promotedPiece, true); } var isEnPassant = false; if (enPassantTargetSquare != "-") { var isCapture = pgnMove[1] == 'x'; if (isCapture) { var len = pgnMove.Length; if (len >= 6) { var rightSide = pgnMove.Substring(4, len - 4); isEnPassant = rightSide.Contains("ep") || rightSide.Contains("e.p."); if (isEnPassant) { return(NotationEngine.CoordinateToPosition(enPassantTargetSquare), promotedPiece, false); } } } } // could be a pawn promotion var pawnCapturePromotion = pawnCapturePromotionPattern.IsMatch(pgnMove); var pawnPromotion = pawnPromotionPattern.IsMatch(pgnMove); if (pawnCapturePromotion || pawnPromotion) { // yeah, it is a pawn promotion promotedPiece = pgnMove.Last(); if (promotedPiece == '+' || promotedPiece == '#') { promotedPiece = pgnMove.Substring(pgnMove.Length - 2, 1)[0]; } var dest = pawnCapturePromotion ? pgnMove.Substring(2, 2) : pgnMove.Substring(0, 2); return(NotationEngine.CoordinateToPosition(dest), promotedPiece, false); } // probably just a regular move pgnMove = pgnMove.Replace("x", "").Replace("+", "").Replace("#", ""); // not sure why I needed this var x = 2; if (pgnMove.Contains("=")) { x = 4; throw new Exception("I didn't think this could happen, so I tested it with an exception."); } return(NotationEngine.CoordinateToPosition(pgnMove.Substring(pgnMove.Length - x, 2)), promotedPiece, false); }
private Square getOriginationPositionForCastling(GameState gameState, Color color) { var rank = color == Color.White ? 1 : 8; var file = 4; var fileChar = NotationEngine.IntToFile(file); var coord = string.Concat(fileChar, rank); var origination = NotationEngine.CoordinateToPosition(coord); return(gameState.Squares.GetSquare(origination)); }
public OperationResult <StateInfo> GetStateInfo(GameState gameState, int piecePosition, int newPiecePosition) { var newActiveColor = gameState.ActiveColor.Reverse(); var stateInfo = new StateInfo(); var oldSquare = gameState.Squares.GetSquare(piecePosition); var isValidCastleAttempt = this.IsValidCastleAttempt(gameState, oldSquare, newPiecePosition, gameState.Attacks); if (isValidCastleAttempt.Success) { stateInfo.IsCastle = isValidCastleAttempt.Result; } else { return(OperationResult <StateInfo> .Fail(isValidCastleAttempt.Message)); } stateInfo.IsPawnPromotion = this.isPawnPromotion(oldSquare, newPiecePosition); if (gameState.EnPassantTargetSquare != "-" && oldSquare.Piece.PieceType == PieceType.Pawn) { var pawnEnPassantPosition = NotationEngine.CoordinateToPosition(gameState.EnPassantTargetSquare); if (newPiecePosition == pawnEnPassantPosition) { stateInfo.IsEnPassant = true; } } var isResign = false; var isDraw = false; // todo: i don't think we can get here // also, this is incomplete if (isDraw || isResign) { if (isDraw) { var score = string.Concat(" ", "1/2-1/2"); gameState.PGN += score; } if (isResign) { var score = string.Concat(" ", newActiveColor == Color.White ? "1-0" : "0-1"); gameState.PGN += score; } } return(OperationResult <StateInfo> .Ok(stateInfo)); }
private void getPawnAttacks(GameState gameState, Square square, List <AttackedSquare> accumulator) { var squares = gameState.Squares; var position = square.Index; var pieceColor = square.Piece.Color; var coord = NotationEngine.PositionToCoordinate(position); int file = NotationEngine.FileToInt(coord[0]); int rank = NotationEngine.PositionToRankInt(position); var directionIndicator = pieceColor == Color.White ? 1 : -1; var homeRankIndicator = pieceColor == Color.White ? 2 : 7; var nextRank = (rank + directionIndicator); var aheadOneRankPosition = NotationEngine.CoordinatePairToPosition(file, nextRank); var aheadOneRankSquare = squares.GetSquare(aheadOneRankPosition); var attacks = new List <AttackedSquare>(); if (!aheadOneRankSquare.Occupied) { //can't attack going forward attacks.Add(new AttackedSquare(square, aheadOneRankSquare, true)); } managePawnAttacks(squares, square, pieceColor, file, rank, directionIndicator, homeRankIndicator, nextRank, attacks, aheadOneRankSquare.Occupied); //add en passant position: -1 indicates null here if (gameState.EnPassantTargetSquare != "-") { var enPassantTargetPosition = NotationEngine.CoordinateToPosition(gameState.EnPassantTargetSquare); var leftPos = NotationEngine.CoordinatePairToPosition(file - 1, nextRank); var rightPos = NotationEngine.CoordinatePairToPosition(file + 1, nextRank); if (enPassantTargetPosition == leftPos || enPassantTargetPosition == rightPos) { var enPassantSquare = squares.GetSquare(enPassantTargetPosition); attacks.Add(new AttackedSquare(square, enPassantSquare)); } } if (attacks.Any()) { accumulator.AddRange(attacks); } }
public static Snapshot Create(string fen) { // 5 could be a bad number if (fen.Length < 5 || !fen.Contains(' ')) { return(null); } var fenRecord = new Snapshot(); var gameData = fen.Split(' '); fenRecord.PiecePlacement = gameData[0]; fenRecord.ActiveColor = gameData[1][0] == 'w' ? Color.White : Color.Black; fenRecord.CastlingAvailability = gameData[2]; fenRecord.EnPassantTargetSquare = gameData[3]; if (fenRecord.EnPassantTargetSquare != "-") { fenRecord.EnPassantTargetPosition = NotationEngine.CoordinateToPosition(fenRecord.EnPassantTargetSquare); } var enPassantTargetPosition = -1; Int32.TryParse(gameData[3], out enPassantTargetPosition); fenRecord.EnPassantTargetPosition = enPassantTargetPosition; int halfmoveClock = 0; Int32.TryParse(gameData[4], out halfmoveClock); fenRecord.HalfmoveClock = halfmoveClock; int fullmoveNumber = 0; Int32.TryParse(gameData[5], out fullmoveNumber); fenRecord.FullmoveNumber = fullmoveNumber; return(fenRecord); }
// duplicated code? //public bool IsDiagonalMove(int startPosition, int endPosition) //{ // var startMod = startPosition % 8; // var endMod = endPosition % 8; // var modDiff = Math.Abs(startMod - endMod); // var startRow = NotationUtility.PositionToRankInt(startPosition); // var endRow = NotationUtility.PositionToRankInt(endPosition); // var rowDiff = Math.Abs(startRow - endRow); // if (modDiff == rowDiff) // { // return true; // } // return false; //} // var remainingKingAttacks = enemyKingAttacks.Except(opponentAttacks); // if (remainingKingAttacks.Any()) { // kingHasEscape = true; // } // if (kingHasEscape) { // return false; // } // //make sure that interposition is not possible // var attackers = opponentAttacks.Where(a => a.Index == enemyKingPosition.Index); // //if there are no attackers there cannot be a single interposition that saves the king // if (attackers == null || !attackers.Any() || attackers.Count() > 1) { // return true; // } // var attacker = attackers.FirstOrDefault(); // var theAttack = this.AttackService.GetKingAttack(attacker, gameState, enemyKingPosition); // var interposers = friendlyAttacks.ToList().Intersect(theAttack); // if (interposers.Any()) { // return false; // } // //there were no friendlies to save the king, checkmate is true // return true; //} public bool IsEnPassant(Square square, int newPiecePosition, string enPassantTargetSquare) { if (enPassantTargetSquare == "-") { return(false); } var piece = square.Piece; if (piece.PieceType != PieceType.Pawn) { return(false); } //only pawns can perform en passant var enPassantPosition = NotationEngine.CoordinateToPosition(enPassantTargetSquare); if (enPassantPosition != newPiecePosition) { return(false); } //if we're not moving to the en passant position, this is not en passant var moveDistance = Math.Abs(square.Index - newPiecePosition); if (!new List <int> { 7, 9 }.Contains(moveDistance)) { return(false); } //is this a diagonal move? if (piece.Color == Color.Black && square.Index < newPiecePosition) { return(false); } //black can't move up if (piece.Color == Color.White && square.Index > newPiecePosition) { return(false); } //white can't move down return(true); }
public Square GetCurrentPositionFromPGNMove(GameState gameState, Piece piece, int newPiecePosition, string pgnMove, bool isCastle) { if (isCastle) { return(getOriginationPositionForCastling(gameState, piece.Color)); } // adding !a.MayOnlyMoveHereIfOccupiedByEnemy fixed the test I was working on, but there may be a deeper issue here. var potentialSquares = gameState.Attacks.Where(a => a.Index == newPiecePosition && ( a.Occupied || (!a.Occupied && !a.MayOnlyMoveHereIfOccupiedByEnemy) ) && a.AttackingSquare.Piece.PieceType == piece.PieceType && a.AttackingSquare.Piece.Color == piece.Color ); if (!potentialSquares.Any()) { // this could be a pawn en passant if ( piece.PieceType == PieceType.Pawn && gameState.EnPassantTargetSquare != "-" && NotationEngine.CoordinateToPosition(gameState.EnPassantTargetSquare) == newPiecePosition ) { var enPassant = gameState.Attacks.Where(a => a.Index == newPiecePosition && a.AttackingSquare.Piece.PieceType == piece.PieceType && a.AttackingSquare.Piece.Color == piece.Color ).First().AttackingSquare; return(enPassant); } var msg = $"No squares found. PGN Move: { pgnMove }"; throw new Exception(msg); } if (potentialSquares.Count() == 1) { return(potentialSquares.First().AttackingSquare); } // differentiate var potentialPositions = from s in gameState.Squares join p in potentialSquares on s.Index equals p.AttackingSquare.Index where s.Piece.Identity == piece.Identity select p; if (!potentialPositions.Any()) { var msg = $"Attempting to differentiate. No squares found. PGN Move: { pgnMove }"; throw new Exception(msg); } // x means capture and shouldn't be used in the equation below var capture = isCapture(pgnMove); var check = isCheck(pgnMove); // todo: refactor to eliminate redundancy // look at the beginning of the pgnMove string to determine which of the pieces are the one that should be moved. // this should only happen if there are two pieces of the same type that can attack here. var newPgnMove = pgnMove.Replace("x", "").Replace("+", ""); var moveLength = newPgnMove.Length; switch (moveLength) { case 2: return(pgnLength2(piece, potentialPositions, capture)); case 3: // this should be a pawn attack that can be made by two pawns // this can also be a pawn promotion return(pgnLength3(potentialPositions, newPgnMove)); case 4: // this would be any other piece return(pgnLength4(gameState, potentialPositions, newPgnMove)); case 5: // we have rank and file, so just find the piece. this should be very rare return(pgnLength5(gameState, newPgnMove)); default: throw new Exception("Failed to find square."); } }