BitboardLayer[] pieceLocations; //0 is white, 1 is black #endregion Fields #region Constructors public AttackedSquaresGetter(ChessBoard c) { lastUpdated = new int[64]; this.c = c; pieceLocations = new BitboardLayer[2]; currAttackedSquares = new BitboardLayer[2][]; kingAttackedSquares = new BitboardLayer[2]; allAttackedSq = new BitboardLayer[2]; allValidMoves = new BitboardLayer[2]; currValidMoves = new BitboardLayer[2][]; currPinnedPcs = new List<int[]>[2]; currCheckers = new List<int[]>[2]; canCastle = new bool[2][]; for (int i = 0; i < 2; i++) { pieceLocations[i] = new BitboardLayer(); currAttackedSquares[i] = new BitboardLayer[64]; kingAttackedSquares[i] = new BitboardLayer(); currValidMoves[i] = new BitboardLayer[64]; allValidMoves[i] = new BitboardLayer(); allAttackedSq[i] = new BitboardLayer(); currPinnedPcs[i] = new List<int[]>(); canCastle[i] = new bool[] { false, false }; currCheckers[i] = new List<int[]>(); for (int j = 0; j < 64; j++) { currAttackedSquares[i][j] = new BitboardLayer(); currValidMoves[i][j] = new BitboardLayer(); } } numIterations = 0; updatePosition(true, new int[] { 63, 63 }); updatePosition(false, new int[] { 0, 0 }); }
public ChessBoard(ChessBoard c) { for (int i = 0; i <= pieceIndex.FLAGS; i++) { white[i] = new BitboardLayer(c.getDict(true)[i]); black[i] = new BitboardLayer(c.getDict(false)[i]); } white_ep = c.getEP(true); black_ep = c.getEP(false); moveList = new List<int[]>(c.getMoveList()); moveNum = c.getMoveNum(); ASG = c.getASG(); }
public ChessBoard(BitboardLayer[] white, BitboardLayer[] black) { if (white == null || black == null) { //pawn ulong temp = Convert.ToUInt64("0000000011111111000000000000000000000000000000000000000000000000", 2); this.black[pieceIndex.PAWN] = new BitboardLayer(temp); temp = Convert.ToUInt64("0000000000000000000000000000000000000000000000001111111100000000", 2); this.white[pieceIndex.PAWN] = new BitboardLayer(temp); //rook temp = Convert.ToUInt64("1000000100000000000000000000000000000000000000000000000000000000", 2); this.black[pieceIndex.ROOK] = new BitboardLayer(temp); temp = Convert.ToUInt64("0000000000000000000000000000000000000000000000000000000010000001", 2); this.white[pieceIndex.ROOK] = new BitboardLayer(temp); //knight temp = Convert.ToUInt64("0100001000000000000000000000000000000000000000000000000000000000", 2); this.black[pieceIndex.KNIGHT] = new BitboardLayer(temp); temp = Convert.ToUInt64("0000000000000000000000000000000000000000000000000000000001000010", 2); this.white[pieceIndex.KNIGHT] = new BitboardLayer(temp); //bishop temp = Convert.ToUInt64("0010010000000000000000000000000000000000000000000000000000000000", 2); this.black[pieceIndex.BISHOP] = new BitboardLayer(temp); temp = Convert.ToUInt64("0000000000000000000000000000000000000000000000000000000000100100", 2); this.white[pieceIndex.BISHOP] = new BitboardLayer(temp); //queen this.black[pieceIndex.QUEEN] = new BitboardLayer(Convert.ToUInt64("0001000000000000000000000000000000000000000000000000000000000000", 2)); this.white[pieceIndex.QUEEN] = new BitboardLayer(Convert.ToUInt64("0000000000000000000000000000000000000000000000000000000000010000", 2)); //king this.black[pieceIndex.KING] = new BitboardLayer(Convert.ToUInt64("0000100000000000000000000000000000000000000000000000000000000000", 2)); this.white[pieceIndex.KING] = new BitboardLayer(Convert.ToUInt64("0000000000000000000000000000000000000000000000000000000000001000", 2)); //set flags used for castling this.white[pieceIndex.FLAGS] = new BitboardLayer(Convert.ToUInt64("111", 2)); //castling flags - left rook, king, right rook this.black[pieceIndex.FLAGS] = new BitboardLayer(Convert.ToUInt64("111", 2)); getAllLocations(true); getAllLocations(false); } else { this.white = white; this.black = black; } ASG = new AttackedSquaresGetter(this); }
public BitboardLayer(BitboardLayer b) { layerData = b.getLayerData(); trueIndicies = new HashSet<int>(); foreach (int i in b.getTrueIndicies()) { trueIndicies.Add(i); } }
int[] getChangedIndices(bool isWhite, int[] lastMove) { int colorIndex = isWhite ? 0 : 1; int oppositeColorIndex = isWhite ? 1 : 0; HashSet<int> retVal = new HashSet<int>(); BitboardLayer nums = new BitboardLayer(c.getDict(true)[pieceIndex.ALL_LOCATIONS].getLayerData() | c.getDict(true)[pieceIndex.ALL_LOCATIONS].getLayerData() | (pieceLocations[colorIndex].getLayerData() ^ c.getDict(isWhite)[pieceIndex.ALL_LOCATIONS].getLayerData())); foreach(int i in nums.getTrueIndicies()) { retVal.Add(i); } int[] arrayForm = new int[retVal.Count]; retVal.CopyTo(arrayForm); return arrayForm; }
void findAttackMove(bool isWhite, int index) { BitboardLayer[] w = c.getDict(true); BitboardLayer[] b = c.getDict(false); BitboardLayer[] dict = isWhite ? w : b; BitboardLayer newAllPos = dict[pieceIndex.ALL_LOCATIONS]; BitboardLayer[] enemyDict = isWhite ? b : w; int white_ep = c.getEP(true); int black_ep = c.getEP(false); int colorIndex = isWhite ? 0 : 1; int oppositeColorIndex = isWhite ? 0 : 1; currAttackedSquares[colorIndex][index] = new BitboardLayer(); currValidMoves[colorIndex][index] = new BitboardLayer(); for (int i = 0; i < currPinnedPcs[colorIndex].Count; i++) { int[] pin = currPinnedPcs[oppositeColorIndex][i]; if (pin[0] == index) currPinnedPcs[colorIndex].Remove(pin); } for (int s = 0; s <= pieceIndex.KING; s++) { if (dict[s].trueAtIndex(index)) { lastUpdated[index] = numIterations; BitboardLayer pieceVM = currValidMoves[colorIndex][index]; BitboardLayer pieceAS = currAttackedSquares[colorIndex][index]; switch (s) { case pieceIndex.PAWN: int enemy_ep = isWhite ? black_ep : white_ep; int direction = isWhite ? -1 : 1; int pawnFile = isWhite ? 6 : 1; int moveIndex = index + (direction * 8); //move one in dir if (moveIndex >= 0 && moveIndex <= 63 && !enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(moveIndex) && !dict[pieceIndex.ALL_LOCATIONS].trueAtIndex(moveIndex)) pieceVM.setAtIndex(moveIndex, true); //captures if (moveIndex - 1 >= 0 && moveIndex - 1 <= 63 && (moveIndex - 1) / 8 == moveIndex / 8) { pieceAS.setAtIndex(moveIndex - 1, true); if (enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(moveIndex - 1)) pieceVM.setAtIndex(moveIndex - 1, true); } if (moveIndex + 1 >= 0 && moveIndex + 1 <= 63 && (moveIndex + 1) / 8 == moveIndex / 8){ pieceAS.setAtIndex(moveIndex + 1, true); if (enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(moveIndex + 1)) pieceVM.setAtIndex(moveIndex + 1, true); } //move two in dir if (index / 8 == pawnFile && !enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(moveIndex) && !dict[pieceIndex.ALL_LOCATIONS].trueAtIndex(moveIndex)) { moveIndex += direction * 8; if (moveIndex >= 0 && moveIndex <= 63 && !enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(moveIndex) && !dict[pieceIndex.ALL_LOCATIONS].trueAtIndex(moveIndex)) pieceVM.setAtIndex(moveIndex, true); } //en passant captures if (enemy_ep != -1 && index + direction * 7 == enemy_ep || index + direction * 9 == enemy_ep){ pieceAS.setAtIndex(enemy_ep, true); pieceVM.setAtIndex(enemy_ep, true); } break; case pieceIndex.ROOK: int[] diffs = new int[] { 8, -8, 1, -1 }; foreach (int diff in diffs) { extendPath(isWhite, index, diff); } break; case pieceIndex.KNIGHT: diffs = new int[]{ 17, 15, 10, 6, -6, -10, -15, -17 }; int[] rightRows = { 16, 16, 8, 8, -8, -8, -16, -16 }; for (int i = 0; i < diffs.Length; i++) { int possibleMove = index + diffs[i]; int rightRow = index + rightRows[i]; if (possibleMove / 8 == rightRow / 8 && possibleMove >= 0 && possibleMove <= 63) { if (checkCollision(isWhite, possibleMove) != 2) { pieceVM.setAtIndex(possibleMove, true); } pieceAS.setAtIndex(possibleMove, true); } } break; case pieceIndex.BISHOP: diffs = new int[] { -7, 7, -9, 9 }; foreach (int diff in diffs) { extendPath(isWhite, index, diff, true); } break; case pieceIndex.QUEEN: diffs = new int[] { -7, 7, -9, 9 }; foreach (int diff in diffs) { extendPath(isWhite, index, diff, true); } diffs = new int[] { 8, -8, 1, -1 }; foreach (int diff in diffs) { extendPath(isWhite, index, diff); } break; case pieceIndex.KING: //int currRow = index / 8; int currCol = index % 8; for (int a = -8; a <= 8; a += 8) { for (int c = -1; c <= 1; c++) { int newIndex = index + a + c; if (newIndex >= 0 && newIndex <= 63 && newIndex % 8 - currCol >= -1 && newIndex % 8 - currCol <= 1) { pieceAS.setAtIndex(newIndex, true); } } } if ((dict[pieceIndex.FLAGS].getLayerData() & flagIndex.KING_CASTLE) > 0) //king can castle { if ((dict[pieceIndex.FLAGS].getLayerData() & flagIndex.RIGHT_ROOK_CASTLE) > 0 && !enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index + 1) && !dict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index + 1) && !enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index + 2) && !dict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index + 2)) canCastle[colorIndex][1] = true; //king can castle right if ((dict[pieceIndex.FLAGS].getLayerData() & flagIndex.LEFT_ROOK_CASTLE) > 0 && !dict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index - 1) && !enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index - 1) && !dict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index - 2) && !enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index - 2) && !dict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index - 3) && !enemyDict[pieceIndex.ALL_LOCATIONS].trueAtIndex(index - 3)) canCastle[colorIndex][0] = true; //king can castle left } break; } break; } } }
//returns the indicies we need to change findAttackMove / getAttackedSquares for /* int[] getChangedIndices(bool isWhite, int[] lastMove) { int colorIndex = isWhite ? 0 : 1; int oppositeColorIndex = isWhite ? 1 : 0; HashSet<int> retVal = new HashSet<int>(); BitboardLayer oldAttackedSquares = new BitboardLayer(currAttackedSquares[colorIndex][lastMove[0]].getLayerData() | currValidMoves[colorIndex][lastMove[0]].getLayerData()); BitboardLayer changedIndicies = new BitboardLayer(pieceLocations[colorIndex].getLayerData() ^ c.getDict(isWhite)[pieceIndex.ALL_LOCATIONS].getLayerData()); int[] sidesInOrder; if (currCheckers[oppositeColorIndex].Count != 0) sidesInOrder = new int[] { oppositeColorIndex, colorIndex }; else sidesInOrder = new int[] { colorIndex, oppositeColorIndex }; int currColorIndex = sidesInOrder[0]; //int consideredIndex = 1 - currColorIndex; //consideredIndex is the index of the opposite side foreach (int index in c.getDict(currColorIndex == 0)[pieceIndex.ALL_LOCATIONS].getTrueIndicies()) { BitboardLayer valAttack = new BitboardLayer(currAttackedSquares[currColorIndex][index].getLayerData() | currValidMoves[currColorIndex][index].getLayerData()); //BitboardLayer valAttack = new BitboardLayer(currAttackedSquares[consideredIndex][index].getLayerData() | currValidMoves[consideredIndex][index].getLayerData()); foreach (int changedIndex in changedIndicies.getTrueIndicies()) { if (valAttack.trueAtIndex(changedIndex)) retVal.Add(index); //was attacking (opposite color) } } currColorIndex = 1 - currColorIndex; if (currCheckers[1 - currColorIndex].Count != 0) //if the first side was checking the second side { foreach (int index in c.getDict(currColorIndex == 0)[pieceIndex.ALL_LOCATIONS].getTrueIndicies()) { retVal.Add(index); } } else { foreach (int index in c.getDict(currColorIndex == 0)[pieceIndex.ALL_LOCATIONS].getTrueIndicies()) { BitboardLayer valAttack = new BitboardLayer(currAttackedSquares[currColorIndex][index].getLayerData() | currValidMoves[currColorIndex][index].getLayerData()); //BitboardLayer valAttack = new BitboardLayer(currAttackedSquares[consideredIndex][index].getLayerData() | currValidMoves[consideredIndex][index].getLayerData()); foreach (int changedIndex in changedIndicies.getTrueIndicies()) { if (valAttack.trueAtIndex(changedIndex)) retVal.Add(index); //was attacking (opposite color) } //if (valAttack.trueAtIndex(c.getDict(isWhite)[pieceIndex.KING].getTrueIndicies()[0])) retVal.Add(index); //is checking (opposite color) } } foreach (List<int[]> pinList in currPinnedPcs) { foreach (int[] pin in pinList) { retVal.Add(pin[0]); retVal.Add(pin[1]); } } retVal.Add(lastMove[1]); foreach (int i in changedIndicies.getTrueIndicies()) { retVal.Add(i); } int[] arrayForm = new int[retVal.Count]; retVal.CopyTo(arrayForm); return arrayForm; } */ //updates both black and white's attacked squares public void updatePosition(bool isWhite, int[] move) { int colorIndex = isWhite ? 0 : 1; int oppositeColorIndex = isWhite ? 1 : 0; //get changed indices before we update all locations int[] changedIndicies = getChangedIndices(isWhite, move); pieceLocations[colorIndex] = c.getDict(isWhite)[pieceIndex.ALL_LOCATIONS]; /* int oldLocation = move[0]; currAttackedSquares[colorIndex][oldLocation] = new BitboardLayer(); */ for (int i = 0; i < 2; i++) { kingAttackedSquares[i] = new BitboardLayer(); currCheckers[i] = new List<int[]>(); currPinnedPcs[i] = new List<int[]>(); } //if move was a capture, nullify all valid moves and attacked squares //pins and checks are cleared and re-revaluated every move, so no need to directly nullify them if (pieceLocations[oppositeColorIndex].trueAtIndex(move[1])) { currValidMoves[oppositeColorIndex][move[1]] = new BitboardLayer(); currAttackedSquares[oppositeColorIndex][move[1]] = new BitboardLayer(); } BitboardLayer allOccupiedSquares = new BitboardLayer(c.getDict(true)[pieceIndex.ALL_LOCATIONS].getLayerData() | c.getDict(false)[pieceIndex.ALL_LOCATIONS].getLayerData()); bool ftupdate = false; foreach (int location in changedIndicies) { numIterations++; /* if (numIterations == 244438) { Debug.Print("Found bug!"); } */ if (allOccupiedSquares.trueAtIndex(location)) { findAttackMove(pieceLocations[0].trueAtIndex(location), location); if (location == 57 && currValidMoves[0][57].getLayerData() == 10489856) { Debug.Print("Knight updated! New value: " + currAttackedSquares[0][52].getLayerData() + " numIterations: " + numIterations); } } else { for (int i = 0; i < 2; i++) { currAttackedSquares[i][location] = new BitboardLayer(); currValidMoves[i][location] = new BitboardLayer(); } } //update valid moves and attacked squares for piece at location } foreach (int[] pin in currPinnedPcs[colorIndex]) { //format: pinner, pinned piece, interval ulong vMoveLayer = 0uL; for (int i = pin[0]; checkCollision(oppositeColorIndex == 1, i) != 3; i += pin[2]) { vMoveLayer |= 1uL << (63 - i); } currValidMoves[oppositeColorIndex][pin[1]].setLayerData(currValidMoves[oppositeColorIndex][pin[1]].getLayerData() & vMoveLayer); } foreach (int[] pin in currPinnedPcs[oppositeColorIndex]) { ulong vMoveLayer = 0uL; for (int i = pin[0]; checkCollision(colorIndex == 1, i) != 3; i += pin[2]) { vMoveLayer |= 1uL << (63 - i); } currValidMoves[colorIndex][pin[1]].setLayerData(currValidMoves[oppositeColorIndex][pin[1]].getLayerData() & vMoveLayer); } //get all attacked squares so we can get king's valid moves for (int i = 0; i < 2; i++) { ulong fullAttackedSq = 0; foreach(BitboardLayer pieceAttackedSq in currAttackedSquares[i]) { fullAttackedSq |= pieceAttackedSq.getLayerData(); } allAttackedSq[i] = new BitboardLayer(fullAttackedSq); } //to make sure only valid moves are considered, bitwise-AND each non-king's valid moves with position of each piece attacking the king and their attack vector //if double-checked, this makes sure no positions will show up //otherwise will automatically limit to capture or block for (int i = 0; i < 2; i++) { int kingPos = c.getDict(i == 1)[pieceIndex.KING].getTrueIndicies()[0]; //if opponent is checking, limit valid moves foreach (int[] checker in currCheckers[1 - i]) { ulong vMoveMask = 0; for (int j = checker[0]; checkCollision(i == 1, j) != 3; j += checker[1]) { vMoveMask |= 1uL << (63 - j); } for(int j = 0; j < 64; j++) { currValidMoves[i][j].setLayerData(currValidMoves[i][j].getLayerData() & vMoveMask); } } //limit king's valid moves BitboardLayer kingVMoves = new BitboardLayer(currAttackedSquares[i][kingPos].getLayerData()); foreach (int possibleMove in kingVMoves.getTrueIndicies()) { if (checkCollision(i == 0, i) == 2) kingVMoves.setAtIndex(possibleMove, false); if (allAttackedSq[1 - i].trueAtIndex(possibleMove)) kingVMoves.setAtIndex(possibleMove, false); if (kingAttackedSquares[1 - i].trueAtIndex(possibleMove)) kingVMoves.setAtIndex(possibleMove, false); } //castling if (canCastle[i][1] && !allAttackedSq[1 - i].trueAtIndex(kingPos + 1) && !allAttackedSq[1 - i].trueAtIndex(kingPos + 2)) //can castle right { kingVMoves.setAtIndex(kingPos + 2, true); } if (canCastle[i][0] && !allAttackedSq[1 - i].trueAtIndex(kingPos - 1) && !allAttackedSq[1 - i].trueAtIndex(kingPos - 2)) { kingVMoves.setAtIndex(kingPos - 2, true); } currValidMoves[i][kingPos] = new BitboardLayer(kingVMoves); } for (int i = 0; i < 2; i++) { ulong fullValidMoves = 0; foreach (BitboardLayer pieceValidMoves in currValidMoves[i]) { fullValidMoves |= pieceValidMoves.getLayerData(); pieceValidMoves.setAtIndex(c.getDict(i == 1)[pieceIndex.KING].getTrueIndicies()[0], false); } allValidMoves[i] = new BitboardLayer(fullValidMoves); } }
void PictureBoxClick(object sender, EventArgs e) { ChessBoard cb = c.getBoard(); int isPlayerIndex = isWhiteMove ? 1 : 2; Point mousePosition = pictureBox1.PointToClient(Cursor.Position); int mouseX = mousePosition.X; int mouseY = mousePosition.Y; int indexClicked = (mouseX / tileXSize) + (8 * (mouseY / tileYSize)); BitboardLayer[] dict = cb.getDict(isWhiteMove); if ((gameMode & isPlayerIndex) == 0) //is not an AI move { if (dict[pieceIndex.ALL_LOCATIONS].trueAtIndex(indexClicked)) { BitboardLayer vMoves = cb.getValidMoves(isWhiteMove, indexClicked); validMoveOverlays = vMoves; selectedPiece = indexClicked; } else if (validMoveOverlays.trueAtIndex(indexClicked) && selectedPiece != -1) { cb.movePiece(isWhiteMove, selectedPiece, indexClicked); cb.getAllLocations(isWhiteMove); cb.getAllLocations(!isWhiteMove); if (cb.checkForMate(isWhiteMove)) { validMoveOverlays = new BitboardLayer(); overlays = new int[64]; c.genOverlay(); paintTiles(); String colorStr = isWhiteMove ? "White" : "Black"; MessageBox.Show("Checkmate! " + colorStr + " wins!"); } selectedPiece = -1; validMoveOverlays = new BitboardLayer(); isWhiteMove = !isWhiteMove; overlays = new int[64]; c.genOverlay(); paintTiles(); GC.Collect(); isPlayerIndex = isWhiteMove ? 1 : 2; if ((gameMode & isPlayerIndex) > 0) //is an AI move { int[] aiMove; if (isWhiteMove) aiMove = gameAIs[1].getAIMove(cb, isWhiteMove, defaultSearchDepth)[0]; else aiMove = gameAIs[0].getAIMove(cb, isWhiteMove, defaultSearchDepth)[0]; cb.movePiece(isWhiteMove, aiMove[0], aiMove[1]); isWhiteMove = !isWhiteMove; } } paintTiles(); GC.Collect(); } }