libscid 0.1.0
Chess applications made easy.
Loading...
Searching...
No Matches
move_predicates.h
Go to the documentation of this file.
1
5#pragma once
6
7#include "scid/core/board.h"
8
9#include <cassert>
10#include <utility>
11
12// These functions use the following move classification:
13// - a VALID move is a move that respects the basic rules for moving the pieces,
14// for example a bishop should move diagonally. Validating this type of moves
15// do not require any info about the current position.
16// - an ATTACK move is a valid move that can capture an enemy piece at the
17// destination square. It takes into account the special rule for pawns that
18// can capture only diagonally and the board position for obstacles, for
19// example a valid diagonal bishop move is an attack move only if the
20// in-between squares are empty. To validate this type of moves it is
21// necessary to know the location of empty squares in the current position.
22// - a PSEUDO-LEGAL move is an attack move, or a non-capture pawn move, or a
23// castle move.
24// - a LEGAL move is a pseudo-legal move where the destination square is not
25// occupied by a friendly piece and that do not leave the king in check.
26// To validate this type of moves it is necessary to know the type and
27// position of every piece.
28
29namespace scid::core {
30
31namespace move_predicates {
32
33constexpr int NSQUARES = 8;
34constexpr int kWPHomeRank = 1;
35constexpr int kBPHomeRank = NSQUARES - 2;
36
37inline bool valid_king(squareT sqFrom, squareT sqTo) {
38 unsigned distRank = 1 + (sqTo / NSQUARES) - (sqFrom / NSQUARES);
39 unsigned distFyle = 1 + (sqTo % NSQUARES) - (sqFrom % NSQUARES);
40 return distRank <= 2 && distFyle <= 2;
41}
42
43inline bool valid_knight(squareT sqFrom, squareT sqTo) {
44 int distRank = (sqTo / NSQUARES) - (sqFrom / NSQUARES);
45 int distFyle = (sqTo % NSQUARES) - (sqFrom % NSQUARES);
46 int distProduct = distRank * distFyle;
47 return (distProduct == 2 || distProduct == -2);
48}
49
50inline int valid_slider(squareT sqFrom, squareT sqTo, pieceT pieceType) {
51 assert(pieceType == QUEEN || pieceType == ROOK || pieceType == BISHOP);
52
53 int distRank = (sqTo / NSQUARES) - (sqFrom / NSQUARES);
54 int distFyle = (sqTo % NSQUARES) - (sqFrom % NSQUARES);
55
56 // Make sure the direction is valid:
57 int sqStep;
58 bool isDiagonal = false;
59 if (distRank == 0) {
60 sqStep = 1; // horizontal
61 } else if (distFyle == 0) {
62 sqStep = NSQUARES; // vertical
63 } else if (distFyle == distRank) {
64 sqStep = NSQUARES + 1;
65 isDiagonal = true;
66 } else if (distFyle == -distRank) {
67 sqStep = NSQUARES - 1;
68 isDiagonal = true;
69 } else {
70 return 0;
71 }
72 if (pieceType == ROOK && isDiagonal)
73 return 0;
74 if (pieceType == BISHOP && !isDiagonal)
75 return 0;
76
77 return sqStep;
78}
79
80inline bool attack_pawn(squareT sqFrom, squareT sqTo, colorT pieceCol) {
81 int distRank = (sqTo / NSQUARES) - (sqFrom / NSQUARES);
82 int distFyle = (sqTo % NSQUARES) - (sqFrom % NSQUARES);
83 if (pieceCol == WHITE && distRank != 1)
84 return false;
85 if (pieceCol == BLACK && distRank != -1)
86 return false;
87
88 return (distFyle == 1 || distFyle == -1);
89}
90
91template <typename TFunc>
92bool attack_slider(squareT sqFrom, squareT sqTo, pieceT pieceType,
93 TFunc isOccupied) {
94 int sqStep = valid_slider(sqFrom, sqTo, pieceType);
95 if (sqStep == 0)
96 return false;
97
98 // Make sure all the in-between squares are empty:
99 if (sqFrom > sqTo)
100 sqStep = -sqStep;
101
102 for (int sq = sqFrom + sqStep; sq != sqTo; sq += sqStep) {
103 if (isOccupied(sq))
104 return false;
105 }
106
107 return true;
108}
109
123template <typename TFunc>
124bool attack(squareT sqFrom, squareT sqTo, pieceT pieceCol, pieceT pieceType,
125 TFunc isOccupied) {
126 switch (pieceType) {
127 case KING:
128 return valid_king(sqFrom, sqTo);
129 case KNIGHT:
130 return valid_knight(sqFrom, sqTo);
131 case PAWN:
132 return attack_pawn(sqFrom, sqTo, pieceCol);
133 default:
134 break;
135 }
136 return attack_slider(sqFrom, sqTo, pieceType, isOccupied);
137}
138
139template <typename TFunc>
140inline bool pseudo_advance_pawn(squareT sqFrom, squareT sqTo, colorT pieceCol,
141 TFunc isOccupied) {
142 if ((sqTo % NSQUARES) != (sqFrom % NSQUARES) // Different file
143 || isOccupied(sqTo)) // Pawns can only capture diagonally
144 return false;
145
146 int fromRank = sqFrom / NSQUARES;
147 int distRank = (sqTo / NSQUARES) - fromRank;
148 if (pieceCol == WHITE)
149 return distRank == 1 || (distRank == 2 && fromRank == kWPHomeRank &&
150 !isOccupied(sqFrom + NSQUARES));
151
152 return distRank == -1 || (distRank == -2 && fromRank == kBPHomeRank &&
153 !isOccupied(sqFrom - NSQUARES));
154}
155
156template <typename TFunc>
157bool pseudo(squareT sqFrom, squareT sqTo, colorT pieceCol, pieceT pieceType,
158 TFunc isOccupied) {
159 // TODO: castle moves
160 if (pieceType == PAWN &&
161 pseudo_advance_pawn(sqFrom, sqTo, pieceCol, isOccupied))
162 return true;
163
164 return attack(sqFrom, sqTo, pieceCol, pieceType, isOccupied);
165}
166
182template <typename TFunc>
183inline std::pair<pieceT, squareT> opens_ray(squareT sqFrom, squareT sqTo,
184 squareT sqRay, TFunc isOccupied) {
185 assert(sqRay != sqFrom);
186
187 int fyleFrom = sqFrom % NSQUARES;
188 int distFyle = (sqRay % NSQUARES) - fyleFrom;
189 int distRank = (sqRay / NSQUARES) - (sqFrom / NSQUARES);
190
191 // Make sure the direction is valid:
192 int fyleEdge;
193 int sqStep;
194 pieceT pt;
195 if (distFyle == 0) {
196 sqStep = NSQUARES; // vertical
197 fyleEdge = -1;
198 pt = ROOK;
199 } else {
200 if (fyleFrom == 0 || fyleFrom == (NSQUARES - 1))
201 return {INVALID_PIECE, 0};
202
203 if (distRank == 0) {
204 sqStep = 1; // horizontal
205 fyleEdge = 0;
206 pt = ROOK;
207 } else if (distFyle == distRank) {
208 sqStep = NSQUARES + 1;
209 fyleEdge = 0;
210 pt = BISHOP;
211 } else if (distFyle == -distRank) {
212 sqStep = NSQUARES - 1;
213 fyleEdge = NSQUARES - 1;
214 pt = BISHOP;
215 } else {
216 return {INVALID_PIECE, 0};
217 }
218 }
219 if (sqFrom > sqRay) {
220 sqStep = -sqStep;
221 fyleEdge = NSQUARES - 1 - fyleEdge;
222 }
223
224 for (int sq = sqFrom + sqStep; sq != sqRay; sq += sqStep) {
225 if (sq == sqTo || isOccupied(sq))
226 return {INVALID_PIECE, 0};
227 }
228
229 for (int sq = sqFrom - sqStep; sq < NSQUARES * NSQUARES; sq -= sqStep) {
230 if (sq < 0 || sq == sqTo)
231 break;
232
233 if (isOccupied(sq))
234 return {pt, static_cast<squareT>(sq)};
235
236 if ((sq % NSQUARES) == fyleEdge)
237 break;
238 }
239 return {INVALID_PIECE, 0};
240}
241
248inline bool blocks_ray(squareT sqFrom, squareT sqTo, squareT sqBlock) {
249 return !move_predicates::attack_slider(sqFrom, sqTo, QUEEN,
250 [&](auto sq) { return sq == sqBlock; });
251}
252
253} // namespace move_predicates
254
255} // namespace scid::core
Chess board constants, piece helpers, square geometry, and directions.
bool attack(squareT sqFrom, squareT sqTo, pieceT pieceCol, pieceT pieceType, TFunc isOccupied)
Validate an ATTACK move, that is if a piece placed at sqFrom can capture an enemy piece at sqTo.
Definition move_predicates.h:124
std::pair< pieceT, squareT > opens_ray(squareT sqFrom, squareT sqTo, squareT sqRay, TFunc isOccupied)
Given a pseudo-legal move, this functions return the type and the location of the piece that can poss...
Definition move_predicates.h:183
bool blocks_ray(squareT sqFrom, squareT sqTo, squareT sqBlock)
Checks if there is a valid ray from sqFrom to sqTo and if a piece on sqBlock would block that ray.
Definition move_predicates.h:248