Belofte version 2.1.9
A promising chess program using the UCI or Winboard interface
movelist.cpp
Go to the documentation of this file.
1/*---------------------------------------------------------------------+
2 * File: movelist.cpp
3 * Project: part of belofte - A Promising Chess Program
4 * Author: yves
5 * SPDX-License-Identifier: GPL-2.0-only
6+----------------------------------------------------------------------*/
7
8#include "belofte.h"
9
10/**
11 * Allow index mapper for char values of piece into int in 1-12 range
12 * to reduce space and easy initialisation
13 */
14namespace belofte {
17}
18
19namespace belofteeval {
20 extern bScore piecevalues[];
21 extern bScore centerplayvalues[];
23}
24
25//-----------------------------------------------------------------------
26
27void bMoveList::addMoveToList(bBasicBoard const& b, bMove& m, bool const isInCheck)
28{
29 if (isInCheck) {
30 m.setCheck();
31 bBasicBoard newboard(b);
32 newboard.applyMove(m);
33 bMoveList ml;
34 if (!ml.atLeastOneMovePossible(newboard))
35 m.setGameEnd();
36 }
37
38 bScore sc;
39 if (m.getGameEnd()) {
40 sc = SCORE_MATE;
41 ++m_nQSMoves; // always consider move ending game as non-silent
42 } else if (isKeepScores()) {
43 // malus points for moving own piece, except for pawn
44 // the greater the value of the piece, the more the penalty
48 if (m.isCapture()) {
50 }
51 if (m.isPawnMove()) {
52 if (m.isPromotion()) {
53 sc += 310;
54 if (m.isMajorPromotion()) sc += 500;
55 } else if (b.pieceCount() < 12 || b.getPly() > 70) {
56 // bonus for playing pawn in endgame
57 // we do not have gamestage here, so there is an approximation
58 sc += 10;
59 }
60 } else if (m.isCastleMove()) {
61 sc += 250; // malus for king move 200, so half a pawn
62 }
63 if (m.isCheck()) sc += 80; // check is less than a pawn, we prefer captures
64 if (m.isNonSilent()) ++m_nQSMoves;
65 } else {
66 sc = 0;
67 if (m.isNonSilent()) ++m_nQSMoves;
68 }
69
70 m.setScore(sc);
71
72#if defined(INCOMPLETE_C11)
73 m_lmoves.insert(m_lmoves.end(), m);
74 if (getBestMoveId()) {
75 // there is a best move registered, so we are adding second or later move
76 if (bestMoveHasImproved(sc)) {
78 }
79 // we clear sorted flag too eagerly, but testing it would cost equal cpu
81 } else {
82 // no bestmove id stored yet, so newly inserted move is best move
83 setBestMoveScore(1, sc);
84 }
85#else
86 if (isKeepScores()) {
87 if (getBestMoveId()) {
88 // there is a best move registered, so we are adding second or later move
89 if (isNeedSorted()) {
90 if (bestMoveHasImproved(sc)) {
91 m_lmoves.insert(m_lmoves.begin(), m);
92 setBestMoveScore(1, sc);
93 } else {
94 // best move has not improved
95 m_lmoves.insert(m_lmoves.end(), m);
96 if (getNumberOfMoves() > 2) {
97 // we do not know if the moves beyond move 1 are in correct order
99 }
100 }
101 } else {
102 // sorting is not required, adding second move
103 m_lmoves.insert(m_lmoves.end(), m);
104 if (bestMoveHasImproved(sc)) {
105 // update best move
107 }
108 }
109 } else {
110 // no bestmove id stored yet, so newly inserted move is best move
111 m_lmoves.insert(m_lmoves.end(), m);
112 setBestMoveScore(1, sc);
114 if (isNeedSorted()) setIsSorted();
115 }
116 } else {
117 // do not keep scores
118 m_lmoves.insert(m_lmoves.end(), m);
119 if (!getBestMoveId()) setBestMoveScore(1, sc);
120 }
121#endif
122}
123
124/**
125 * Store score of move and update best move
126 * @param moveid of move to be stored [1 -> max number of moves]
127 * @param score to be stored
128 * @return true if best score updated
129 */
130bool bMoveList::setScoreOfMove(movenum_t const moveid, bScore const score)
131{
132 if (getBestMoveId()) {
133 // there is a best move registered, so we are adding second or later move
134 if (bestMoveHasImproved(score)) {
135 setBestMoveScore(moveid, score);
136 // the best move is no longer the first move
138 } else {
139 setMoveScore(moveid, score);
140 return false;
141 }
142 } else {
143 // no bestmove id stored yet, so newly inserted move is best move
144 setBestMoveScore(moveid, score);
145 // list is sorted as only one member
146 if (isNeedSorted()) setIsSorted();
147 }
148 return true;
149}
150
151/**
152 * Store score of move and update best move
153 * @param moveid of move to be stored [1 -> max number of moves]
154 * @param score to be stored
155 */
157{
158 if (getBestMoveId()) {
159 // there is a best move registered, so we are adding second or later move
160 if (bestMoveHasImproved(score)) {
161 setBestMoveScore(moveid, score);
162 } else {
163 setMoveScore(moveid, score);
164 }
165 } else {
166 // no bestmove id stored yet, so newly inserted move is best move
167 setBestMoveScore(moveid, score);
168 }
169}
170
171/**
172 * Only add move to movelist if valid
173 * @param b board on which move is calculated
174 * @param m move to be added
175 * @return number of moves added (1), meaning player is not in check after move
176 */
178{
179 bBasicBoard newboard(b);
180 newboard.applyWhiteMove(m);
181
182 if (belofte::cWhiteKingClass->isAttacked(newboard, newboard.getWhiteKingPos())) return 0;
183 addMoveToList(b, m, belofte::cBlackKingClass->isAttacked(newboard, newboard.getBlackKingPos()));
184 return 1;
185}
186
187/**
188 * Only add move to movelist if valid
189 * @param b board on which move is calculated
190 * @param m move to be added
191 * @return number of moves added (1), meaning player is not in check after move
192 */
194{
195 bBasicBoard newboard(b);
196 newboard.applyBlackMove(m);
197
198 if (belofte::cBlackKingClass->isAttacked(newboard, newboard.getBlackKingPos())) return 0;
199 addMoveToList(b, m, belofte::cWhiteKingClass->isAttacked(newboard, newboard.getWhiteKingPos()));
200 return 1;
201}
202
203/**
204 * Only add move to movelist if valid
205 * @param b board on which move is calculated
206 * @param m move to be added
207 * @return number of moves added (4), meaning, player is not in check after move
208 */
210{
211 m.setPawnMove();
212 bBasicBoard newboard(b);
213 newboard.applyWhiteMove(m);
214 if (belofte::cWhiteKingClass->isAttacked(newboard, newboard.getWhiteKingPos())) return 0;
215
216 // we are promoting, but did apply a dummy move on top
217 // so modify dest board with custom pieces and check for in check
218 case_t to = m.to();
219 case_t bKingPos = newboard.getBlackKingPos();
220
221 /// @todo optimize in case of uncover check just by moving pawn
222 newboard.setPiece(to, tPiece::W_QUEEN);
223 bool oppInCheckByQueen = belofte::cBlackKingClass->isAttacked(newboard, bKingPos);
224 m.setPromotion(tPPiece::N_P_QUEEN); addMoveToList(b, m, oppInCheckByQueen);
225 m.clearIsCheck();
226
227 newboard.setPiece(to, tPiece::W_KNIGHT);
228 bool oppInCheck = belofte::cBlackKingClass->isAttacked(newboard, bKingPos);
229 m.setPromotion(tPPiece::N_P_KNIGHT); addMoveToList(b, m, oppInCheck);
230 m.clearIsCheck();
231
232 newboard.setPiece(to, tPiece::W_ROOK);
233 // do not re-evaluate check by rook if queen did not give check
234 oppInCheck = oppInCheckByQueen && belofte::cBlackKingClass->isAttacked(newboard, bKingPos);
235 m.setPromotion(tPPiece::N_P_ROOK); addMoveToList(b, m, oppInCheck);
236 m.clearIsCheck();
237
238 newboard.setPiece(to, tPiece::W_BISHOP);
239 oppInCheck = oppInCheckByQueen && belofte::cBlackKingClass->isAttacked(newboard, bKingPos);
240 m.setPromotion(tPPiece::N_P_BISHOP); addMoveToList(b, m, oppInCheck);
241 return 4;
242}
243
244/**
245 * Only add move to movelist if valid
246 * @param b board on which move is calculated
247 * @param m move to be added
248 * @return number of moves added (4), meaning, player is not in check after move
249 */
251{
252 m.setPawnMove();
253 bBasicBoard newboard(b);
254 newboard.applyBlackMove(m);
255 if (belofte::cBlackKingClass->isAttacked(newboard, newboard.getBlackKingPos())) return 0;
256
257 // we are promoting, but did apply a dummy move on top
258 // so modify dest board with custom pieces and check for in check
259 case_t to = m.to();
260 case_t wKingPos = newboard.getWhiteKingPos();
261
262 /// @todo optimize in case of uncover check just by moving pawn
263 newboard.setPiece(to, tPiece::B_QUEEN);
264 bool oppInCheckByQueen = belofte::cWhiteKingClass->isAttacked(newboard, wKingPos);
265 m.setPromotion(tPPiece::N_P_QUEEN); addMoveToList(b, m, oppInCheckByQueen);
266 m.clearIsCheck();
267
268 newboard.setPiece(to, tPiece::B_KNIGHT);
269 bool oppInCheck = belofte::cWhiteKingClass->isAttacked(newboard, wKingPos);
270 m.setPromotion(tPPiece::N_P_KNIGHT); addMoveToList(b, m, oppInCheck);
271 m.clearIsCheck();
272
273 newboard.setPiece(to, tPiece::B_ROOK);
274 // do not re-evaluate check by rook if queen did not give check
275 oppInCheck = oppInCheckByQueen && belofte::cWhiteKingClass->isAttacked(newboard, wKingPos);
276 m.setPromotion(tPPiece::N_P_ROOK); addMoveToList(b, m, oppInCheck);
277 m.clearIsCheck();
278
279 newboard.setPiece(to, tPiece::B_BISHOP);
280 oppInCheck = oppInCheckByQueen && belofte::cWhiteKingClass->isAttacked(newboard, wKingPos);
281 m.setPromotion(tPPiece::N_P_BISHOP); addMoveToList(b, m, oppInCheck);
282 return 4;
283}
284
285//-----------------------------------------------------------------------
286
287/**
288 * sort moves and update bestmove id
289 * if less than 5 moves, sort all
290 * if more than 5 moves, and less than 3 qs moves, sort 3 best
291 * if more than 5 moves and at least 3 qs moves, sort qsmoves + 1
292 */
294{
295 size_t nMoves = m_lmoves.size();
296 if (nMoves > 1) {
298 if (isNotSorted() && isNeedSorted()) {
299 if (nMoves >= 5) {
300 // 5 or more moves
301 if (m_nQSMoves < 3) {
302 // only interested in 3 best scores in case of a few QS moves
303 // first 2 will be qs move, and a single non qsmove will be added
304 std::partial_sort(m_lmoves.begin(), m_lmoves.begin() + 2, m_lmoves.end(),
305 [](bMove const& a, bMove const& b) { return a > b; });
306 } else if (m_nQSMoves < nMoves) {
307 // interested in all m_nQSMoves + 1 best scores
308 std::partial_sort(m_lmoves.begin(), m_lmoves.begin() + m_nQSMoves, m_lmoves.end(),
309 [](bMove const& a, bMove const& b) { return a > b; });
310 } else {
311 // all moves are qs moves and there are 5 or more moves
312 std::sort(m_lmoves.begin(), m_lmoves.end(),
313 [](bMove const& a, bMove const& b) { return a > b; });
314 }
315 } else {
316 // sort all in case of 1 to 4 elements
317 std::sort(m_lmoves.begin(), m_lmoves.end(),
318 [](bMove const& a, bMove const& b) { return a > b; });
319 }
320 setBestMoveId(1);
321 setIsSorted();
322 }
323 } else if (nMoves == 1) {
324 setBestMoveId(1);
326 } else {
328 }
329#if defined(INCOMPLETE_C11)
330 setIsSorted();
331#endif
332}
333
334//-----------------------------------------------------------------------
335
336/**
337 * generate moves if not yet generated
338 * @return number of moves generated
339 */
341{
342 if (!isGenerated()) {
344 side_t sidetomove = b.getColourToMove();
345 if (sidetomove == tSide::SIDE_WHITE) {
346 for (case_t iCase = 0; iCase < 64; ++iCase) {
347 piece_t p = b.getPiece(iCase);
348 if (bPiece::isWhitePiece(p)) {
349 (bPiece::getPieceClass(p))->GenerateMoves(b, iCase, *this);
350 }
351 }
352 } else {
353 for (case_t iCase = 64; iCase > 0;) {
354 --iCase;
355 piece_t p = b.getPiece(iCase);
356 if (bPiece::isBlackPiece(p)) {
357 (bPiece::getPieceClass(p))->GenerateMoves(b, iCase, *this);
358 }
359 }
360 }
361 }
362
363 if (getNumberOfMoves()) {
365 return getNumberOfMoves();
366 }
367 return 0;
368}
369
371{
372 m_lmoves.clear();
374 setIsSorted(); // if no moves or a single move is registered, sorted flag is set
376 m_nQSMoves = 0;
378}
379
380/**
381 * reduce the number of QS moves to filter out the best ones only
382 */
384{
385 if (m_bestmoveid && (m_nQSMoves > 1) && isSorted()) {
386 // there is more than 1 QS move and all moves are sorted
387 // break QS moves on move that is much worse than the best moves
388 bScore currentScore = m_lmoves[m_bestmoveid - 1].getScore();
389 for (movenum_t nMoveID = 1; nMoveID < m_nQSMoves - 1 && nMoveID < m_lmoves.size() - 1; ++nMoveID) {
390 if (m_lmoves[nMoveID].getScore() < currentScore - 200) {
391 // next move in ordered list is less than 200 points below previous one
392 m_nQSMoves = nMoveID;
393 break;
394 }
395 }
396 }
397 return m_nQSMoves;
398}
399
400/**
401 * see if at least one move can be played e.g. (stale-)mate eval
402 * called from addMoveToList, iterative search main loop and gameEndedResult
403 */
405{
406 if (isPossibleMove()) return true;
407
409 if (bPiece::getPieceClass(tPiece::W_KING)->hasValidMovePreflightCheck(b, b.getWhiteKingPos()))
410 { setIsPossibleMove(); return true; }
411 for (case_t iCase = 0; iCase < 64; ++iCase) {
412 piece_t p = b.getPiece(iCase);
413 if (bPiece::isWhitePiece(p) && p != tPiece::W_KING) {
414 if (bPiece::getPieceClass(p)->hasValidMovePreflightCheck(b, iCase))
415 { setIsPossibleMove(); return true; }
416 }
417 }
418 } else {
419 if (bPiece::getPieceClass(tPiece::B_KING)->hasValidMovePreflightCheck(b, b.getBlackKingPos()))
420 { setIsPossibleMove(); return true; }
421 for (case_t iCase = 64; iCase > 0;) {
422 --iCase;
423 piece_t p = b.getPiece(iCase);
424 if (bPiece::isBlackPiece(p) && p != tPiece::B_KING) {
425 if (bPiece::getPieceClass(p)->hasValidMovePreflightCheck(b, iCase))
426 { setIsPossibleMove(); return true; }
427 }
428 }
429 }
430 return false;
431}
432
433//-----------------------------------------------------------------------
434
435std::ostream& operator<<(std::ostream& os, bMoveList const& ml)
436{
437 os << "#" << static_cast<int>(ml.getNumberOfMoves()) << " ";
438 if (ml.getNumberOfQSMoves()) {
439 os << "QS #" << static_cast<int>(ml.getNumberOfQSMoves()) << " ";
440 }
441 for (bMove const& m: ml.m_lmoves) {
442 os << m << m.getMoveEvalStr() << " ";
443 }
444 return os;
445}
446
447// eof
This is the main include file, needs to be included before any other include.
uint_fast8_t movenum_t
Definition belofte.h:100
uint8_t case_t
Definition belofte.h:96
constexpr piece_t getPiece(case_t const cf) const
Definition basicboard.h:172
boardInfo_t applyBlackMove(bMove const &m)
boardInfo_t applyWhiteMove(bMove const &m)
constexpr case_t getBlackKingPos() const
Definition basicboard.h:208
constexpr case_t getWhiteKingPos() const
Definition basicboard.h:206
constexpr int8_t pieceCount() const
Definition basicboard.h:221
constexpr side_t getColourToMove() const
Definition basicboard.h:162
void setPiece(case_t const cf, piece_t const piece)
Definition basicboard.h:182
constexpr plynum_t getPly() const
Definition basicboard.h:75
void setCheck()
Definition basicmove.h:111
void setGameEnd()
Definition basicmove.h:117
void clearIsCheck()
Definition basicmove.h:113
constexpr bool isCastleMove() const
Definition basicmove.h:101
constexpr bool isNonSilent() const
Definition basicmove.h:133
void setPawnMove()
Definition basicmove.h:99
constexpr bool isPawnMove() const
Definition basicmove.h:97
constexpr bool isCapture() const
Definition basicmove.h:93
constexpr case_t to() const
Definition basicmove.h:58
constexpr bool isPromotion() const
Definition basicmove.h:82
constexpr bool isMajorPromotion() const
Definition basicmove.h:84
void setPromotion(const ppiece_t p)
Definition basicmove.h:86
constexpr bool getGameEnd() const
Definition basicmove.h:115
constexpr bool isCheck() const
Definition basicmove.h:109
constexpr case_t from() const
Definition basicmove.h:56
Definition move.h:13
std::string getMoveEvalStr() const
Definition move.cpp:17
void setScore(bScore const score)
Definition move.h:55
constexpr bool isGenerated() const
Definition movelist.h:83
void emptyMoveList()
Definition movelist.cpp:370
movenum_t generateMoves(bBasicBoard const &b)
generate moves if not yet generated
Definition movelist.cpp:340
void setScoreOfMoveUnsorted(movenum_t const moveid, bScore const score)
Store score of move and update best move.
Definition movelist.cpp:156
void setBestMoveScore(movenum_t const moveid, bScore const score)
Definition movelist.h:61
constexpr bool isKeepScores() const
Definition movelist.h:118
movenum_t addBlackMoveIfValid(bBasicBoard const &b, bMove &m)
Only add move to movelist if valid.
Definition movelist.cpp:193
void clearIsSorted()
Definition movelist.h:95
void sortMoves()
sort moves and update bestmove id if less than 5 moves, sort all if more than 5 moves,...
Definition movelist.cpp:293
void clearBestMoveId()
Definition movelist.h:70
void setMoveScore(movenum_t const moveid, bScore const score)
Definition movelist.h:63
bool setScoreOfMove(movenum_t const moveid, bScore const score)
Store score of move and update best move.
Definition movelist.cpp:130
movenum_t addBlackPromotionIfValid(bBasicBoard const &b, bMove &m)
Only add move to movelist if valid.
Definition movelist.cpp:250
void setIsPossibleMove()
Definition movelist.h:109
void clearIsPossibleMove()
Definition movelist.h:111
void clearIsGenerated()
Definition movelist.h:87
constexpr movenum_t getBestMoveId() const
Definition movelist.h:66
void setIsGenerated()
Definition movelist.h:85
constexpr bool isPossibleMove() const
Definition movelist.h:107
void setIsSorted()
Definition movelist.h:93
bMoveList()
Definition movelist.h:17
constexpr bool isNotSorted() const
Definition movelist.h:91
movenum_t addWhiteMoveIfValid(bBasicBoard const &b, bMove &m)
Only add move to movelist if valid.
Definition movelist.cpp:177
constexpr movenum_t getNumberOfMoves() const
Definition movelist.h:34
void setIsOnlyMove()
Definition movelist.h:101
bool atLeastOneMovePossible(bBasicBoard &b)
see if at least one move can be played e.g.
Definition movelist.cpp:404
void clearIsOnlyMove()
Definition movelist.h:103
constexpr movenum_t getNumberOfQSMoves() const
Definition movelist.h:36
void setBestMoveId(movenum_t const n)
Definition movelist.h:68
movenum_t adjustQSMoves()
reduce the number of QS moves to filter out the best ones only
Definition movelist.cpp:383
constexpr bool isNeedSorted() const
Definition movelist.h:114
constexpr bool isSorted() const
Definition movelist.h:89
movenum_t addWhitePromotionIfValid(bBasicBoard const &b, bMove &m)
Only add move to movelist if valid.
Definition movelist.cpp:209
static bPiece * getPieceClass(piece_t const piece)
static class member function
Definition piece.cpp:160
static bool isBlackPiece(piece_t const p)
static class member function
Definition piece.cpp:254
static bool isWhitePiece(piece_t const p)
static class member function
Definition piece.cpp:247
constexpr bScore SCORE_MATE
Definition eval.h:24
int16_t bScore
Definition eval.h:11
std::ostream & operator<<(std::ostream &os, bMoveList const &ml)
Definition movelist.cpp:435
Allow index mapper for char values of piece into int in 1-12 range to reduce space and easy initialis...
Definition eval.cpp:22
bBlackKing const * cBlackKingClass
Definition movelist.cpp:16
bWhiteKing const * cWhiteKingClass
Definition movelist.cpp:15
bScore centerplayvalues[]
Definition eval.cpp:388
bScore piecevalues[]
Definition eval.cpp:231
bScore piecevalues_movepenalty[]
Definition eval.cpp:237
enum tSide side_t
Definition piece.h:65
@ W_KNIGHT
Definition piece.h:39
@ B_BISHOP
Definition piece.h:40
@ W_ROOK
Definition piece.h:39
@ B_ROOK
Definition piece.h:40
@ W_QUEEN
Definition piece.h:39
@ B_KING
Definition piece.h:40
@ B_KNIGHT
Definition piece.h:40
@ W_KING
Definition piece.h:39
@ B_QUEEN
Definition piece.h:40
@ W_BISHOP
Definition piece.h:39
enum tPiece piece_t
Definition piece.h:44
@ N_P_KNIGHT
Definition piece.h:48
@ N_P_QUEEN
Definition piece.h:48
@ N_P_ROOK
Definition piece.h:48
@ N_P_BISHOP
Definition piece.h:48
@ SIDE_WHITE
Definition piece.h:61