]> git.sesse.net Git - stockfish/blob - src/movegen.cpp
Standardize Comments
[stockfish] / src / movegen.cpp
1 /*
2   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3   Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
4
5   Stockfish is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9
10   Stockfish is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "movegen.h"
20
21 #include <cassert>
22 #include <initializer_list>
23
24 #include "bitboard.h"
25 #include "position.h"
26
27 namespace Stockfish {
28
29 namespace {
30
31   template<GenType Type, Direction D, bool Enemy>
32   ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) {
33
34     if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
35     {
36         *moveList++ = make<PROMOTION>(to - D, to, QUEEN);
37         if constexpr (Enemy && Type == CAPTURES)
38         {
39             *moveList++ = make<PROMOTION>(to - D, to, ROOK);
40             *moveList++ = make<PROMOTION>(to - D, to, BISHOP);
41             *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
42         }
43     }
44
45     if constexpr ((Type == QUIETS && !Enemy) || Type == EVASIONS || Type == NON_EVASIONS)
46     {
47         *moveList++ = make<PROMOTION>(to - D, to, ROOK);
48         *moveList++ = make<PROMOTION>(to - D, to, BISHOP);
49         *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
50     }
51
52     return moveList;
53   }
54
55
56   template<Color Us, GenType Type>
57   ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
58
59     constexpr Color     Them     = ~Us;
60     constexpr Bitboard  TRank7BB = (Us == WHITE ? Rank7BB    : Rank2BB);
61     constexpr Bitboard  TRank3BB = (Us == WHITE ? Rank3BB    : Rank6BB);
62     constexpr Direction Up       = pawn_push(Us);
63     constexpr Direction UpRight  = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
64     constexpr Direction UpLeft   = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
65
66     const Bitboard emptySquares = ~pos.pieces();
67     const Bitboard enemies      =  Type == EVASIONS ? pos.checkers()
68                                                     : pos.pieces(Them);
69
70     Bitboard pawnsOn7    = pos.pieces(Us, PAWN) &  TRank7BB;
71     Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
72
73     // Single and double pawn pushes, no promotions
74     if constexpr (Type != CAPTURES)
75     {
76         Bitboard b1 = shift<Up>(pawnsNotOn7)   & emptySquares;
77         Bitboard b2 = shift<Up>(b1 & TRank3BB) & emptySquares;
78
79         if constexpr (Type == EVASIONS) // Consider only blocking squares
80         {
81             b1 &= target;
82             b2 &= target;
83         }
84
85         if constexpr (Type == QUIET_CHECKS)
86         {
87             // To make a quiet check, you either make a direct check by pushing a pawn
88             // or push a blocker pawn that is not on the same file as the enemy king.
89             // Discovered check promotion has been already generated amongst the captures.
90             Square ksq = pos.square<KING>(Them);
91             Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq);
92             b1 &= pawn_attacks_bb(Them, ksq) | shift<   Up>(dcCandidatePawns);
93             b2 &= pawn_attacks_bb(Them, ksq) | shift<Up+Up>(dcCandidatePawns);
94         }
95
96         while (b1)
97         {
98             Square to = pop_lsb(b1);
99             *moveList++ = make_move(to - Up, to);
100         }
101
102         while (b2)
103         {
104             Square to = pop_lsb(b2);
105             *moveList++ = make_move(to - Up - Up, to);
106         }
107     }
108
109     // Promotions and underpromotions
110     if (pawnsOn7)
111     {
112         Bitboard b1 = shift<UpRight>(pawnsOn7) & enemies;
113         Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
114         Bitboard b3 = shift<Up     >(pawnsOn7) & emptySquares;
115
116         if constexpr (Type == EVASIONS)
117             b3 &= target;
118
119         while (b1)
120             moveList = make_promotions<Type, UpRight, true>(moveList, pop_lsb(b1));
121
122         while (b2)
123             moveList = make_promotions<Type, UpLeft, true>(moveList, pop_lsb(b2));
124
125         while (b3)
126             moveList = make_promotions<Type, Up,    false>(moveList, pop_lsb(b3));
127     }
128
129     // Standard and en passant captures
130     if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
131     {
132         Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
133         Bitboard b2 = shift<UpLeft >(pawnsNotOn7) & enemies;
134
135         while (b1)
136         {
137             Square to = pop_lsb(b1);
138             *moveList++ = make_move(to - UpRight, to);
139         }
140
141         while (b2)
142         {
143             Square to = pop_lsb(b2);
144             *moveList++ = make_move(to - UpLeft, to);
145         }
146
147         if (pos.ep_square() != SQ_NONE)
148         {
149             assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
150
151             // An en passant capture cannot resolve a discovered check
152             if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
153                 return moveList;
154
155             b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
156
157             assert(b1);
158
159             while (b1)
160                 *moveList++ = make<EN_PASSANT>(pop_lsb(b1), pos.ep_square());
161         }
162     }
163
164     return moveList;
165   }
166
167
168   template<Color Us, PieceType Pt, bool Checks>
169   ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
170
171     static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
172
173     Bitboard bb = pos.pieces(Us, Pt);
174
175     while (bb)
176     {
177         Square from = pop_lsb(bb);
178         Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
179
180         // To check, you either move freely a blocker or make a direct check.
181         if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from)))
182             b &= pos.check_squares(Pt);
183
184         while (b)
185             *moveList++ = make_move(from, pop_lsb(b));
186     }
187
188     return moveList;
189   }
190
191
192   template<Color Us, GenType Type>
193   ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
194
195     static_assert(Type != LEGAL, "Unsupported type in generate_all()");
196
197     constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations
198     const Square ksq = pos.square<KING>(Us);
199     Bitboard target;
200
201     // Skip generating non-king moves when in double check
202     if (Type != EVASIONS || !more_than_one(pos.checkers()))
203     {
204         target = Type == EVASIONS     ?  between_bb(ksq, lsb(pos.checkers()))
205                : Type == NON_EVASIONS ? ~pos.pieces( Us)
206                : Type == CAPTURES     ?  pos.pieces(~Us)
207                                       : ~pos.pieces(   ); // QUIETS || QUIET_CHECKS
208
209         moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
210         moveList = generate_moves<Us, KNIGHT, Checks>(pos, moveList, target);
211         moveList = generate_moves<Us, BISHOP, Checks>(pos, moveList, target);
212         moveList = generate_moves<Us,   ROOK, Checks>(pos, moveList, target);
213         moveList = generate_moves<Us,  QUEEN, Checks>(pos, moveList, target);
214     }
215
216     if (!Checks || pos.blockers_for_king(~Us) & ksq)
217     {
218         Bitboard b = attacks_bb<KING>(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target);
219         if (Checks)
220             b &= ~attacks_bb<QUEEN>(pos.square<KING>(~Us));
221
222         while (b)
223             *moveList++ = make_move(ksq, pop_lsb(b));
224
225         if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
226             for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
227                 if (!pos.castling_impeded(cr) && pos.can_castle(cr))
228                     *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr));
229     }
230
231     return moveList;
232   }
233
234 } // namespace
235
236
237 // <CAPTURES>     Generates all pseudo-legal captures plus queen promotions
238 // <QUIETS>       Generates all pseudo-legal non-captures and underpromotions
239 // <EVASIONS>     Generates all pseudo-legal check evasions
240 // <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
241 // <QUIET_CHECKS> Generates all pseudo-legal non-captures giving check,
242 //                except castling and promotions
243 //
244 // Returns a pointer to the end of the move list.
245
246 template<GenType Type>
247 ExtMove* generate(const Position& pos, ExtMove* moveList) {
248
249   static_assert(Type != LEGAL, "Unsupported type in generate()");
250   assert((Type == EVASIONS) == bool(pos.checkers()));
251
252   Color us = pos.side_to_move();
253
254   return us == WHITE ? generate_all<WHITE, Type>(pos, moveList)
255                      : generate_all<BLACK, Type>(pos, moveList);
256 }
257
258 // Explicit template instantiations
259 template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
260 template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
261 template ExtMove* generate<EVASIONS>(const Position&, ExtMove*);
262 template ExtMove* generate<QUIET_CHECKS>(const Position&, ExtMove*);
263 template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
264
265
266 // generate<LEGAL> generates all the legal moves in the given position
267
268 template<>
269 ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
270
271   Color us = pos.side_to_move();
272   Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us);
273   Square ksq = pos.square<KING>(us);
274   ExtMove* cur = moveList;
275
276   moveList = pos.checkers() ? generate<EVASIONS    >(pos, moveList)
277                             : generate<NON_EVASIONS>(pos, moveList);
278   while (cur != moveList)
279       if (  ((pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT)
280           && !pos.legal(*cur))
281           *cur = (--moveList)->move;
282       else
283           ++cur;
284
285   return moveList;
286 }
287
288 } // namespace Stockfish