Skip to content

Commit

Permalink
add Three Musketeers. needs collinearN and connectPieceTypes. (#755)
Browse files Browse the repository at this point in the history
  • Loading branch information
RainRat authored Feb 13, 2024
1 parent 052dce6 commit 7d42030
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
parse_attribute("flagPieceSafe", v->flagPieceSafe);
parse_attribute("checkCounting", v->checkCounting);
parse_attribute("connectN", v->connectN);
parse_attribute("connectPieceTypes", v->connectPieceTypes, v->pieceToChar);
parse_attribute("connectHorizontal", v->connectHorizontal);
parse_attribute("connectVertical", v->connectVertical);
parse_attribute("connectDiagonal", v->connectDiagonal);
Expand All @@ -535,6 +536,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
parse_attribute("connectRegion1Black", v->connectRegion1[BLACK]);
parse_attribute("connectRegion2Black", v->connectRegion2[BLACK]);
parse_attribute("connectNxN", v->connectNxN);
parse_attribute("collinearN", v->collinearN);
parse_attribute("connectValue", v->connectValue);
parse_attribute("materialCounting", v->materialCounting);
parse_attribute("adjudicateFullBoard", v->adjudicateFullBoard);
Expand Down
51 changes: 45 additions & 6 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2703,7 +2703,7 @@ bool Position::is_optional_game_end(Value& result, int ply, int countStarted) co

/// Position::is_immediate_game_end() tests whether the position ends the game
/// immediately by a variant rule, i.e., there are no more legal moves.
/// It does not not detect stalemates.
/// It does not detect stalemates.

bool Position::is_immediate_game_end(Value& result, int ply) const {

Expand Down Expand Up @@ -2789,14 +2789,23 @@ bool Position::is_immediate_game_end(Value& result, int ply) const {
result = mated_in(ply);
return true;
}

//Calculate eligible pieces for connection once.
Bitboard connectPieces = 0;
for (PieceSet ps = connect_piece_types(); ps;){
PieceType pt = pop_lsb(ps);
connectPieces |= pieces(pt);
};
connectPieces &= pieces(~sideToMove);

// Connect-n
if (connect_n() > 0)
{
Bitboard b;

for (Direction d : var->connect_directions)
{
b = pieces(~sideToMove);
b = connectPieces;
for (int i = 1; i < connect_n() && b; i++)
b &= shift(d, b);
if (b)
Expand All @@ -2807,15 +2816,15 @@ bool Position::is_immediate_game_end(Value& result, int ply) const {
}
}

if ((var->connectRegion1[~sideToMove] & pieces(~sideToMove)) && (var->connectRegion2[~sideToMove] & pieces(~sideToMove)))
if ((var->connectRegion1[~sideToMove] & connectPieces) && (var->connectRegion2[~sideToMove] & connectPieces))
{
Bitboard target = var->connectRegion2[~sideToMove];
Bitboard current = var->connectRegion1[~sideToMove] & pieces(~sideToMove);
Bitboard current = var->connectRegion1[~sideToMove] & connectPieces;

while (true) {
Bitboard newBitboard = 0;
for (Direction d : var->connect_directions) {
newBitboard |= shift(d, current | newBitboard) & pieces(~sideToMove); // the "| newBitboard" here probably saves a few loops
newBitboard |= shift(d, current | newBitboard) & connectPieces; // the "| newBitboard" here probably saves a few loops
}

if (newBitboard & target) {
Expand All @@ -2835,7 +2844,7 @@ bool Position::is_immediate_game_end(Value& result, int ply) const {

if (connect_nxn())
{
Bitboard connectors = pieces(~sideToMove);
Bitboard connectors = connectPieces;
for (int i = 1; i < connect_nxn() && connectors; i++)
connectors &= shift<SOUTH>(connectors) & shift<EAST>(connectors) & shift<SOUTH_EAST>(connectors);
if (connectors)
Expand All @@ -2845,6 +2854,36 @@ bool Position::is_immediate_game_end(Value& result, int ply) const {
}
}

// Collinear-n
if (collinear_n() > 0) {
Bitboard allPieces = connectPieces;
for (Direction d : var->connect_directions) {
Bitboard b = allPieces;
while (b) {
Square s = pop_lsb(b);

int total_count = 1; // Start with the current piece

// Check in both directions
for (int sign : {-1, 1}) {
Bitboard shifted = shift(sign * d, square_bb(s));
while (shifted) {
if (shifted & b) {
total_count++;
b &= ~shifted; // Remove this piece from further consideration
}
shifted = shift(sign * d, shifted);
}
}

if (total_count >= collinear_n()) {
result = convert_mate_value(-var->connectValue, ply);
return true;
}
}
}
}

// Check for bikjang rule (Janggi), double passing, or board running full
if ( (st->pliesFromNull > 0 && ((st->bikjang && st->previous->bikjang) || (st->pass && st->previous->pass)))
|| (var->adjudicateFullBoard && !(~pieces() & board_bb())))
Expand Down
12 changes: 12 additions & 0 deletions src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,13 @@ class Position {
bool flag_reached(Color c) const;
bool check_counting() const;
int connect_n() const;
PieceSet connect_piece_types() const;
bool connect_horizontal() const;
bool connect_vertical() const;
bool connect_diagonal() const;
const std::vector<Direction>& getConnectDirections() const;
int connect_nxn() const;
int collinear_n() const;

CheckCount checks_remaining(Color c) const;
MaterialCounting material_counting() const;
Expand Down Expand Up @@ -1035,6 +1037,11 @@ inline int Position::connect_n() const {
return var->connectN;
}

inline PieceSet Position::connect_piece_types() const {
assert(var != nullptr);
return var->connectPieceTypes;
}

inline bool Position::connect_horizontal() const {
assert(var != nullptr);
return var->connectHorizontal;
Expand All @@ -1058,6 +1065,11 @@ inline int Position::connect_nxn() const {
return var->connectNxN;
}

inline int Position::collinear_n() const {
assert(var != nullptr);
return var->collinearN;
}

inline CheckCount Position::checks_remaining(Color c) const {
return st->checksRemaining[c];
}
Expand Down
11 changes: 11 additions & 0 deletions src/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2064,6 +2064,17 @@ Variant* Variant::conclude() {
connect_directions.push_back(SOUTH_EAST);
}

// If not a connect variant, set connectPieceTypes to no pieces.
if ( !(connectRegion1[WHITE] || connectRegion1[BLACK] || connectN || connectNxN || collinearN) )
{
connectPieceTypes = NO_PIECE_SET;
}
//Otherwise optimize to pieces actually in the game.
else
{
connectPieceTypes = connectPieceTypes & pieceTypes;
};

return this;
}

Expand Down
2 changes: 2 additions & 0 deletions src/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,14 @@ struct Variant {
bool flagPieceSafe = false;
bool checkCounting = false;
int connectN = 0;
PieceSet connectPieceTypes = ~NO_PIECE_SET;
bool connectHorizontal = true;
bool connectVertical = true;
bool connectDiagonal = true;
Bitboard connectRegion1[COLOR_NB] = {};
Bitboard connectRegion2[COLOR_NB] = {};
int connectNxN = 0;
int collinearN = 0;
Value connectValue = VALUE_MATE;
MaterialCounting materialCounting = NO_MATERIAL_COUNTING;
bool adjudicateFullBoard = false;
Expand Down
18 changes: 17 additions & 1 deletion src/variants.ini
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@
# flagPieceSafe: the flag piece must be safe to win [bool] (default: false)
# checkCounting: enable check count win rule (check count is communicated via FEN, see 3check) [bool] (default: false)
# connectN: number of aligned pieces for win [int] (default: 0)
# connectPieceTypes: pieces evaluated for connection rule [PieceSet] (default: *)
# connectVertical: connectN looks at Vertical rows [bool] (default: true)
# connectHorizontal: connectN looks at Horizontal rows [bool] (default: true)
# connectDiagonal: connectN looks at Diagonal rows [bool] (default: true)
Expand All @@ -288,6 +289,7 @@
# connectRegion1Black: "
# connectRegion2Black: "
# connectNxN: connect a tight NxN square for win [int] (default: 0)
# collinearN: arrange N pieces collinearly (other squares can be between pieces) [int] (default: 0)
# connectValue: result in case of connect [Value] (default: win)
# materialCounting: enable material counting rules [MaterialCounting] (default: none)
# adjudicateFullBoard: apply material counting immediately when board is full [bool] (default: false)
Expand Down Expand Up @@ -1546,7 +1548,7 @@ nMoveRule = 0
#https://ludii.games/details.php?keyword=Djara-Badakh
#https://ludii.games/details.php?keyword=Tuk%20Tak
customPiece1 = p:mKmNmAmD
#moves anywhere on the board, KNAD is an list of all possible moves on a 3x3
#moves anywhere on the board, KNAD is a list of all possible moves on a 3x3
startFen = 3/3/3[PPPppp] w - - 0 1
mustDrop = true
nMoveRule = 0
Expand Down Expand Up @@ -1912,3 +1914,17 @@ enclosingDrop = anyside
#http://gamescrafters.berkeley.edu/games.php?game=connect4
[cfour-misere:cfour]
connectValue = loss

#https://www.ludii.games/details.php?keyword=Three%20Musketeers
[three-musketeers]
pieceToCharTable = P......M..............p......m..............
startFen = ppppM/ppppp/ppMpp/ppppp/Mpppp
maxRank = 5
maxFile = 5
collinearN = 3
connectDiagonal = false
customPiece1 = m:cW
customPiece2 = p:mW
connectValue = loss
stalemateValue = win
connectPieceTypes = m

0 comments on commit 7d42030

Please sign in to comment.