| | 1 | | using Rudim.Common; |
| | 2 | | using Rudim.Search; |
| | 3 | | using System.Collections.Generic; |
| | 4 | | using System.Threading; |
| | 5 | |
|
| | 6 | | namespace Rudim.Board |
| | 7 | | { |
| | 8 | | public partial class BoardState |
| | 9 | | { |
| | 10 | | public void GenerateMoves() |
| | 11 | | { |
| 1815060 | 12 | | Moves = new List<Move>(); |
| | 13 | |
|
| 1815060 | 14 | | GeneratePawnMoves(); |
| 1815060 | 15 | | GenerateBishopMoves(); |
| 1815060 | 16 | | GenerateKnightMoves(); |
| 1815060 | 17 | | GenerateRookMoves(); |
| 1815060 | 18 | | GenerateQueenMoves(); |
| 1815060 | 19 | | GenerateKingMoves(); |
| 1815060 | 20 | | } |
| | 21 | |
|
| | 22 | | private void GenerateKingMoves() |
| | 23 | | { |
| 1815060 | 24 | | int source = Pieces[(int)SideToMove, (int)Piece.King].GetLsb(); |
| 1815060 | 25 | | Bitboard attacks = new Bitboard(Bitboard.KingAttacks[source]); |
| | 26 | |
|
| 11530929 | 27 | | while (attacks.Board > 0) |
| | 28 | | { |
| 9715869 | 29 | | int target = attacks.GetLsb(); |
| 9715869 | 30 | | attacks.ClearBit(target); |
| 9715869 | 31 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | 32 | | { |
| | 33 | | continue; |
| | 34 | | } |
| 5246021 | 35 | | AddMoveToMovesList(source, target); |
| | 36 | | } |
| | 37 | |
|
| 1815060 | 38 | | GenerateCastleMoves(); |
| 1815060 | 39 | | } |
| | 40 | | public Move FindBestMove(int depth, CancellationToken cancellationToken, ref bool debugMode) |
| | 41 | | { |
| 5 | 42 | | IterativeDeepening.Search(this, depth, cancellationToken, ref debugMode); |
| 5 | 43 | | return IterativeDeepening.BestMove; |
| | 44 | | } |
| | 45 | |
|
| | 46 | | private void GenerateQueenMoves() |
| | 47 | | { |
| 1815060 | 48 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Queen]; |
| 3172881 | 49 | | while (bitboard.Board > 0) |
| | 50 | | { |
| 1357821 | 51 | | int source = bitboard.GetLsb(); |
| 1357821 | 52 | | Bitboard attacks = Bitboard.GetQueenAttacksFromTable((Square)source, Occupancies[(int)Side.Both]); |
| | 53 | |
|
| 16724908 | 54 | | while (attacks.Board > 0) |
| | 55 | | { |
| 15367087 | 56 | | int target = attacks.GetLsb(); |
| | 57 | |
|
| 15367087 | 58 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | 59 | | { |
| 5309774 | 60 | | attacks.ClearBit(target); |
| 5309774 | 61 | | continue; |
| | 62 | | } |
| | 63 | |
|
| 10057313 | 64 | | AddMoveToMovesList(source, target); |
| | 65 | |
|
| 10057313 | 66 | | attacks.ClearBit(target); |
| | 67 | | } |
| | 68 | |
|
| 1357821 | 69 | | bitboard.ClearBit(source); |
| | 70 | | } |
| 1815060 | 71 | | } |
| | 72 | |
|
| | 73 | | private void GenerateRookMoves() |
| | 74 | | { |
| 1815060 | 75 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Rook]; |
| 4974840 | 76 | | while (bitboard.Board > 0) |
| | 77 | | { |
| 3159780 | 78 | | int source = bitboard.GetLsb(); |
| 3159780 | 79 | | Bitboard attacks = Bitboard.GetRookAttacksFromTable((Square)source, Occupancies[(int)Side.Both]); |
| | 80 | |
|
| 19101066 | 81 | | while (attacks.Board > 0) |
| | 82 | | { |
| 15941286 | 83 | | int target = attacks.GetLsb(); |
| | 84 | |
|
| 15941286 | 85 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | 86 | | { |
| 5872299 | 87 | | attacks.ClearBit(target); |
| 5872299 | 88 | | continue; |
| | 89 | | } |
| | 90 | |
|
| 10068987 | 91 | | AddMoveToMovesList(source, target); |
| | 92 | |
|
| 10068987 | 93 | | attacks.ClearBit(target); |
| | 94 | | } |
| | 95 | |
|
| 3159780 | 96 | | bitboard.ClearBit(source); |
| | 97 | | } |
| 1815060 | 98 | | } |
| | 99 | |
|
| | 100 | | private void GenerateKnightMoves() |
| | 101 | | { |
| 1815060 | 102 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Knight]; |
| 4082633 | 103 | | while (bitboard.Board > 0) |
| | 104 | | { |
| 2267573 | 105 | | int source = bitboard.GetLsb(); |
| 2267573 | 106 | | Bitboard attacks = new Bitboard(Bitboard.KnightAttacks[source]); |
| | 107 | |
|
| 15043450 | 108 | | while (attacks.Board > 0) |
| | 109 | | { |
| 12775877 | 110 | | int target = attacks.GetLsb(); |
| | 111 | |
|
| 12775877 | 112 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | 113 | | { |
| 3104244 | 114 | | attacks.ClearBit(target); |
| 3104244 | 115 | | continue; |
| | 116 | | } |
| | 117 | |
|
| 9671633 | 118 | | AddMoveToMovesList(source, target); |
| | 119 | |
|
| 9671633 | 120 | | attacks.ClearBit(target); |
| | 121 | | } |
| | 122 | |
|
| 2267573 | 123 | | bitboard.ClearBit(source); |
| | 124 | | } |
| 1815060 | 125 | | } |
| | 126 | |
|
| | 127 | | private void GenerateBishopMoves() |
| | 128 | | { |
| 1815060 | 129 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Bishop]; |
| 4212586 | 130 | | while (bitboard.Board > 0) |
| | 131 | | { |
| 2397526 | 132 | | int source = bitboard.GetLsb(); |
| 2397526 | 133 | | Bitboard attacks = Bitboard.GetBishopAttacksFromTable((Square)source, Occupancies[(int)Side.Both]); |
| | 134 | |
|
| 15034281 | 135 | | while (attacks.Board > 0) |
| | 136 | | { |
| 12636755 | 137 | | int target = attacks.GetLsb(); |
| | 138 | |
|
| 12636755 | 139 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | 140 | | { |
| 3043387 | 141 | | attacks.ClearBit(target); |
| 3043387 | 142 | | continue; |
| | 143 | | } |
| | 144 | |
|
| 9593368 | 145 | | AddMoveToMovesList(source, target); |
| | 146 | |
|
| 9593368 | 147 | | attacks.ClearBit(target); |
| | 148 | | } |
| | 149 | |
|
| 2397526 | 150 | | bitboard.ClearBit(source); |
| | 151 | | } |
| 1815060 | 152 | | } |
| | 153 | |
|
| | 154 | | private void GeneratePawnMoves() |
| | 155 | | { |
| 1815060 | 156 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Pawn]; |
| 13420476 | 157 | | while (bitboard.Board > 0) |
| | 158 | | { |
| 11605416 | 159 | | int source = bitboard.GetLsb(); |
| 11605416 | 160 | | GeneratePawnPushes(source); |
| 11605416 | 161 | | GenerateEnPassants(source); |
| 11605416 | 162 | | GeneratePawnAttacks(source); |
| | 163 | |
|
| 11605416 | 164 | | bitboard.ClearBit(source); |
| | 165 | | } |
| 1815060 | 166 | | } |
| | 167 | |
|
| | 168 | | private void GenerateEnPassants(int source) |
| | 169 | | { |
| 11605416 | 170 | | if (EnPassantSquare == Square.NoSquare) |
| 11287484 | 171 | | return; |
| | 172 | |
|
| 317932 | 173 | | Bitboard attacks = new Bitboard(Bitboard.PawnAttacks[(int)SideToMove, source] & (1ul << (int)EnPassantSquare |
| 317932 | 174 | | if (attacks.Board > 0) |
| | 175 | | { |
| 16717 | 176 | | int target = attacks.GetLsb(); |
| 16717 | 177 | | AddPawnMove(source, target, true, false); |
| | 178 | | } |
| | 179 | |
|
| 317932 | 180 | | } |
| | 181 | |
|
| | 182 | |
|
| | 183 | | private void GeneratePawnPushes(int source) |
| | 184 | | { |
| 11605416 | 185 | | if (SideToMove == Side.Black) |
| | 186 | | { |
| 5761101 | 187 | | int oneSquarePush = source + 8; |
| 7142654 | 188 | | if (Occupancies[(int)Side.Both].GetBit(oneSquarePush) != 0) return; |
| 4379548 | 189 | | AddPawnMove(source, oneSquarePush, false, false); |
| 4379548 | 190 | | if (source is <= (int)Square.h7 and >= (int)Square.a7) |
| | 191 | | { |
| 2901663 | 192 | | int twoSquarePush = oneSquarePush + 8; |
| 3374252 | 193 | | if (Occupancies[(int)Side.Both].GetBit(twoSquarePush) != 0) return; |
| 2429074 | 194 | | AddPawnMove(source, twoSquarePush, false, true); |
| | 195 | | } |
| | 196 | | } |
| | 197 | | else |
| | 198 | | { |
| 5844315 | 199 | | int oneSquarePush = source - 8; |
| 7239904 | 200 | | if (Occupancies[(int)Side.Both].GetBit(oneSquarePush) != 0) return; |
| 4448726 | 201 | | AddPawnMove(source, oneSquarePush, false, false); |
| 4448726 | 202 | | if (source is <= (int)Square.h2 and >= (int)Square.a2) |
| | 203 | | { |
| 2989384 | 204 | | int twoSquarePush = oneSquarePush - 8; |
| 3277523 | 205 | | if (Occupancies[(int)Side.Both].GetBit(twoSquarePush) != 0) return; |
| 2701245 | 206 | | AddPawnMove(source, twoSquarePush, false, true); |
| | 207 | | } |
| | 208 | | } |
| 5638472 | 209 | | } |
| | 210 | |
|
| | 211 | | private void GeneratePawnAttacks(int source) |
| | 212 | | { |
| 11605416 | 213 | | Bitboard attacks = new Bitboard(Bitboard.PawnAttacks[(int)SideToMove, source] & Occupancies[(int)SideToMove. |
| | 214 | |
|
| 13616736 | 215 | | while (attacks.Board > 0) |
| | 216 | | { |
| 2011320 | 217 | | int target = attacks.GetLsb(); |
| | 218 | |
|
| 2011320 | 219 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | 220 | | { |
| 0 | 221 | | attacks.ClearBit(target); |
| 0 | 222 | | continue; |
| | 223 | | } |
| | 224 | |
|
| 2011320 | 225 | | AddPawnMove(source, target, false, false); |
| | 226 | |
|
| 2011320 | 227 | | attacks.ClearBit(target); |
| | 228 | | } |
| 11605416 | 229 | | } |
| | 230 | | private void GenerateCastleMoves() |
| | 231 | | { |
| | 232 | | // Squares should be empty and shouldn't castle through check, can avoid checking if landing position is che |
| | 233 | |
|
| 1815060 | 234 | | Bitboard occ = Occupancies[(int)Side.Both]; |
| 1815060 | 235 | | if (SideToMove == Side.White) |
| | 236 | | { |
| 920811 | 237 | | if (Castle.HasFlag(Castle.WhiteShort)) |
| | 238 | | { |
| 537662 | 239 | | if (occ.GetBit(Square.f1) == 0 && occ.GetBit(Square.g1) == 0 && !IsSquareAttacked(Square.e1, Side.Bl |
| 179021 | 240 | | Moves.Add(new Move(Square.e1, Square.g1, MoveTypes.Castle)); |
| | 241 | | } |
| 920811 | 242 | | if (Castle.HasFlag(Castle.WhiteLong)) |
| | 243 | | { |
| 579572 | 244 | | if (occ.GetBit(Square.d1) == 0 && occ.GetBit(Square.c1) == 0 && occ.GetBit(Square.b1) == 0 && !IsSqu |
| 183611 | 245 | | Moves.Add(new Move(Square.e1, Square.c1, MoveTypes.Castle)); |
| | 246 | | } |
| | 247 | | } |
| | 248 | | else |
| | 249 | | { |
| 894249 | 250 | | if (Castle.HasFlag(Castle.BlackShort)) |
| | 251 | | { |
| 560227 | 252 | | if (occ.GetBit(Square.f8) == 0 && occ.GetBit(Square.g8) == 0 && !IsSquareAttacked(Square.e8, Side.Wh |
| 329146 | 253 | | Moves.Add(new Move(Square.e8, Square.g8, MoveTypes.Castle)); |
| | 254 | | } |
| 894249 | 255 | | if (Castle.HasFlag(Castle.BlackLong)) |
| | 256 | | { |
| 732269 | 257 | | if (occ.GetBit(Square.d8) == 0 && occ.GetBit(Square.c8) == 0 && occ.GetBit(Square.b8) == 0 && !IsSqu |
| 335817 | 258 | | Moves.Add(new Move(Square.e8, Square.c8, MoveTypes.Castle)); |
| | 259 | | } |
| | 260 | | } |
| 1631449 | 261 | | } |
| | 262 | |
|
| | 263 | | private bool IsSquareAttacked(Square square, Side attackingSide) |
| | 264 | | { |
| 8552304 | 265 | | if ((Bitboard.GetBishopAttacksFromTable(square, Occupancies[(int)Side.Both]).Board & Pieces[(int)attackingSi |
| 173808 | 266 | | return true; |
| 8378496 | 267 | | if ((Bitboard.GetRookAttacksFromTable(square, Occupancies[(int)Side.Both]).Board & Pieces[(int)attackingSide |
| 67210 | 268 | | return true; |
| 8311286 | 269 | | if ((Bitboard.GetQueenAttacksFromTable(square, Occupancies[(int)Side.Both]).Board & Pieces[(int)attackingSid |
| 503385 | 270 | | return true; |
| 7807901 | 271 | | if ((Bitboard.KnightAttacks[(int)square] & Pieces[(int)attackingSide, (int)Piece.Knight].Board) != 0) |
| 120338 | 272 | | return true; |
| 7687563 | 273 | | if ((Bitboard.PawnAttacks[(int)attackingSide.Other(), (int)square] & Pieces[(int)attackingSide, (int)Piece.P |
| 264327 | 274 | | return true; |
| 7423236 | 275 | | if ((Bitboard.KingAttacks[(int)square] & Pieces[(int)attackingSide, (int)Piece.King].Board) != 0) |
| 2 | 276 | | return true; |
| 7423234 | 277 | | return false; |
| | 278 | | } |
| | 279 | |
|
| | 280 | | private void AddPawnMove(int source, int target, bool enpassant, bool doublePush) |
| | 281 | | { |
| | 282 | | // This assumes all incoming pawn moves are valid |
| 15986630 | 283 | | if (target is >= (int)Square.a1 and <= (int)Square.h1 || target is <= (int)Square.h8 and >= (int)Square.a8) |
| | 284 | | { |
| 214115 | 285 | | bool capture = IsSquareCapture(target); |
| | 286 | |
|
| 214115 | 287 | | Moves.Add(new Move((Square)source, (Square)target, capture ? MoveTypes.KnightPromotionCapture : MoveType |
| 214115 | 288 | | Moves.Add(new Move((Square)source, (Square)target, capture ? MoveTypes.BishopPromotionCapture : MoveType |
| 214115 | 289 | | Moves.Add(new Move((Square)source, (Square)target, capture ? MoveTypes.RookPromotionCapture : MoveTypes. |
| 214115 | 290 | | Moves.Add(new Move((Square)source, (Square)target, capture ? MoveTypes.QueenPromotionCapture : MoveTypes |
| | 291 | | } |
| 15772515 | 292 | | else if (enpassant || doublePush) |
| | 293 | | { |
| 5147036 | 294 | | Moves.Add(new Move((Square)source, (Square)target, enpassant ? MoveTypes.EnPassant : MoveTypes.DoublePus |
| | 295 | | } |
| | 296 | | else |
| | 297 | | { |
| 10625479 | 298 | | AddMoveToMovesList(source, target); |
| | 299 | | } |
| 10625479 | 300 | | } |
| | 301 | |
|
| | 302 | |
|
| | 303 | | private void AddMoveToMovesList(int source, int target) |
| | 304 | | { |
| 55262801 | 305 | | MoveType moveType = IsSquareCapture(target) ? MoveTypes.Capture : MoveTypes.Quiet; |
| 55262801 | 306 | | Move move = new Move((Square)source, (Square)target, moveType); |
| 55262801 | 307 | | Moves.Add(move); |
| 55262801 | 308 | | } |
| | 309 | | private bool IsSquareCapture(int target) |
| | 310 | | { |
| 55476916 | 311 | | return Occupancies[(int)SideToMove.Other()].GetBit(target) == 1; |
| | 312 | | } |
| | 313 | |
|
| | 314 | | public void MakeNullMove() |
| | 315 | | { |
| 120437 | 316 | | History.SaveBoardHistory(Piece.None, EnPassantSquare, Castle, BoardHash, LastDrawKiller, BestMove); |
| 120437 | 317 | | UpdateEnPassant(Move.NoMove); |
| 120437 | 318 | | FlipSideToMove(); |
| 120437 | 319 | | } |
| | 320 | |
|
| | 321 | | public void UndoNullMove() |
| | 322 | | { |
| 120437 | 323 | | History.BoardHistory history = History.RestoreBoardHistory(); |
| 120437 | 324 | | FlipSideToMove(); |
| 120437 | 325 | | LastDrawKiller = history.LastDrawKiller; |
| 120437 | 326 | | BoardHash = history.BoardHash; |
| 120437 | 327 | | Castle = history.CastlingRights; |
| 120437 | 328 | | EnPassantSquare = history.EnPassantSquare; |
| 120437 | 329 | | } |
| | 330 | | } |
| | 331 | | } |