mirror of
https://github.com/lichess-org/dartchess.git
synced 2026-05-26 13:51:01 +00:00
+6
-4
@@ -108,7 +108,7 @@ class Board {
|
||||
|
||||
/// Parse the board part of a FEN string and returns a Board.
|
||||
///
|
||||
/// Throws a [FenError] if the provided FEN string is not valid.
|
||||
/// Throws a [FenException] if the provided FEN string is not valid.
|
||||
factory Board.parseFen(String boardFen) {
|
||||
Board board = Board.empty;
|
||||
int rank = 7;
|
||||
@@ -123,18 +123,20 @@ class Board {
|
||||
if (code < 57) {
|
||||
file += code - 48;
|
||||
} else {
|
||||
if (file >= 8 || rank < 0) throw const FenError('ERR_BOARD');
|
||||
if (file >= 8 || rank < 0) {
|
||||
throw const FenException(IllegalFenCause.board);
|
||||
}
|
||||
final square = Square(file + rank * 8);
|
||||
final promoted = i + 1 < boardFen.length && boardFen[i + 1] == '~';
|
||||
final piece = _charToPiece(c, promoted);
|
||||
if (piece == null) throw const FenError('ERR_BOARD');
|
||||
if (piece == null) throw const FenException(IllegalFenCause.board);
|
||||
if (promoted) i++;
|
||||
board = board.setPieceAt(square, piece);
|
||||
file++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rank != 0 || file != 8) throw const FenError('ERR_BOARD');
|
||||
if (rank != 0 || file != 8) throw const FenException(IllegalFenCause.board);
|
||||
return board;
|
||||
}
|
||||
|
||||
|
||||
+100
-2
@@ -630,10 +630,108 @@ class DropMove extends Move {
|
||||
int get hashCode => Object.hash(to, role);
|
||||
}
|
||||
|
||||
/// An enumeration of the possible causes of an illegal FEN string.
|
||||
enum IllegalFenCause {
|
||||
/// The FEN string is not in the correct format.
|
||||
format,
|
||||
|
||||
/// The board part of the FEN string is invalid.
|
||||
board,
|
||||
|
||||
/// The turn part of the FEN string is invalid.
|
||||
turn,
|
||||
|
||||
/// The castling part of the FEN string is invalid.
|
||||
castling,
|
||||
|
||||
/// The en passant part of the FEN string is invalid.
|
||||
enPassant,
|
||||
|
||||
/// The halfmove clock part of the FEN string is invalid.
|
||||
halfmoveClock,
|
||||
|
||||
/// The fullmove number part of the FEN string is invalid.
|
||||
fullmoveNumber,
|
||||
|
||||
/// The remaining checks part of the FEN string is invalid.
|
||||
remainingChecks,
|
||||
|
||||
/// The pockets part of the FEN string is invalid.
|
||||
pockets,
|
||||
}
|
||||
|
||||
/// An exception thrown when trying to parse an invalid FEN string.
|
||||
@immutable
|
||||
class FenError implements Exception {
|
||||
class FenException implements Exception {
|
||||
/// Constructs a [FenException] with a [cause].
|
||||
const FenException(this.cause);
|
||||
|
||||
/// The cause of the exception.
|
||||
final IllegalFenCause cause;
|
||||
|
||||
@override
|
||||
String toString() => 'FenException: ${cause.name}';
|
||||
}
|
||||
|
||||
/// Exception thrown when trying to play an illegal move.
|
||||
@immutable
|
||||
class PlayException implements Exception {
|
||||
/// Constructs a [PlayException] with a [message].
|
||||
const PlayException(this.message);
|
||||
|
||||
/// The exception message.
|
||||
final String message;
|
||||
const FenError(this.message);
|
||||
|
||||
@override
|
||||
String toString() => 'PlayException: $message';
|
||||
}
|
||||
|
||||
/// Enumeration of the possible causes of an illegal setup.
|
||||
enum IllegalSetupCause {
|
||||
/// There are no pieces on the board.
|
||||
empty,
|
||||
|
||||
/// The player not to move is in check.
|
||||
oppositeCheck,
|
||||
|
||||
/// There are impossibly many checkers, two sliding checkers are
|
||||
/// aligned, or check is not possible because the last move was a
|
||||
/// double pawn push.
|
||||
///
|
||||
/// Such a position cannot be reached by any sequence of legal moves.
|
||||
impossibleCheck,
|
||||
|
||||
/// There are pawns on the backrank.
|
||||
pawnsOnBackrank,
|
||||
|
||||
/// A king is missing, or there are too many kings.
|
||||
kings,
|
||||
|
||||
/// A variant specific rule is violated.
|
||||
variant,
|
||||
}
|
||||
|
||||
/// Exception thrown when trying to create a [Position] from an illegal [Setup].
|
||||
@immutable
|
||||
class PositionSetupException implements Exception {
|
||||
/// Constructs a [PositionSetupException] with a [cause].
|
||||
const PositionSetupException(this.cause);
|
||||
|
||||
/// The cause of the exception.
|
||||
final IllegalSetupCause cause;
|
||||
|
||||
static const empty = PositionSetupException(IllegalSetupCause.empty);
|
||||
static const oppositeCheck =
|
||||
PositionSetupException(IllegalSetupCause.oppositeCheck);
|
||||
static const impossibleCheck =
|
||||
PositionSetupException(IllegalSetupCause.impossibleCheck);
|
||||
static const pawnsOnBackrank =
|
||||
PositionSetupException(IllegalSetupCause.pawnsOnBackrank);
|
||||
static const kings = PositionSetupException(IllegalSetupCause.kings);
|
||||
static const variant = PositionSetupException(IllegalSetupCause.variant);
|
||||
|
||||
@override
|
||||
String toString() => 'PositionSetupException: ${cause.name}';
|
||||
}
|
||||
|
||||
/// Represents the different possible rules of chess and its variants
|
||||
|
||||
+2
-2
@@ -135,11 +135,11 @@ class PgnGame<T extends PgnNodeData> {
|
||||
///
|
||||
/// Headers can include an optional 'Variant' and 'Fen' key.
|
||||
///
|
||||
/// Throws a [PositionError] if it does not meet basic validity requirements.
|
||||
/// Throws a [PositionSetupException] if it does not meet basic validity requirements.
|
||||
static Position startingPosition(PgnHeaders headers,
|
||||
{bool? ignoreImpossibleCheck}) {
|
||||
final rule = Rule.fromPgn(headers['Variant']);
|
||||
if (rule == null) throw PositionError.variant;
|
||||
if (rule == null) throw PositionSetupException.variant;
|
||||
if (!headers.containsKey('FEN')) {
|
||||
return Position.initialPosition(rule);
|
||||
}
|
||||
|
||||
+41
-91
@@ -517,12 +517,12 @@ abstract class Position<T extends Position<T>> {
|
||||
|
||||
/// Plays a move and returns the updated [Position].
|
||||
///
|
||||
/// Throws a [PlayError] if the move is not legal.
|
||||
/// Throws a [PlayException] if the move is not legal.
|
||||
Position<T> play(Move move) {
|
||||
if (isLegal(move)) {
|
||||
return playUnchecked(move);
|
||||
} else {
|
||||
throw PlayError('Invalid move $move');
|
||||
throw PlayException('Invalid move $move');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,30 +622,30 @@ abstract class Position<T extends Position<T>> {
|
||||
|
||||
/// Returns the SAN of this [Move] and the updated [Position].
|
||||
///
|
||||
/// Throws a [PlayError] if the move is not legal.
|
||||
/// Throws a [PlayException] if the move is not legal.
|
||||
(Position<T>, String) makeSan(Move move) {
|
||||
if (isLegal(move)) {
|
||||
return makeSanUnchecked(move);
|
||||
} else {
|
||||
throw PlayError('Invalid move $move');
|
||||
throw PlayException('Invalid move $move');
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the SAN of this [Move] from the current [Position].
|
||||
///
|
||||
/// Throws a [PlayError] if the move is not legal.
|
||||
/// Throws a [PlayException] if the move is not legal.
|
||||
@Deprecated('Use makeSan instead')
|
||||
String toSan(Move move) {
|
||||
if (isLegal(move)) {
|
||||
return makeSanUnchecked(move).$2;
|
||||
} else {
|
||||
throw PlayError('Invalid move $move');
|
||||
throw PlayException('Invalid move $move');
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the SAN representation of the [Move] with the updated [Position].
|
||||
///
|
||||
/// Throws a [PlayError] if the move is not legal.
|
||||
/// Throws a [PlayException] if the move is not legal.
|
||||
@Deprecated('Use makeSan instead')
|
||||
(Position<T>, String) playToSan(Move move) {
|
||||
if (isLegal(move)) {
|
||||
@@ -658,7 +658,7 @@ abstract class Position<T extends Position<T>> {
|
||||
: san;
|
||||
return (newPos, suffixed);
|
||||
} else {
|
||||
throw PlayError('Invalid move $move');
|
||||
throw PlayException('Invalid move $move');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,27 +675,27 @@ abstract class Position<T extends Position<T>> {
|
||||
|
||||
/// Checks the legality of this position.
|
||||
///
|
||||
/// Throws a [PositionError] if it does not meet basic validity requirements.
|
||||
/// Throws a [PositionSetupException] if it does not meet basic validity requirements.
|
||||
void validate({bool? ignoreImpossibleCheck}) {
|
||||
if (board.occupied.isEmpty) {
|
||||
throw PositionError.empty;
|
||||
throw PositionSetupException.empty;
|
||||
}
|
||||
if (board.kings.size != 2) {
|
||||
throw PositionError.kings;
|
||||
throw PositionSetupException.kings;
|
||||
}
|
||||
final ourKing = board.kingOf(turn);
|
||||
if (ourKing == null) {
|
||||
throw PositionError.kings;
|
||||
throw PositionSetupException.kings;
|
||||
}
|
||||
final otherKing = board.kingOf(turn.opposite);
|
||||
if (otherKing == null) {
|
||||
throw PositionError.kings;
|
||||
throw PositionSetupException.kings;
|
||||
}
|
||||
if (kingAttackers(otherKing, turn).isNotEmpty) {
|
||||
throw PositionError.oppositeCheck;
|
||||
throw PositionSetupException.oppositeCheck;
|
||||
}
|
||||
if (SquareSet.backranks.isIntersected(board.pawns)) {
|
||||
throw PositionError.pawnsOnBackrank;
|
||||
throw PositionSetupException.pawnsOnBackrank;
|
||||
}
|
||||
final skipImpossibleCheck = ignoreImpossibleCheck ?? false;
|
||||
if (!skipImpossibleCheck) {
|
||||
@@ -734,7 +734,7 @@ abstract class Position<T extends Position<T>> {
|
||||
|
||||
/// Checks if checkers are legal in this position.
|
||||
///
|
||||
/// Throws a [PositionError.impossibleCheck] if it does not meet validity
|
||||
/// Throws a [PositionSetupException.impossibleCheck] if it does not meet validity
|
||||
/// requirements.
|
||||
void _validateCheckers(Square ourKing) {
|
||||
final checkers = kingAttackers(ourKing, turn.opposite);
|
||||
@@ -752,14 +752,14 @@ abstract class Position<T extends Position<T>> {
|
||||
.withoutSquare(pushedTo)
|
||||
.withSquare(pushedFrom))
|
||||
.isNotEmpty)) {
|
||||
throw PositionError.impossibleCheck;
|
||||
throw PositionSetupException.impossibleCheck;
|
||||
}
|
||||
} else {
|
||||
// Multiple sliding checkers aligned with king.
|
||||
if (checkers.size > 2 ||
|
||||
(checkers.size == 2 &&
|
||||
ray(checkers.first!, checkers.last!).has(ourKing))) {
|
||||
throw PositionError.impossibleCheck;
|
||||
throw PositionSetupException.impossibleCheck;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1048,7 +1048,7 @@ class Chess extends Position<Chess> {
|
||||
|
||||
/// Set up a playable [Chess] position.
|
||||
///
|
||||
/// Throws a [PositionError] if the [Setup] does not meet basic validity
|
||||
/// Throws a [PositionSetupException] if the [Setup] does not meet basic validity
|
||||
/// requirements.
|
||||
/// Optionnaly pass a `ignoreImpossibleCheck` boolean if you want to skip that
|
||||
/// requirement.
|
||||
@@ -1119,7 +1119,7 @@ class Antichess extends Position<Antichess> {
|
||||
|
||||
/// Set up a playable [Antichess] position.
|
||||
///
|
||||
/// Throws a [PositionError] if the [Setup] does not meet basic validity
|
||||
/// Throws a [PositionSetupException] if the [Setup] does not meet basic validity
|
||||
/// requirements.
|
||||
/// Optionnaly pass a `ignoreImpossibleCheck` boolean if you want to skip that
|
||||
/// requirement.
|
||||
@@ -1133,10 +1133,10 @@ class Antichess extends Position<Antichess> {
|
||||
@override
|
||||
void validate({bool? ignoreImpossibleCheck}) {
|
||||
if (board.occupied.isEmpty) {
|
||||
throw PositionError.empty;
|
||||
throw PositionSetupException.empty;
|
||||
}
|
||||
if (SquareSet.backranks.isIntersected(board.pawns)) {
|
||||
throw PositionError.pawnsOnBackrank;
|
||||
throw PositionSetupException.pawnsOnBackrank;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1257,7 +1257,7 @@ class Atomic extends Position<Atomic> {
|
||||
|
||||
/// Set up a playable [Atomic] position.
|
||||
///
|
||||
/// Throws a [PositionError] if the [Setup] does not meet basic validity
|
||||
/// Throws a [PositionSetupException] if the [Setup] does not meet basic validity
|
||||
/// requirements.
|
||||
/// Optionnaly pass a `ignoreImpossibleCheck` boolean if you want to skip that
|
||||
/// requirement.
|
||||
@@ -1284,24 +1284,24 @@ class Atomic extends Position<Atomic> {
|
||||
/// Checks the legality of this position.
|
||||
///
|
||||
/// Validation is like chess, but it allows our king to be missing.
|
||||
/// Throws a [PositionError] if it does not meet basic validity requirements.
|
||||
/// Throws a [PositionSetupException] if it does not meet basic validity requirements.
|
||||
@override
|
||||
void validate({bool? ignoreImpossibleCheck}) {
|
||||
if (board.occupied.isEmpty) {
|
||||
throw PositionError.empty;
|
||||
throw PositionSetupException.empty;
|
||||
}
|
||||
if (board.kings.size > 2) {
|
||||
throw PositionError.kings;
|
||||
throw PositionSetupException.kings;
|
||||
}
|
||||
final otherKing = board.kingOf(turn.opposite);
|
||||
if (otherKing == null) {
|
||||
throw PositionError.kings;
|
||||
throw PositionSetupException.kings;
|
||||
}
|
||||
if (kingAttackers(otherKing, turn).isNotEmpty) {
|
||||
throw PositionError.oppositeCheck;
|
||||
throw PositionSetupException.oppositeCheck;
|
||||
}
|
||||
if (SquareSet.backranks.isIntersected(board.pawns)) {
|
||||
throw PositionError.pawnsOnBackrank;
|
||||
throw PositionSetupException.pawnsOnBackrank;
|
||||
}
|
||||
final skipImpossibleCheck = ignoreImpossibleCheck ?? false;
|
||||
final ourKing = board.kingOf(turn);
|
||||
@@ -1474,7 +1474,7 @@ class Crazyhouse extends Position<Crazyhouse> {
|
||||
|
||||
/// Set up a playable [Crazyhouse] position.
|
||||
///
|
||||
/// Throws a [PositionError] if the [Setup] does not meet basic validity
|
||||
/// Throws a [PositionSetupException] if the [Setup] does not meet basic validity
|
||||
/// requirements.
|
||||
/// Optionnaly pass a `ignoreImpossibleCheck` boolean if you want to skip that
|
||||
/// requirement.
|
||||
@@ -1494,13 +1494,13 @@ class Crazyhouse extends Position<Crazyhouse> {
|
||||
void validate({bool? ignoreImpossibleCheck}) {
|
||||
super.validate(ignoreImpossibleCheck: ignoreImpossibleCheck);
|
||||
if (pockets == null) {
|
||||
throw PositionError.variant;
|
||||
throw PositionSetupException.variant;
|
||||
} else {
|
||||
if (pockets!.count(Role.king) > 0) {
|
||||
throw PositionError.kings;
|
||||
throw PositionSetupException.kings;
|
||||
}
|
||||
if (pockets!.size + board.occupied.size > 64) {
|
||||
throw PositionError.variant;
|
||||
throw PositionSetupException.variant;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1601,7 +1601,7 @@ class KingOfTheHill extends Position<KingOfTheHill> {
|
||||
|
||||
/// Set up a playable [KingOfTheHill] position.
|
||||
///
|
||||
/// Throws a [PositionError] if the [Setup] does not meet basic validity
|
||||
/// Throws a [PositionSetupException] if the [Setup] does not meet basic validity
|
||||
/// requirements.
|
||||
/// Optionnaly pass a `ignoreImpossibleCheck` boolean if you want to skip that
|
||||
/// requirement.
|
||||
@@ -1682,13 +1682,13 @@ class ThreeCheck extends Position<ThreeCheck> {
|
||||
|
||||
/// Set up a playable [ThreeCheck] position.
|
||||
///
|
||||
/// Throws a [PositionError] if the [Setup] does not meet basic validity
|
||||
/// Throws a [PositionSetupException] if the [Setup] does not meet basic validity
|
||||
/// requirements.
|
||||
/// Optionnaly pass a `ignoreImpossibleCheck` boolean if you want to skip that
|
||||
/// requirement.
|
||||
factory ThreeCheck.fromSetup(Setup setup, {bool? ignoreImpossibleCheck}) {
|
||||
if (setup.remainingChecks == null) {
|
||||
throw PositionError.variant;
|
||||
throw PositionSetupException.variant;
|
||||
} else {
|
||||
final pos = ThreeCheck(
|
||||
board: setup.board,
|
||||
@@ -1850,7 +1850,7 @@ class RacingKings extends Position<RacingKings> {
|
||||
|
||||
/// Set up a playable [RacingKings] position.
|
||||
///
|
||||
/// Throws a [PositionError] if the [Setup] does not meet basic validity
|
||||
/// Throws a [PositionSetupException] if the [Setup] does not meet basic validity
|
||||
/// requirements.
|
||||
/// Optionnaly pass a `ignoreImpossibleCheck` boolean if you want to skip that
|
||||
/// requirement.
|
||||
@@ -1937,21 +1937,21 @@ class Horde extends Position<Horde> {
|
||||
@override
|
||||
void validate({bool? ignoreImpossibleCheck}) {
|
||||
if (board.occupied.isEmpty) {
|
||||
throw PositionError.empty;
|
||||
throw PositionSetupException.empty;
|
||||
}
|
||||
|
||||
if (board.kings.size != 1) {
|
||||
throw PositionError.kings;
|
||||
throw PositionSetupException.kings;
|
||||
}
|
||||
|
||||
final otherKing = board.kingOf(turn.opposite);
|
||||
if (otherKing != null && kingAttackers(otherKing, turn).isNotEmpty) {
|
||||
throw PositionError.oppositeCheck;
|
||||
throw PositionSetupException.oppositeCheck;
|
||||
}
|
||||
|
||||
// white can have pawns on back rank
|
||||
if (SquareSet.backranks.isIntersected(board.black.intersect(board.pawns))) {
|
||||
throw PositionError.pawnsOnBackrank;
|
||||
throw PositionSetupException.pawnsOnBackrank;
|
||||
}
|
||||
|
||||
final skipImpossibleCheck = ignoreImpossibleCheck ?? false;
|
||||
@@ -2273,56 +2273,6 @@ class Outcome {
|
||||
}
|
||||
}
|
||||
|
||||
enum IllegalSetup {
|
||||
/// There are no pieces on the board.
|
||||
empty,
|
||||
|
||||
/// The player not to move is in check.
|
||||
oppositeCheck,
|
||||
|
||||
/// There are impossibly many checkers, two sliding checkers are
|
||||
/// aligned, or check is not possible because the last move was a
|
||||
/// double pawn push.
|
||||
///
|
||||
/// Such a position cannot be reached by any sequence of legal moves.
|
||||
impossibleCheck,
|
||||
|
||||
/// There are pawns on the backrank.
|
||||
pawnsOnBackrank,
|
||||
|
||||
/// A king is missing, or there are too many kings.
|
||||
kings,
|
||||
|
||||
/// A variant specific rule is violated.
|
||||
variant,
|
||||
}
|
||||
|
||||
@immutable
|
||||
class PlayError implements Exception {
|
||||
final String message;
|
||||
const PlayError(this.message);
|
||||
|
||||
@override
|
||||
String toString() => 'PlayError($message)';
|
||||
}
|
||||
|
||||
/// Error when trying to create a [Position] from an illegal [Setup].
|
||||
@immutable
|
||||
class PositionError implements Exception {
|
||||
final IllegalSetup cause;
|
||||
const PositionError(this.cause);
|
||||
|
||||
static const empty = PositionError(IllegalSetup.empty);
|
||||
static const oppositeCheck = PositionError(IllegalSetup.oppositeCheck);
|
||||
static const impossibleCheck = PositionError(IllegalSetup.impossibleCheck);
|
||||
static const pawnsOnBackrank = PositionError(IllegalSetup.pawnsOnBackrank);
|
||||
static const kings = PositionError(IllegalSetup.kings);
|
||||
static const variant = PositionError(IllegalSetup.variant);
|
||||
|
||||
@override
|
||||
String toString() => 'PositionError(${cause.name})';
|
||||
}
|
||||
|
||||
@immutable
|
||||
class Castles {
|
||||
/// SquareSet of rooks that have not moved yet.
|
||||
|
||||
+18
-16
@@ -63,10 +63,10 @@ class Setup {
|
||||
/// * Accepts multiple spaces and underscores (`_`) as separators between
|
||||
/// FEN fields.
|
||||
///
|
||||
/// Throws a [FenError] if the provided FEN is not valid.
|
||||
/// Throws a [FenException] if the provided FEN is not valid.
|
||||
factory Setup.parseFen(String fen) {
|
||||
final parts = fen.split(RegExp(r'[\s_]+'));
|
||||
if (parts.isEmpty) throw const FenError('ERR_FEN');
|
||||
if (parts.isEmpty) throw const FenException(IllegalFenCause.format);
|
||||
|
||||
// board and pockets
|
||||
final boardPart = parts.removeAt(0);
|
||||
@@ -75,7 +75,7 @@ class Setup {
|
||||
if (boardPart.endsWith(']')) {
|
||||
final pocketStart = boardPart.indexOf('[');
|
||||
if (pocketStart == -1) {
|
||||
throw const FenError('ERR_FEN');
|
||||
throw const FenException(IllegalFenCause.format);
|
||||
}
|
||||
board = Board.parseFen(boardPart.substring(0, pocketStart));
|
||||
pockets = _parsePockets(
|
||||
@@ -101,7 +101,7 @@ class Setup {
|
||||
} else if (turnPart == 'b') {
|
||||
turn = Side.black;
|
||||
} else {
|
||||
throw const FenError('ERR_TURN');
|
||||
throw const FenException(IllegalFenCause.turn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,9 @@ class Setup {
|
||||
final epPart = parts.removeAt(0);
|
||||
if (epPart != '-') {
|
||||
epSquare = Square.parse(epPart);
|
||||
if (epSquare == null) throw const FenError('ERR_EP_SQUARE');
|
||||
if (epSquare == null) {
|
||||
throw const FenException(IllegalFenCause.enPassant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,21 +135,21 @@ class Setup {
|
||||
}
|
||||
final halfmoves = halfmovePart != null ? _parseSmallUint(halfmovePart) : 0;
|
||||
if (halfmoves == null) {
|
||||
throw const FenError('ERR_HALFMOVES');
|
||||
throw const FenException(IllegalFenCause.halfmoveClock);
|
||||
}
|
||||
|
||||
final fullmovesPart = parts.isNotEmpty ? parts.removeAt(0) : null;
|
||||
final fullmoves =
|
||||
fullmovesPart != null ? _parseSmallUint(fullmovesPart) : 1;
|
||||
if (fullmoves == null) {
|
||||
throw const FenError('ERR_FULLMOVES');
|
||||
throw const FenException(IllegalFenCause.fullmoveNumber);
|
||||
}
|
||||
|
||||
final remainingChecksPart = parts.isNotEmpty ? parts.removeAt(0) : null;
|
||||
(int, int)? remainingChecks;
|
||||
if (remainingChecksPart != null) {
|
||||
if (earlyRemainingChecks != null) {
|
||||
throw const FenError('ERR_REMAINING_CHECKS');
|
||||
throw const FenException(IllegalFenCause.remainingChecks);
|
||||
}
|
||||
remainingChecks = _parseRemainingChecks(remainingChecksPart);
|
||||
} else if (earlyRemainingChecks != null) {
|
||||
@@ -155,7 +157,7 @@ class Setup {
|
||||
}
|
||||
|
||||
if (parts.isNotEmpty) {
|
||||
throw const FenError('ERR_FEN');
|
||||
throw const FenException(IllegalFenCause.format);
|
||||
}
|
||||
|
||||
return Setup(
|
||||
@@ -269,14 +271,14 @@ class Pockets {
|
||||
|
||||
Pockets _parsePockets(String pocketPart) {
|
||||
if (pocketPart.length > 64) {
|
||||
throw const FenError('ERR_POCKETS');
|
||||
throw const FenException(IllegalFenCause.pockets);
|
||||
}
|
||||
Pockets pockets = Pockets.empty;
|
||||
for (int i = 0; i < pocketPart.length; i++) {
|
||||
final c = pocketPart[i];
|
||||
final piece = Piece.fromChar(c);
|
||||
if (piece == null) {
|
||||
throw const FenError('ERR_POCKETS');
|
||||
throw const FenException(IllegalFenCause.pockets);
|
||||
}
|
||||
pockets = pockets.increment(piece.color, piece.role);
|
||||
}
|
||||
@@ -289,18 +291,18 @@ Pockets _parsePockets(String pocketPart) {
|
||||
final white = _parseSmallUint(parts[1]);
|
||||
final black = _parseSmallUint(parts[2]);
|
||||
if (white == null || white > 3 || black == null || black > 3) {
|
||||
throw const FenError('ERR_REMAINING_CHECKS');
|
||||
throw const FenException(IllegalFenCause.remainingChecks);
|
||||
}
|
||||
return (3 - white, 3 - black);
|
||||
} else if (parts.length == 2) {
|
||||
final white = _parseSmallUint(parts[0]);
|
||||
final black = _parseSmallUint(parts[1]);
|
||||
if (white == null || white > 3 || black == null || black > 3) {
|
||||
throw const FenError('ERR_REMAINING_CHECKS');
|
||||
throw const FenException(IllegalFenCause.remainingChecks);
|
||||
}
|
||||
return (white, black);
|
||||
} else {
|
||||
throw const FenError('ERR_REMAINING_CHECKS');
|
||||
throw const FenException(IllegalFenCause.remainingChecks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +329,7 @@ SquareSet _parseCastlingFen(Board board, String castlingPart) {
|
||||
backrank)
|
||||
.squares;
|
||||
} else {
|
||||
throw const FenError('ERR_CASTLING');
|
||||
throw const FenException(IllegalFenCause.castling);
|
||||
}
|
||||
for (final square in candidates) {
|
||||
if (board.kings.has(square)) break;
|
||||
@@ -339,7 +341,7 @@ SquareSet _parseCastlingFen(Board board, String castlingPart) {
|
||||
}
|
||||
if ((const SquareSet.fromRank(Rank.first) & unmovedRooks).size > 2 ||
|
||||
(const SquareSet.fromRank(Rank.eighth) & unmovedRooks).size > 2) {
|
||||
throw const FenError('ERR_CASTLING');
|
||||
throw const FenException(IllegalFenCause.castling);
|
||||
}
|
||||
return unmovedRooks;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user