| | | 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 | | { |
| | 1819284 | 12 | | Moves = new List<Move>(); |
| | | 13 | | |
| | 1819284 | 14 | | GeneratePawnMoves(); |
| | 1819284 | 15 | | GenerateBishopMoves(); |
| | 1819284 | 16 | | GenerateKnightMoves(); |
| | 1819284 | 17 | | GenerateRookMoves(); |
| | 1819284 | 18 | | GenerateQueenMoves(); |
| | 1819284 | 19 | | GenerateKingMoves(); |
| | 1819284 | 20 | | } |
| | | 21 | | |
| | | 22 | | private void GenerateKingMoves() |
| | | 23 | | { |
| | 1819284 | 24 | | int source = Pieces[(int)SideToMove, (int)Piece.King].GetLsb(); |
| | 1819284 | 25 | | Bitboard attacks = new Bitboard(Bitboard.KingAttacks[source]); |
| | | 26 | | |
| | 11556563 | 27 | | while (attacks.Board > 0) |
| | | 28 | | { |
| | 9737279 | 29 | | int target = attacks.GetLsb(); |
| | 9737279 | 30 | | attacks.ClearBit(target); |
| | 9737279 | 31 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | | 32 | | { |
| | | 33 | | continue; |
| | | 34 | | } |
| | 5262184 | 35 | | AddMoveToMovesList(source, target); |
| | | 36 | | } |
| | | 37 | | |
| | 1819284 | 38 | | GenerateCastleMoves(); |
| | 1819284 | 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 | | { |
| | 1819284 | 48 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Queen]; |
| | 3177105 | 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 | | } |
| | 1819284 | 71 | | } |
| | | 72 | | |
| | | 73 | | private void GenerateRookMoves() |
| | | 74 | | { |
| | 1819284 | 75 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Rook]; |
| | 4984531 | 76 | | while (bitboard.Board > 0) |
| | | 77 | | { |
| | 3165247 | 78 | | int source = bitboard.GetLsb(); |
| | 3165247 | 79 | | Bitboard attacks = Bitboard.GetRookAttacksFromTable((Square)source, Occupancies[(int)Side.Both]); |
| | | 80 | | |
| | 19155186 | 81 | | while (attacks.Board > 0) |
| | | 82 | | { |
| | 15989939 | 83 | | int target = attacks.GetLsb(); |
| | | 84 | | |
| | 15989939 | 85 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | | 86 | | { |
| | 5881165 | 87 | | attacks.ClearBit(target); |
| | 5881165 | 88 | | continue; |
| | | 89 | | } |
| | | 90 | | |
| | 10108774 | 91 | | AddMoveToMovesList(source, target); |
| | | 92 | | |
| | 10108774 | 93 | | attacks.ClearBit(target); |
| | | 94 | | } |
| | | 95 | | |
| | 3165247 | 96 | | bitboard.ClearBit(source); |
| | | 97 | | } |
| | 1819284 | 98 | | } |
| | | 99 | | |
| | | 100 | | private void GenerateKnightMoves() |
| | | 101 | | { |
| | 1819284 | 102 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Knight]; |
| | 4086857 | 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 | | } |
| | 1819284 | 125 | | } |
| | | 126 | | |
| | | 127 | | private void GenerateBishopMoves() |
| | | 128 | | { |
| | 1819284 | 129 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Bishop]; |
| | 4220537 | 130 | | while (bitboard.Board > 0) |
| | | 131 | | { |
| | 2401253 | 132 | | int source = bitboard.GetLsb(); |
| | 2401253 | 133 | | Bitboard attacks = Bitboard.GetBishopAttacksFromTable((Square)source, Occupancies[(int)Side.Both]); |
| | | 134 | | |
| | 15064041 | 135 | | while (attacks.Board > 0) |
| | | 136 | | { |
| | 12662788 | 137 | | int target = attacks.GetLsb(); |
| | | 138 | | |
| | 12662788 | 139 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | | 140 | | { |
| | 3048330 | 141 | | attacks.ClearBit(target); |
| | 3048330 | 142 | | continue; |
| | | 143 | | } |
| | | 144 | | |
| | 9614458 | 145 | | AddMoveToMovesList(source, target); |
| | | 146 | | |
| | 9614458 | 147 | | attacks.ClearBit(target); |
| | | 148 | | } |
| | | 149 | | |
| | 2401253 | 150 | | bitboard.ClearBit(source); |
| | | 151 | | } |
| | 1819284 | 152 | | } |
| | | 153 | | |
| | | 154 | | private void GeneratePawnMoves() |
| | | 155 | | { |
| | 1819284 | 156 | | Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Pawn]; |
| | 13445228 | 157 | | while (bitboard.Board > 0) |
| | | 158 | | { |
| | 11625944 | 159 | | int source = bitboard.GetLsb(); |
| | 11625944 | 160 | | GeneratePawnPushes(source); |
| | 11625944 | 161 | | GenerateEnPassants(source); |
| | 11625944 | 162 | | GeneratePawnAttacks(source); |
| | | 163 | | |
| | 11625944 | 164 | | bitboard.ClearBit(source); |
| | | 165 | | } |
| | 1819284 | 166 | | } |
| | | 167 | | |
| | | 168 | | private void GenerateEnPassants(int source) |
| | | 169 | | { |
| | 11625944 | 170 | | if (EnPassantSquare == Square.NoSquare) |
| | 11305078 | 171 | | return; |
| | | 172 | | |
| | 320866 | 173 | | Bitboard attacks = new Bitboard(Bitboard.PawnAttacks[(int)SideToMove, source] & (1ul << (int)EnPassantSquare |
| | 320866 | 174 | | if (attacks.Board > 0) |
| | | 175 | | { |
| | 17370 | 176 | | int target = attacks.GetLsb(); |
| | 17370 | 177 | | AddPawnMove(source, target, true, false); |
| | | 178 | | } |
| | | 179 | | |
| | 320866 | 180 | | } |
| | | 181 | | |
| | | 182 | | |
| | | 183 | | private void GeneratePawnPushes(int source) |
| | | 184 | | { |
| | 11625944 | 185 | | if (SideToMove == Side.Black) |
| | | 186 | | { |
| | 5773921 | 187 | | int oneSquarePush = source + 8; |
| | 7158581 | 188 | | if (Occupancies[(int)Side.Both].GetBit(oneSquarePush) != 0) return; |
| | 4389261 | 189 | | AddPawnMove(source, oneSquarePush, false, false); |
| | 4389261 | 190 | | if (source is <= (int)Square.h7 and >= (int)Square.a7) |
| | | 191 | | { |
| | 2905897 | 192 | | int twoSquarePush = oneSquarePush + 8; |
| | 3378510 | 193 | | if (Occupancies[(int)Side.Both].GetBit(twoSquarePush) != 0) return; |
| | 2433284 | 194 | | AddPawnMove(source, twoSquarePush, false, true); |
| | | 195 | | } |
| | | 196 | | } |
| | | 197 | | else |
| | | 198 | | { |
| | 5852023 | 199 | | int oneSquarePush = source - 8; |
| | 7249603 | 200 | | if (Occupancies[(int)Side.Both].GetBit(oneSquarePush) != 0) return; |
| | 4454443 | 201 | | AddPawnMove(source, oneSquarePush, false, false); |
| | 4454443 | 202 | | if (source is <= (int)Square.h2 and >= (int)Square.a2) |
| | | 203 | | { |
| | 2990696 | 204 | | int twoSquarePush = oneSquarePush - 8; |
| | 3278946 | 205 | | if (Occupancies[(int)Side.Both].GetBit(twoSquarePush) != 0) return; |
| | 2702446 | 206 | | AddPawnMove(source, twoSquarePush, false, true); |
| | | 207 | | } |
| | | 208 | | } |
| | 5649557 | 209 | | } |
| | | 210 | | |
| | | 211 | | private void GeneratePawnAttacks(int source) |
| | | 212 | | { |
| | 11625944 | 213 | | Bitboard attacks = new Bitboard(Bitboard.PawnAttacks[(int)SideToMove, source] & Occupancies[(int)SideToMove. |
| | | 214 | | |
| | 13637901 | 215 | | while (attacks.Board > 0) |
| | | 216 | | { |
| | 2011957 | 217 | | int target = attacks.GetLsb(); |
| | | 218 | | |
| | 2011957 | 219 | | if (Occupancies[(int)SideToMove].GetBit(target) == 1) |
| | | 220 | | { |
| | 0 | 221 | | attacks.ClearBit(target); |
| | 0 | 222 | | continue; |
| | | 223 | | } |
| | | 224 | | |
| | 2011957 | 225 | | AddPawnMove(source, target, false, false); |
| | | 226 | | |
| | 2011957 | 227 | | attacks.ClearBit(target); |
| | | 228 | | } |
| | 11625944 | 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 | | |
| | 1819284 | 234 | | Bitboard occ = Occupancies[(int)Side.Both]; |
| | 1819284 | 235 | | if (SideToMove == Side.White) |
| | | 236 | | { |
| | 922871 | 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 | | } |
| | 922871 | 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 | | { |
| | 896413 | 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 | | } |
| | 896413 | 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 | | } |
| | 1635673 | 261 | | } |
| | | 262 | | |
| | | 263 | | private bool IsSquareAttacked(Square square, Side attackingSide) |
| | | 264 | | { |
| | 8561032 | 265 | | if ((Bitboard.GetBishopAttacksFromTable(square, Occupancies[(int)Side.Both]).Board & Pieces[(int)attackingSi |
| | 173915 | 266 | | return true; |
| | 8387117 | 267 | | if ((Bitboard.GetRookAttacksFromTable(square, Occupancies[(int)Side.Both]).Board & Pieces[(int)attackingSide |
| | 67780 | 268 | | return true; |
| | 8319337 | 269 | | if ((Bitboard.GetQueenAttacksFromTable(square, Occupancies[(int)Side.Both]).Board & Pieces[(int)attackingSid |
| | 503385 | 270 | | return true; |
| | 7815952 | 271 | | if ((Bitboard.KnightAttacks[(int)square] & Pieces[(int)attackingSide, (int)Piece.Knight].Board) != 0) |
| | 120338 | 272 | | return true; |
| | 7695614 | 273 | | if ((Bitboard.PawnAttacks[(int)attackingSide.Other(), (int)square] & Pieces[(int)attackingSide, (int)Piece.P |
| | 264384 | 274 | | return true; |
| | 7431230 | 275 | | if ((Bitboard.KingAttacks[(int)square] & Pieces[(int)attackingSide, (int)Piece.King].Board) != 0) |
| | 2 | 276 | | return true; |
| | 7431228 | 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 |
| | 16008761 | 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 | | } |
| | 15794646 | 292 | | else if (enpassant || doublePush) |
| | | 293 | | { |
| | 5153100 | 294 | | Moves.Add(new Move((Square)source, (Square)target, enpassant ? MoveTypes.EnPassant : MoveTypes.DoublePus |
| | | 295 | | } |
| | | 296 | | else |
| | | 297 | | { |
| | 10641546 | 298 | | AddMoveToMovesList(source, target); |
| | | 299 | | } |
| | 10641546 | 300 | | } |
| | | 301 | | |
| | | 302 | | |
| | | 303 | | private void AddMoveToMovesList(int source, int target) |
| | | 304 | | { |
| | 55355908 | 305 | | MoveType moveType = IsSquareCapture(target) ? MoveTypes.Capture : MoveTypes.Quiet; |
| | 55355908 | 306 | | Move move = new Move((Square)source, (Square)target, moveType); |
| | 55355908 | 307 | | Moves.Add(move); |
| | 55355908 | 308 | | } |
| | | 309 | | private bool IsSquareCapture(int target) |
| | | 310 | | { |
| | 55570023 | 311 | | return Occupancies[(int)SideToMove.Other()].GetBit(target) == 1; |
| | | 312 | | } |
| | | 313 | | |
| | | 314 | | public void MakeNullMove() |
| | | 315 | | { |
| | 120438 | 316 | | History.SaveBoardHistory(Piece.None, EnPassantSquare, Castle, BoardHash, LastDrawKiller, BestMove); |
| | 120438 | 317 | | UpdateEnPassant(Move.NoMove); |
| | 120438 | 318 | | FlipSideToMove(); |
| | 120438 | 319 | | } |
| | | 320 | | |
| | | 321 | | public void UndoNullMove() |
| | | 322 | | { |
| | 120438 | 323 | | History.BoardHistory history = History.RestoreBoardHistory(); |
| | 120438 | 324 | | FlipSideToMove(); |
| | 120438 | 325 | | LastDrawKiller = history.LastDrawKiller; |
| | 120438 | 326 | | BoardHash = history.BoardHash; |
| | 120438 | 327 | | Castle = history.CastlingRights; |
| | 120438 | 328 | | EnPassantSquare = history.EnPassantSquare; |
| | 120438 | 329 | | } |
| | | 330 | | } |
| | | 331 | | } |