diff --git a/lib/src/board.dart b/lib/src/board.dart index beced45..ac1c6ef 100644 --- a/lib/src/board.dart +++ b/lib/src/board.dart @@ -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; } diff --git a/lib/src/models.dart b/lib/src/models.dart index 919965d..565a067 100644 --- a/lib/src/models.dart +++ b/lib/src/models.dart @@ -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 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 FenError implements Exception { +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 diff --git a/lib/src/pgn.dart b/lib/src/pgn.dart index aa22783..c729aad 100644 --- a/lib/src/pgn.dart +++ b/lib/src/pgn.dart @@ -135,11 +135,11 @@ class PgnGame { /// /// 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); } diff --git a/lib/src/position.dart b/lib/src/position.dart index 6d04209..21b963a 100644 --- a/lib/src/position.dart +++ b/lib/src/position.dart @@ -517,12 +517,12 @@ abstract class Position> { /// 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 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> { /// 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, 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, String) playToSan(Move move) { if (isLegal(move)) { @@ -658,7 +658,7 @@ abstract class Position> { : san; return (newPos, suffixed); } else { - throw PlayError('Invalid move $move'); + throw PlayException('Invalid move $move'); } } @@ -675,27 +675,27 @@ abstract class Position> { /// 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> { /// 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> { .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 { /// 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 { /// 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 { @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 { /// 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 { /// 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 { /// 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 { 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 { /// 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 { /// 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 { /// 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 { @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. diff --git a/lib/src/setup.dart b/lib/src/setup.dart index 752c52f..717acf3 100644 --- a/lib/src/setup.dart +++ b/lib/src/setup.dart @@ -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; } diff --git a/test/board_test.dart b/test/board_test.dart index 57ff655..ae9cd44 100644 --- a/test/board_test.dart +++ b/test/board_test.dart @@ -56,10 +56,13 @@ void main() { }); test('invalid board fen', () { - expect(() => Board.parseFen('4k2r/8/8/8/8/RR2K2R'), - throwsA(predicate((e) => e is FenError && e.message == 'ERR_BOARD'))); + expect( + () => Board.parseFen('4k2r/8/8/8/8/RR2K2R'), + throwsA(predicate( + (e) => e is FenException && e.cause == IllegalFenCause.board))); - expect(() => Board.parseFen('lol'), throwsA(const TypeMatcher())); + expect(() => Board.parseFen('lol'), + throwsA(const TypeMatcher())); }); test('make board fen', () { diff --git a/test/position_test.dart b/test/position_test.dart index abf8a67..983dbf3 100644 --- a/test/position_test.dart +++ b/test/position_test.dart @@ -298,16 +298,18 @@ void main() { test('Empty board', () { expect( () => Chess.fromSetup(Setup.parseFen(kEmptyFEN)), - throwsA(predicate( - (e) => e is PositionError && e.cause == IllegalSetup.empty))); + throwsA(predicate((e) => + e is PositionSetupException && + e.cause == IllegalSetupCause.empty))); }); test('Missing king', () { expect( () => Chess.fromSetup(Setup.parseFen( 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQ1BNR w HAkq - 0 1')), - throwsA(predicate( - (e) => e is PositionError && e.cause == IllegalSetup.kings))); + throwsA(predicate((e) => + e is PositionSetupException && + e.cause == IllegalSetupCause.kings))); }); test('Opposite check', () { @@ -315,7 +317,8 @@ void main() { () => Chess.fromSetup(Setup.parseFen( 'rnbqkbnr/pppp1ppp/8/8/8/8/PPPPQPPP/RNB1KBNR w KQkq - 0 1')), throwsA(predicate((e) => - e is PositionError && e.cause == IllegalSetup.oppositeCheck))); + e is PositionSetupException && + e.cause == IllegalSetupCause.oppositeCheck))); }); test('Backrank pawns', () { @@ -323,8 +326,8 @@ void main() { () => Chess.fromSetup(Setup.parseFen( 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPNP/RNBQKBPR w KQkq - 0 1')), throwsA(predicate((e) => - e is PositionError && - e.cause == IllegalSetup.pawnsOnBackrank))); + e is PositionSetupException && + e.cause == IllegalSetupCause.pawnsOnBackrank))); }); test('checkers alignment', () { @@ -333,8 +336,8 @@ void main() { () => Chess.fromSetup( Setup.parseFen('3R4/8/q4k2/2B5/1NK5/3b4/8/8 w - - 0 1')), throwsA(predicate((e) => - e is PositionError && - e.cause == IllegalSetup.impossibleCheck))); + e is PositionSetupException && + e.cause == IllegalSetupCause.impossibleCheck))); // Checkers aligned with opponent king are fine. Chess.fromSetup(Setup.parseFen( @@ -345,8 +348,8 @@ void main() { () => Chess.fromSetup( Setup.parseFen('8/8/8/1k6/3Pp3/8/8/4KQ2 b - d3 0 1')), throwsA(predicate((e) => - e is PositionError && - e.cause == IllegalSetup.impossibleCheck))); + e is PositionSetupException && + e.cause == IllegalSetupCause.impossibleCheck))); }); }); @@ -509,7 +512,7 @@ void main() { expect( () => Chess.initial .play(const NormalMove(from: Square.e4, to: Square.e6)), - throwsA(const TypeMatcher())); + throwsA(const TypeMatcher())); }); test('e2 e4 on standard position', () { @@ -622,8 +625,8 @@ void main() { expect( () => Chess.fromSetup(setup), throwsA(predicate((e) => - e is PositionError && - e.cause == IllegalSetup.impossibleCheck))); + e is PositionSetupException && + e.cause == IllegalSetupCause.impossibleCheck))); final pos = Chess.fromSetup(setup, ignoreImpossibleCheck: true); const enPassant = NormalMove(from: Square.d5, to: Square.c6); expect(pos.isLegal(enPassant), false); diff --git a/test/setup_test.dart b/test/setup_test.dart index 6546c25..b6dfef4 100644 --- a/test/setup_test.dart +++ b/test/setup_test.dart @@ -34,8 +34,8 @@ void main() { () => Setup.parseFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w BGL') .unmovedRooks, - throwsA( - predicate((e) => e is FenError && e.message == 'ERR_CASTLING'))); + throwsA(predicate( + (e) => e is FenException && e.cause == IllegalFenCause.castling))); }); test('parse en passant square', () {