예제 #1
0
        public void testQueenMovementWithCapture()
        {
            DoktorChessAIBoard ourBoard = new DoktorChessAIBoard(gameType.queenAndPawns, boardSearchConfig.getDebugConfig());
            squarePos srcSquare = new squarePos(3, 3);

            ourBoard.addPiece(pieceType.queen, pieceColour.white, srcSquare.x, srcSquare.y);
            queenSquare queenie = (queenSquare)ourBoard[srcSquare];

            // Place a black pawn on the board, and ensure that we can capture it, and
            // that we cannot move through it.
            ourBoard.addPiece(pieceType.pawn, pieceColour.black, 1, 1);

            sizableArray<move> possibleMoves = queenie.getPossibleMoves(ourBoard);
            List<move> expectedmoves = getExpectedMoveSquares(queenie);

            // We don't expect to be able to move to (0,0), since that square is behind an
            // enemy pawn..
            bool found = false;
            for (int i = 0; i < expectedmoves.Count; i++)
            {
                if (expectedmoves[i].dstPos.x == 0 && expectedmoves[i].dstPos.y == 0)
                {
                    expectedmoves.RemoveAt(i);
                    found = true;
                    break;
                }
            }
            if (!found)
                throw new ArgumentOutOfRangeException();

            VectorMovementTests.testListsAreOfSameMoves(expectedmoves, possibleMoves);
        }
예제 #2
0
        public static square makeSquare(pieceType newType, pieceColour newColour, squarePos newPos)
        {
            square toRet ;
            switch (newType)
            {
                case pieceType.none:
                    toRet = new square(newPos);
                    break;
                case pieceType.pawn:
                    toRet = new pawnSquare(newPos, newColour);
                    break;
                case pieceType.rook:
                    toRet = new rookSquare(newPos, newColour);
                    break;
                case pieceType.bishop:
                    toRet = new bishopSquare(newPos, newColour);
                    break;
                case pieceType.knight:
                    toRet = new knightSquare(newPos, newColour);
                    break;
                case pieceType.queen:
                    toRet = new queenSquare(newPos, newColour);
                    break;
                case pieceType.king:
                    toRet = new kingSquare(newPos, newColour);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            return toRet;
        }
예제 #3
0
        public move(square src, square dst, pieceType toPromoteTo)
        {
            srcPos = src.position;
            dstPos = dst.position;
            _srcSquare = src;

            if (src.type != pieceType.pawn)
                throw new Exception("Attempt to promote non-pawn");
            if (toPromoteTo == pieceType.pawn)
                throw new Exception("Attempt to promote a pawn to a pawn");
            if (toPromoteTo == pieceType.none)
                throw new Exception("Attempt to promote a pawn to an empty space");

            isPawnPromotion = true;
            typeToPromoteTo = toPromoteTo;

            if (dst.type != pieceType.none)
            {
                isCapture = true;
                capturedSquare = dst;
                capturedSquarePos = dst.position;
            }

            if (src.type == pieceType.king)
                findCastlingRookPositions();
        }
예제 #4
0
        public void testQueenMovement()
        {
            DoktorChessAIBoard ourBoard = new DoktorChessAIBoard(gameType.queenAndPawns, boardSearchConfig.getDebugConfig());
            squarePos srcSquare = new squarePos(3, 3);

            ourBoard.addPiece(pieceType.queen, pieceColour.white, srcSquare.x, srcSquare.y);
            queenSquare queenie = (queenSquare) ourBoard[srcSquare];

            sizableArray<move> possibleMoves = queenie.getPossibleMoves(ourBoard);
            List<move> expectedmoves = getExpectedMoveSquares(queenie);

            VectorMovementTests.testListsAreOfSameMoves(expectedmoves, possibleMoves);
        }
예제 #5
0
        public void initFromMove(move initWith)
        {
            movedPieceSrc = initWith.srcPos;
            movedPieceDst = initWith.dstPos;

            // If this is an en passant capture, we need to express that the capture square is not the dest.
            if (initWith.capturedSquare == null ||
                initWith.capturedSquare.position.isSameSquareAs(initWith.dstPos))
            {
                hasExtraCaptureSquare = false;
            }
            else
            {
                hasExtraCaptureSquare = true;
                extraCaptureSquarePos = initWith.capturedSquare.position;
            }
        }
예제 #6
0
        public move(square src, square dst)
        {
            srcPos = src.position;
            dstPos = dst.position;
            _srcSquare = src;

            // If we are capturing, fill in relevant info.
            if (dst.type != pieceType.none)
            {
                isCapture = true;
                capturedSquare = dst;
                capturedSquarePos = dst.position;
            }

            if (src.type == pieceType.king)
                findCastlingRookPositions();
        }
예제 #7
0
        public move(square src, square dst, square captured)
        {
            srcPos = src.position;
            dstPos = dst.position;
            _srcSquare = src;

            Debug.Assert(dst.type == pieceType.none);

            if (captured.type != pieceType.none)
            {
                isCapture = true;
                capturedSquare = captured;
                capturedSquarePos = captured.position;
            }

            if (src.type == pieceType.king)
                findCastlingRookPositions();
        }
예제 #8
0
 public int this[squarePos position]
 {
     get { return threats[position.x, position.y]; }
     set { threats[position.x, position.y] = value; }
 }
예제 #9
0
        public void remove(square toRemove)
        {
            int posDirection = toRemove.colour == pieceColour.white ? 1 : -1;

            // Remove the actual piece, and what it threatens.
            foreach (int threatenedSquareFlat in toRemove.coveredSquares )
            {
                squarePos threatenedSquare = squarePos.unflatten(threatenedSquareFlat);

                this[threatenedSquare] -= posDirection;

                // The removed piece no longer threatens this square.
                piecesWhichThreatenSquare[threatenedSquare.x, threatenedSquare.y][toRemove.position] = false;
            }
            toRemove.coveredSquares.Clear();

            // and now force a re-evaluation of things that threatened this square.
            sizableArray<int> piecesToRecalc = new sizableArray<int>(piecesWhichThreatenSquare[toRemove.position.x, toRemove.position.y].Count);
            piecesToRecalc.AddRange(piecesWhichThreatenSquare[toRemove.position.x, toRemove.position.y]);

            foreach (int toRecalcFlat in piecesToRecalc)
            {
                square toRecalc = _parentBoard[squarePos.unflatten(toRecalcFlat)];

                // Knights can never be blocked from accessing squares.
                if (toRecalc.type == pieceType.knight)
                    continue;

                //Pawns can always access their two attack squares.
                if (toRecalc.type == pieceType.pawn)
                    continue;

                int toRecalcAddition = toRecalc.colour == pieceColour.white ? 1 : -1;

                // Remove covered squares which are on the other 'side' of us - for example, if a rook
                // is to our left, remove squares to our right from it.
                int offX = toRecalc.position.x - toRemove.position.x;
                int offY = toRecalc.position.y - toRemove.position.y;

                int sx;
                if (offX < 0)
                    sx = +1;
                else if (offX > 0)
                    sx = -1;
                else
                    sx = 0;

                int sy;
                if (offY < 0)
                    sy = +1;
                else if (offY > 0)
                    sy = -1;
                else
                    sy = 0;

                // Look right up to the edge for all pieces apart from the king, which can look only
                // one square in each direction.
                int limitx = DoktorChessAIBoard.sizeX;
                int limity = DoktorChessAIBoard.sizeY;
                if (toRecalc.type == pieceType.king)
                {
                    limitx = toRecalc.position.x + (2 * sx);
                    limity = toRecalc.position.x + (2 * sx);

                    if (limitx > DoktorChessAIBoard.sizeX)
                        limitx = DoktorChessAIBoard.sizeX;
                    if (limity > DoktorChessAIBoard.sizeY)
                        limity = DoktorChessAIBoard.sizeY;
                }

                //Debug.WriteLine(toRecalc.type + " @ " + toRecalc.position + ":");
                int removex = toRemove.position.x + sx;
                int removey = toRemove.position.y + sy;
                while(removex >= 0 && removex < limitx &&
                      removey >= 0 && removey < limity   )
                {
                    squarePos pos = new squarePos(removex, removey);

                    this[pos] += toRecalcAddition;
                    piecesWhichThreatenSquare[removex, removey][toRecalc.position] = true;
                    toRecalc.coveredSquares[pos.flatten()] = true;

                    //Debug.WriteLine("Added discovered " + pos);

                    // we can threaten one piece, but no farther.
                    if (_parentBoard[removex, removey].type != pieceType.none)
                        break;

                    removex += sx;
                    removey += sy;

                }
            }
            sanityCheck();
        }
예제 #10
0
 public void add(squarePos pos)
 {
     add(pos.x, pos.y);
 }
예제 #11
0
 /// <summary>
 /// Get/Set the square at the given position
 /// </summary>
 /// <param name="myPos">Position to examine</param>
 /// <returns></returns>
 public square this[squarePos myPos]
 {
     // ReSharper disable UnusedMember.Local
     get { return _squares[myPos.x, myPos.y]; }
     protected set { _squares[myPos.x, myPos.y] = value; }
     // ReSharper restore UnusedMember.Local
 }
예제 #12
0
        /// <summary>
        /// Move a piece on the board back to its original position, without reverting the entire
        /// move.
        /// </summary>
        /// <param name="srcPos">Square to move from</param>
        /// <param name="dstPos">Square to move to</param>
        protected virtual void unmovePiece(squarePos srcPos, squarePos dstPos)
        {
            // store the piece which is moving before we erase it
            square movingPiece = this[dstPos];

            // Erase the old destination
            this[dstPos] = new square(dstPos);

            // Move our piece back to its source square
            this[srcPos] = movingPiece;
            this[srcPos].position = srcPos;
        }
예제 #13
0
        private void findCastlingRookPositions()
        {
            setCastling();

            if (!isACastling)
                return;

            if (dstPos.x > srcPos.x)
            {
                castlingRookSrcPos = new squarePos(7, dstPos.y) ;
                castlingRookDstPos = new squarePos(dstPos.x - 1, dstPos.y);
            }
            else if (srcPos.x > dstPos.x)
            {
                castlingRookSrcPos = new squarePos(0, dstPos.y);
                castlingRookDstPos = new squarePos(dstPos.x + 1, dstPos.y);
            }
            else
                throw new Exception("Malformed castling");
        }
예제 #14
0
 public loopConfig(squarePos position, vectorDirection dir)
 {
     switch (dir)
     {
         case vectorDirection.left:
             startX = position.x - 1;
             finishX = -1;
             startY = position.y;
             finishY = startY + 1;
             directionX = -1;
             directionY = 0;
             break;
         case vectorDirection.right:
             startX = position.x + 1;
             finishX = baseBoard.sizeX;
             startY = position.y;
             finishY = startY + 1;
             directionX = +1;
             directionY = 0;
             break;
         case vectorDirection.up:
             startY = position.y + 1;
             finishY = baseBoard.sizeY;
             startX = position.x;
             finishX = position.x + 1;
             directionX = 0;
             directionY = +1;
             break;
         case vectorDirection.down:
             startY = position.y - 1;
             finishY = -1;
             startX = position.x;
             finishX = position.x + 1;
             directionX = 0;
             directionY = -1;
             break;
         case vectorDirection.leftup:
             startY = position.y + 1;
             startX = position.x - 1;
             finishY = baseBoard.sizeY;
             finishX = -1;
             directionY = +1;
             directionX = -1;
             break;
         case vectorDirection.leftdown:
             startY = position.y - 1;
             startX = position.x - 1;
             finishY = -1;
             finishX = -1;
             directionY = -1;
             directionX = -1;
             break;
         case vectorDirection.rightup:
             startY = position.y + 1;
             startX = position.x + 1;
             finishY = baseBoard.sizeY;
             finishX = baseBoard.sizeX;
             directionY = +1;
             directionX = +1;
             break;
         case vectorDirection.rightdown:
             startY = position.y - 1;
             startX = position.x + 1;
             finishY = -1;
             finishX = baseBoard.sizeX;
             directionY = -1;
             directionX = +1;
             break;
         default:
             throw new ArgumentOutOfRangeException("dir");
     }
 }
예제 #15
0
        /// <summary>
        /// Find moves in a given direction, including captures
        /// </summary>
        /// <param name="addTo"></param>
        /// <param name="onThis">The board to move on</param>
        /// <param name="dir">The vectorDirection to move in</param>
        /// <returns>A List&lt;move&gt; of moves</returns>
        public sizableArray<move> getMovesForVector(sizableArray<move> addTo, baseBoard onThis, vectorDirection dir)
        {
            if (addTo == null)
                addTo = new sizableArray<move>(8);

            loopConfig lcfg = new loopConfig(position, dir);

            int x = lcfg.startX;
            int y = lcfg.startY;
            while ((x != lcfg.finishX) && (y != lcfg.finishY))
            {
                squarePos sqPos = new squarePos(x, y);

                // If the square is empty, we can move to it..
                if (onThis[sqPos].type == pieceType.none)
                {
                    addTo.Add(new move(onThis[position], onThis[sqPos]));
                }
                else
                {
                    if (onThis[sqPos].colour != colour )
                    {
                        // the square is occupied by an enemy piece. we can move to it,
                        // but no further.
                        addTo.Add(new move(onThis[position], onThis[sqPos]));
                    }
                    break;
                }

                x += lcfg.directionX;
                y += lcfg.directionY;
            }

            return addTo;
        }
예제 #16
0
 public pawnSquare(squarePos newPos, pieceColour newColour)
     : base(newPos, newColour)
 {
     type = pieceType.pawn;
 }
예제 #17
0
 public queenSquare(squarePos newPos, pieceColour newColour)
     : base(newPos, newColour)
 {
     type = pieceType.queen;
 }
예제 #18
0
 public bishopSquare(squarePos newPos, pieceColour newColour)
     : base(newPos, newColour)
 {
     type = pieceType.bishop;
 }
예제 #19
0
        /// <summary>
        /// Add a piece to the board
        /// </summary>
        /// <param name="newType">Type of piece to add</param>
        /// <param name="newColour">Colour of piece to add</param>
        /// <param name="dstPos">Location of piece to add</param>
        /// <returns></returns>
        protected virtual square addPiece(pieceType newType, pieceColour newColour, squarePos dstPos)
        {
            this[dstPos] = square.makeSquare(newType, newColour, dstPos);

            addToArrays(this[dstPos]);

            return this[dstPos];
        }
예제 #20
0
        /// <summary>
        /// Add a piece to this board
        /// </summary>
        /// <param name="newSquare">square containing piece to add</param>
        /// <param name="newSquarePos">location to add the specified square at</param>
        protected virtual void addPiece(square newSquare, squarePos newSquarePos)
        {
            newSquare.position = newSquarePos;
            this[newSquarePos] = newSquare;

            addToArrays(newSquare);
        }
예제 #21
0
        public void add(int x, int y)
        {
            // This is slightly complicated by the fact that any new piece could block other pieces
            // from threatening squares. Because of this, we keep per-piece 'threat lists' containing
            // the a list of squares threatened by the given piece, and if a piece is placed in to a
            // square threatened by another piece, we then re-evaluate the threatening pieces.

            // Find squares which threaten the square we are placing in to, and stash them in another
            // list
            sizableArray<int> piecesToRecalc = new sizableArray<int>(piecesWhichThreatenSquare[x, y].Count);
            piecesToRecalc.AddRange( piecesWhichThreatenSquare[x, y] );

            // Now, add the new pieces threatened squares
            sizableArray<square> potentialDestSquares = _parentBoard[x, y].getCoveredSquares(_parentBoard);

            // Since our threat map is always stored from white's viewpoint, we should add or subtract
            // depending if threatened pieces are white or black.
            int mapAddition = _parentBoard[x, y].colour == pieceColour.white ? 1 : -1;

            // Now cycle through each move and apply them to our map, and to the piece.
            //_parentBoard[x, y].coveredSquares.Clear();
            foreach (square potentialDstSq in potentialDestSquares)
            {
                // Add the threatened squares to our threat map
                this[potentialDstSq.position] += mapAddition;

                speedySquareList piecesWhichThreatenThisSq = piecesWhichThreatenSquare[potentialDstSq.position.x, potentialDstSq.position.y];
                // update our list of pieces threatening each square
                piecesWhichThreatenThisSq[squarePos.flatten(x, y)] = true;

                // and our list of squares covered by piece
                _parentBoard[x, y].coveredSquares[potentialDstSq.position.flatten()] = true;
            }

            // and then recalculate pieces that need it. To save time, we don't re-evaluate everything-
            // we just remove extra squares based on the piece.
            foreach (int toRecalcPos in piecesToRecalc)
            {
                square toRecalc = _parentBoard[toRecalcPos];

                int toRecalcAddition = toRecalc.colour == pieceColour.white ? 1 : -1;

                // Knights can never be blocked from accessing squares.
                if (toRecalc.type == pieceType.knight)
                    continue;

                 //Pawns can always access their two attack squares.
                if (toRecalc.type == pieceType.pawn)
                    continue;

                // Remove covered squares which are on the other 'side' of us - for example, if a rook
                // is to our left, remove squares to our right from it.

                int offX = toRecalc.position.x - x;
                int offY = toRecalc.position.y - y;

                int sx;
                if (offX < 0)
                    sx = +1;
                else if (offX > 0)
                    sx = -1;
                else
                    sx = 0;

                int sy;
                if (offY < 0)
                    sy = +1;
                else if (offY > 0)
                    sy = -1;
                else
                    sy = 0;

                // Look right up to the edge for all pieces apart from the king, which can look only
                // one square in each direction.
                int limitx = DoktorChessAIBoard.sizeX;
                int limity = DoktorChessAIBoard.sizeY;
                if (toRecalc.type == pieceType.king)
                {
                    limitx = toRecalc.position.x + (sx * 2);
                    limity = toRecalc.position.x + (sy * 2);

                    if (limitx > DoktorChessAIBoard.sizeX)
                        limitx = DoktorChessAIBoard.sizeX;
                    if (limity > DoktorChessAIBoard.sizeY)
                        limity = DoktorChessAIBoard.sizeY;
                }

                //Debug.WriteLine( toRecalc.type + toRecalc.position + ":");
                int removex = x + sx;
                int removey = y + sy;
                while(removex >= 0 && removex < limitx &&
                    removey >= 0 && removey < limity )
                {
                    squarePos toRemoveSqPos = new squarePos(removex, removey);

                    this[toRemoveSqPos] -= toRecalcAddition;

                    speedySquareList piecesWhichThreatenThisSq = piecesWhichThreatenSquare[removex, removey];

                    piecesWhichThreatenThisSq[toRecalc.position] = false;
                    toRecalc.coveredSquares[ toRemoveSqPos ] = false;

                    //Debug.WriteLine("Removed now-blocked " + pos);

                    // we can threaten one piece, but no farther.
                    if (_parentBoard[removex, removey].type != pieceType.none)
                        break;

                    removex += sx;
                    removey += sy;

                }
            }
            sanityCheck();
        }
예제 #22
0
        public void testVectorMovement(List<squarePos> expectedPos, vectorDirection dir)
        {
            DoktorChessAIBoard ourBoard = new DoktorChessAIBoard(gameType.queenAndPawns, boardSearchConfig.getDebugConfig());
            squarePos srcSquare = new squarePos(3, 3);

            ourBoard.addPiece(pieceType.queen, pieceColour.white, srcSquare.x, srcSquare.y);
            queenSquare queenie = (queenSquare)ourBoard[srcSquare];

            List<move> expectedmoves = new List<move>(expectedPos.Count);

            foreach (squarePos thisPos in expectedPos)
                expectedmoves.Add(new move((square) queenie, ourBoard[thisPos]));

            sizableArray<move> possibleMoves = queenie.getMovesForVector(null, ourBoard, dir);

            testListsAreOfSameMoves(expectedmoves, possibleMoves);
        }
예제 #23
0
 public rookSquare(squarePos newPos, pieceColour newColour)
     : base(newPos, newColour)
 {
     type = pieceType.rook;
 }
예제 #24
0
 // ReSharper disable MemberCanBeProtected.Global
 public square(squarePos newPos, pieceColour newColour)
 {
     movedCount = 0;
     position = newPos;
     colour = newColour;
 }
예제 #25
0
 public knightSquare(squarePos newPos, pieceColour newColour)
     : base(newPos, newColour)
 {
     type = pieceType.knight;
 }
예제 #26
0
 // ReSharper restore MemberCanBeProtected.Global
 public square(int newx, int newy)
 {
     movedCount = 0;
     position = new squarePos(newx, newy);
 }
예제 #27
0
        protected void getSquaresCoveredForVector(sizableArray<square> addTo, baseBoard onThis, vectorDirection dir)
        {
            if (addTo == null)
                addTo = new sizableArray<square>(8);

            loopConfig lcfg = new loopConfig(position, dir);

            int x = lcfg.startX;
            int y = lcfg.startY;
            while ((x != lcfg.finishX) && (y != lcfg.finishY))
            {
                squarePos sqPos = new squarePos(x, y);

                // If the square is empty, we can move to it..
                if (onThis[sqPos].type == pieceType.none)
                {
                    addTo.Add( onThis[sqPos] );
                }
                else
                {
                    // the square is occupied by some piece. We are covering it, but we cannot go any further.
                    addTo.Add( onThis[sqPos] );
                    break;
                }

                x += lcfg.directionX;
                y += lcfg.directionY;
            }

            return;
        }
예제 #28
0
        /// <summary>
        /// Move a piece on the board, without performing the move itself (eg castling, or a capture)
        /// </summary>
        /// <param name="toMove">The piece to move</param>
        /// <param name="destPos">The location to move to</param>
        protected virtual void movePiece(square toMove, squarePos destPos)
        {
            squarePos srcPos = toMove.position;

            this[destPos] = toMove;
            this[destPos].position = destPos;
            this[srcPos] = new square(srcPos);
        }
예제 #29
0
 public square(squarePos newPos)
 {
     movedCount = 0;
     position = newPos;
     type = pieceType.none;
 }
예제 #30
0
 public bool isSameSquareAs(squarePos compareTo)
 {
     return isSameSquareAs(compareTo.x, compareTo.y);
 }