Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Square extension type #41

Merged
merged 16 commits into from
Aug 1, 2024
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.8.0

### Breaking changes:
- `Square` is now an extension type.
- Introduce `File` and `Rank` types.

## 0.7.1

- Add Piece.kind, Role.letter and Role.uppercaseLetter getters.
Expand Down
10 changes: 3 additions & 7 deletions benchmark/dartchess_benchmark.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:benchmark/benchmark.dart';
import 'package:dartchess/dartchess.dart';
import 'dart:io';
import 'dart:io' as io;

void main() {
benchmark('make fen from initial position', () {
Expand Down Expand Up @@ -37,19 +37,15 @@ void main() {
legalMovesPos.legalMoves.length;
});

benchmark('algebraic legal moves', () {
algebraicLegalMoves(legalMovesPos);
});

benchmark('parsePgn - kasparov-deep-blue', () {
final String data =
File('./data/kasparov-deep-blue-1997.pgn').readAsStringSync();
io.File('./data/kasparov-deep-blue-1997.pgn').readAsStringSync();

PgnGame.parseMultiGamePgn(data);
});

final game = PgnGame.parsePgn(
File('./data/lichess-bullet-game.pgn').readAsStringSync());
io.File('./data/lichess-bullet-game.pgn').readAsStringSync());
benchmark('makePgn', () {
game.makePgn();
});
Expand Down
8 changes: 3 additions & 5 deletions example/dartchess_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ 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 = algebraicLegalMoves(pos);
// Generate legal moves
assert(pos.legalMoves.length == 16);

assert(legalMoves['e2']!.length == 2);

const move = NormalMove(from: 12, to: 28);
const move = NormalMove(from: Square.e2, to: Square.e4);

assert(pos.isLegal(move));

Expand Down
20 changes: 7 additions & 13 deletions lib/src/attacks.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import './square_set.dart';
import './utils.dart';
import './models.dart';

/// Gets squares attacked or defended by a king on [Square].
SquareSet kingAttacks(Square square) {
assert(square >= 0 && square < 64);
return _kingAttacks[square];
}

/// Gets squares attacked or defended by a knight on [Square].
SquareSet knightAttacks(Square square) {
assert(square >= 0 && square < 64);
return _knightAttacks[square];
}

/// Gets squares attacked or defended by a pawn of the given [Side] on [Square].
SquareSet pawnAttacks(Side side, Square square) {
assert(square >= 0 && square < 64);
return _pawnAttacks[side]![square];
}

Expand Down Expand Up @@ -89,18 +85,16 @@ SquareSet _computeRange(Square square, List<int> deltas) {
SquareSet range = SquareSet.empty;
for (final delta in deltas) {
final sq = square + delta;
if (0 <= sq &&
sq < 64 &&
(squareFile(square) - squareFile(sq)).abs() <= 2) {
range = range.withSquare(sq);
if (0 <= sq && sq < 64 && (square.file - Square(sq).file).abs() <= 2) {
range = range.withSquare(Square(sq));
}
}
return range;
}

List<T> _tabulate<T>(T Function(Square square) f) {
final List<T> table = [];
for (Square square = 0; square < 64; square++) {
for (final square in Square.values) {
table.insert(square, f(square));
}
return table;
Expand All @@ -116,18 +110,18 @@ final _pawnAttacks = {
};

final _fileRange =
_tabulate((sq) => SquareSet.fromFile(squareFile(sq)).withoutSquare(sq));
_tabulate((sq) => SquareSet.fromFile(sq.file).withoutSquare(sq));
final _rankRange =
_tabulate((sq) => SquareSet.fromRank(squareRank(sq)).withoutSquare(sq));
_tabulate((sq) => SquareSet.fromRank(sq.rank).withoutSquare(sq));
final _diagRange = _tabulate((sq) {
final shift = 8 * (squareRank(sq) - squareFile(sq));
final shift = 8 * (sq.rank - sq.file);
return (shift >= 0
? SquareSet.diagonal.shl(shift)
: SquareSet.diagonal.shr(-shift))
.withoutSquare(sq);
});
final _antiDiagRange = _tabulate((sq) {
final shift = 8 * (squareRank(sq) + squareFile(sq) - 7);
final shift = 8 * (sq.rank + sq.file - 7);
return (shift >= 0
? SquareSet.antidiagonal.shl(shift)
: SquareSet.antidiagonal.shr(-shift))
Expand Down
4 changes: 2 additions & 2 deletions lib/src/board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class Board {
file += code - 48;
} else {
if (file >= 8 || rank < 0) throw const FenError('ERR_BOARD');
final square = file + rank * 8;
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');
Expand All @@ -147,7 +147,7 @@ class Board {
int empty = 0;
for (int rank = 7; rank >= 0; rank--) {
for (int file = 0; file < 8; file++) {
final square = file + rank * 8;
final square = Square(file + rank * 8);
final piece = pieceAt(square);
if (piece == null) {
empty++;
Expand Down
3 changes: 0 additions & 3 deletions lib/src/constants.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
const kFileNames = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
const kRankNames = ['1', '2', '3', '4', '5', '6', '7', '8'];

/// The board part of the initial position in the FEN format.
const kInitialBoardFEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR';

Expand Down
9 changes: 4 additions & 5 deletions lib/src/debug.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import './board.dart';
import './models.dart';
import './position.dart';
import './square_set.dart';
import './utils.dart';

/// Takes a string and returns a SquareSet. Useful for debugging/testing purposes.
///
Expand Down Expand Up @@ -34,7 +33,7 @@ SquareSet makeSquareSet(String rep) {
for (int x = 0; x < 8; x++) {
final repSq = table[y][x];
if (repSq == '1') {
ret = ret.withSquare(x + y * 8);
ret = ret.withSquare(Square(x + y * 8));
}
}
}
Expand All @@ -46,7 +45,7 @@ String humanReadableSquareSet(SquareSet sq) {
final buffer = StringBuffer();
for (int y = 7; y >= 0; y--) {
for (int x = 0; x < 8; x++) {
final square = x + y * 8;
final square = Square(x + y * 8);
buffer.write(sq.has(square) ? '1' : '.');
buffer.write(x < 7 ? ' ' : '\n');
}
Expand All @@ -59,7 +58,7 @@ String humanReadableBoard(Board board) {
final buffer = StringBuffer();
for (int y = 7; y >= 0; y--) {
for (int x = 0; x < 8; x++) {
final square = x + y * 8;
final square = Square(x + y * 8);
final p = board.pieceAt(square);
final col = p != null ? p.fenChar : '.';
buffer.write(col);
Expand Down Expand Up @@ -100,7 +99,7 @@ int perft(Position pos, int depth, {bool shouldLog = false}) {
for (final entry in pos.legalMoves.entries) {
final from = entry.key;
final dests = entry.value;
final promotions = squareRank(from) == (pos.turn == Side.white ? 6 : 1) &&
final promotions = from.rank == (pos.turn == Side.white ? 6 : 1) &&
pos.board.pawns.has(from)
? promotionRoles
: [null];
Expand Down
Loading