Skip to content

Commit

Permalink
rule: Add ELFilja Rules
Browse files Browse the repository at this point in the history
  • Loading branch information
calcitem committed Dec 29, 2024
1 parent 47723af commit 978f669
Show file tree
Hide file tree
Showing 72 changed files with 668 additions and 89 deletions.
2 changes: 2 additions & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ DPICHANGED
Diagnosticable
Dieharder
Dua
ELFilja
FONTCHANGE
FRE
GLIBCXX
Expand Down Expand Up @@ -100,6 +101,7 @@ dartpad
dms
dooz
eula
elfilja
firstguess
fluentui
fst
Expand Down
96 changes: 93 additions & 3 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,22 @@ bool Position::put_piece(Square s, bool updateRecord)
lastMillFromSquare[sideToMove] = SQ_NONE;
lastMillToSquare[sideToMove] = SQ_NONE;

if (rule.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase::removalBasedOnMillCounts) {
if (pieceInHandCount[WHITE] == 0 &&
pieceInHandCount[BLACK] == 0) {
if (!handle_placing_phase_end()) {
change_side_to_move();
}

// Check if Stalemate and change side to move if needed
if (check_if_game_is_over()) {
return true;
}
return true;
}
}

// Begin of set side to move

// Board is full at the end of Placing phase
Expand Down Expand Up @@ -844,10 +860,17 @@ bool Position::put_piece(Square s, bool updateRecord)
// End of set side to move
} else {
// If forming Mill
int rm = pieceToRemoveCount[sideToMove] = rule.mayRemoveMultiple ?
int rm = 0;

if (rule.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase::removalBasedOnMillCounts) {
rm = pieceToRemoveCount[sideToMove] = 0;
} else {
rm = pieceToRemoveCount[sideToMove] = rule.mayRemoveMultiple ?
n :
1;
update_key_misc();
update_key_misc();
}

if (rule.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase::
Expand Down Expand Up @@ -883,7 +906,25 @@ bool Position::put_piece(Square s, bool updateRecord)
return true;
}
} else {
action = Action::remove;
if (rule.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase::removalBasedOnMillCounts) {
if (pieceInHandCount[WHITE] == 0 &&
pieceInHandCount[BLACK] == 0) {
if (!handle_placing_phase_end()) {
change_side_to_move();
}

// Check if Stalemate and change side to move if needed
if (check_if_game_is_over()) {
return true;
}
return true;
} else {
change_side_to_move();
}
} else {
action = Action::remove;
}
return true;
}
}
Expand Down Expand Up @@ -1121,6 +1162,9 @@ bool Position::handle_placing_phase_end()
if (rule.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase::markAndDelayRemovingPieces) {
remove_marked_pieces();
} else if (rule.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase::removalBasedOnMillCounts) {
calculate_removal_based_on_mill_counts();
} else if (invariant) {
if (rule.isDefenderMoveFirst == true) {
set_side_to_move(BLACK);
Expand Down Expand Up @@ -1342,6 +1386,47 @@ void Position::remove_marked_pieces()
}
}

inline void Position::calculate_removal_based_on_mill_counts()
{
int whiteMills = total_mills_count(WHITE);
int blackMills = total_mills_count(BLACK);

int whiteRemove = 1;
int blackRemove = 1;

if (whiteMills == 0 && blackMills == 0) {
whiteRemove = 1;
blackRemove = 1;
} else if (whiteMills > 0 && blackMills == 0) {
whiteRemove = 2;
blackRemove = 1;
} else if (blackMills > 0 && whiteMills == 0) {
whiteRemove = 1;
blackRemove = 2;
} else {
if (whiteMills == blackMills) {
whiteRemove = whiteMills;
blackRemove = blackMills;
} else {
if (whiteMills > blackMills) {
blackRemove = blackMills;
whiteRemove = blackRemove + 1;
} else if (whiteMills < blackMills) {
whiteRemove = whiteMills;
blackRemove = whiteRemove + 1;
} else {
assert(false);
}
}
}

pieceToRemoveCount[WHITE] = whiteRemove;
pieceToRemoveCount[BLACK] = blackRemove;

// TODO: Bits count is not enough
update_key_misc();
}

inline void Position::set_side_to_move(Color c)
{
if (sideToMove != c) {
Expand Down Expand Up @@ -1395,6 +1480,11 @@ Key Position::update_key_misc()

// TODO: pieceToRemoveCount[sideToMove] or
// abs(pieceToRemoveCount[sideToMove] - pieceToRemoveCount[~sideToMove])?
// TODO: If pieceToRemoveCount[sideToMove]! <= 3,
// the top 2 bits can store its value correctly;
// if it is greater than 3, since only 2 bits are left,
// the storage will be truncated or directly get 0,
// and the original value cannot be completely retained.
st.key |= static_cast<Key>(pieceToRemoveCount[sideToMove])
<< (CHAR_BIT * sizeof(Key) - Zobrist::KEY_MISC_BIT);

Expand Down
1 change: 1 addition & 0 deletions src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class Position
bool move_piece(Square from, Square to);

int total_mills_count(Color c);
void calculate_removal_based_on_mill_counts();
bool is_board_full_removal_at_placing_phase_end();
bool is_adjacent_to(Square s, Color c);

Expand Down
5 changes: 5 additions & 0 deletions src/rule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ const Rule RULES[N_RULES] = {
BoardFullAction::firstAndSecondPlayerRemovePiece,
StalemateAction::removeOpponentsPieceAndMakeNextMove, true, 100, 100,
true},
{"ELFilja", "ELFilja", 12, 3, 3, false,
MillFormationActionInPlacingPhase::removalBasedOnMillCounts, false, false,
false, false, true, false,
BoardFullAction::firstAndSecondPlayerRemovePiece,
StalemateAction::endWithStalemateLoss, false, 100, 100, true},
{"Experimental", "Experimental", 12, 3, 3, true,
MillFormationActionInPlacingPhase::removeOpponentsPieceFromBoard, false,
true, false, false, true, false,
Expand Down
3 changes: 2 additions & 1 deletion src/rule.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum class MillFormationActionInPlacingPhase {
removeOpponentsPieceFromHandThenYourTurn = 2,
opponentRemovesOwnPiece = 3,
markAndDelayRemovingPieces = 4,
removalBasedOnMillCounts = 5,
};

enum class BoardFullAction {
Expand Down Expand Up @@ -115,7 +116,7 @@ struct Rule
bool threefoldRepetitionRule;
};

constexpr auto N_RULES = 10;
constexpr auto N_RULES = 11;
extern const Rule RULES[N_RULES];
extern Rule rule;
extern bool set_rule(int ruleIdx) noexcept;
Expand Down
2 changes: 1 addition & 1 deletion src/ucioption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ void init(OptionsMap &o)
static_cast<int>(
MillFormationActionInPlacingPhase::removeOpponentsPieceFromBoard),
static_cast<int>(
MillFormationActionInPlacingPhase::markAndDelayRemovingPieces),
MillFormationActionInPlacingPhase::removalBasedOnMillCounts),
on_millFormationActionInPlacingPhase);
o["MayMoveInPlacingPhase"] << Option(false, on_mayMoveInPlacingPhase);
o["IsDefenderMoveFirst"] << Option(false, on_isDefenderMoveFirst);
Expand Down
97 changes: 93 additions & 4 deletions src/ui/flutter_app/lib/game_page/services/engine/position.dart
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,22 @@ class Position {
GameController().gameInstance.focusIndex = squareToIndex[s];
SoundManager().playTone(Sound.place);

if (DB().ruleSettings.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase.removalBasedOnMillCounts) {
if (pieceInHandCount[PieceColor.white]! == 0 &&
pieceInHandCount[PieceColor.black]! == 0) {
if (!handlePlacingPhaseEnd()) {
changeSideToMove();
}

// Check if Stalemate and change side to move if needed
if (_checkIfGameIsOver()) {
return true;
}
return true;
}
}

// Begin of set side to move

// Board is full at the end of Placing phase
Expand Down Expand Up @@ -813,9 +829,16 @@ class Position {
// End of set side to move
} else {
// If forming Mill
final int rm = pieceToRemoveCount[sideToMove] =
DB().ruleSettings.mayRemoveMultiple ? n : 1;
_updateKeyMisc();
int rm = 0;

if (DB().ruleSettings.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase.removalBasedOnMillCounts) {
rm = pieceToRemoveCount[sideToMove] = 0;
} else {
rm = pieceToRemoveCount[sideToMove] =
DB().ruleSettings.mayRemoveMultiple ? n : 1;
_updateKeyMisc();
}

GameController().gameInstance.focusIndex = squareToIndex[s];
SoundManager().playTone(Sound.mill);
Expand Down Expand Up @@ -870,7 +893,25 @@ class Position {
return true;
}
} else {
action = Act.remove;
if (DB().ruleSettings.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase.removalBasedOnMillCounts) {
if (pieceInHandCount[PieceColor.white]! == 0 &&
pieceInHandCount[PieceColor.black]! == 0) {
if (!handlePlacingPhaseEnd()) {
changeSideToMove();
}

// Check if Stalemate and change side to move if needed
if (_checkIfGameIsOver()) {
return true;
}
return true;
} else {
changeSideToMove();
}
} else {
action = Act.remove;
}
return true;
}
}
Expand Down Expand Up @@ -1133,6 +1174,9 @@ class Position {
if (DB().ruleSettings.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase.markAndDelayRemovingPieces) {
_removeMarkedStones();
} else if (DB().ruleSettings.millFormationActionInPlacingPhase ==
MillFormationActionInPlacingPhase.removalBasedOnMillCounts) {
_calculateRemovalBasedOnMillCounts();
} else if (invariant) {
if (DB().ruleSettings.isDefenderMoveFirst == true) {
setSideToMove(PieceColor.black);
Expand Down Expand Up @@ -1269,6 +1313,46 @@ class Position {
}
}

void _calculateRemovalBasedOnMillCounts() {
final int whiteMills = totalMillsCount(PieceColor.white);
final int blackMills = totalMillsCount(PieceColor.black);

int whiteRemove = 1;
int blackRemove = 1;

if (whiteMills == 0 && blackMills == 0) {
whiteRemove = 1;
blackRemove = 1;
} else if (whiteMills > 0 && blackMills == 0) {
whiteRemove = 2;
blackRemove = 1;
} else if (blackMills > 0 && whiteMills == 0) {
whiteRemove = 1;
blackRemove = 2;
} else {
if (whiteMills == blackMills) {
whiteRemove = whiteMills;
blackRemove = blackMills;
} else {
if (whiteMills > blackMills) {
blackRemove = blackMills;
whiteRemove = blackRemove + 1;
} else if (whiteMills < blackMills) {
whiteRemove = whiteMills;
blackRemove = whiteRemove + 1;
} else {
assert(false);
}
}
}

pieceToRemoveCount[PieceColor.white] = whiteRemove;
pieceToRemoveCount[PieceColor.black] = blackRemove;

// TODO: Bits count is not enough
_updateKeyMisc();
}

void setSideToMove(PieceColor c) {
if (sideToMove != c) {
sideToMove = c;
Expand Down Expand Up @@ -1324,6 +1408,11 @@ class Position {

// TODO: pieceToRemoveCount[sideToMove] or
// abs(pieceToRemoveCount[sideToMove] - pieceToRemoveCount[~sideToMove])?
// TODO: If pieceToRemoveCount[sideToMove]! <= 3,
// the top 2 bits can store its value correctly;
// if it is greater than 3, since only 2 bits are left,
// the storage will be truncated or directly get 0,
// and the original value cannot be completely retained.
st.key |= pieceToRemoveCount[sideToMove]! << (32 - _Zobrist.keyMiscBit);
}

Expand Down
8 changes: 7 additions & 1 deletion src/ui/flutter_app/lib/l10n/intl_af.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1619,5 +1619,11 @@
"showAdvantageGraph": "Wys voordeelgrafiek",
"@showAdvantageGraph": {},
"advantageGraphHint": "Voorspronggrafiek sal slegs geteken word nadat die KI 'n skuif maak. Dit sal nie geteken word wanneer die menslike speler 'n skuif maak nie.",
"@advantageGraphHint": {}
"@advantageGraphHint": {},
"removalBasedOnMillCounts": "Skakel verwydering uit totdat alle stukke geplaas is. Spelers verwyder stukke op grond van Morabaraba-tellings.",
"@removalBasedOnMillCounts": {},
"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": {}
}
8 changes: 7 additions & 1 deletion src/ui/flutter_app/lib/l10n/intl_am.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1619,5 +1619,11 @@
"showAdvantageGraph": "የጥቅም ግራፍን አሳይ",
"@showAdvantageGraph": {},
"advantageGraphHint": "አቋሙ ግራፊክ ከAI ተንቀሳቃሽ ከተደረገ በኋላ ብቻ ይሳምራል። የሰው ተጫዋች አንቀሳቃሽ ሲደረግ ግራፊኩ አይሳምርም።",
"@advantageGraphHint": {}
"@advantageGraphHint": {},
"removalBasedOnMillCounts": "የማስወገድ ሂደቱን እስከ ሁሉም ቅዱሳት እንዲቀመጡ ድረስ አትቻል. ተጫዋቾች ቅዱሳትን በሚቆጣጠሩበት መሠረት ያስወግዳሉ።",
"@removalBasedOnMillCounts": {},
"removalBasedOnMillCounts_Detail": "የማስወገድ ሂደቱን እስከ ሁሉም ቅዱሳት እንዲቀመጡ ድረስ አትቻል። ተጫዋቾች ቅዱሳትን በሚቆጣጠሩበት መሠረት ያስወግዳሉ፡ ከሆነ ብዛት አንድ አይደለም ከሆነ፣ ብዛት በላዩ አንዱ ተጫዋች አንድ ተጨማሪ ያስወግዳል እና የተለየ አንድ ቅዱስ ልዩነት ይቆያል። ከሆነ አንድ ብቻ ከሚከናውነው የቅዱሳት ተውጣጥፋ ሂደት ጋር፣ ያ ተጫዋች ሁለት ቅዱሳት ያስወግዳል እና አስተማማኝ አድርጎ በወደቀው ተጫዋች አንድ ቅዱስ ይወጣል። ከሆነ ሁለቱም ቅዱሳት አይቆጣጠሩም ከሆነ፣ ሁለቱም አንድ ቅዱስ ይወጣሉ።",
"@removalBasedOnMillCounts_Detail": {},
"elfilja": "ኢልፊልጃ",
"@elfilja": {}
}
8 changes: 7 additions & 1 deletion src/ui/flutter_app/lib/l10n/intl_ar.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1521,5 +1521,11 @@
"showAdvantageGraph": "إظهار مخطط الميزة",
"@showAdvantageGraph": {},
"advantageGraphHint": "سيتم رسم مخطط الأفضلية فقط بعد أن يقوم الذكاء الاصطناعي باللعب. لن يتم رسمه عندما يقوم اللاعب البشري باللعب.",
"@advantageGraphHint": {}
"@advantageGraphHint": {},
"removalBasedOnMillCounts": "ممنوع الإزالة حتى يتم وضع جميع القطع. يقوم اللاعبون بإزالة القطع حسب عدد الطواحين المشكلة.",
"@removalBasedOnMillCounts": {},
"removalBasedOnMillCounts_Detail": "ممنوع الإزالة حتى يتم وضع جميع القطع. يقوم اللاعبون بإزالة القطع حسب عدد الطواحين المشكلة: إذا تساوى العدد، يزيل كلاهما نفس العدد ؛ واذا كان أحدهم يمتلك طواحين أكثر، فإنه يزيل قطعة إضافية واحدة مع الحفاظ على فارق قطعة واحدة. إذا قام أحد اللاعبين فقط بتشكيل طواحين،فإنه يزيل قطعتين بينما يزيل الآخر واحدة. إذا لم يشكل اي منهما طواحين، يزيل كل منهما قطعة واحدة من قطعه الخاصة.",
"@removalBasedOnMillCounts_Detail": {},
"elfilja": "إلفيلجا",
"@elfilja": {}
}
8 changes: 7 additions & 1 deletion src/ui/flutter_app/lib/l10n/intl_az.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1619,5 +1619,11 @@
"showAdvantageGraph": "Üstünlük qrafikini göstər",
"@showAdvantageGraph": {},
"advantageGraphHint": "Üstünlük qrafiki yalnız süni intellekt hərəkət etdikdən sonra çəkiləcək. İnsan oyunçu hərəkət edəndə o çəkilməyəcək.",
"@advantageGraphHint": {}
"@advantageGraphHint": {},
"removalBasedOnMillCounts": "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.",
"@removalBasedOnMillCounts": {},
"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": {}
}
Loading

0 comments on commit 978f669

Please sign in to comment.