Chess.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  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_promotion(const Move& move, Colour colour) const
  160. {
  161. auto piece = get_piece(move.from);
  162. if (move.promote_to == Type::Pawn || move.promote_to == Type::King) {
  163. // attempted promotion to invalid piece
  164. return false;
  165. }
  166. if (piece.type != Type::Pawn && move.promote_to != Type::None) {
  167. // attempted promotion from invalid piece
  168. return false;
  169. }
  170. unsigned promotion_rank = (colour == Colour::White) ? 7 : 0;
  171. if (move.to.rank != promotion_rank && move.promote_to != Type::None) {
  172. // attempted promotion from invalid rank
  173. return false;
  174. }
  175. if (piece.type == Type::Pawn && move.to.rank == promotion_rank && move.promote_to == Type::None) {
  176. // attempted move to promotion rank without promoting
  177. return false;
  178. }
  179. return true;
  180. }
  181. bool Board::is_legal(const Move& move, Colour colour) const
  182. {
  183. if (colour == Colour::None)
  184. colour = turn();
  185. if (!is_legal_no_check(move, colour))
  186. return false;
  187. if (!is_legal_promotion(move, colour))
  188. return false;
  189. Board clone = *this;
  190. clone.apply_illegal_move(move, colour);
  191. if (clone.in_check(colour))
  192. return false;
  193. // Don't allow castling through check or out of check.
  194. Vector<Square> check_squares;
  195. if (colour == Colour::White && move.from == Square("e1") && get_piece(Square("e1")) == Piece(Colour::White, Type::King)) {
  196. if (move.to == Square("a1") || move.to == Square("c1")) {
  197. check_squares = { Square("e1"), Square("d1"), Square("c1") };
  198. } else if (move.to == Square("h1") || move.to == Square("g1")) {
  199. check_squares = { Square("e1"), Square("f1"), Square("g1") };
  200. }
  201. } else if (colour == Colour::Black && move.from == Square("e8") && get_piece(Square("e8")) == Piece(Colour::Black, Type::King)) {
  202. if (move.to == Square("a8") || move.to == Square("c8")) {
  203. check_squares = { Square("e8"), Square("d8"), Square("c8") };
  204. } else if (move.to == Square("h8") || move.to == Square("g8")) {
  205. check_squares = { Square("e8"), Square("f8"), Square("g8") };
  206. }
  207. }
  208. for (auto& square : check_squares) {
  209. Board clone = *this;
  210. clone.set_piece(move.from, EmptyPiece);
  211. clone.set_piece(square, { colour, Type::King });
  212. if (clone.in_check(colour))
  213. return false;
  214. }
  215. return true;
  216. }
  217. bool Board::is_legal_no_check(const Move& move, Colour colour) const
  218. {
  219. auto piece = get_piece(move.from);
  220. if (piece.colour != colour)
  221. // attempted move of opponent's piece
  222. return false;
  223. if (move.to.rank > 7 || move.to.file > 7)
  224. // attempted move outside of board
  225. return false;
  226. if (piece.type == Type::Pawn) {
  227. int dir = (colour == Colour::White) ? +1 : -1;
  228. unsigned start_rank = (colour == Colour::White) ? 1 : 6;
  229. 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. if (move.to.rank != move.from.rank + dir)
  235. // attempted backwards or sideways move
  236. return false;
  237. if (move.to.file == move.from.file && get_piece(move.to).type == Type::None) {
  238. // Regular pawn move.
  239. return true;
  240. }
  241. if (move.to.file == move.from.file + 1 || move.to.file == move.from.file - 1) {
  242. unsigned other_start_rank = (colour == Colour::White) ? 6 : 1;
  243. unsigned en_passant_rank = (colour == Colour::White) ? 4 : 3;
  244. Move en_passant_last_move = { { other_start_rank, move.to.file }, { en_passant_rank, move.to.file } };
  245. if (get_piece(move.to).colour == opposing_colour(colour)) {
  246. // Pawn capture.
  247. return true;
  248. }
  249. if (m_last_move.has_value() && move.from.rank == en_passant_rank && m_last_move.value() == en_passant_last_move
  250. && get_piece(en_passant_last_move.to) == Piece(opposing_colour(colour), Type::Pawn)) {
  251. // En passant.
  252. return true;
  253. }
  254. }
  255. return false;
  256. } else if (piece.type == Type::Knight) {
  257. int rank_delta = abs(move.to.rank - move.from.rank);
  258. int file_delta = abs(move.to.file - move.from.file);
  259. if (get_piece(move.to).colour != colour && max(rank_delta, file_delta) == 2 && min(rank_delta, file_delta) == 1) {
  260. return true;
  261. }
  262. } else if (piece.type == Type::Bishop) {
  263. int rank_delta = move.to.rank - move.from.rank;
  264. int file_delta = move.to.file - move.from.file;
  265. if (abs(rank_delta) == abs(file_delta)) {
  266. int dr = rank_delta / abs(rank_delta);
  267. int df = file_delta / abs(file_delta);
  268. for (Square sq = move.from; sq != move.to; sq.rank += dr, sq.file += df) {
  269. if (get_piece(sq).type != Type::None && sq != move.from) {
  270. return false;
  271. }
  272. }
  273. if (get_piece(move.to).colour != colour) {
  274. return true;
  275. }
  276. }
  277. } else if (piece.type == Type::Rook) {
  278. int rank_delta = move.to.rank - move.from.rank;
  279. int file_delta = move.to.file - move.from.file;
  280. if (rank_delta == 0 || file_delta == 0) {
  281. int dr = (rank_delta) ? rank_delta / abs(rank_delta) : 0;
  282. int df = (file_delta) ? file_delta / abs(file_delta) : 0;
  283. for (Square sq = move.from; sq != move.to; sq.rank += dr, sq.file += df) {
  284. if (get_piece(sq).type != Type::None && sq != move.from) {
  285. return false;
  286. }
  287. }
  288. if (get_piece(move.to).colour != colour) {
  289. return true;
  290. }
  291. }
  292. } else if (piece.type == Type::Queen) {
  293. int rank_delta = move.to.rank - move.from.rank;
  294. int file_delta = move.to.file - move.from.file;
  295. if (abs(rank_delta) == abs(file_delta) || rank_delta == 0 || file_delta == 0) {
  296. int dr = (rank_delta) ? rank_delta / abs(rank_delta) : 0;
  297. int df = (file_delta) ? file_delta / abs(file_delta) : 0;
  298. for (Square sq = move.from; sq != move.to; sq.rank += dr, sq.file += df) {
  299. if (get_piece(sq).type != Type::None && sq != move.from) {
  300. return false;
  301. }
  302. }
  303. if (get_piece(move.to).colour != colour) {
  304. return true;
  305. }
  306. }
  307. } else if (piece.type == Type::King) {
  308. int rank_delta = move.to.rank - move.from.rank;
  309. int file_delta = move.to.file - move.from.file;
  310. if (abs(rank_delta) <= 1 && abs(file_delta) <= 1) {
  311. if (get_piece(move.to).colour != colour) {
  312. return true;
  313. }
  314. }
  315. if (colour == Colour::White) {
  316. 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) {
  317. return true;
  318. } 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) {
  319. return true;
  320. }
  321. } else {
  322. 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) {
  323. return true;
  324. } 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) {
  325. return true;
  326. }
  327. }
  328. }
  329. return false;
  330. }
  331. bool Board::in_check(Colour colour) const
  332. {
  333. Square king_square = { 50, 50 };
  334. Square::for_each([&](const Square& square) {
  335. if (get_piece(square) == Piece(colour, Type::King)) {
  336. king_square = square;
  337. return IterationDecision::Break;
  338. }
  339. return IterationDecision::Continue;
  340. });
  341. bool check = false;
  342. Square::for_each([&](const Square& square) {
  343. if (is_legal_no_check({ square, king_square }, opposing_colour(colour))) {
  344. check = true;
  345. return IterationDecision::Break;
  346. }
  347. return IterationDecision::Continue;
  348. });
  349. return check;
  350. }
  351. bool Board::apply_move(const Move& move, Colour colour)
  352. {
  353. if (colour == Colour::None)
  354. colour = turn();
  355. if (!is_legal(move, colour))
  356. return false;
  357. return apply_illegal_move(move, colour);
  358. }
  359. bool Board::apply_illegal_move(const Move& move, Colour colour)
  360. {
  361. Board clone = *this;
  362. clone.m_previous_states = {};
  363. clone.m_moves = {};
  364. auto state_count = 0;
  365. if (m_previous_states.contains(clone))
  366. state_count = m_previous_states.get(clone).value();
  367. m_previous_states.set(clone, state_count + 1);
  368. m_moves.append(move);
  369. m_turn = opposing_colour(colour);
  370. m_last_move = move;
  371. m_moves_since_capture++;
  372. if (move.from == Square("a1") || move.to == Square("a1") || move.from == Square("e1"))
  373. m_white_can_castle_queenside = false;
  374. if (move.from == Square("h1") || move.to == Square("h1") || move.from == Square("e1"))
  375. m_white_can_castle_kingside = false;
  376. if (move.from == Square("a8") || move.to == Square("a8") || move.from == Square("e8"))
  377. m_black_can_castle_queenside = false;
  378. if (move.from == Square("h8") || move.to == Square("h8") || move.from == Square("e8"))
  379. m_black_can_castle_kingside = false;
  380. if (colour == Colour::White && move.from == Square("e1") && get_piece(Square("e1")) == Piece(Colour::White, Type::King)) {
  381. if (move.to == Square("a1") || move.to == Square("c1")) {
  382. set_piece(Square("e1"), EmptyPiece);
  383. set_piece(Square("a1"), EmptyPiece);
  384. set_piece(Square("c1"), { Colour::White, Type::King });
  385. set_piece(Square("d1"), { Colour::White, Type::Rook });
  386. return true;
  387. } else if (move.to == Square("h1") || move.to == Square("g1")) {
  388. set_piece(Square("e1"), EmptyPiece);
  389. set_piece(Square("h1"), EmptyPiece);
  390. set_piece(Square("g1"), { Colour::White, Type::King });
  391. set_piece(Square("f1"), { Colour::White, Type::Rook });
  392. return true;
  393. }
  394. } else if (colour == Colour::Black && move.from == Square("e8") && get_piece(Square("e8")) == Piece(Colour::Black, Type::King)) {
  395. if (move.to == Square("a8") || move.to == Square("c8")) {
  396. set_piece(Square("e8"), EmptyPiece);
  397. set_piece(Square("a8"), EmptyPiece);
  398. set_piece(Square("c8"), { Colour::Black, Type::King });
  399. set_piece(Square("d8"), { Colour::Black, Type::Rook });
  400. return true;
  401. } else if (move.to == Square("h8") || move.to == Square("g8")) {
  402. set_piece(Square("e8"), EmptyPiece);
  403. set_piece(Square("h8"), EmptyPiece);
  404. set_piece(Square("g8"), { Colour::Black, Type::King });
  405. set_piece(Square("f8"), { Colour::Black, Type::Rook });
  406. return true;
  407. }
  408. }
  409. if (get_piece(move.from).type == Type::Pawn && ((colour == Colour::Black && move.to.rank == 0) || (colour == Colour::White && move.to.rank == 7))) {
  410. // Pawn Promotion
  411. set_piece(move.to, { colour, move.promote_to });
  412. set_piece(move.from, EmptyPiece);
  413. return true;
  414. }
  415. if (get_piece(move.from).type == Type::Pawn && move.from.file != move.to.file && get_piece(move.to).type == Type::None) {
  416. // En passant.
  417. if (colour == Colour::White) {
  418. set_piece({ move.to.rank - 1, move.to.file }, EmptyPiece);
  419. } else {
  420. set_piece({ move.to.rank + 1, move.to.file }, EmptyPiece);
  421. }
  422. m_moves_since_capture = 0;
  423. }
  424. if (get_piece(move.to).colour != Colour::None)
  425. m_moves_since_capture = 0;
  426. set_piece(move.to, get_piece(move.from));
  427. set_piece(move.from, EmptyPiece);
  428. return true;
  429. }
  430. Move Board::random_move(Colour colour) const
  431. {
  432. if (colour == Colour::None)
  433. colour = turn();
  434. Move move = { { 50, 50 }, { 50, 50 } };
  435. int probability = 1;
  436. generate_moves([&](Move m) {
  437. if (rand() % probability == 0)
  438. move = m;
  439. ++probability;
  440. return IterationDecision::Continue;
  441. });
  442. return move;
  443. }
  444. Board::Result Board::game_result() const
  445. {
  446. if (m_resigned != Colour::None)
  447. return (m_resigned == Colour::White) ? Result::WhiteResign : Result::BlackResign;
  448. bool sufficient_material = false;
  449. bool no_more_pieces_allowed = false;
  450. Optional<Square> bishop;
  451. Square::for_each([&](Square sq) {
  452. if (get_piece(sq).type == Type::Queen || get_piece(sq).type == Type::Rook || get_piece(sq).type == Type::Pawn) {
  453. sufficient_material = true;
  454. return IterationDecision::Break;
  455. }
  456. if (get_piece(sq).type != Type::None && get_piece(sq).type != Type::King && no_more_pieces_allowed) {
  457. sufficient_material = true;
  458. return IterationDecision::Break;
  459. }
  460. if (get_piece(sq).type == Type::Knight)
  461. no_more_pieces_allowed = true;
  462. if (get_piece(sq).type == Type::Bishop) {
  463. if (bishop.has_value()) {
  464. if (get_piece(sq).colour == get_piece(bishop.value()).colour) {
  465. sufficient_material = true;
  466. return IterationDecision::Break;
  467. } else if (sq.is_light() != bishop.value().is_light()) {
  468. sufficient_material = true;
  469. return IterationDecision::Break;
  470. }
  471. no_more_pieces_allowed = true;
  472. } else {
  473. bishop = sq;
  474. }
  475. }
  476. return IterationDecision::Continue;
  477. });
  478. if (!sufficient_material)
  479. return Result::InsufficientMaterial;
  480. bool are_legal_moves = false;
  481. generate_moves([&](Move m) {
  482. (void)m;
  483. are_legal_moves = true;
  484. return IterationDecision::Break;
  485. });
  486. if (are_legal_moves) {
  487. if (m_moves_since_capture >= 75 * 2)
  488. return Result::SeventyFiveMoveRule;
  489. if (m_moves_since_capture == 50 * 2)
  490. return Result::FiftyMoveRule;
  491. auto repeats = m_previous_states.get(*this);
  492. if (repeats.has_value()) {
  493. if (repeats.value() == 3)
  494. return Result::ThreeFoldRepetition;
  495. if (repeats.value() >= 5)
  496. return Result::FiveFoldRepetition;
  497. }
  498. return Result::NotFinished;
  499. }
  500. if (in_check(turn()))
  501. return Result::CheckMate;
  502. return Result::StaleMate;
  503. }
  504. Colour Board::game_winner() const
  505. {
  506. if (game_result() == Result::CheckMate)
  507. return opposing_colour(turn());
  508. return Colour::None;
  509. }
  510. int Board::game_score() const
  511. {
  512. switch (game_winner()) {
  513. case Colour::White:
  514. return +1;
  515. case Colour::Black:
  516. return -1;
  517. case Colour::None:
  518. return 0;
  519. }
  520. return 0;
  521. }
  522. bool Board::game_finished() const
  523. {
  524. return game_result() != Result::NotFinished;
  525. }
  526. int Board::material_imbalance() const
  527. {
  528. int imbalance = 0;
  529. Square::for_each([&](Square square) {
  530. int value = 0;
  531. switch (get_piece(square).type) {
  532. case Type::Pawn:
  533. value = 1;
  534. break;
  535. case Type::Knight:
  536. case Type::Bishop:
  537. value = 3;
  538. break;
  539. case Type::Rook:
  540. value = 5;
  541. break;
  542. case Type::Queen:
  543. value = 9;
  544. break;
  545. default:
  546. break;
  547. }
  548. if (get_piece(square).colour == Colour::White) {
  549. imbalance += value;
  550. } else {
  551. imbalance -= value;
  552. }
  553. return IterationDecision::Continue;
  554. });
  555. return imbalance;
  556. }
  557. bool Board::is_promotion_move(const Move& move, Colour colour) const
  558. {
  559. if (colour == Colour::None)
  560. colour = turn();
  561. unsigned promotion_rank = (colour == Colour::White) ? 7 : 0;
  562. if (move.to.rank != promotion_rank)
  563. return false;
  564. if (get_piece(move.from).type != Type::Pawn)
  565. return false;
  566. Move queen_move = move;
  567. queen_move.promote_to = Type::Queen;
  568. if (!is_legal(queen_move, colour))
  569. return false;
  570. return true;
  571. }
  572. bool Board::operator==(const Board& other) const
  573. {
  574. bool equal_squares = true;
  575. Square::for_each([&](Square sq) {
  576. if (get_piece(sq) != other.get_piece(sq)) {
  577. equal_squares = false;
  578. return IterationDecision::Break;
  579. }
  580. return IterationDecision::Continue;
  581. });
  582. if (!equal_squares)
  583. return false;
  584. if (m_white_can_castle_queenside != other.m_white_can_castle_queenside)
  585. return false;
  586. if (m_white_can_castle_kingside != other.m_white_can_castle_kingside)
  587. return false;
  588. if (m_black_can_castle_queenside != other.m_black_can_castle_queenside)
  589. return false;
  590. if (m_black_can_castle_kingside != other.m_black_can_castle_kingside)
  591. return false;
  592. return turn() == other.turn();
  593. }
  594. void Board::set_resigned(Chess::Colour c)
  595. {
  596. m_resigned = c;
  597. }
  598. String Board::result_to_string(Result r) const
  599. {
  600. switch (r) {
  601. case Result::CheckMate:
  602. if (m_turn == Chess::Colour::White)
  603. return "Black wins by Checkmate";
  604. else
  605. return "White wins by Checkmate";
  606. case Result::WhiteResign:
  607. return "Black wins by Resignation";
  608. case Result::BlackResign:
  609. return "White wins by Resignation";
  610. case Result::StaleMate:
  611. return "Draw by Stalemate";
  612. case Chess::Board::Result::FiftyMoveRule:
  613. return "Draw by 50 move rule";
  614. case Chess::Board::Result::SeventyFiveMoveRule:
  615. return "Draw by 75 move rule";
  616. case Chess::Board::Result::ThreeFoldRepetition:
  617. return "Draw by threefold repetition";
  618. case Chess::Board::Result::FiveFoldRepetition:
  619. return "Draw by fivefold repetition";
  620. case Chess::Board::Result::InsufficientMaterial:
  621. return "Draw by insufficient material";
  622. case Chess::Board::Result::NotFinished:
  623. return "Game not finished";
  624. default:
  625. ASSERT_NOT_REACHED();
  626. }
  627. }
  628. }