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); }
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; }
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(); }
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); }
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; } }
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(); }
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(); }
public int this[squarePos position] { get { return threats[position.x, position.y]; } set { threats[position.x, position.y] = value; } }
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(); }
public void add(squarePos pos) { add(pos.x, pos.y); }
/// <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 }
/// <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; }
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"); }
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"); } }
/// <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<move> 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; }
public pawnSquare(squarePos newPos, pieceColour newColour) : base(newPos, newColour) { type = pieceType.pawn; }
public queenSquare(squarePos newPos, pieceColour newColour) : base(newPos, newColour) { type = pieceType.queen; }
public bishopSquare(squarePos newPos, pieceColour newColour) : base(newPos, newColour) { type = pieceType.bishop; }
/// <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]; }
/// <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); }
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(); }
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); }
public rookSquare(squarePos newPos, pieceColour newColour) : base(newPos, newColour) { type = pieceType.rook; }
// ReSharper disable MemberCanBeProtected.Global public square(squarePos newPos, pieceColour newColour) { movedCount = 0; position = newPos; colour = newColour; }
public knightSquare(squarePos newPos, pieceColour newColour) : base(newPos, newColour) { type = pieceType.knight; }
// ReSharper restore MemberCanBeProtected.Global public square(int newx, int newy) { movedCount = 0; position = new squarePos(newx, newy); }
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; }
/// <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); }
public square(squarePos newPos) { movedCount = 0; position = newPos; type = pieceType.none; }
public bool isSameSquareAs(squarePos compareTo) { return isSameSquareAs(compareTo.x, compareTo.y); }