if (pos.flip_enclosed_pieces())
{
// Stable pieces
- Bitboard edges = (FileABB | file_bb(pos.max_file()) | Rank1BB | rank_bb(pos.max_rank())) & pos.board_bb();
- Bitboard edgePieces = pos.pieces(Us) & edges;
- while (edgePieces)
+ if (pos.flip_enclosed_pieces() == REVERSI)
{
- Bitboard connectedEdge = attacks_bb(Us, ROOK, pop_lsb(&edgePieces), ~(pos.pieces(Us) & edges)) & edges;
- if (!more_than_one(connectedEdge & ~pos.pieces(Us)))
- score += make_score(300, 300);
- else if (!(connectedEdge & ~pos.pieces()))
- score += make_score(200, 200);
+ Bitboard edges = (FileABB | file_bb(pos.max_file()) | Rank1BB | rank_bb(pos.max_rank())) & pos.board_bb();
+ Bitboard edgePieces = pos.pieces(Us) & edges;
+ while (edgePieces)
+ {
+ Bitboard connectedEdge = attacks_bb(Us, ROOK, pop_lsb(&edgePieces), ~(pos.pieces(Us) & edges)) & edges;
+ if (!more_than_one(connectedEdge & ~pos.pieces(Us)))
+ score += make_score(300, 300);
+ else if (!(connectedEdge & ~pos.pieces()))
+ score += make_score(200, 200);
+ }
}
// Unstable
while (drops)
{
Square s = pop_lsb(&drops);
- Bitboard b = attacks_bb(Them, QUEEN, s, ~pos.pieces(Us)) & ~PseudoAttacks[Them][KING][s] & pos.pieces(Them);
- while(b)
- unstable |= between_bb(s, pop_lsb(&b));
+ if (pos.flip_enclosed_pieces() == REVERSI)
+ {
+ Bitboard b = attacks_bb(Them, QUEEN, s, ~pos.pieces(Us)) & ~PseudoAttacks[Them][KING][s] & pos.pieces(Them);
+ while(b)
+ unstable |= between_bb(s, pop_lsb(&b));
+ }
+ else
+ unstable |= PseudoAttacks[Them][KING][s] & pos.pieces(Us);
}
score -= make_score(200, 200) * popcount(unstable);
}
return value == "makruk" || value == "asean" || value == "none";
}
+ template <> bool set(const std::string& value, EnclosingRule& target) {
+ target = value == "reversi" ? REVERSI
+ : value == "ataxx" ? ATAXX
+ : NO_ENCLOSING;
+ return value == "reversi" || value == "ataxx" || value == "none";
+ }
+
template <> bool set(const std::string& value, Bitboard& target) {
char file;
int rank;
p->name = "immobile";
return p;
}
+ PieceInfo* ataxx_piece() {
+ PieceInfo* p = new PieceInfo();
+ p->name = "ataxx";
+ p->betza = "mDNA";
+ p->stepsQuiet = {2 * NORTH_WEST, 2 * NORTH + WEST, 2 * NORTH, 2 * NORTH + EAST, 2 * NORTH_EAST,
+ NORTH + 2 * WEST, NORTH + 2 * EAST, 2 * WEST, 2 * EAST, SOUTH + 2 * WEST, SOUTH + 2 * EAST,
+ 2 * SOUTH_WEST, 2 * SOUTH + WEST, 2 * SOUTH, 2 * SOUTH + EAST, 2 * SOUTH_EAST};
+ return p;
+ }
PieceInfo* cannon_piece() {
PieceInfo* p = new PieceInfo();
p->name = "cannon";
add(CLOBBER_PIECE, clobber_piece());
add(BREAKTHROUGH_PIECE, breakthrough_piece());
add(IMMOBILE_PIECE, immobile_piece());
+ add(ATAXX_PIECE, ataxx_piece());
add(CANNON, cannon_piece());
add(JANGGI_CANNON, janggi_cannon_piece());
add(SOLDIER, soldier_piece());
st->castlingRights &= ~cr;
}
+ // Flip enclosed pieces
+ st->flippedPieces = 0;
+ if (flip_enclosed_pieces() && !is_pass(m))
+ {
+ // Find end of rows to be flipped
+ if (flip_enclosed_pieces() == REVERSI)
+ {
+ Bitboard b = attacks_bb(us, QUEEN, to, board_bb() & ~pieces(~us)) & ~PseudoAttacks[us][KING][to] & pieces(us);
+ while(b)
+ st->flippedPieces |= between_bb(to, pop_lsb(&b));
+ }
+ else
+ {
+ assert(flip_enclosed_pieces() == ATAXX);
+ st->flippedPieces = PseudoAttacks[us][KING][to] & pieces(~us);
+ }
+
+ // Flip pieces
+ Bitboard to_flip = st->flippedPieces;
+ while(to_flip)
+ {
+ Square s = pop_lsb(&to_flip);
+ Piece flipped = piece_on(s);
+ Piece resulting = ~flipped;
+
+ // remove opponent's piece
+ remove_piece(s);
+ k ^= Zobrist::psq[flipped][s];
+ st->materialKey ^= Zobrist::psq[flipped][pieceCount[flipped]];
+ st->nonPawnMaterial[them] -= PieceValue[MG][flipped];
+
+ // add our piece
+ put_piece(resulting, s);
+ k ^= Zobrist::psq[resulting][s];
+ st->materialKey ^= Zobrist::psq[resulting][pieceCount[resulting]-1];
+ st->nonPawnMaterial[us] += PieceValue[MG][resulting];
+ }
+ }
+
// Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) == DROP)
{
set_castling_right(us, to);
}
}
- // Flip enclosed pieces
- if (flip_enclosed_pieces())
- {
- st->flippedPieces = 0;
- // Find end of rows to be flipped
- Bitboard b = attacks_bb(us, QUEEN, to, board_bb() & ~pieces(~us)) & ~PseudoAttacks[us][KING][to] & pieces(us);
- while(b)
- st->flippedPieces |= between_bb(to, pop_lsb(&b));
- // Flip pieces
- Bitboard to_flip = st->flippedPieces;
- while(to_flip)
- {
- Square s = pop_lsb(&to_flip);
- Piece flipped = piece_on(s);
- Piece resulting = ~flipped;
-
- // remove opponent's piece
- remove_piece(s);
- k ^= Zobrist::psq[flipped][s];
- st->materialKey ^= Zobrist::psq[flipped][pieceCount[flipped]];
- st->nonPawnMaterial[them] -= PieceValue[MG][flipped];
-
- // add our piece
- put_piece(resulting, s);
- k ^= Zobrist::psq[resulting][s];
- st->materialKey ^= Zobrist::psq[resulting][pieceCount[resulting]-1];
- st->nonPawnMaterial[us] += PieceValue[MG][resulting];
- }
- }
}
else if (type_of(m) != CASTLING)
move_piece(from, to);
else
{
if (type_of(m) == DROP)
- {
- if (flip_enclosed_pieces())
- {
- // Flip pieces
- Bitboard to_flip = st->flippedPieces;
- while(to_flip)
- {
- Square s = pop_lsb(&to_flip);
- Piece resulting = ~piece_on(s);
- remove_piece(s);
- put_piece(resulting, s);
- }
- }
undrop_piece(make_piece(us, in_hand_piece_type(m)), to); // Remove the dropped piece
- }
else
move_piece(to, from); // Put the piece back at the source square
}
}
+ if (flip_enclosed_pieces())
+ {
+ // Flip pieces
+ Bitboard to_flip = st->flippedPieces;
+ while(to_flip)
+ {
+ Square s = pop_lsb(&to_flip);
+ Piece resulting = ~piece_on(s);
+ remove_piece(s);
+ put_piece(resulting, s);
+ }
+ }
+
// Finally point our state pointer back to the previous state
st = st->previous;
--gamePly;
bool captures_to_hand() const;
bool first_rank_pawn_drops() const;
bool drop_on_top() const;
- bool enclosing_drop() const;
+ EnclosingRule enclosing_drop() const;
Bitboard drop_region(Color c) const;
Bitboard drop_region(Color c, PieceType pt) const;
bool sittuyin_rook_drop() const;
bool pass_on_stalemate() const;
Bitboard promoted_soldiers(Color c) const;
bool makpong() const;
- bool flip_enclosed_pieces() const;
+ EnclosingRule flip_enclosed_pieces() const;
// winning conditions
int n_move_rule() const;
int n_fold_rule() const;
return var->dropOnTop;
}
-inline bool Position::enclosing_drop() const {
+inline EnclosingRule Position::enclosing_drop() const {
assert(var != nullptr);
return var->enclosingDrop;
}
b &= var->enclosingDropStart;
else
{
- Bitboard theirs = pieces(~c);
- b &= shift<NORTH >(theirs) | shift<SOUTH >(theirs)
- | shift<NORTH_EAST>(theirs) | shift<SOUTH_WEST>(theirs)
- | shift<EAST >(theirs) | shift<WEST >(theirs)
- | shift<SOUTH_EAST>(theirs) | shift<NORTH_WEST>(theirs);
- Bitboard b2 = b;
- while (b2)
+ if (enclosing_drop() == REVERSI)
{
- Square s = pop_lsb(&b2);
- if (!(attacks_bb(c, QUEEN, s, board_bb() & ~pieces(~c)) & ~PseudoAttacks[c][KING][s] & pieces(c)))
- b ^= s;
+ Bitboard theirs = pieces(~c);
+ b &= shift<NORTH >(theirs) | shift<SOUTH >(theirs)
+ | shift<NORTH_EAST>(theirs) | shift<SOUTH_WEST>(theirs)
+ | shift<EAST >(theirs) | shift<WEST >(theirs)
+ | shift<SOUTH_EAST>(theirs) | shift<NORTH_WEST>(theirs);
+ Bitboard b2 = b;
+ while (b2)
+ {
+ Square s = pop_lsb(&b2);
+ if (!(attacks_bb(c, QUEEN, s, board_bb() & ~pieces(~c)) & ~PseudoAttacks[c][KING][s] & pieces(c)))
+ b ^= s;
+ }
+ }
+ else
+ {
+ assert(enclosing_drop() == ATAXX);
+ Bitboard ours = pieces(c);
+ b &= shift<NORTH >(ours) | shift<SOUTH >(ours)
+ | shift<NORTH_EAST>(ours) | shift<SOUTH_WEST>(ours)
+ | shift<EAST >(ours) | shift<WEST >(ours)
+ | shift<SOUTH_EAST>(ours) | shift<NORTH_WEST>(ours);
}
}
}
return var->nFoldRule;
}
-inline bool Position::flip_enclosed_pieces() const {
+inline EnclosingRule Position::flip_enclosed_pieces() const {
assert(var != nullptr);
return var->flipEnclosedPieces;
}
: make_score(10, 10) * (1 + isSlowLeaper) * (f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile / 2));
if (pt == SOLDIER && r < v->soldierPromotionRank)
psq[pc][s] -= score * (v->soldierPromotionRank - r) / (4 + f);
- if (v->enclosingDrop)
+ if (v->enclosingDrop == REVERSI)
{
if (f == FILE_A && (r == RANK_1 || r == v->maxRank))
psq[pc][s] += make_score(1000, 1000);
NO_COUNTING, MAKRUK_COUNTING, ASEAN_COUNTING
};
+enum EnclosingRule {
+ NO_ENCLOSING, REVERSI, ATAXX
+};
+
enum Phase {
PHASE_ENDGAME,
PHASE_MIDGAME = 128,
ClobberPieceValueMg = 300, ClobberPieceValueEg = 300,
BreakthroughPieceValueMg = 300, BreakthroughPieceValueEg = 300,
ImmobilePieceValueMg = 50, ImmobilePieceValueEg = 50,
+ AtaxxPieceValueMg = 100, AtaxxPieceValueEg = 100,
CannonPieceValueMg = 800, CannonPieceValueEg = 700,
JanggiCannonPieceValueMg = 800, JanggiCannonPieceValueEg = 600,
SoldierValueMg = 200, SoldierValueEg = 270,
FERS, MET = FERS, ALFIL, FERS_ALFIL, SILVER, KHON = SILVER, AIWOK, BERS, DRAGON = BERS,
ARCHBISHOP, CHANCELLOR, AMAZON, KNIBIS, BISKNI, KNIROO, ROOKNI,
SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, DRAGON_HORSE,
- CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, CANNON, JANGGI_CANNON,
+ CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, ATAXX_PIECE, CANNON, JANGGI_CANNON,
SOLDIER, HORSE, ELEPHANT, JANGGI_ELEPHANT, BANNER,
WAZIR, COMMONER, CENTAUR, KING,
ALL_PIECES = 0,
FersValueMg, AlfilValueMg, FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg,
ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, KnirooValueMg, RookniValueMg,
ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg,
- ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg,
+ ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg,
CannonPieceValueMg, JanggiCannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, JanggiElephantValueMg, BannerValueMg,
- WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+ WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
FersValueMg, AlfilValueMg, FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg,
ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, KnirooValueMg, RookniValueMg,
ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg,
- ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg,
+ ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg,
CannonPieceValueMg, JanggiCannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, JanggiElephantValueMg, BannerValueMg,
- WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+ WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg,
ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, KnirooValueEg, RookniValueEg,
ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg,
- ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg,
+ ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg,
CannonPieceValueEg, JanggiCannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, JanggiElephantValueEg, BannerValueEg,
- WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+ WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg,
ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, KnirooValueEg, RookniValueEg,
ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg,
- ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg,
+ ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg,
CannonPieceValueEg, JanggiCannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, JanggiElephantValueEg, BannerValueEg,
- WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
+ WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
v->blackFlag = Rank1BB;
return v;
}
+ Variant* ataxx_variant() {
+ Variant* v = fairy_variant_base();
+ v->pieceToCharTable = "P.................p.................";
+ v->maxRank = RANK_7;
+ v->maxFile = FILE_G;
+ v->reset_pieces();
+ v->add_piece(ATAXX_PIECE, 'p');
+ v->startFen = "P5p/7/7/7/7/7/p5P[PPPPPPPPPPPPPPPPPPPPPPPPPppppppppppppppppppppppppp] w 0 1";
+ v->promotionPieceTypes = {};
+ v->pieceDrops = true;
+ v->doubleStep = false;
+ v->castling = false;
+ v->immobilityIllegal = false;
+ v->stalemateValue = -VALUE_MATE;
+ v->stalematePieceCount = true;
+ v->passOnStalemate = true;
+ v->enclosingDrop = ATAXX;
+ v->flipEnclosedPieces = ATAXX;
+ v->materialCounting = UNWEIGHTED_MATERIAL;
+ return v;
+ }
Variant* minixiangqi_variant() {
Variant* v = fairy_variant_base();
v->variantTemplate = "xiangqi";
add("shatar", shatar_variant());
add("clobber", clobber_variant());
add("breakthrough", breakthrough_variant());
+ add("ataxx", ataxx_variant());
add("minixiangqi", minixiangqi_variant());
#ifdef LARGEBOARDS
add("shogi", shogi_variant());
bool firstRankPawnDrops = false;
bool promotionZonePawnDrops = false;
bool dropOnTop = false;
- bool enclosingDrop = false;
+ EnclosingRule enclosingDrop = NO_ENCLOSING;
Bitboard enclosingDropStart = 0;
Bitboard whiteDropRegion = AllSquares;
Bitboard blackDropRegion = AllSquares;
bool makpongRule = false;
bool flyingGeneral = false;
Rank soldierPromotionRank = RANK_1;
- bool flipEnclosedPieces = false;
+ EnclosingRule flipEnclosedPieces = NO_ENCLOSING;
// game end
int nMoveRule = 50;
int nFoldRule = 3;
# clobber
# breakthrough
# immobile (piece without moves)
+# ataxx (mDNA)
# cannon
# janggiCannon
# soldier
# [Value]: game result for the side to move [win, loss, draw]
# [MaterialCounting]: material couting rules for adjudication [janggi, unweighted, whitedrawodds, blackdrawodds, none]
# [CountingRule]: makruk or ASEAN counting rules [makruk, asean, none]
+# [EnclosingRule]: reversi or ataxx enclosing rules [reversi, ataxx, none]
### Rule definition options
# variantTemplate: only relevant for usage in XBoard/WinBoard GUI [values: fairy, shogi] (default: fairy)
# firstRankPawnDrops: allow pawn drops to first rank [bool] (default: false)
# promotionZonePawnDrops: allow pawn drops in promotion zone [bool] (default: false)
# dropOnTop: piece drops need to be on top of pieces on board (e.g., for connect4) [bool] (default: false)
-# enclosingDrop: require piece drop to enclose opponent's pieces (e.g., for othello) [bool] (default: false)
+# enclosingDrop: require piece drop to enclose pieces [EnclosingRule] (default: none)
# enclosingDropStart: drop region for starting phase disregarding enclosingDrop (e.g., for reversi) [Bitboard]
# whiteDropRegion: restrict region for piece drops of all white pieces [Bitboard]
# blackDropRegion: restrict region for piece drops of all black pieces [Bitboard]
# makpongRule: the king may not move away from check [bool] (default: false)
# flyingGeneral: disallow general face-off like in xiangqi [bool] (default: false)
# soldierPromotionRank: restrict soldier to shogi pawn movements until reaching n-th rank [bool] (default: 1)
-# flipEnclosedPieces: change color of pieces that are enclosed by a drop (e.g., for othello) [bool] (default: false)
+# flipEnclosedPieces: change color of pieces that are enclosed by a drop [EnclosingRule] (default: none)
# nMoveRule: move count for 50/n-move rule [int] (default: 50)
# nFoldRule: move count for 3/n-fold repetition rule [int] (default: 3)
# nFoldValue: result in case of 3/n-fold repetition [Value] (default: draw)
stalemateValue = loss
stalematePieceCount = true
materialCounting = unweighted
-enclosingDrop = true
+enclosingDrop = reversi
enclosingDropStart = d4 e4 d5 e5
immobilityIllegal = false
-flipEnclosedPieces = true
+flipEnclosedPieces = reversi
passOnStalemate = false
[othello:reversi]
expect perft.exp sittuyin "fen 8/6s1/5P2/3n4/pR2K2S/1P6/1k4p1/8[] w - - 1 50" 4 268869 > /dev/null
expect perft.exp seirawan startpos 5 27639803 > /dev/null
expect perft.exp seirawan "fen reb1k2r/ppppqppp/2nb1n2/4p3/4P3/N1P2N2/PB1PQPPP/RE2KBHR[h] b KQkqc - 3 7" 5 31463761 > /dev/null
+ expect perft.exp ataxx startpos 4 155888 > /dev/null
+ expect perft.exp ataxx "fen 7/7/7/7/ppppppp/ppppppp/PPPPPPP[PPPPPPPPPPPPPPPPPPPPPppppppppppppppppppppp] w 0 1" 5 452980 > /dev/null
fi
# large-board variants