Skip to content

Commit

Permalink
rule: Fix ELFilja Rules
Browse files Browse the repository at this point in the history
In the case where neither player forms a mill (according to the scenario),
each player must remove one of their own pieces, not the opponent's.
  • Loading branch information
calcitem committed Dec 30, 2024
1 parent 978f669 commit 02acb55
Show file tree
Hide file tree
Showing 67 changed files with 304 additions and 120 deletions.
43 changes: 33 additions & 10 deletions src/movegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,36 @@ ExtMove *generate<PLACE>(Position &pos, ExtMove *moveList)
}

/// generate<REMOVE> generates all removes.
/// Returns a pointer to the end of the move moves.
/// Returns a pointer to the end of the move list.
template <>
ExtMove *generate<REMOVE>(Position &pos, ExtMove *moveList)
{
const Color us = pos.side_to_move();
const Color them = ~us;

// Determine if we need to remove our own pieces based on pieceToRemoveCount
bool removeOwnPieces = pos.pieceToRemoveCount[us] < 0;

// Set the color of pieces to remove: own or opponent's
const Color removeColor = removeOwnPieces ? us : them;

ExtMove *cur = moveList;

// Handle stalemate removal
if (pos.is_stalemate_removal()) {
for (auto i = SQUARE_NB - 1; i >= 0; i--) {
for (int i = SQUARE_NB - 1; i >= 0; i--) {
Square s = MoveList<LEGAL>::movePriorityList[i];
if (pos.get_board()[s] & make_piece(them)) {
if (pos.is_adjacent_to(s, us) == true) {

// Check if the square has a piece of the color to remove
if (pos.get_board()[s] & make_piece(removeColor)) {
// If removing opponent's pieces, check adjacency to 'us'
// If removing own pieces, adjacency check may differ or be
// omitted
if (!removeOwnPieces && pos.is_adjacent_to(s, us)) {
*cur++ = static_cast<Move>(-s);
}
// If removing own pieces, allow removal without adjacency
else if (removeOwnPieces) {
*cur++ = static_cast<Move>(-s);
}
}
Expand All @@ -122,20 +138,27 @@ ExtMove *generate<REMOVE>(Position &pos, ExtMove *moveList)
return cur;
}

if (pos.is_all_in_mills(them)) {
for (auto i = SQUARE_NB - 1; i >= 0; i--) {
// Handle removal when all opponent's pieces are in mills
if (pos.is_all_in_mills(removeColor)) {
for (int i = SQUARE_NB - 1; i >= 0; i--) {
Square s = MoveList<LEGAL>::movePriorityList[i];
if (pos.get_board()[s] & make_piece(them)) {

// Check if the square has a piece of the color to remove
if (pos.get_board()[s] & make_piece(removeColor)) {
*cur++ = static_cast<Move>(-s);
}
}
return cur;
}

// not is all in mills
for (auto i = SQUARE_NB - 1; i >= 0; i--) {
// Handle general removal (not all in mills)
for (int i = SQUARE_NB - 1; i >= 0; i--) {
const Square s = MoveList<LEGAL>::movePriorityList[i];
if (pos.get_board()[s] & make_piece(them)) {

// Check if the square has a piece of the color to remove
if (pos.get_board()[s] & make_piece(removeColor)) {
// If the rule allows removing from mills always
// or the piece is not part of a potential mill, allow removal
if (rule.mayRemoveFromMillsAlways ||
!pos.potential_mills_count(s, NOBODY)) {
*cur++ = static_cast<Move>(-s);
Expand Down
47 changes: 31 additions & 16 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -792,8 +792,8 @@ bool Position::put_piece(Square s, bool updateRecord)
if (n == 0) {
// If no Mill

if (pieceToRemoveCount[WHITE] > 0 ||
pieceToRemoveCount[BLACK] > 0) {
if (pieceToRemoveCount[WHITE] != 0 ||
pieceToRemoveCount[BLACK] != 0) {
assert(false);
return false;
}
Expand Down Expand Up @@ -1033,12 +1033,17 @@ bool Position::remove_piece(Square s, bool updateRecord)
if (action != Action::remove)
return false;

if (pieceToRemoveCount[sideToMove] <= 0)
return false;

// if piece is not their
if (!(make_piece(~side_to_move()) & board[s]))
if (pieceToRemoveCount[sideToMove] == 0) {
return false;
} else if (pieceToRemoveCount[sideToMove] > 0) {
if (!(make_piece(~side_to_move()) & board[s])) {
return false;
}
} else {
if (!(make_piece(side_to_move()) & board[s])) {
return false;
}
}

if (is_stalemate_removal()) {
if (is_adjacent_to(s, sideToMove) == false) {
Expand Down Expand Up @@ -1091,11 +1096,16 @@ bool Position::remove_piece(Square s, bool updateRecord)

currentSquare[sideToMove] = SQ_0;

pieceToRemoveCount[sideToMove]--;
if (pieceToRemoveCount[sideToMove] > 0) {
pieceToRemoveCount[sideToMove]--;
} else {
pieceToRemoveCount[sideToMove]++;
}

update_key_misc();

// Need to remove rest pieces.
if (pieceToRemoveCount[sideToMove] > 0) {
if (pieceToRemoveCount[sideToMove] != 0) {
return true;
}

Expand All @@ -1108,7 +1118,7 @@ bool Position::remove_piece(Square s, bool updateRecord)
}
}

if (pieceToRemoveCount[sideToMove] > 0) {
if (pieceToRemoveCount[sideToMove] != 0) {
return true;
}

Expand Down Expand Up @@ -1144,8 +1154,11 @@ bool Position::select_piece(Square s)
bool Position::handle_placing_phase_end()
{
if (phase != Phase::placing || pieceInHandCount[WHITE] > 0 ||
pieceInHandCount[BLACK] > 0 || pieceToRemoveCount[WHITE] > 0 ||
pieceToRemoveCount[BLACK] > 0) {
pieceInHandCount[BLACK] > 0 ||
((pieceToRemoveCount[WHITE] < 0 ? -pieceToRemoveCount[WHITE] :
pieceToRemoveCount[WHITE]) > 0) ||
((pieceToRemoveCount[BLACK] < 0 ? -pieceToRemoveCount[BLACK] :
pieceToRemoveCount[BLACK]) > 0)) {
return false;
}

Expand Down Expand Up @@ -1334,7 +1347,8 @@ bool Position::check_if_game_is_over()
}
}

if (pieceToRemoveCount[sideToMove] > 0) {
if (pieceToRemoveCount[sideToMove] > 0 ||
pieceToRemoveCount[sideToMove] < 0) {
action = Action::remove;
}

Expand Down Expand Up @@ -1395,8 +1409,8 @@ inline void Position::calculate_removal_based_on_mill_counts()
int blackRemove = 1;

if (whiteMills == 0 && blackMills == 0) {
whiteRemove = 1;
blackRemove = 1;
whiteRemove = -1;
blackRemove = -1;
} else if (whiteMills > 0 && blackMills == 0) {
whiteRemove = 2;
blackRemove = 1;
Expand Down Expand Up @@ -1445,7 +1459,8 @@ inline void Position::set_side_to_move(Color c)
action = Action::place;
}

if (pieceToRemoveCount[sideToMove] > 0) {
if (pieceToRemoveCount[sideToMove] > 0 ||
pieceToRemoveCount[sideToMove] < 0) {
action = Action::remove;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ class CanNotRemoveSelf implements RemoveResponse {
const CanNotRemoveSelf();
}

class ShouldRemoveSelf implements RemoveResponse {
const ShouldRemoveSelf();
}

class CanNotRemoveMill implements RemoveResponse {
const CanNotRemoveMill();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,19 @@ class TapHandler {
showTip(S.of(context).tipSelectOpponentsPiece);
}
break;
case ShouldRemoveSelf():
logger
.i("$_logTag removePiece: Should Remove our piece, skip [$sq]");
if (GameController().gameInstance.gameMode ==
GameMode.humanVsHuman) {
final String side =
controller.position.sideToMove.playerName(context);
showTip(
"${S.of(context).tipSelectOwnPiece} ${S.of(context).tipToMove(side)}");
} else {
showTip(S.of(context).tipSelectOwnPiece);
}
break;
case CanNotRemoveMill():
logger.i(
"$_logTag removePiece: Cannot remove piece from Mill, skip [$sq]",
Expand Down
53 changes: 27 additions & 26 deletions src/ui/flutter_app/lib/game_page/services/engine/position.dart
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,8 @@ class Position {

// Parts 8-9: Need to remove
counts = parts.getRange(8, 10).map(int.parse).toList();
if (counts.any((int count) => count < 0 || count > 3)) {
logger.e('Invalid need to remove count. Must be 0, 1, 2, or 3.');
if (counts.any((int count) => count < -7 || count > 7)) {
logger.e('Invalid need to remove count. Must be between -7 and 7.');
return false;
}

Expand Down Expand Up @@ -751,8 +751,8 @@ class Position {
if (n == 0) {
// If no Mill

if (pieceToRemoveCount[PieceColor.white]! > 0 ||
pieceToRemoveCount[PieceColor.black]! > 0) {
if (pieceToRemoveCount[PieceColor.white]! != 0 ||
pieceToRemoveCount[PieceColor.black]! != 0) {
logger.e("[position] putPiece: pieceToRemoveCount is not 0.");
return false;
}
Expand Down Expand Up @@ -1041,13 +1041,16 @@ class Position {
return const IllegalAction();
}

if (pieceToRemoveCount[sideToMove]! <= 0) {
if (pieceToRemoveCount[sideToMove]! == 0) {
return const NoPieceToRemove();
}

// If piece is not their
if (!(sideToMove.opponent == _board[s])) {
return const CanNotRemoveSelf();
} else if (pieceToRemoveCount[sideToMove]! > 0) {
if (!(sideToMove.opponent == _board[s])) {
return const CanNotRemoveSelf();
}
} else {
if (!(sideToMove == _board[s])) {
return const ShouldRemoveSelf();
}
}

if (isStalemateRemoval(sideToMove)) {
Expand Down Expand Up @@ -1089,7 +1092,12 @@ class Position {

_currentSquare[sideToMove] = 0;

pieceToRemoveCount[sideToMove] = pieceToRemoveCount[sideToMove]! - 1;
if (pieceToRemoveCount[sideToMove]! > 0) {
pieceToRemoveCount[sideToMove] = pieceToRemoveCount[sideToMove]! - 1;
} else {
pieceToRemoveCount[sideToMove] = pieceToRemoveCount[sideToMove]! + 1;
}

_updateKeyMisc();

// Need to remove rest pieces.
Expand Down Expand Up @@ -1156,8 +1164,8 @@ class Position {
if (phase != Phase.placing ||
pieceInHandCount[PieceColor.white]! > 0 ||
pieceInHandCount[PieceColor.black]! > 0 ||
pieceToRemoveCount[PieceColor.white]! > 0 ||
pieceToRemoveCount[PieceColor.black]! > 0) {
pieceToRemoveCount[PieceColor.white]!.abs() > 0 ||
pieceToRemoveCount[PieceColor.black]!.abs() > 0) {
return false;
}

Expand Down Expand Up @@ -1288,7 +1296,8 @@ class Position {
}
}

if (pieceToRemoveCount[sideToMove]! > 0) {
if (pieceToRemoveCount[sideToMove]! > 0 ||
pieceToRemoveCount[sideToMove]! < 0) {
action = Act.remove;
}

Expand Down Expand Up @@ -1321,8 +1330,8 @@ class Position {
int blackRemove = 1;

if (whiteMills == 0 && blackMills == 0) {
whiteRemove = 1;
blackRemove = 1;
whiteRemove = -1;
blackRemove = -1;
} else if (whiteMills > 0 && blackMills == 0) {
whiteRemove = 2;
blackRemove = 1;
Expand Down Expand Up @@ -1372,10 +1381,9 @@ class Position {
logger.e("[position] setSideToMove: Invalid pieceInHandCount.");
}

if (pieceToRemoveCount[sideToMove]! > 0) {
if (pieceToRemoveCount[sideToMove]! > 0 ||
pieceToRemoveCount[sideToMove]! < 0) {
action = Act.remove;
} else if (pieceToRemoveCount[sideToMove]! < 0) {
logger.e("[position] setSideToMove: Invalid pieceToRemoveCount.");
}
}

Expand Down Expand Up @@ -1724,13 +1732,6 @@ extension SetupPosition on Position {
pieceToRemoveCount[PieceColor.black] =
pos.pieceToRemoveCount[PieceColor.black]!;

if (pieceToRemoveCount[PieceColor.white]! < 0 ||
pieceToRemoveCount[PieceColor.black]! < 0) {
logger.e(
"[position] copyWith: pieceToRemoveCount is less than 0.",
);
}

isNeedStalemateRemoval = pos.isNeedStalemateRemoval;
isStalemateRemoving = pos.isStalemateRemoving;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ class SetupPositionToolbarState extends State<SetupPositionToolbar> {
GameController().position.pieceToRemoveCount[newPieceColor] =
newPieceCountNeedRemove[newPieceColor]!;

if (DB().ruleSettings.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase.removalBasedOnMillCounts) {
// TODO: Adapt removalBasedOnMillCounts
// final int whiteMills = GameController().position.totalMillsCount(PieceColor.white);
// final int blackMills = GameController().position.totalMillsCount(PieceColor.black);
}

if (next == true) {
if (limit == 0 || newPieceCountNeedRemove[newPieceColor] == 0) {
GameController()
Expand Down Expand Up @@ -608,7 +615,8 @@ class SetupPositionToolbarState extends State<SetupPositionToolbar> {

if (GameController()
.position
.pieceToRemoveCount[GameController().position.sideToMove]! >
.pieceToRemoveCount[GameController().position.sideToMove]!
.abs() >
0) {
GameController().position.action = Act.remove;
}
Expand Down
4 changes: 3 additions & 1 deletion src/ui/flutter_app/lib/l10n/intl_af.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1625,5 +1625,7 @@
"removalBasedOnMillCounts_Detail": "Skakel verwydering uit totdat alle stukke geplaas is. Spelers verwyder stukke op grond van Morabaraba-tellings: as dit gelyk is, verwyder albei dieselfde aantal stukke; as een meer het, verwyder hy een ekstra, met ’n verskil van net één stuk. As slegs een Morabaraba vorm, verwyder daardie speler twee stukke en die ander speler een. As geen speler ’n Morabaraba vorm nie, verwyder albei een stuk.",
"@removalBasedOnMillCounts_Detail": {},
"elfilja": "ELFilja",
"@elfilja": {}
"@elfilja": {},
"tipSelectOwnPiece": "Kies een van jou stukke.",
"@tipSelectOwnPiece": {}
}
4 changes: 3 additions & 1 deletion src/ui/flutter_app/lib/l10n/intl_am.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1625,5 +1625,7 @@
"removalBasedOnMillCounts_Detail": "የማስወገድ ሂደቱን እስከ ሁሉም ቅዱሳት እንዲቀመጡ ድረስ አትቻል። ተጫዋቾች ቅዱሳትን በሚቆጣጠሩበት መሠረት ያስወግዳሉ፡ ከሆነ ብዛት አንድ አይደለም ከሆነ፣ ብዛት በላዩ አንዱ ተጫዋች አንድ ተጨማሪ ያስወግዳል እና የተለየ አንድ ቅዱስ ልዩነት ይቆያል። ከሆነ አንድ ብቻ ከሚከናውነው የቅዱሳት ተውጣጥፋ ሂደት ጋር፣ ያ ተጫዋች ሁለት ቅዱሳት ያስወግዳል እና አስተማማኝ አድርጎ በወደቀው ተጫዋች አንድ ቅዱስ ይወጣል። ከሆነ ሁለቱም ቅዱሳት አይቆጣጠሩም ከሆነ፣ ሁለቱም አንድ ቅዱስ ይወጣሉ።",
"@removalBasedOnMillCounts_Detail": {},
"elfilja": "ኢልፊልጃ",
"@elfilja": {}
"@elfilja": {},
"tipSelectOwnPiece": "ከጨዋታ ቁሞችዎ አንዱን ይምረጡ።",
"@tipSelectOwnPiece": {}
}
4 changes: 3 additions & 1 deletion src/ui/flutter_app/lib/l10n/intl_ar.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1527,5 +1527,7 @@
"removalBasedOnMillCounts_Detail": "ممنوع الإزالة حتى يتم وضع جميع القطع. يقوم اللاعبون بإزالة القطع حسب عدد الطواحين المشكلة: إذا تساوى العدد، يزيل كلاهما نفس العدد ؛ واذا كان أحدهم يمتلك طواحين أكثر، فإنه يزيل قطعة إضافية واحدة مع الحفاظ على فارق قطعة واحدة. إذا قام أحد اللاعبين فقط بتشكيل طواحين،فإنه يزيل قطعتين بينما يزيل الآخر واحدة. إذا لم يشكل اي منهما طواحين، يزيل كل منهما قطعة واحدة من قطعه الخاصة.",
"@removalBasedOnMillCounts_Detail": {},
"elfilja": "إلفيلجا",
"@elfilja": {}
"@elfilja": {},
"tipSelectOwnPiece": "اختر إحدى قطعك.",
"@tipSelectOwnPiece": {}
}
4 changes: 3 additions & 1 deletion src/ui/flutter_app/lib/l10n/intl_az.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1625,5 +1625,7 @@
"removalBasedOnMillCounts_Detail": "Bütün fişlər yerləşdirilənə qədər çıxarışı dayandırın. Oyunçular fişləri dəyirman sayına əsasən çıxarırlar: əgər saylar bərabərdirsə, hər iki oyunçu eyni sayda fiş çıxarır; birinin sayı çoxdursa, o, əlavə bir fiş çıxararaq fərqi bir fiş səviyyəsində saxlayır. Yalnız bir oyunçu dəyirman yığıbsa, o, iki fiş çıxarır, digər oyunçu isə bir fiş çıxarır. Heç biri dəyirman yığmayıbsa, hər ikisi bir fiş çıxarır.",
"@removalBasedOnMillCounts_Detail": {},
"elfilja": "ELFilja",
"@elfilja": {}
"@elfilja": {},
"tipSelectOwnPiece": "Fişlərinizdən birini seçin.",
"@tipSelectOwnPiece": {}
}
4 changes: 3 additions & 1 deletion src/ui/flutter_app/lib/l10n/intl_be.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1625,5 +1625,7 @@
"removalBasedOnMillCounts_Detail": "Выключыце выдаленне, пакуль усе фігуры не будуць расстаўлены. Гульцы выдаляюць фігуры на падставе колькасці млыноў: калі колькасць аднолькавая, абодва выдаляюць аднолькавую колькасць; калі ў аднаго больш, ён выдаляе лішнюю фігуру, захоўваючы розніцу ў адну фігуру. Калі млын утварыў толькі адзін гульняр, ён выдаляе дзве фігуры, а другі — адну. Калі ніводны млын не ўтварыўся, абодва выдаляюць па адной фігуры.",
"@removalBasedOnMillCounts_Detail": {},
"elfilja": "Эльфілья",
"@elfilja": {}
"@elfilja": {},
"tipSelectOwnPiece": "Абярыце адну са сваіх фігур.",
"@tipSelectOwnPiece": {}
}
Loading

0 comments on commit 02acb55

Please sign in to comment.