mirror of
https://github.com/lichess-org/dartchess.git
synced 2026-05-26 13:51:01 +00:00
More refactoring: remove utils
This commit is contained in:
@@ -37,10 +37,6 @@ void main() {
|
||||
legalMovesPos.legalMoves.length;
|
||||
});
|
||||
|
||||
benchmark('algebraic legal moves', () {
|
||||
legalMovesOf(legalMovesPos);
|
||||
});
|
||||
|
||||
benchmark('parsePgn - kasparov-deep-blue', () {
|
||||
final String data =
|
||||
io.File('./data/kasparov-deep-blue-1997.pgn').readAsStringSync();
|
||||
|
||||
@@ -6,10 +6,8 @@ void main() {
|
||||
'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
|
||||
final pos = Chess.fromSetup(setup);
|
||||
|
||||
// Generate legal moves in algebraic notation
|
||||
final legalMoves = legalMovesOf(pos);
|
||||
|
||||
assert(legalMoves[Square.e2]!.length == 2);
|
||||
// Generate legal moves
|
||||
assert(pos.legalMoves.length == 16);
|
||||
|
||||
const move = NormalMove(from: Square.e2, to: Square.e4);
|
||||
|
||||
|
||||
+12
-10
@@ -1,6 +1,5 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import './utils.dart';
|
||||
import './square_set.dart';
|
||||
|
||||
/// The chessboard side, white or black.
|
||||
@@ -207,6 +206,9 @@ extension type const Square._(int value) implements int {
|
||||
///
|
||||
/// Throws a [FormatException] if the algebraic notation is invalid.
|
||||
factory Square.fromName(String algebraic) {
|
||||
if (algebraic.length != 2) {
|
||||
throw FormatException('Invalid algebraic notation: $algebraic');
|
||||
}
|
||||
final file = algebraic.codeUnitAt(0) - 97;
|
||||
final rank = algebraic.codeUnitAt(1) - 49;
|
||||
if (file < 0 || file > 7 || rank < 0 || rank > 7) {
|
||||
@@ -219,11 +221,10 @@ extension type const Square._(int value) implements int {
|
||||
///
|
||||
/// Returns either a [Square] or `null` if the algebraic notation is invalid.
|
||||
static Square? parse(String algebraic) {
|
||||
if (algebraic.length != 2) return null;
|
||||
final file = algebraic.codeUnitAt(0) - 97;
|
||||
final rank = algebraic.codeUnitAt(1) - 49;
|
||||
if (file < 0 || file > 7 || rank < 0 || rank > 7) {
|
||||
return null;
|
||||
}
|
||||
if (file < 0 || file > 7 || rank < 0 || rank > 7) return null;
|
||||
return Square(rank * 8 + file);
|
||||
}
|
||||
|
||||
@@ -319,6 +320,7 @@ extension type const Square._(int value) implements int {
|
||||
static const g8 = Square(62);
|
||||
static const h8 = Square(63);
|
||||
|
||||
/// All squares on the chessboard, from a1 to h8.
|
||||
static const values = [
|
||||
a1,
|
||||
b1,
|
||||
@@ -648,11 +650,11 @@ sealed class Move {
|
||||
static Move? parse(String str) {
|
||||
if (str[1] == '@' && str.length == 4) {
|
||||
final role = Role.fromChar(str[0]);
|
||||
final to = parseSquare(str.substring(2));
|
||||
final to = Square.parse(str.substring(2));
|
||||
if (role != null && to != null) return DropMove(to: to, role: role);
|
||||
} else if (str.length == 4 || str.length == 5) {
|
||||
final from = parseSquare(str.substring(0, 2));
|
||||
final to = parseSquare(str.substring(2, 4));
|
||||
final from = Square.parse(str.substring(0, 2));
|
||||
final to = Square.parse(str.substring(2, 4));
|
||||
Role? promotion;
|
||||
if (str.length == 5) {
|
||||
promotion = Role.fromChar(str[4]);
|
||||
@@ -692,8 +694,8 @@ class NormalMove extends Move {
|
||||
///
|
||||
/// Throws a [FormatException] if the UCI string is invalid.
|
||||
factory NormalMove.fromUci(String uci) {
|
||||
final from = parseSquare(uci.substring(0, 2));
|
||||
final to = parseSquare(uci.substring(2, 4));
|
||||
final from = Square.parse(uci.substring(0, 2));
|
||||
final to = Square.parse(uci.substring(2, 4));
|
||||
Role? promotion;
|
||||
if (uci.length == 5) {
|
||||
promotion = Role.fromChar(uci[4]);
|
||||
@@ -749,7 +751,7 @@ class DropMove extends Move {
|
||||
/// Throws a [FormatException] if the UCI string is invalid.
|
||||
factory DropMove.fromUci(String uci) {
|
||||
final role = Role.fromChar(uci[0]);
|
||||
final to = parseSquare(uci.substring(2));
|
||||
final to = Square.parse(uci.substring(2));
|
||||
if (role != null && to != null) {
|
||||
return DropMove(to: to, role: role);
|
||||
}
|
||||
|
||||
+2
-3
@@ -5,7 +5,6 @@ import 'package:meta/meta.dart';
|
||||
import './setup.dart';
|
||||
import './models.dart';
|
||||
import './position.dart';
|
||||
import './utils.dart';
|
||||
|
||||
typedef PgnHeaders = Map<String, String>;
|
||||
|
||||
@@ -404,12 +403,12 @@ class PgnCommentShape {
|
||||
/// Parse the PGN for any comment or return null.
|
||||
static PgnCommentShape? fromPgn(String str) {
|
||||
final color = CommentShapeColor.parseShapeColor(str.substring(0, 1));
|
||||
final from = parseSquare(str.substring(1, 3));
|
||||
final from = Square.parse(str.substring(1, 3));
|
||||
if (color == null || from == null) return null;
|
||||
if (str.length == 3) {
|
||||
return PgnCommentShape(color: color, from: from, to: from);
|
||||
}
|
||||
final to = parseSquare(str.substring(3, 5));
|
||||
final to = Square.parse(str.substring(3, 5));
|
||||
if (str.length == 5 && to != null) {
|
||||
return PgnCommentShape(color: color, from: from, to: to);
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ abstract class Position<T extends Position<T>> {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
final destination = parseSquare(san.substring(san.length - 2));
|
||||
final destination = Square.parse(san.substring(san.length - 2));
|
||||
if (destination == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -402,7 +402,7 @@ abstract class Position<T extends Position<T>> {
|
||||
return null;
|
||||
}
|
||||
|
||||
final destination = parseSquare(san);
|
||||
final destination = Square.parse(san);
|
||||
if (destination == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -439,7 +439,7 @@ abstract class Position<T extends Position<T>> {
|
||||
}
|
||||
|
||||
// The final two moves define the destination
|
||||
final destination = parseSquare(san.substring(san.length - 2));
|
||||
final destination = Square.parse(san.substring(san.length - 2));
|
||||
if (destination == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -472,7 +472,7 @@ abstract class Position<T extends Position<T>> {
|
||||
return null;
|
||||
}
|
||||
if (san.length == 2) {
|
||||
final sourceSquare = parseSquare(san);
|
||||
final sourceSquare = Square.parse(san);
|
||||
if (sourceSquare == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+1
-2
@@ -4,7 +4,6 @@ import 'dart:math' as math;
|
||||
import './square_set.dart';
|
||||
import './models.dart';
|
||||
import './board.dart';
|
||||
import './utils.dart';
|
||||
|
||||
/// A not necessarily legal position.
|
||||
@immutable
|
||||
@@ -120,7 +119,7 @@ class Setup {
|
||||
if (parts.isNotEmpty) {
|
||||
final epPart = parts.removeAt(0);
|
||||
if (epPart != '-') {
|
||||
epSquare = parseSquare(epPart);
|
||||
epSquare = Square.parse(epPart);
|
||||
if (epSquare == null) throw const FenError('ERR_EP_SQUARE');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,3 @@
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import './models.dart';
|
||||
import './position.dart';
|
||||
|
||||
/// Parses a string like 'a1', 'a2', etc. and returns a [Square] or `null` if the square
|
||||
/// doesn't exist.
|
||||
Square? parseSquare(String str) {
|
||||
if (str.length != 2) return null;
|
||||
final file = str.codeUnitAt(0) - 'a'.codeUnitAt(0);
|
||||
final rank = str.codeUnitAt(1) - '1'.codeUnitAt(0);
|
||||
if (file < 0 || file >= 8 || rank < 0 || rank >= 8) return null;
|
||||
return Square(file + 8 * rank);
|
||||
}
|
||||
|
||||
/// Gets all the legal moves of this position as a map from the origin square to the set of destination squares.
|
||||
///
|
||||
/// Includes both possible representations of castling moves (unless `chess960` is true).
|
||||
IMap<Square, ISet<Square>> legalMovesOf(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);
|
||||
}
|
||||
|
||||
/// Utility for nullable fields in copyWith methods
|
||||
class Box<T> {
|
||||
const Box(this.value);
|
||||
|
||||
@@ -48,6 +48,17 @@ void main() {
|
||||
expect(Square.fromName('h8'), Square.h8);
|
||||
expect(Square.fromName('e4'), Square.e4);
|
||||
expect(() => Square.fromName('i1'), throwsFormatException);
|
||||
expect(() => Square.fromName('a9'), throwsFormatException);
|
||||
expect(() => Square.fromName('a11'), throwsFormatException);
|
||||
});
|
||||
|
||||
test('parse', () {
|
||||
expect(Square.parse('a1'), Square.a1);
|
||||
expect(Square.parse('h8'), Square.h8);
|
||||
expect(Square.parse('e4'), Square.e4);
|
||||
expect(Square.parse('a9'), isNull);
|
||||
expect(Square.parse('i1'), isNull);
|
||||
expect(Square.parse('a11'), isNull);
|
||||
});
|
||||
|
||||
test('offset', () {
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:dartchess/dartchess.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('legalMovesOf 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 = legalMovesOf(pos);
|
||||
expect(moves[Square.g7], contains(Square.h8));
|
||||
expect(moves[Square.g7], isNot(contains(Square.g8)));
|
||||
});
|
||||
|
||||
test('legalMovesOf with regular castle', () {
|
||||
final wtm =
|
||||
Chess.fromSetup(Setup.parseFen('r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1'));
|
||||
expect(
|
||||
legalMovesOf(wtm)[Square.e1],
|
||||
equals({
|
||||
Square.a1,
|
||||
Square.c1,
|
||||
Square.d1,
|
||||
Square.d2,
|
||||
Square.e2,
|
||||
Square.f1,
|
||||
Square.f2,
|
||||
Square.g1,
|
||||
Square.h1
|
||||
}));
|
||||
expect(legalMovesOf(wtm)[Square.e8], null);
|
||||
|
||||
final btm =
|
||||
Chess.fromSetup(Setup.parseFen('r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1'));
|
||||
expect(
|
||||
legalMovesOf(btm)[Square.e8],
|
||||
equals({
|
||||
Square.a8,
|
||||
Square.c8,
|
||||
Square.d7,
|
||||
Square.d8,
|
||||
Square.e7,
|
||||
Square.f7,
|
||||
Square.f8,
|
||||
Square.g8,
|
||||
Square.h8
|
||||
}));
|
||||
expect(legalMovesOf(btm)[Square.e1], null);
|
||||
});
|
||||
|
||||
test('legalMovesOf with chess960 castle', () {
|
||||
final pos = Chess.fromSetup(Setup.parseFen(
|
||||
'rk2r3/pppbnppp/3p2n1/P2Pp3/4P2q/R5NP/1PP2PP1/1KNQRB2 b Kkq - 0 1'));
|
||||
expect(legalMovesOf(pos, isChess960: true)[Square.b8],
|
||||
equals(ISet(const {Square.a8, Square.c8, Square.e8})));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user