Commit graph

545 commits

Author SHA1 Message Date
086f9c5666 [board] Replace PieceSet's derived Hash and PartialEq with bespoke implementation
The piece set duplicates some data to make lookups more efficient. Only use the
necessary, unique data for these functions.
2025-05-31 15:07:20 -07:00
34e8c08c36 [moves, position] Move MoveRecord to the moves crate 2025-05-31 14:32:39 -07:00
b8f45aaece [position] Add some documentation to MoveRecord 2025-05-31 14:28:27 -07:00
a4b713a558 [position] Remove dead move_generator code 2025-05-29 09:21:25 -07:00
942d9fe47b [explorer, moves, position] Implement a moves command in explorer
The moves command writes all possible moves to the terminal.

Move the previous implementation of the moves command, which marked squares that
a piece could move to, to a 'movement' command.
2025-05-28 16:25:55 -07:00
43abbe3fe2 [position] Register a captured piece in the MoveRecord
For unmake_move, so the captured piece can be restored.
2025-05-28 16:23:46 -07:00
2106e05d57 [moves] Implement AllPiecesMoveGenerator
A generator that yields moves for all pieces on the board. This generator combines
the shape-specific move generators developed in prior commits into a single
iterator that yields all moves.

Implement FusedIterator for all generators so AllPiecesMoveGenerator can avoid
doing a bunch of extra work once each iterator has yielded None.
2025-05-28 16:22:16 -07:00
19c6c6701a [position] Fix broken tests build
The make_move tests were trying to access the last capture piece directly from a
slice of captures pieces (implementation prior to CapturesList). Implement
CapturesList::last() to return an Option<&Piece> and use it in the tests.
2025-05-27 12:04:24 -07:00
085697d38f [position] Remove position::position module
Move the implementation of Position to the top-level position.rs file.
2025-05-27 11:59:42 -07:00
db489af50b [position] Move unmake move stuff to an unmake_move module 2025-05-27 11:52:17 -07:00
a8ea248972 [position] Derive Default implementation for Position 2025-05-27 11:51:08 -07:00
e3ca466737 [position] Move capture list to a CapturesList struct
This one has a custom Display implementation for easier integration with Positon's.
2025-05-27 11:51:08 -07:00
eb6f2000a9 [board, moves, position] Implement KingMoveGenerator
Implement a move generator that emits moves for the king(s) of a particular color.
There will, of course, only ever be one king per side in any valid board, but
this iterator can (in theory) handle multiple kings on the board. This iterator
is almost entirely copypasta of the SliderMoveGenerator. The major difference is
castling.

Castle moves are emitted by a helper CastleIterator type. This struct collects
information about whether the given color can castle on each side of the board
and then emits moves for each side, if indicated.

Do some light refactoring of the castle-related methods on Board to accommodate
this move generator. Remove the dependency on internal state and rename the
"can_castle" method to color_can_castle.

In order to facilitate creating castling moves without relying on Board, remove
the origin and target squares from the encoded castling move. Code that makes
a castling move already looks up castling parameters to move the king and rook to
the right squares, so encoding those squares was redundant. This change
necessitated some updates to position.

Lastly, bring in a handful of unit tests courtesy of Claude. Apparently, it's my
new best coding friend. 🙃
2025-05-26 23:37:33 -07:00
f005d94fc2 [bitboard, board, core, moves] Implement SliderMoveGenerator
This generator produces moves for slider pieces: bishops, rooks, and queens. All
of these pieces behave identically, though with different sets of rays that
emanate from the origin square. Claude helped me significantly with the
implementation and unit testing. All the unit tests that took advantage of Claude
for implementation are marked as such with an _ai_claude suffix to the test name.

One unique aspect of this move generator that Claude suggested to me was to use
loop { } instead of a recursive call to next() when the internal iterators expire.
I may try to port this to the other move generators in the future.

To support this move generator, implement a Slider enum in core that represents
one of the three slider pieces.

Add Board::bishops(), Board::rooks() and Board::queens() to return BitBoards of
those pieces. These are analogous to the pawns() and knights() methods that return
their corresponding pieces.

Also in the board create, replace the separate sight method implementations with
a macro. These are all the same, but with a different sight method called under
the hood.

Finally, derive Clone and Debug for the bit_scanner types.
2025-05-26 17:41:43 -07:00
2c6a7828bc [moves] Two new pawn move tests 2025-05-25 11:05:21 -07:00
faca844733 [moves] Knight move generator and tests 2025-05-25 11:05:10 -07:00
3f3842c7c8 [moves] Add several macros to help with testing: ply! and assert_move_list!
ply! implements a small DSL for writing moves in code using a natural-ish
algebraic notation.

assert_move_list! takes a generator and an expected list of moves and asserts
that they're equal. This macro is mostly a copy from one I wrote earlier in the
position crate.
2025-05-25 11:04:49 -07:00
09bf17d66b [moves] Implement promotions and en passant in the PawnMoveGenerator
Add another sub-iterator to the PawnMoveGenerator that produces promotion pushes
or capture moves (depending on move_type) when a pawn moves to the back rank.

Also implement en passant moves.

Fix a bug in the Left and Right captures branches where the wrong neighbors used
to calculate origin squares.

Add a whole bunch of tests. Still missing several cases though.
2025-05-24 18:01:14 -07:00
ab587f379f [board] Fix a bug in PieceSet::opposing_occupancy
This method should compute occupancy of everything except the provided color. Instead
it was computing the inverse.
2025-05-24 17:54:46 -07:00
5466693c1b [position] Remove empty implementation of Position::unmake_move 2025-05-23 18:39:38 -07:00
1da08df430 [board] Implement a couple handy piece getters
Board::find_pieces returns a BitBoard for all the pieces matching the given Piece.

Board::enemies returns a BitBoard of all the enemy pieces of a given Color.

Board::pawns returns a BitBoard of all the pawns of a given Color.
2025-05-23 18:39:18 -07:00
c02f0170b9 [bitboard] Export the bit_scanner module
Clients can access TrailingBitScanner and LeadingBitScanner directly now.
2025-05-23 18:38:15 -07:00
994f17091b [board] Implement Board::unwrap_color
Unwrap an Option<Color> using the board's active color in the default case.
2025-05-23 18:37:13 -07:00
574ab803dd [moves] Implement a move generator for pawns
I used Claude to help me figure this out. First time using AI for coding. It was
actually rather helpful!

Calculate BitBoards representing the various kinds of pawn moves when the move
generator is created, and then iterate through them.

En passant still isn't implemented here. This code has not been tested yet either.
2025-05-23 18:36:22 -07:00
af2bff348f [core] Add an Option<i8> argument to Square::neighbor
This argument allows computing the neighbor n squares away along the direction.
Technically, squares n>1 are not neighbors… but this is a convenient calculation.
2025-05-23 18:34:19 -07:00
3684e9b425 [board, core, bitboard] Clean up casts between Rank, File and BitBoard
Let BitBoard::rank and BitBoard::file take a Rank and File directly, instead of a
u8 by reference. And then make the Rank/File::as_index const and return a value
rather than a reference.

All this allows you to convert between Rank, File, and BitBoard at compile tile
(i.e. as a const method) rather than needing to do runtime stuff.
2025-05-23 18:32:18 -07:00
588f049290 [position] Remove display module 2025-05-23 14:15:38 -07:00
5c5d9d5018 [board] Remove some dead code from Board 2025-05-23 14:14:49 -07:00
a9268ad194 [position] Add move tracking to Position
Create a new MoveRecord struct that tracks the move (aka ply) and irreversible
board properties. This should make it easier to unmake moves in the future.
2025-05-23 10:00:20 -07:00
05c62dcd99 [position] Remove CastleEvaluationError from this crate
It moved to board.
2025-05-23 09:58:29 -07:00
b8a51990a3 [board, position] Implement some methods to check for whether a king is in check
Remove some dead code from Position.
2025-05-23 09:57:48 -07:00
0abe9b6c19 [board, position] Add a color argument to opposing_sight
New convention: active_color_ methods operate on the active color of the Board.
Methods without that prefix take a color parameter and operate on that.

Refactor opposing_sight to do this.
2025-05-23 09:56:47 -07:00
a92ec9aba3 [board] Add an option to display a board with ASCII characters 2025-05-23 09:53:59 -07:00
ddd14e8999 [board] Define two new types for the Clock properties of Board
Helpful for other parts of the code.
2025-05-23 09:53:29 -07:00
d5c0330fbe [board] Add PieceSet::find_pieces
Takes a Piece and returns a BitBoard representing where pieces of that shape and
color are on the board.
2025-05-23 09:52:22 -07:00
609cda0fe5 [position] Remove castle module (it went to board) 2025-05-23 09:50:57 -07:00
e89bca9877 [board] Remove empty errors module 2025-05-21 10:09:55 -07:00
dbca7b4f88 [position, board] Move castle, movement, and sight modules to the board crate
Nothing about this code depends on Position, so push it down to a lower layer.
2025-05-21 10:08:59 -07:00
9a4fa827f9 [position] Add two new negative tests for making pawn moves
- Ensure you cannot move a pawn to the last rank without a promotion move.
- Ensure a pawn cannot make an illegal move, and that the board state remains
  as it was before the move was attempted.
2025-05-21 09:51:16 -07:00
10ba21f7e3 [explorer] Remove the unused starting command; add aliases to make (m) and place (p) 2025-05-21 08:27:15 -07:00
85c1a395c4 [position] Streamline the implementation of castling_{king,rook}
Instead of using .and_then with an if/else, use .filter and pass the predicate.
Saves a few lines.
2025-05-21 08:26:34 -07:00
feaa81bbd8 [position, moves] Implement some castling tests
Add white castling for both wings. Found some bugs in how king sight is computed
while writing these.

In order for the king to perform the castle, the Movement bitboard needs to return
the two squares the king can castle to. That means anytime movement is calculated
for the king, the (relatively expensive) castling evaluation needs to happen.

Write two new helper static functions to create a Move for castling and promotion
moves. Since structs cannot have any functions with the same name, the two methods
that return properties related to those moves (Move::castle and Move::promotion)
need to be renamed. They're now called Move::castle_wing and Move::promotion_shape.
2025-05-21 08:25:49 -07:00
7c9c5484ba [position, board] Remove a bunch of dead code 2025-05-20 19:29:39 -07:00
039fd2b080 [position, board, core, moves] Implement a bunch of make_move code
Implement making double push and promotion moves. Then write several tests to
exercise these. Add convenient static functions to the Move struct to build moves
quickly, without using the Builder.

Add a is_promotable_rank() method to Rank to check that a rank can be used for
promotion moves.

The tests found and fixed a bug in pawn movement where the en passant square was
being discarded when deciding whether an e.p. move can be made.
2025-05-20 19:29:02 -07:00
6591619e32 [position] Misc changes
- Add a captures list to the Position struct
- Implement ToFenStr
- Update the imports list, which has been sorely out-of-date in the tree for many commits now.
2025-05-19 17:00:48 -07:00
97552302cb [position] Move castle evaluation code to its own submodule of position
Move the tests too.
2025-05-19 16:59:13 -07:00
54ac88aaf7 [board] Remove some old PlacedPiece code 2025-05-19 16:50:44 -07:00
0c1863acb9 [board, core, moves, position] Implement castling
Implement a new method on Position that evaluates whether the active color can castle
on a given wing of the board. Then, implement making a castling move in the position.

Make a new Wing enum in the core crate to specify kingside or queenside. Replace the
Castle enum from the board crate with this one. This caused a lot of churn...

Along the way fix a bunch of tests.

Note: there's still no way to actually make a castling move in explorer.
2025-05-19 16:50:30 -07:00
6816e350eb [moves] Clean up implementation of Move and export Kind enum 2025-05-19 14:19:05 -07:00
9010f1e9c2 [explorer, moves, core] Improve error handling in explorer
Implement thiserror::Error for a bunch of error types, and remove string errors
from the implementation of the command handler in explorer.

Clean up parsing of basic types all over the place.

Update Cargo files to include thiserror and anyhow.
2025-05-19 14:18:31 -07:00