예제 #1
0
        public void testIteratorOverEmptyCollection()
        {
            sizableArray<move> foo = new sizableArray<move>(5);

            if (foo.Length != 0)
                throw new Exception("Iterator length not zero while iterating over an empty collection");

            foreach (move thisMove in foo)
                throw new Exception("Iterator returned something while iterating over an empty collection");
        }
예제 #2
0
        public override sizableArray<square> getCoveredSquares(baseBoard onThis)
        {
            sizableArray<square> toRet = new sizableArray<square>(20);

            getSquaresCoveredForVector(toRet, onThis, vectorDirection.left);
            getSquaresCoveredForVector(toRet, onThis, vectorDirection.down);
            getSquaresCoveredForVector(toRet, onThis, vectorDirection.right);
            getSquaresCoveredForVector(toRet, onThis, vectorDirection.up);

            return toRet;
        }
예제 #3
0
        public override sizableArray<move> getPossibleMoves(baseBoard onThis)
        {
            sizableArray<move> toRet = new sizableArray<move>(20);

            getMovesForVector(toRet, onThis, vectorDirection.left);
            getMovesForVector(toRet, onThis, vectorDirection.down);
            getMovesForVector(toRet, onThis, vectorDirection.right);
            getMovesForVector(toRet, onThis, vectorDirection.up);

            return toRet;
        }
예제 #4
0
        public override sizableArray<square> getCoveredSquares(baseBoard parentBoard)
        {
            sizableArray<square> toRet = new sizableArray<square>(8);

            foreach (squarePosOffset potentialSquare in potentialSquares)
            {
                int posX = position.x + potentialSquare.x;
                int posY = position.y + potentialSquare.y;

                if (IsOnBoard(potentialSquare.x, potentialSquare.y))
                    toRet.Add( parentBoard[posX, posY] );
            }

            return toRet;
        }
예제 #5
0
        public override sizableArray<move> getPossibleMoves(baseBoard onThis)
        {
            sizableArray<move> possibleMoves = new sizableArray<move>(potentialSquares.Length + 2);

            findFreeOrCapturableIfOnBoard(possibleMoves, onThis, potentialSquares);

            if (canCastle(onThis, true))
            {
                // Castling kingside is possible. It is represented as a two-space move by the king.
                possibleMoves.Add(new move(this, onThis[position.right(2)]));
            }
            if (canCastle(onThis, false))
            {
                // Likewise, queenside.
                possibleMoves.Add(new move(this, onThis[position.left(2)]));
            }

            return possibleMoves;
        }
예제 #6
0
        public override sizableArray<square> getCoveredSquares(baseBoard parentBoard)
        {
            sizableArray<square> toRet = new sizableArray<square>(2);
            int direction = (colour == pieceColour.white) ? 1 : -1;

            if ((position.y + direction < baseBoard.sizeY) &&
                (position.y + direction > -1))
            {
                // Check the two diagonals
                if (position.x > 0)
                {
                    toRet.Add( parentBoard[position.up(direction).leftOne()] );
                }
                if (position.x < baseBoard.sizeX - 1)
                {
                    toRet.Add( parentBoard[position.up(direction).rightOne()] );
                }
            }
            return toRet;
        }
예제 #7
0
        public static void testListsAreOfSameMoves(List<move> expectedmoves, sizableArray<move> actualMoves)
        {
            Assert.AreEqual(expectedmoves.Count, actualMoves.Length, "Incorrect amount of moves");

            foreach (move thisPossibleMove in actualMoves)
            {
                for (int i = 0; i < expectedmoves.Count; i++)
                {
                    move thisExpectedMove = expectedmoves[i];

                    if ((thisExpectedMove.srcPos.isSameSquareAs(thisPossibleMove.srcPos) &&
                         (thisExpectedMove.dstPos.isSameSquareAs(thisPossibleMove.dstPos))))
                    {
                        expectedmoves.Remove(thisExpectedMove);
                        break;
                    }
                }
            }

            Assert.AreEqual(0, expectedmoves.Count, "Unexpected move found");
        }
예제 #8
0
        public override lineAndScore findBestMove()
        {
            sizableArray<move> movesToConsider = getMoves(colToMove);

            // Filter out any moves in to check
            sizableArray<move> movesNotIntoCheck = new sizableArray<move>(movesToConsider.Length);
            pieceColour movingCol = colToMove;
            foreach (move consideredMove in movesToConsider)
            {
                doMove(consideredMove);

                if (!isPlayerInCheck(movingCol))
                    movesNotIntoCheck.Add(consideredMove);

                undoMove(consideredMove);
            }

            // and then select a random move.
            int rndNum = rnd.Next(movesNotIntoCheck.Length);
            move randomMove = movesNotIntoCheck[rndNum];

            return new lineAndScore(new move[] { randomMove }, 0, null);
        }
예제 #9
0
        public void testIterator()
        {
            sizableArray<move> foo = new sizableArray<move>(5);

            move move1 = new move(new square(0,0), new square(1,1));
            move move2 = new move(new square(1,1), new square(2,2));

            foo.Add(move1);
            foo.Add(move2);

            bool move1seen = false;
            bool move2seen = false;

            foreach (move iterated in foo)
            {
                Debug.WriteLine("Iterated object " + iterated);
                if (iterated == move1)
                {
                    if (move1seen)
                        throw new Exception("Move one iterated twice");
                    move1seen = true;
                }
                else if (iterated == move2)
                {
                    if (move2seen)
                        throw new Exception("Move two iterated twice");
                    move2seen = true;
                }
                else if (iterated == null)
                    throw new Exception("Iterator returned null");
                else
                    throw new Exception("Iterator returned something crazy");
            }

            if (!move1seen || !move2seen)
                throw new Exception("Iterator did not iterate over all elements");
        }
예제 #10
0
        public override sizableArray<move> getPossibleMoves(baseBoard onThis)
        {
            sizableArray<move> toRet = new sizableArray<move>(40);

            int direction;
            if (colour == pieceColour.white)
                direction = +1;
            else
                direction = -1;

            // We can capture upward diagonally, if immediate diagonal upward squares
            // contain an enemy piece.
            if ((position.y + direction < baseBoard.sizeY) &&
                (position.y + direction > -1) )
            {
                // Check for en passant. En passant can never cause a promotion.
                if (position.x > 0)
                {
                    square adjacentLeft = onThis[position.leftOne()];
                    if (canEnPassantTo(adjacentLeft, onThis))
                        toRet.Add(new move(onThis[position], onThis[position.up(direction).leftOne()], adjacentLeft));
                }
                if (position.x < baseBoard.sizeX - 1)
                {
                    square adjacentRight = onThis[position.rightOne()];
                    if (canEnPassantTo(adjacentRight, onThis))
                        toRet.Add(new move(onThis[position], onThis[position.up(direction).rightOne()], adjacentRight));
                }

                // And we can move forward two if we haven't moved this piece yet, and both
                // squares are empty. It is assumed that this can't cause a promotion, so explicitly
                // prevent this move from moving in to the back row.
                if (movedCount == 0)
                {
                    if (position.y + (direction * 2) < baseBoard.sizeY &&
                        position.y + (direction * 2) > -1)
                    {
                        // check back row
                        if (position.y != (colour == pieceColour.white ? 7 : 0))
                        {
                            if (onThis[position.up(direction * 2)].type == pieceType.none &&
                                onThis[position.up(direction * 1)].type == pieceType.none)
                                toRet.Add(new move(onThis[position], onThis[position.up(direction*2)]));
                        }
                    }
                }

                // All of the other moves could cause a promotion, so call addPawnMovesToSquare so that
                // promoting moves are added.

                // Check the two diagonals
                if (position.x > 0)
                {
                    if (onThis[position.up(direction).leftOne()].containsPieceNotOfColour(colour))
                        addPawnMovesToSquare(toRet, onThis[position], onThis[position.up(direction).leftOne()]);
                }
                if (position.x < baseBoard.sizeX - 1)
                {
                    if (onThis[position.up(direction).rightOne()].containsPieceNotOfColour(colour))
                        addPawnMovesToSquare(toRet, onThis[position], onThis[position.up(direction).rightOne()]);
                }

                // We can move forward one if that square is empty.
                if (onThis[position.up(direction)].type == pieceType.none)
                {
                    if (onThis[position.up(direction)].type == pieceType.none)
                        addPawnMovesToSquare(toRet, onThis[position], onThis[position.up(direction)]);
                }
            }

            return toRet;
        }
예제 #11
0
        private void prioritizeMoves(sizableArray<move> movesToConsider, int depthLeft)
        {
            // Bring any moves which are 'probably good' to the top of our list. Hold an array of bools, and set
            // each one which corresponds to a good move, so that we can avoid moving anything twice.
            bool[] movesReordered = new bool[movesToConsider.Length];
            int[] reorderedMovesToConsider = new int[movesToConsider.Length];
            int reorderedCount = 0;

            int n = 0;
            if (_killerStore != null)
            {
                // If one of these moves caused a cutoff last time, consider that move first.
                foreach (move consideredMove in movesToConsider)
                {
                    if (_killerStore.contains(consideredMove, depthLeft))
                    {
                        movesReordered[n] = true;
                        reorderedMovesToConsider[reorderedCount++] = n;
                    }
                    n++;
                }
            }

            // Consider any capturing or pawn promotions first, too
            n = 0;
            foreach (move thisMove in movesToConsider)
            {
                if (thisMove.isCapture ||      // captures are good.
                    thisMove.isPawnPromotion)  // pawn promotions are good.
                {
                    if (movesReordered[n] == false)
                    {
                        movesReordered[n] = true;
                        reorderedMovesToConsider[reorderedCount++] = n;
                    }
                }
                n++;
            }

            // Swap any good moves such that they are at the top
            int swapCount = 0;
            for (int i = 0; i < reorderedCount; i++)
                movesToConsider.bringToPosition(reorderedMovesToConsider[i], swapCount++);
        }
예제 #12
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();
        }
예제 #13
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();
        }
예제 #14
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;
        }
예제 #15
0
        protected sizableArray<move> findFreeOrCapturableIfOnBoard(sizableArray<move> returnArray, baseBoard onThis, squarePosOffset[] potentialSquareOffsets)
        {
            if (returnArray == null)
                returnArray = new sizableArray<move>(potentialSquareOffsets.Length);

            foreach (squarePosOffset potentialSquareOffset in potentialSquareOffsets)
            {
                if (!IsOnBoard(potentialSquareOffset.x, potentialSquareOffset.y))
                    continue;

                square destSquare = onThis[potentialSquareOffset.x + position.x, potentialSquareOffset.y + position.y];

                if (destSquare.type == pieceType.none)
                {
                    // Square is free.
                    returnArray.Add(new move(this, destSquare));
                }
                else
                {
                    if (destSquare.colour != this.colour)
                    {
                        // We can capture.
                        returnArray.Add(new move(this, destSquare));
                    }
                }
            }

            return returnArray;
        }
예제 #16
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;
        }
예제 #17
0
        private void addPawnMovesToSquare(sizableArray<move> moveList, square src, square dst)
        {
            // If we are moving in to the end row, we should promote. Handle this.
            if (dst.position.y == (colour == pieceColour.white ? 7 : 0) )
            {
                // OK. Promotions it is.
                pieceType[] promotionOptions = new[] {
                                                      pieceType.queen,
                                                      pieceType.rook,
                                                      pieceType.knight,
                                                      pieceType.bishop
                                                  };

                foreach (pieceType promotionOption in promotionOptions)
                {
                    moveList.Add(new move(src, dst, promotionOption));
                }
            }
            else
            {
                moveList.Add(new move(src, dst));
            }
        }
예제 #18
0
        /// <summary>
        /// Return a collecetion of all moves one player may be able to make.
        /// Note that moves in to check will also be returned.
        /// </summary>
        /// <param name="toMoveColour">Side to examine</param>
        /// <returns></returns>
        public sizableArray<move> getMoves(pieceColour toMoveColour)
        {
            List<square> occupiedSquares = getPiecesForColour(toMoveColour);

            // Generously guess the size of this array
            sizableArray<move> possibleMoves = new sizableArray<move>(occupiedSquares.Count * 50);

            // Add all moves from all pieces
            foreach (square occupiedSquare in occupiedSquares)
                possibleMoves.AddRange(occupiedSquare.getPossibleMoves(this));

            return possibleMoves;
        }