Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Three Musketeers. needs collinearN and connectPieceTypes. #755

Merged
merged 9 commits into from
Feb 13, 2024
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) {
ianfab marked this conversation as resolved.
Show resolved Hide resolved
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
Loading