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 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)); }
private Square pgnLength4(GameState gameState, IEnumerable <AttackedSquare> potentialPositions, string newPgnMove) { var ambiguityResolver = newPgnMove[1]; var isRank = IsRank(ambiguityResolver); // this could be either a rank or a file List <int> ambiguityResolutionSet; if (isRank) { int rank = 0; Int32.TryParse(ambiguityResolver.ToString(), out rank); ambiguityResolutionSet = this._orthogonalService.GetEntireRank(rank - 1);// needs to be using zero-based rank offset } else { var iFile = NotationEngine.FileToInt(ambiguityResolver); ambiguityResolutionSet = this._orthogonalService.GetEntireFile(iFile); } var intersection = potentialPositions.Select(a => a.AttackingSquare.Index).Intersect(ambiguityResolutionSet); if (intersection.Count() > 1) { throw new Exception("There should not be more than one item found here."); } return(gameState.Squares.GetSquare(intersection.First())); }
private string squarePairToPGNMove(GameState gameState, Color playerColor, int startPos, int endPos) { var destinationSquare = gameState.Squares.GetSquare(endPos); var isCapture = destinationSquare.Occupied && destinationSquare.Piece.Color != playerColor; var attacks = gameState.Attacks.Where(a => a.AttackingSquare.Index == startPos); if (attacks == null || !attacks.Any() || !attacks.Any(a => a.Index == endPos)) { throw new Exception("No attacks can be made on this ending square."); } var square = gameState.Squares.GetSquare(startPos); if (!square.Occupied) { throw new Exception("Bad coordinates were given."); } var piece = square.Piece; if (piece.Color != playerColor) { throw new Exception("Color doesn't match given positions."); } var notationPiece = char.ToUpper(piece.Identity); var coord = NotationEngine.PositionToCoordinate(endPos); var pgnMove = getPgnMove(notationPiece, piece, coord, startPos, endPos, isCapture, gameState); return(pgnMove); }
private bool isValidKnightMove(int position, int tempPosition, int file, int rank) { var isValidCoordinate = GeneralEngine.IsValidCoordinate(tempPosition); if (!isValidCoordinate) { return(false); } var tempCoord = NotationEngine.PositionToCoordinate(tempPosition); var tempFile = NotationEngine.FileToInt(tempCoord[0]); var tempRank = (int)tempCoord[1]; var fileDiff = Math.Abs(tempFile - file); var rankDiff = Math.Abs(tempRank - rank); if (fileDiff > 2 || fileDiff < 1) { return(false); } if (rankDiff > 2 || rankDiff < 1) { return(false); } return(true); }
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 pgnLength5(GameState gameState, string newPgnMove) { var _file = NotationEngine.FileToInt(newPgnMove[1]); var _rank = 0; Int32.TryParse(newPgnMove[2].ToString(), out _rank); var pos = NotationEngine.CoordinatePairToPosition(_file, _rank); return(gameState.Squares.GetSquare(pos)); }
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)); }
private Square pgnLength3(IEnumerable <AttackedSquare> potentialPositions, string newPgnMove) { var ambiguityResolver = newPgnMove[0]; var files = this._orthogonalService.GetEntireFile(NotationEngine.FileToInt(ambiguityResolver)); // this will always be a file if this is a pawn var potentialSquares = potentialPositions.Where(a => files.Contains(a.AttackingSquare.Index)).ToList(); if (potentialSquares.Count() > 1) { throw new Exception("There should not be more than one item found here."); } return(potentialSquares.First().AttackingSquare); }
public OperationResult <GameState> MakeMove(GameState gameState, string pgnMove) { var pair = _pgnService.PGNMoveToSquarePair(gameState, pgnMove); //todo: what about piece promotion? if (pair.promotedPiece == '-') { return(this.MakeMove(gameState, pair.piecePosition, pair.newPiecePosition)); } var promotedPieceType = NotationEngine.GetPieceTypeFromCharacter(pair.promotedPiece); return(this.MakeMove(gameState, pair.piecePosition, pair.newPiecePosition, promotedPieceType)); }
private void getPawnDiagonalAttack(List <Square> squares, Square square, Color pieceColor, int fileIndicator, int nextRank, List <AttackedSquare> attacks) { var pos = NotationEngine.CoordinatePairToPosition(fileIndicator, nextRank); var attackedSquare = squares.GetSquare(pos); if (attackedSquare.Occupied && attackedSquare.Piece.Color != pieceColor) { attacks.Add(new AttackedSquare(square, attackedSquare, false, true)); } else { attacks.Add(new AttackedSquare(square, attackedSquare, false, true, true)); } }
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 string getEnPassantCoord(List <Square> squares, Color activeColor, int piecePosition, int newPiecePosition) { var piece = squares.GetPiece(newPiecePosition); if (piece.PieceType != PieceType.Pawn) { return("-"); } var diff = Math.Abs(piecePosition - newPiecePosition); if (diff != 16) {//16 is two rows return("-"); } var moveMarker = activeColor == Color.White ? 8 : -8; var enPassantSquare = piecePosition + moveMarker; return(NotationEngine.PositionToCoordinate(enPassantSquare)); }
private string getPGNMoveBeginState(char notationPiece, string coord, int startPos, int endPos, bool isCapture) { string pgnMove = string.Empty; switch (notationPiece) { case 'P': if (isCapture) { var file = NotationEngine.PositionToFileChar(startPos); pgnMove = string.Concat(file, coord); } else { pgnMove = coord; } break; case 'K': var moveDiff = startPos - endPos; switch (moveDiff) { case -2: pgnMove = "O-O"; break; case 2: pgnMove = "O-O-O"; break; default: pgnMove = string.Concat(notationPiece, coord); break; } break; default: pgnMove = string.Concat(notationPiece, coord); break; } return(pgnMove); }
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 List <Square> GetSquares(Snapshot fen) { var squares = new List <Square>(); var rows = fen.PiecePlacement.Split('/'); for (int i = 0; i < 8; i++) { int rowIndex = 7 - i; //leftSideIndex is the left side of the board, in numbers: 0 8 16 24 32 40 48 56 int leftSideIndex = 8 * (rowIndex); int charIndex = 0; var row = rows[i]; foreach (char c in row) { if (char.IsNumber(c)) { int advanceSquare = 0; Int32.TryParse(c.ToString(), out advanceSquare); //gotta make empty squares for (int j = 0; j < advanceSquare; j++) { var index = leftSideIndex + charIndex + j; squares.Add( new Square(index, NotationEngine.PositionToCoordinate(index), null) ); } //in FEN we move ahead the number of squares that the number says charIndex = charIndex + advanceSquare; } else { var index = leftSideIndex + charIndex; squares.Add( new Square(index, NotationEngine.PositionToCoordinate(index), NotationEngine.GetPieceFromCharacter(c)) ); charIndex++; } } } return(squares.OrderBy(a => a.Index).ToList()); }
private void getKnightAttacks(GameState gameState, Square square, List <AttackedSquare> accumulator) { var squares = gameState.Squares; var currentPosition = square.Index; var pieceColor = square.Piece.Color; var attacks = new List <AttackedSquare>(); var coord = NotationEngine.PositionToCoordinate(currentPosition); var file = NotationEngine.FileToInt(coord[0]); var rank = (int)coord[1]; var potentialPositions = new List <int> { 6, 10, 15, 17, -6, -10, -15, -17 }; foreach (var potentialPosition in potentialPositions) { var position = currentPosition + potentialPosition; var _isValidKnightMove = isValidKnightMove(currentPosition, position, file, rank); var _isValidMove = isValidMove(accumulator, gameState, square, position, pieceColor); var _isValidCoordinate = GeneralEngine.IsValidCoordinate(position); if (!_isValidKnightMove || !_isValidMove.IsValid || !_isValidCoordinate) { continue; } var attackedSquare = squares.GetSquare(position); if (!attackedSquare.Occupied) { attacks.Add(new AttackedSquare(square, attackedSquare)); } else { attacks.Add(new AttackedSquare(square, attackedSquare, isProtecting: attackedSquare.Piece.Color == pieceColor)); } } 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); }
private void managePawnAttacks(List <Square> squares, Square square, Color pieceColor, int file, int rank, int directionIndicator, int homeRankIndicator, int nextRank, List <AttackedSquare> attacks, bool aheadOneRankSquareOccupied) { var notOnFarLeftFile = file - 1 >= 0; var notOnFarRightFile = file + 1 <= 7; if (notOnFarLeftFile) { //get attack square on left var fileIndicator = file - 1; getPawnDiagonalAttack(squares, square, pieceColor, fileIndicator, nextRank, attacks); } if (notOnFarRightFile) { //get attack square on right var fileIndicator = file + 1; getPawnDiagonalAttack(squares, square, pieceColor, fileIndicator, nextRank, attacks); } //can't move ahead two if the one in front of you is blocked. if (aheadOneRankSquareOccupied) { return; } //have to plus one here because rank is zero based and coordinate is base 1 //if this pawn is on it's home rank, then add a second attack square. var isOnHomeRank = rank + 1 == homeRankIndicator; if (isOnHomeRank) { var forwardOne = nextRank + directionIndicator; var rankForwardPosition = NotationEngine.CoordinatePairToPosition(file, forwardOne); var rankForwardSquare = squares.GetSquare(rankForwardPosition); //pawns don't attack forward, so we don't have attacks when people occupy ahead of us if (!rankForwardSquare.Occupied) { attacks.Add(new AttackedSquare(square, rankForwardSquare, true)); } } }
private int getOrthogonalLineTerminator(Direction direction, int position) { var file = NotationEngine.PositionToFileInt(position); var rank = NotationEngine.PositionToRankInt(position); var iterator = getIteratorByDirectionEnum(direction); switch (direction) { case Direction.Forward: return(this.GetEntireFile(file).Max() + iterator); case Direction.Backward: return(this.GetEntireFile(file).Min() + iterator); case Direction.Right: return(this.GetEntireRank(rank).Max() + iterator); case Direction.Left: return(this.GetEntireRank(rank).Min() + iterator); } return(0); }
//private List<int> getEntireDiagonalLine(int pos1, int pos2) //{ // //diagonal moves: rise LtoR / or RtoL \ // var diff = Math.Abs(pos1 - pos2); // var ltr = diff % 9 == 0; // var rtl = diff % 7 == 0; // if (!ltr && !rtl) // { // throw new Exception("What? This is supposed to be diagonal."); // } // var dxs = new List<int> { pos1, pos2 }; // //smallest # will be closest to the left or right in the line // //the left terminating position is evenly divisible by 8 // //the right terminating position is evently divisible by 7 // //find terminators // //left // var increment = ltr ? 9 : 7; // var smallest = dxs.Min(); // var largest = dxs.Max(); // var nextSmallest = smallest; // while (nextSmallest % 8 > 0 && nextSmallest >= 0) // { // nextSmallest = nextSmallest - increment; // if (nextSmallest >= 0 && !dxs.Contains(nextSmallest)) // { // dxs.Add(nextSmallest); // } // }; // //right // var nextLargest = largest; // while (nextLargest % 7 > 0 && nextLargest <= 63) // { // nextLargest = nextLargest + increment; // if (nextLargest <= 63 && !dxs.Contains(nextLargest)) // { // dxs.Add(nextLargest); // } // }; // //fill in the middle // var mid = smallest; // var nextMid = mid; // if (diff > increment) // { // while (nextMid < largest) // { // nextMid = nextMid + increment; // if (!dxs.Contains(nextMid)) // { // dxs.Add(nextMid); // } // } // } // return dxs; //} private List <int> getEntireOrthogonalLine(bool isRankMove, AttackedSquare x, bool trim = false) { List <int> result; if (isRankMove) { var file = NotationEngine.PositionToFile(x.Index); result = this._orthogonalService.GetEntireFile(file); } else { var rank = NotationEngine.PositionToRank(x.Index); result = this._orthogonalService.GetEntireRank(rank); } if (!trim) { return(result); } var low = x.Index > x.AttackingSquare.Index ? x.AttackingSquare.Index : x.Index; var high = x.Index < x.AttackingSquare.Index ? x.AttackingSquare.Index : x.Index; return(result.Where(a => a > low && a < high).ToList()); }
// 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); }
private string getPgnMove(char notationPiece, Piece piece, string coord, int startPos, int endPos, bool isCapture, GameState gameState) { var captureMarker = isCapture ? "x" : string.Empty; var pgnMove = getPGNMoveBeginState(notationPiece, coord, startPos, endPos, isCapture); var result = string.Empty; // figure out if additional information needs to be placed on the pgn move // this should only need to happen if the pieces are of the same type var movingSquare = gameState.Squares.Where(a => a.Index == startPos).First(); var otherSquaresOfThisTypeWithThisAttack = from s in gameState.Attacks where s.Index == endPos && s.AttackingSquare.Index != movingSquare.Index && s.AttackingSquare.Piece.PieceType == movingSquare.Piece.PieceType && s.AttackingSquare.Piece.Color == gameState.ActiveColor select s; if (otherSquaresOfThisTypeWithThisAttack.Count() <= 0) { return(string.Concat(pgnMove.Substring(0, 1), captureMarker, pgnMove.Substring(1, pgnMove.Length - 1))); } var secondPiece = otherSquaresOfThisTypeWithThisAttack.First(); if (secondPiece.AttackingSquare.Piece.PieceType == PieceType.Pawn && !isCapture) { result = string.Concat(pgnMove.Substring(0, 1), captureMarker, pgnMove.Substring(1, pgnMove.Length - 1)); return(result); } // if other piece is on same the file of departure (if they differ); or var movingPieceFile = NotationEngine.PositionToFileChar(startPos); var otherPieceFile = NotationEngine.PositionToFileChar(secondPiece.Index); if (movingPieceFile != otherPieceFile) { if (notationPiece == 'P') { result = string.Concat(movingPieceFile, captureMarker, pgnMove); } else { result = string.Concat(pgnMove.Substring(0, 1), movingPieceFile, captureMarker, pgnMove.Substring(1, pgnMove.Length - 1)); } return(result); } else { // the rank of departure (if the files are the same but the ranks differ) // these are index = 0 rank/file, so add one for PGN output var movingPieceRank = NotationEngine.PositionToRankInt(startPos) + 1; var otherPieceRank = NotationEngine.PositionToRankInt(secondPiece.Index) + 1; if (movingPieceRank != otherPieceRank) { result = string.Concat(pgnMove.Substring(0, 1), movingPieceRank, captureMarker, pgnMove.Substring(1, pgnMove.Length - 1)); return(result); } else { // both the rank and file // (if neither alone is sufficient to identify the piece—which occurs only in rare cases where one or more pawns have promoted, // resulting in a player having three or more identical pieces able to reach the same square). result = string.Concat(pgnMove.Substring(0, 1), movingPieceFile, movingPieceRank, captureMarker, pgnMove.Substring(1, 2)); return(result); } } }
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."); } }