Chess.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <AK/Assertions.h>
  27. #include <AK/LogStream.h>
  28. #include <AK/String.h>
  29. #include <AK/StringBuilder.h>
  30. #include <AK/Vector.h>
  31. #include <LibChess/Chess.h>
  32. #include <stdlib.h>
  33. namespace Chess {
  34. String char_for_piece(Chess::Type type)
  35. {
  36. switch (type) {
  37. case Type::Knight:
  38. return "N";
  39. case Type::Bishop:
  40. return "B";
  41. case Type::Rook:
  42. return "R";
  43. case Type::Queen:
  44. return "Q";
  45. case Type::King:
  46. return "K";
  47. case Type::Pawn:
  48. default:
  49. return "";
  50. }
  51. }
  52. Chess::Type piece_for_char_promotion(const StringView& str)
  53. {
  54. String string = String(str).to_lowercase();
  55. if (string == "")
  56. return Type::None;
  57. if (string == "n")
  58. return Type::Knight;
  59. if (string == "b")
  60. return Type::Bishop;
  61. if (string == "r")
  62. return Type::Rook;
  63. if (string == "q")
  64. return Type::Queen;
  65. if (string == "k")
  66. return Type::King;
  67. return Type::None;
  68. }
  69. Colour opposing_colour(Colour colour)
  70. {
  71. return (colour == Colour::White) ? Colour::Black : Colour::White;
  72. }
  73. Square::Square(const StringView& name)
  74. {
  75. ASSERT(name.length() == 2);
  76. char filec = name[0];
  77. char rankc = name[1];
  78. if (filec >= 'a' && filec <= 'h') {
  79. file = filec - 'a';
  80. } else if (filec >= 'A' && filec <= 'H') {
  81. file = filec - 'A';
  82. } else {
  83. ASSERT_NOT_REACHED();
  84. }
  85. if (rankc >= '1' && rankc <= '8') {
  86. rank = rankc - '1';
  87. } else {
  88. ASSERT_NOT_REACHED();
  89. }
  90. }
  91. String Square::to_algebraic() const
  92. {
  93. StringBuilder builder;
  94. builder.append(file + 'a');
  95. builder.append(rank + '1');
  96. return builder.build();
  97. }
  98. Move::Move(const StringView& algebraic)
  99. : from(algebraic.substring_view(0, 2))
  100. , to(algebraic.substring_view(2, 2))
  101. , promote_to(piece_for_char_promotion((algebraic.length() >= 5) ? algebraic.substring_view(4, 1) : ""))
  102. {
  103. }
  104. String Move::to_long_algebraic() const
  105. {
  106. StringBuilder builder;
  107. builder.append(from.to_algebraic());
  108. builder.append(to.to_algebraic());
  109. builder.append(char_for_piece(promote_to).to_lowercase());
  110. return builder.build();
  111. }
  112. Board::Board()
  113. {
  114. // Fill empty spaces.
  115. for (unsigned rank = 2; rank < 6; ++rank) {
  116. for (unsigned file = 0; file < 8; ++file) {
  117. set_piece({ rank, file }, EmptyPiece);
  118. }
  119. }
  120. // Fill white pawns.
  121. for (unsigned file = 0; file < 8; ++file) {
  122. set_piece({ 1, file }, { Colour::White, Type::Pawn });
  123. }
  124. // Fill black pawns.
  125. for (unsigned file = 0; file < 8; ++file) {
  126. set_piece({ 6, file }, { Colour::Black, Type::Pawn });
  127. }
  128. // Fill while pieces.
  129. set_piece(Square("a1"), { Colour::White, Type::Rook });
  130. set_piece(Square("b1"), { Colour::White, Type::Knight });
  131. set_piece(Square("c1"), { Colour::White, Type::Bishop });
  132. set_piece(Square("d1"), { Colour::White, Type::Queen });
  133. set_piece(Square("e1"), { Colour::White, Type::King });
  134. set_piece(Square("f1"), { Colour::White, Type::Bishop });
  135. set_piece(Square("g1"), { Colour::White, Type::Knight });
  136. set_piece(Square("h1"), { Colour::White, Type::Rook });
  137. // Fill black pieces.
  138. set_piece(Square("a8"), { Colour::Black, Type::Rook });
  139. set_piece(Square("b8"), { Colour::Black, Type::Knight });
  140. set_piece(Square("c8"), { Colour::Black, Type::Bishop });
  141. set_piece(Square("d8"), { Colour::Black, Type::Queen });
  142. set_piece(Square("e8"), { Colour::Black, Type::King });
  143. set_piece(Square("f8"), { Colour::Black, Type::Bishop });
  144. set_piece(Square("g8"), { Colour::Black, Type::Knight });
  145. set_piece(Square("h8"), { Colour::Black, Type::Rook });
  146. }
  147. Piece Board::get_piece(const Square& square) const
  148. {
  149. ASSERT(square.rank < 8);
  150. ASSERT(square.file < 8);
  151. return m_board[square.rank][square.file];
  152. }
  153. Piece Board::set_piece(const Square& square, const Piece& piece)
  154. {
  155. ASSERT(square.rank < 8);
  156. ASSERT(square.file < 8);
  157. return m_board[square.rank][square.file] = piece;
  158. }
  159. bool Board::is_legal(const Move& move, Colour colour) const
  160. {
  161. if (colour == Colour::None)
  162. colour = turn();
  163. if (!is_legal_no_check(move, colour))
  164. return false;
  165. Board clone = *this;
  166. clone.apply_illegal_move(move, colour);
  167. if (clone.in_check(colour))
  168. return false;
  169. // Don't allow castling through check or out of check.
  170. Vector<Square> check_squares;
  171. if (colour == Colour::White && move.from == Square("e1") && get_piece(Square("e1")) == Piece(Colour::White, Type::King)) {
  172. if (move.to == Square("a1") || move.to == Square("c1")) {
  173. check_squares = { Square("e1"), Square("d1"), Square("c1") };
  174. } else if (move.to == Square("h1") || move.to == Square("g1")) {
  175. check_squares = { Square("e1"), Square("f1"), Square("g1") };
  176. }
  177. } else if (colour == Colour::Black && move.from == Square("e8") && get_piece(Square("e8")) == Piece(Colour::Black, Type::King)) {
  178. if (move.to == Square("a8") || move.to == Square("c8")) {
  179. check_squares = { Square("e8"), Square("d8"), Square("c8") };
  180. } else if (move.to == Square("h8") || move.to == Square("g8")) {
  181. check_squares = { Square("e8"), Square("f8"), Square("g8") };
  182. }
  183. }
  184. for (auto& square : check_squares) {
  185. Board clone = *this;
  186. clone.set_piece(move.from, EmptyPiece);
  187. clone.set_piece(square, { colour, Type::King });
  188. if (clone.in_check(colour))
  189. return false;
  190. }
  191. return true;
  192. }
  193. bool Board::is_legal_no_check(const Move& move, Colour colour) const
  194. {
  195. auto piece = get_piece(move.from);
  196. if (piece.colour != colour)
  197. return false;
  198. if (move.to.rank > 7 || move.to.file > 7)
  199. return false;
  200. if (piece.type != Type::Pawn && move.promote_to != Type::None)
  201. return false;
  202. if (move.promote_to == Type::Pawn || move.promote_to == Type::King)
  203. return false;
  204. if (piece.type == Type::Pawn) {
  205. int dir = (colour == Colour::White) ? +1 : -1;
  206. unsigned start_rank = (colour == Colour::White) ? 1 : 6;
  207. unsigned other_start_rank = (colour == Colour::White) ? 6 : 1;
  208. unsigned en_passant_rank = (colour == Colour::White) ? 4 : 3;
  209. unsigned promotion_rank = (colour == Colour::White) ? 7 : 0;
  210. if (move.to.rank == promotion_rank) {
  211. if (move.promote_to == Type::Pawn || move.promote_to == Type::King || move.promote_to == Type::None)
  212. return false;
  213. } else if (move.promote_to != Type::None) {
  214. return false;
  215. }
  216. if (move.to.rank == move.from.rank + dir && move.to.file == move.from.file && get_piece(move.to).type == Type::None) {
  217. // Regular pawn move.
  218. return true;
  219. } else if (move.to.rank == move.from.rank + dir && (move.to.file == move.from.file + 1 || move.to.file == move.from.file - 1)) {
  220. Move en_passant_last_move = { { other_start_rank, move.to.file }, { en_passant_rank, move.to.file } };
  221. if (get_piece(move.to).colour == opposing_colour(colour)) {
  222. // Pawn capture.
  223. return true;
  224. } else if (m_last_move.has_value() && move.from.rank == en_passant_rank && m_last_move.value() == en_passant_last_move
  225. && get_piece(en_passant_last_move.to) == Piece(opposing_colour(colour), Type::Pawn)) {
  226. // En passant.
  227. return true;
  228. }
  229. } else if (move.from.rank == start_rank && move.to.rank == move.from.rank + (2 * dir) && move.to.file == move.from.file
  230. && get_piece(move.to).type == Type::None && get_piece({ move.from.rank + dir, move.from.file }).type == Type::None) {
  231. // 2 square pawn move from initial position.
  232. return true;
  233. }
  234. return false;
  235. } else if (piece.type == Type::Knight) {
  236. int rank_delta = abs(move.to.rank - move.from.rank);
  237. int file_delta = abs(move.to.file - move.from.file);
  238. if (get_piece(move.to).colour != colour && max(rank_delta, file_delta) == 2 && min(rank_delta, file_delta) == 1) {
  239. return true;
  240. }
  241. } else if (piece.type == Type::Bishop) {
  242. int rank_delta = move.to.rank - move.from.rank;
  243. int file_delta = move.to.file - move.from.file;
  244. if (abs(rank_delta) == abs(file_delta)) {
  245. int dr = rank_delta / abs(rank_delta);
  246. int df = file_delta / abs(file_delta);
  247. for (Square sq = move.from; sq != move.to; sq.rank += dr, sq.file += df) {
  248. if (get_piece(sq).type != Type::None && sq != move.from) {
  249. return false;
  250. }
  251. }
  252. if (get_piece(move.to).colour != colour) {
  253. return true;
  254. }
  255. }
  256. } else if (piece.type == Type::Rook) {
  257. int rank_delta = move.to.rank - move.from.rank;
  258. int file_delta = move.to.file - move.from.file;
  259. if (rank_delta == 0 || file_delta == 0) {
  260. int dr = (rank_delta) ? rank_delta / abs(rank_delta) : 0;
  261. int df = (file_delta) ? file_delta / abs(file_delta) : 0;
  262. for (Square sq = move.from; sq != move.to; sq.rank += dr, sq.file += df) {
  263. if (get_piece(sq).type != Type::None && sq != move.from) {
  264. return false;
  265. }
  266. }
  267. if (get_piece(move.to).colour != colour) {
  268. return true;
  269. }
  270. }
  271. } else if (piece.type == Type::Queen) {
  272. int rank_delta = move.to.rank - move.from.rank;
  273. int file_delta = move.to.file - move.from.file;
  274. if (abs(rank_delta) == abs(file_delta) || rank_delta == 0 || file_delta == 0) {
  275. int dr = (rank_delta) ? rank_delta / abs(rank_delta) : 0;
  276. int df = (file_delta) ? file_delta / abs(file_delta) : 0;
  277. for (Square sq = move.from; sq != move.to; sq.rank += dr, sq.file += df) {
  278. if (get_piece(sq).type != Type::None && sq != move.from) {
  279. return false;
  280. }
  281. }
  282. if (get_piece(move.to).colour != colour) {
  283. return true;
  284. }
  285. }
  286. } else if (piece.type == Type::King) {
  287. int rank_delta = move.to.rank - move.from.rank;
  288. int file_delta = move.to.file - move.from.file;
  289. if (abs(rank_delta) <= 1 && abs(file_delta) <= 1) {
  290. if (get_piece(move.to).colour != colour) {
  291. return true;
  292. }
  293. }
  294. if (colour == Colour::White) {
  295. if ((move.to == Square("a1") || move.to == Square("c1")) && m_white_can_castle_queenside && get_piece(Square("b1")).type == Type::None && get_piece(Square("c1")).type == Type::None && get_piece(Square("d1")).type == Type::None) {
  296. return true;
  297. } else if ((move.to == Square("h1") || move.to == Square("g1")) && m_white_can_castle_kingside && get_piece(Square("f1")).type == Type::None && get_piece(Square("g1")).type == Type::None) {
  298. return true;
  299. }
  300. } else {
  301. if ((move.to == Square("a8") || move.to == Square("c8")) && m_black_can_castle_queenside && get_piece(Square("b8")).type == Type::None && get_piece(Square("c8")).type == Type::None && get_piece(Square("d8")).type == Type::None) {
  302. return true;
  303. } else if ((move.to == Square("h8") || move.to == Square("g8")) && m_black_can_castle_kingside && get_piece(Square("f8")).type == Type::None && get_piece(Square("g8")).type == Type::None) {
  304. return true;
  305. }
  306. }
  307. }
  308. return false;
  309. }
  310. bool Board::in_check(Colour colour) const
  311. {
  312. Square king_square = { 50, 50 };
  313. Square::for_each([&](const Square& square) {
  314. if (get_piece(square) == Piece(colour, Type::King)) {
  315. king_square = square;
  316. return IterationDecision::Break;
  317. }
  318. return IterationDecision::Continue;
  319. });
  320. bool check = false;
  321. Square::for_each([&](const Square& square) {
  322. if (is_legal_no_check({ square, king_square }, opposing_colour(colour))) {
  323. check = true;
  324. return IterationDecision::Break;
  325. }
  326. return IterationDecision::Continue;
  327. });
  328. return check;
  329. }
  330. bool Board::apply_move(const Move& move, Colour colour)
  331. {
  332. if (colour == Colour::None)
  333. colour = turn();
  334. if (!is_legal(move, colour))
  335. return false;
  336. return apply_illegal_move(move, colour);
  337. }
  338. bool Board::apply_illegal_move(const Move& move, Colour colour)
  339. {
  340. Board clone = *this;
  341. clone.m_previous_states = {};
  342. clone.m_moves = {};
  343. auto state_count = 0;
  344. if (m_previous_states.contains(clone))
  345. state_count = m_previous_states.get(clone).value();
  346. m_previous_states.set(clone, state_count + 1);
  347. m_moves.append(move);
  348. m_turn = opposing_colour(colour);
  349. m_last_move = move;
  350. m_moves_since_capture++;
  351. if (move.from == Square("a1") || move.to == Square("a1") || move.from == Square("e1"))
  352. m_white_can_castle_queenside = false;
  353. if (move.from == Square("h1") || move.to == Square("h1") || move.from == Square("e1"))
  354. m_white_can_castle_kingside = false;
  355. if (move.from == Square("a8") || move.to == Square("a8") || move.from == Square("e8"))
  356. m_black_can_castle_queenside = false;
  357. if (move.from == Square("h8") || move.to == Square("h8") || move.from == Square("e8"))
  358. m_black_can_castle_kingside = false;
  359. if (colour == Colour::White && move.from == Square("e1") && get_piece(Square("e1")) == Piece(Colour::White, Type::King)) {
  360. if (move.to == Square("a1") || move.to == Square("c1")) {
  361. set_piece(Square("e1"), EmptyPiece);
  362. set_piece(Square("a1"), EmptyPiece);
  363. set_piece(Square("c1"), { Colour::White, Type::King });
  364. set_piece(Square("d1"), { Colour::White, Type::Rook });
  365. return true;
  366. } else if (move.to == Square("h1") || move.to == Square("g1")) {
  367. set_piece(Square("e1"), EmptyPiece);
  368. set_piece(Square("h1"), EmptyPiece);
  369. set_piece(Square("g1"), { Colour::White, Type::King });
  370. set_piece(Square("f1"), { Colour::White, Type::Rook });
  371. return true;
  372. }
  373. } else if (colour == Colour::Black && move.from == Square("e8") && get_piece(Square("e8")) == Piece(Colour::Black, Type::King)) {
  374. if (move.to == Square("a8") || move.to == Square("c8")) {
  375. set_piece(Square("e8"), EmptyPiece);
  376. set_piece(Square("a8"), EmptyPiece);
  377. set_piece(Square("c8"), { Colour::Black, Type::King });
  378. set_piece(Square("d8"), { Colour::Black, Type::Rook });
  379. return true;
  380. } else if (move.to == Square("h8") || move.to == Square("g8")) {
  381. set_piece(Square("e8"), EmptyPiece);
  382. set_piece(Square("h8"), EmptyPiece);
  383. set_piece(Square("g8"), { Colour::Black, Type::King });
  384. set_piece(Square("f8"), { Colour::Black, Type::Rook });
  385. return true;
  386. }
  387. }
  388. if (get_piece(move.from).type == Type::Pawn && ((colour == Colour::Black && move.to.rank == 0) || (colour == Colour::White && move.to.rank == 7))) {
  389. // Pawn Promotion
  390. set_piece(move.to, { colour, move.promote_to });
  391. set_piece(move.from, EmptyPiece);
  392. return true;
  393. }
  394. if (get_piece(move.from).type == Type::Pawn && move.from.file != move.to.file && get_piece(move.to).type == Type::None) {
  395. // En passant.
  396. if (colour == Colour::White) {
  397. set_piece({ move.to.rank - 1, move.to.file }, EmptyPiece);
  398. } else {
  399. set_piece({ move.to.rank + 1, move.to.file }, EmptyPiece);
  400. }
  401. m_moves_since_capture = 0;
  402. }
  403. if (get_piece(move.to).colour != Colour::None)
  404. m_moves_since_capture = 0;
  405. set_piece(move.to, get_piece(move.from));
  406. set_piece(move.from, EmptyPiece);
  407. return true;
  408. }
  409. Move Board::random_move(Colour colour) const
  410. {
  411. if (colour == Colour::None)
  412. colour = turn();
  413. Move move = { { 50, 50 }, { 50, 50 } };
  414. int probability = 1;
  415. generate_moves([&](Move m) {
  416. if (rand() % probability == 0)
  417. move = m;
  418. ++probability;
  419. return IterationDecision::Continue;
  420. });
  421. return move;
  422. }
  423. Board::Result Board::game_result() const
  424. {
  425. bool sufficient_material = false;
  426. bool no_more_pieces_allowed = false;
  427. Optional<Square> bishop;
  428. Square::for_each([&](Square sq) {
  429. if (get_piece(sq).type == Type::Queen || get_piece(sq).type == Type::Rook || get_piece(sq).type == Type::Pawn) {
  430. sufficient_material = true;
  431. return IterationDecision::Break;
  432. }
  433. if (get_piece(sq).type != Type::None && get_piece(sq).type != Type::King && no_more_pieces_allowed) {
  434. sufficient_material = true;
  435. return IterationDecision::Break;
  436. }
  437. if (get_piece(sq).type == Type::Knight)
  438. no_more_pieces_allowed = true;
  439. if (get_piece(sq).type == Type::Bishop) {
  440. if (bishop.has_value()) {
  441. if (get_piece(sq).colour == get_piece(bishop.value()).colour) {
  442. sufficient_material = true;
  443. return IterationDecision::Break;
  444. } else if (sq.is_light() != bishop.value().is_light()) {
  445. sufficient_material = true;
  446. return IterationDecision::Break;
  447. }
  448. no_more_pieces_allowed = true;
  449. } else {
  450. bishop = sq;
  451. }
  452. }
  453. return IterationDecision::Continue;
  454. });
  455. if (!sufficient_material)
  456. return Result::InsufficientMaterial;
  457. bool are_legal_moves = false;
  458. generate_moves([&](Move m) {
  459. (void)m;
  460. are_legal_moves = true;
  461. return IterationDecision::Break;
  462. });
  463. if (are_legal_moves) {
  464. if (m_moves_since_capture >= 75 * 2)
  465. return Result::SeventyFiveMoveRule;
  466. if (m_moves_since_capture == 50 * 2)
  467. return Result::FiftyMoveRule;
  468. auto repeats = m_previous_states.get(*this);
  469. if (repeats.has_value()) {
  470. if (repeats.value() == 3)
  471. return Result::ThreeFoldRepetition;
  472. if (repeats.value() >= 5)
  473. return Result::FiveFoldRepetition;
  474. }
  475. return Result::NotFinished;
  476. }
  477. if (in_check(turn()))
  478. return Result::CheckMate;
  479. return Result::StaleMate;
  480. }
  481. Colour Board::game_winner() const
  482. {
  483. if (game_result() == Result::CheckMate)
  484. return opposing_colour(turn());
  485. return Colour::None;
  486. }
  487. int Board::game_score() const
  488. {
  489. switch (game_winner()) {
  490. case Colour::White:
  491. return +1;
  492. case Colour::Black:
  493. return -1;
  494. case Colour::None:
  495. return 0;
  496. }
  497. return 0;
  498. }
  499. bool Board::game_finished() const
  500. {
  501. return game_result() != Result::NotFinished;
  502. }
  503. int Board::material_imbalance() const
  504. {
  505. int imbalance = 0;
  506. Square::for_each([&](Square square) {
  507. int value = 0;
  508. switch (get_piece(square).type) {
  509. case Type::Pawn:
  510. value = 1;
  511. break;
  512. case Type::Knight:
  513. case Type::Bishop:
  514. value = 3;
  515. break;
  516. case Type::Rook:
  517. value = 5;
  518. break;
  519. case Type::Queen:
  520. value = 9;
  521. break;
  522. default:
  523. break;
  524. }
  525. if (get_piece(square).colour == Colour::White) {
  526. imbalance += value;
  527. } else {
  528. imbalance -= value;
  529. }
  530. return IterationDecision::Continue;
  531. });
  532. return imbalance;
  533. }
  534. bool Board::is_promotion_move(const Move& move, Colour colour) const
  535. {
  536. if (colour == Colour::None)
  537. colour = turn();
  538. Move queen_move = move;
  539. queen_move.promote_to = Type::Queen;
  540. if (!is_legal(queen_move, colour))
  541. return false;
  542. if (get_piece(move.from).type == Type::Pawn && ((colour == Colour::Black && move.to.rank == 0) || (colour == Colour::White && move.to.rank == 7)))
  543. return true;
  544. return false;
  545. }
  546. bool Board::operator==(const Board& other) const
  547. {
  548. bool equal_squares = true;
  549. Square::for_each([&](Square sq) {
  550. if (get_piece(sq) != other.get_piece(sq)) {
  551. equal_squares = false;
  552. return IterationDecision::Break;
  553. }
  554. return IterationDecision::Continue;
  555. });
  556. if (!equal_squares)
  557. return false;
  558. if (m_white_can_castle_queenside != other.m_white_can_castle_queenside)
  559. return false;
  560. if (m_white_can_castle_kingside != other.m_white_can_castle_kingside)
  561. return false;
  562. if (m_black_can_castle_queenside != other.m_black_can_castle_queenside)
  563. return false;
  564. if (m_black_can_castle_kingside != other.m_black_can_castle_kingside)
  565. return false;
  566. return turn() == other.turn();
  567. }
  568. }