From e3a7b4b754a77b151fb6ed5d3dca9f271fff9a68 Mon Sep 17 00:00:00 2001 From: Calcitem Date: Fri, 7 Jun 2024 01:14:27 +0800 Subject: [PATCH] Lasker Morris: Allow pieces to move before they have all been placed --- .gitignore | 1 + src/mills.cpp | 1 + src/movegen.cpp | 10 +++++++++- src/movepick.cpp | 3 ++- src/position.cpp | 26 +++++++++++++++++++++----- src/position.h | 2 ++ src/search.cpp | 10 +++++++--- src/ui/qt/game_interaction.cpp | 6 +++--- src/ui/qt/game_ui.cpp | 5 +++++ 9 files changed, 51 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index b10458f9ac..37c043b167 100644 --- a/.gitignore +++ b/.gitignore @@ -438,6 +438,7 @@ cov-int flutter_export_environment.sh .autogit build/ +out/ # Snapcraft *.snap diff --git a/src/mills.cpp b/src/mills.cpp index 5209a539f3..4a4b2aebf8 100644 --- a/src/mills.cpp +++ b/src/mills.cpp @@ -461,6 +461,7 @@ bool is_star_squares_full(Position *pos) return ret; } +// TODO: For Lasker Morris Depth get_search_depth(const Position *pos) { Depth d = 0; diff --git a/src/movegen.cpp b/src/movegen.cpp index 5588a98439..2f376197f8 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -61,8 +61,13 @@ ExtMove *generate(Position &pos, ExtMove *moveList) template <> ExtMove *generate(Position &pos, ExtMove *moveList) { + const Color us = pos.side_to_move(); ExtMove *cur = moveList; + if (pos.piece_in_hand_count(us) == 0) { + return cur; + } + for (auto s : MoveList::movePriorityList) { if (!pos.get_board()[s]) { *cur++ = static_cast(s); @@ -129,11 +134,14 @@ ExtMove *generate(Position &pos, ExtMove *moveList) switch (pos.get_action()) { case Action::select: case Action::place: + // Generate both PLACE and MOVE actions if the phase is placing if (pos.get_phase() == Phase::placing || pos.get_phase() == Phase::ready) { - return generate(pos, moveList); + cur = generate(pos, moveList); + return generate(pos, cur); } + // Generate MOVE actions if the phase is moving if (pos.get_phase() == Phase::moving) { return generate(pos, moveList); } diff --git a/src/movepick.cpp b/src/movepick.cpp index 2f8cc392fd..f751f5af95 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -69,7 +69,8 @@ void MovePicker::score() to, pos.side_to_move(), from); #ifndef SORT_MOVE_WITHOUT_HUMAN_KNOWLEDGE - // TODO(calcitem): rule.mayRemoveMultiple adapt other rules + // TODO(calcitem): rule.mayRemoveMultiple adapt other rules such as + // Lasker Morris if (type_of(m) != MOVETYPE_REMOVE) { // all phrase, check if place sq can close mill if (ourMillsCount > 0) { diff --git a/src/position.cpp b/src/position.cpp index 685e087dc1..7798cf02ca 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -690,8 +690,7 @@ bool Position::put_piece(Square s, bool updateRecord) { const Color us = sideToMove; - if (phase == Phase::gameOver || action != Action::place || - !(SQ_BEGIN <= s && s < SQ_END) || board[s]) { + if (phase == Phase::gameOver || !(SQ_BEGIN <= s && s < SQ_END)) { return false; } @@ -701,7 +700,17 @@ bool Position::put_piece(Square s, bool updateRecord) start(); } - if (phase == Phase::placing) { + // Emanuel Lasker Morris: Allow moving pieces during the placing phase + if (phase == Phase::placing && can_move_during_placing_phase()) { + // Allow moving if the piece is already on the board + if (board[s] & make_piece(us)) { + currentSquare = s; + action = Action::place; + return true; + } + } + + if (phase == Phase::placing && action == Action::place) { const auto piece = static_cast((0x01 | make_piece(sideToMove)) + rule.pieceCount - pieceInHandCount[us]); @@ -995,7 +1004,9 @@ bool Position::remove_piece(Square s, bool updateRecord) bool Position::select_piece(Square s) { - if (phase != Phase::moving) + // Allow selecting pieces during placing phase if allowed + if (phase != Phase::moving && + !(phase == Phase::placing && can_move_during_placing_phase())) return false; if (action != Action::select && action != Action::place) @@ -1046,6 +1057,11 @@ bool Position::handle_placing_phase_end() return true; } +inline bool Position::can_move_during_placing_phase() const +{ + return rule.mayMoveInPlacingPhase; // TODO: piece in hand +} + bool Position::resign(Color loser) { if (phase == Phase::ready || phase == Phase::gameOver || @@ -1257,7 +1273,7 @@ inline void Position::set_side_to_move(Color c) them = ~sideToMove; - if (pieceInHandCount[sideToMove] == 0) { + if (pieceInHandCount[sideToMove] == 0 || can_move_during_placing_phase()) { phase = Phase::moving; action = Action::select; } else { diff --git a/src/position.h b/src/position.h index c2925e39b4..159b50dd57 100644 --- a/src/position.h +++ b/src/position.h @@ -191,6 +191,8 @@ class Position bool handle_placing_phase_end(); + bool can_move_during_placing_phase() const; + // Data members Piece board[SQUARE_EXT_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; diff --git a/src/search.cpp b/src/search.cpp index 2f577763bf..29dd7f9eca 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -103,7 +103,9 @@ int Thread::search() std::chrono::steady_clock::time_point cycleEnd; #endif - if (rootPos->get_phase() == Phase::moving) { + if (rootPos->get_phase() == Phase::moving || + (rootPos->get_phase() == Phase::placing && + rule.mayMoveInPlacingPhase)) { #ifdef RULE_50 if (posKeyHistory.size() >= rule.nMoveRule) { return 50; @@ -123,10 +125,12 @@ int Thread::search() assert(posKeyHistory.size() < 256); } - if (rootPos->get_phase() == Phase::placing) { + if (rootPos->get_phase() == Phase::placing && !rule.mayMoveInPlacingPhase) { posKeyHistory.clear(); rootPos->st.rule50 = 0; - } else if (rootPos->get_phase() == Phase::moving) { + } else if (rootPos->get_phase() == Phase::moving || + (rootPos->get_phase() == Phase::placing && + rule.mayMoveInPlacingPhase)) { rootPos->st.rule50 = static_cast(posKeyHistory.size()); } diff --git a/src/ui/qt/game_interaction.cpp b/src/ui/qt/game_interaction.cpp index 3cf7dee1d7..7cfa44418a 100644 --- a/src/ui/qt/game_interaction.cpp +++ b/src/ui/qt/game_interaction.cpp @@ -62,7 +62,7 @@ bool Game::validateClick(QPointF p, File &f, Rank &r) bool Game::performAction(File f, Rank r, QPointF p) { - // Judge whether to select, place or remove the piece + // Judge whether to select, place, move, or remove the piece bool result = false; PieceItem *piece; QGraphicsItem *item = scene.itemAt(p, QTransform()); @@ -87,8 +87,8 @@ bool Game::performAction(File f, Rank r, QPointF p) break; } - // If the moving is not successful, try to reselect. There is no break - // here + // If placing is not successful, try to reselect or move. There is no + // break here [[fallthrough]]; case Action::select: diff --git a/src/ui/qt/game_ui.cpp b/src/ui/qt/game_ui.cpp index 6a14f1f5cb..81f65c1e52 100644 --- a/src/ui/qt/game_ui.cpp +++ b/src/ui/qt/game_ui.cpp @@ -287,6 +287,11 @@ void Game::setTips() tips = turnStr + " to remove a piece. " + std::to_string(p.pieceToRemoveCount[p.sideToMove]) + " pieces can be removed."; + } else if (rule.mayMoveInPlacingPhase && + (p.action == Action::select || p.action == Action::place)) { + tips = turnStr + " to place or move a piece. " + + std::to_string(p.pieceInHandCount[p.sideToMove]) + + " pieces remain unplaced."; } break;