Document legalMoves castling format

Re-introduce `makeLegalMoves` to export legal moves in a convenient
format.

Closes #33
This commit is contained in:
Vincent Velociter
2024-08-02 11:10:33 +02:00
parent 05af18077d
commit a9d42cbade
4 changed files with 115 additions and 1 deletions
+1
View File
@@ -13,3 +13,4 @@ export 'src/setup.dart';
export 'src/position.dart';
export 'src/debug.dart';
export 'src/pgn.dart';
export 'src/utils.dart';
+10 -1
View File
@@ -7,6 +7,7 @@ import 'models.dart';
import 'board.dart';
import 'setup.dart';
import 'square_set.dart';
import 'utils.dart';
/// A base class for playable chess or chess variant positions.
///
@@ -181,6 +182,14 @@ abstract class Position<T extends Position<T>> {
}
/// Gets all the legal moves of this position.
///
/// Returns a [SquareSet] of all the legal moves for each [Square].
///
/// In order to support Chess960, the castling move format is encoded as the
/// king-to-rook move only.
///
/// Use the [makeLegalMoves] helper to get all the legal moves including alternative
/// castling moves.
IMap<Square, SquareSet> get legalMoves {
final context = _makeContext();
if (context.isVariantEnd) return IMap(const {});
@@ -193,7 +202,7 @@ abstract class Position<T extends Position<T>> {
/// Gets all the legal drops of this position.
SquareSet get legalDrops => SquareSet.empty;
/// SquareSet of pieces giving check.
/// Square set of pieces giving check.
SquareSet get checkers {
final king = board.kingOf(turn);
return king != null ? kingAttackers(king, turn.opposite) : SquareSet.empty;
+37
View File
@@ -0,0 +1,37 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'models.dart';
import 'position.dart';
/// Returns all the legal moves of the [Position] in a convenient format.
///
/// Includes both possible representations of castling moves (unless `chess960` is true).
IMap<Square, ISet<Square>> makeLegalMoves(
Position pos, {
bool isChess960 = false,
}) {
final Map<Square, ISet<Square>> result = {};
for (final entry in pos.legalMoves.entries) {
final dests = entry.value.squares;
if (dests.isNotEmpty) {
final from = entry.key;
final destSet = dests.toSet();
if (!isChess960 &&
from == pos.board.kingOf(pos.turn) &&
entry.key.file == 4) {
if (dests.contains(Square.a1)) {
destSet.add(Square.c1);
} else if (dests.contains(Square.a8)) {
destSet.add(Square.c8);
}
if (dests.contains(Square.h1)) {
destSet.add(Square.g1);
} else if (dests.contains(Square.h8)) {
destSet.add(Square.g8);
}
}
result[from] = ISet(destSet);
}
}
return IMap(result);
}
+67
View File
@@ -0,0 +1,67 @@
import 'package:dartchess/dartchess.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:test/test.dart';
void main() {
test('makeLegalMoves with Kh8', () {
final setup = Setup.parseFen(
'r1bq1r2/3n2k1/p1p1pp2/3pP2P/8/PPNB2Q1/2P2P2/R3K3 b Q - 1 22',
);
final pos = Chess.fromSetup(setup);
final moves = makeLegalMoves(pos);
expect(moves[Square.g7], contains(Square.h8));
expect(moves[Square.g7], isNot(contains(Square.g8)));
});
test('makeLegalMoves with regular castle', () {
final wtm =
Chess.fromSetup(Setup.parseFen('r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1'));
expect(
makeLegalMoves(wtm)[Square.e1],
equals(
{
Square.a1,
Square.c1,
Square.d1,
Square.d2,
Square.e2,
Square.f1,
Square.f2,
Square.g1,
Square.h1,
},
),
);
expect(makeLegalMoves(wtm)[Square.e8], null);
final btm =
Chess.fromSetup(Setup.parseFen('r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1'));
expect(
makeLegalMoves(btm)[Square.e8],
equals({
Square.a8,
Square.c8,
Square.d7,
Square.d8,
Square.e7,
Square.f7,
Square.f8,
Square.g8,
Square.h8,
}),
);
expect(makeLegalMoves(btm)[Square.e1], null);
});
test('makeLegalMoves with chess960 castle', () {
final pos = Chess.fromSetup(
Setup.parseFen(
'rk2r3/pppbnppp/3p2n1/P2Pp3/4P2q/R5NP/1PP2PP1/1KNQRB2 b Kkq - 0 1',
),
);
expect(
makeLegalMoves(pos, isChess960: true)[Square.b8],
equals(ISet(const {Square.a8, Square.c8, Square.e8})),
);
});
}