Пример #1
0
        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());
        }
Пример #2
0
        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));
        }
Пример #3
0
        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);
        }
Пример #4
0
        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));
        }
Пример #5
0
        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);
        }
Пример #6
0
        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));
        }
Пример #7
0
        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));
        }
Пример #8
0
        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);
            }
        }
Пример #9
0
        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);
        }
Пример #10
0
        // 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);
        }
Пример #11
0
        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.");
            }
        }