5个版本
0.0.5 | 2022年11月30日 |
---|---|
0.0.4 | 2022年11月29日 |
0.0.3 | 2022年11月29日 |
0.0.2 | 2022年11月27日 |
0.0.1 | 2022年11月24日 |
#1042 in Game dev
44 每月下载量
255KB
5K SLoC
A Fast Chess Library In Rust
This library handles the process of move generation within a chess engine or chess program.
This is a library to manage chess game state and move generation.
需要Rust 1.31或更高版本
This library requires rust version 1.27 or greater in order to check for the BMI2 instruction-set at compile-time. Additionally, this build is compatible with rust 2018 which, I believe, requires rust 1.31.
注意:由于在AMD架构上的性能不佳,已禁用bmi2。我选择公开两个相关函数,如果CPU支持bmi2。
示例
增量式走法生成与捕获/非捕获排序
这里我们通过增量式走法生成遍历所有走法,这在不需要查看所有走法的情况下(例如在引擎搜索函数中)是非常理想的。
use candidate::MoveGen;
use candidate::Board;
use candidate::EMPTY;
// create a board with the initial position
let board = Board::default();
// create an iterable
let mut iterable = MoveGen::new_legal(&board);
// make sure .len() works.
assert_eq!(iterable.len(), 20); // the .len() function does *not* consume the iterator
// lets iterate over targets.
let targets = board.color_combined(!board.side_to_move());
iterable.set_iterator_mask(targets);
// count the number of targets
let mut count = 0;
for _ in &mut iterable {
count += 1;
// This move captures one of my opponents pieces (with the exception of en passant)
}
// now, iterate over the rest of the moves
iterable.set_iterator_mask(!EMPTY);
for _ in &mut iterable {
count += 1;
// This move does not capture anything
}
// make sure it works
assert_eq!(count, 20);
设置棋盘位置
Board结构尝试在所有时间点保持棋盘位置的合法性。这在通过用户输入设置棋盘时可能会很麻烦。
为了处理这个问题,在3.1.0版本中引入了BoardBuilder
结构。该BoardBuilder
结构遵循非消耗性构建器模式,可以通过Board::try_from(...)
或board_builder.try_into()
将其转换为Result<Board, Error>
。
use candidate::{Board, BoardBuilder, Piece, Square, Color};
use std::convert::TryInto;
let mut board_builder = BoardBuilder::new();
board_builder.piece(Square::A1, Piece::King, Color::White)
.piece(Square::A8, Piece::Rook, Color::Black)
.piece(Square::D1, Piece::King, Color::Black);
let board: Board = board_builder.try_into()?;
移动棋子
在这里,我们在棋盘上移动棋子。棋盘是一个在创建时复制的结构,这意味着每次移动棋子时,都会创建一个新的棋盘。你可以使用board.make_move()
来更新当前位置,但不能撤销移动。棋盘结构为了减少复制时间而进行了大小优化。
use candidate::{Board, ChessMove, Square, Color};
let m = ChessMove::new(Square::D2, Square::D4, None);
let board = Board::default();
assert_eq!(board.make_move_new(m).side_to_move(), Color::Black);
表示完整游戏
下棋不仅仅是棋盘上的东西。游戏对象Game
跟踪游戏的历史,以允许提出和棋、认输、50回合规则和棋、重复和棋以及需要游戏历史的任何一般事物。
use candidate::{Game, Square, ChessMove};
let b1c3 = ChessMove::new(Square::B1, Square::C3, None);
let c3b1 = ChessMove::new(Square::C3, Square::B1, None);
let b8c6 = ChessMove::new(Square::B8, Square::C6, None);
let c6b8 = ChessMove::new(Square::C6, Square::B8, None);
let mut game = Game::new();
assert_eq!(game.can_declare_draw(), false);
game.make_move(b1c3);
game.make_move(b8c6);
game.make_move(c3b1);
game.make_move(c6b8);
assert_eq!(game.can_declare_draw(), false); // position has shown up twice
game.make_move(b1c3);
game.make_move(b8c6);
game.make_move(c3b1);
game.make_move(c6b8);
assert_eq!(game.can_declare_draw(), true); // position has shown up three times
FEN 字符串
BoardBuilder
、Board
和Game
都实现了FromStr
,允许你将FEN字符串转换为对象。此外,BoardBuilder
和Board
实现了std::fmt::Display
,可以将它们转换为FEN字符串。
use candidate::Board;
use std::str::FromStr;
assert_eq!(
Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
.expect("Valid Position"),
Board::default()
);
编译时选项
在编译时,我强烈推荐使用RUSTFLAGS="-C target-cpu=native",特别是为了获取几乎所有现代CPU上可用的popcnt和ctzl指令。这被内部用来确定位图上有多少个棋子,以及棋子位于哪个方格。由于这里使用的类型系统,这些任务实际上只需要一条指令。此外,通过使用此标志,在具有这些指令的机器上启用了BMI2。
BMI2
BMI2指令集在支持它的机器上使用。这以两种方式加快了逻辑的速度
- 它使用内置指令来完成与魔法位图相同的逻辑。
- 它通过将移动存储在u16而不是u64中来减少缓存负载,可以通过单条指令将其解压缩为u64。
在没有BMI2的目标上,库回退到魔法位图。这是在编译时检查的。
Shakmaty
另一个Rust棋类库是'shakmaty'包。这是一个很棒的库,比这个库有更多的功能。它支持各种棋类变体,以及UCI协议。然而,这些功能是有代价的,并且在这个库的所有测试用例中,它的性能都比我这个库快。为了比较这两个库,我已经将'shakmaty'支持添加到'chess_perft'应用程序中,并将许多基准测试移动到该包中。你可以在这里查看结果:https://github.com/jordanbray/chess_perft。
它做什么
这个库允许你从一个FEN格式的字符串创建棋盘,列出棋盘上所有合法的移动,并执行移动。
这个库还允许你查看各种棋盘状态信息,如王车易位权。
这个库有非常快的移动生成(其存在的首要目的),这将得到进一步优化。所有使棋子移动生成快速的技巧都被使用。
它不做什么
这不仅仅是一个棋类引擎,而是移动生成器。这不仅仅是一个棋类用户界面,而是移动生成器。这不仅仅是一个棋类PGN解析器,数据库,UCI通信器,XBOARD/WinBoard协议,网站或特级大师。只是一个谦逊的移动生成器。
更多即将到来
API 文档
https://jordanbray.github.io/chess/chess/.
历史
该项目是从 https://github.com/jordanbray/chess 分支出来的,版本为 91fe8e2
。几个未合并的PR已添加到这个版本中 - 这些提交的作者已设置为反映实际作者。与chess
crate保持向后兼容不是一个目标。在1.0之前,预计会有破坏性变化。请查看CHANGELOG.md以获取详细信息 - 所有破坏性变化都应标记为 BREAKING
从分支以来的一些改进
- 构建时间有了显著提高。现在rust-analyzer实际上可以工作了(感谢KarelPeeters)
- 对于完全填充的棋盘,检查
status
的速度提高了2-3倍(感谢AlexanderHarrison) - 使用新的
cache_game_state
功能,使用Game
的速度提高了10-20倍,适用于合理大小的游戏(对于更大的游戏,提高更多)。如果您处于嵌入式环境且无法容忍额外的KB内存,您可以禁用此功能。 - 检查未清理的走法的合法性提高了4-5倍
Game::make_move
现在返回包含走法的SAN表示的Option<String>
。为了避免在热路径上的开销,Board::make_move
仍然返回bool。- 为
Game
添加了可选的仪器,使用tracing - 只需使用instrument_game
功能。 - 添加了便利方法
Board::en_passant_target
和Board::has_checkers
。目前,Board::en_passant
比Board::en_passant_target
稍快。 - 添加了性能基准测试
这有没有什么好处?
依赖关系
~140–280KB