3 个版本
0.9.4 | 2023年9月19日 |
---|---|
0.9.3 | 2023年9月17日 |
0.9.2 |
|
0.9.1 | 2023年9月17日 |
在 游戏开发 中排名 #317
170KB
2.5K SLoC
chessgen
一个简单快速的棋子移动生成器。
这是我将棋子移动生成器用新的编程语言重写的下一版,也可能是最后一版。
自2014年以来,我已经在以下语言中实现了相同的引擎:
Rust 实现是最新的,也是功能最强大的,在代码质量和功能方面都达到了最高水平。
不同语言版本的性能比较。
Perft 计算给定深度下所有可能移动的数量,是测试棋子生成器性能和结果的一种常见方法。
Rust 实现的性能与 C++ 相当,对于更大深度的移动,它表现得更快,这令人惊讶。
perft 二进制文件启动时间较长的原因可能是使用循环和 Mutex 初始化缓存数组。
cache: Box<[Mutex<PerfTCacheEntry>]>
...
let mut cache = Vec::with_capacity(cache_size);
for _ in 0..cache_size {
cache.push(Mutex::new(PerfTCacheEntry::new()))
}
与 C++ 对比
std::atomic<CacheEntry> *cache;
...
cache = new std::atomic<CacheEntry>[cacheSize];
以下是我使用 AMD Ryzen 7 5800H 和 Ubuntu 23.04 上标准棋盘布局的 perft 结果。
PERFT 7: 3,195,901,860 combinations PERFT 8: 84,998,978,956 combinations PERFT 9: 2,439,530,234,167 combinations
多线程,带缓存
_______| PERFT 7 _| PERFT 8 __| PERFT 9 __| C++: | 1.05s | 28.99s | 36m45s | Rust: | 1.59s | 26.28s | 20m13s | GO: | 2.88s | 2m16s | --- |
多线程,无缓存
_______| PERFT 7 _| PERFT 8 __| PERFT 9 __| C++: | 3,850 | 1m52s | 54m17s | Rust: | 3.78s | 1m56s | 58m48s | GO: | 18.38s | 9m57s | --- |
示例
初始化棋盘
use chessgen::ChessBoard;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// standard chess board
let mut board = ChessBoard::STANDARD;
// creating board from FEN
board = ChessBoard::from_fen(ChessBoard::STANDARD_BOARD_FEN)?;
board = ChessBoard::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")?;
// creating board from String representation
board = ChessBoard::from_string(
"
a b c d e f g h
8 r n b q k b n r 8
7 p p p p p p p p 7
6 - - - - - - - - 6
5 - - - - - - - - 5
4 - - - - - - - - 4
3 - - - - - - - - 3
2 P P P P P P P P 2
1 R N B Q K B N R 1
a b c d e f g h
",
)?;
// creating board from String representation without decoration
board = ChessBoard::from_string(
"
r n b q k b n r
p p p p p p p p
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
P P P P P P P P
R N B Q K B N R
",
)?;
println!("{}", board);
Ok(())
}
输出
a b c d e f g h 8 r n b q k b n r 8 7 p p p p p p p p 7 6 - - - - - - - - 6 5 - - - - - - - - 5 4 - - - - - - - - 4 3 - - - - - - - - 3 2 P P P P P P P P 2 1 R N B Q K B N R 1 a b c d e f g h
生成攻击和移动
参见: ChessProgramming Pseudo Legal Move
use chessgen::{ChessBoard, Color};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let board = ChessBoard::from_string(
"
- - - - q - - k
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - B - - -
- - - Q K - - -
",
)?;
// attacks
println!("Attacks:\n{}", board.attacks(Color::Black));
// legal moves
print!("Legal moves: ");
for m in board.legal_moves() {
print!("{} ", m)
}
println!("\n");
// pseudo legal moves
print!("Pseudo legal moves: ");
board.moves(&mut |m| print!("{} ", m));
println!("\n");
Ok(())
}
输出
Attacks: a b c d e f g h 8 x x x x - x x x 8 7 - - - x x x x x 7 6 - - x - x - x - 6 5 - x - - x - - x 5 4 x - - - x - - - 4 3 - - - - x - - - 3 2 - - - - x - - - 2 1 - - - - - - - - 1 a b c d e f g h Legal moves: d1a1 d1b1 d1c1 d1d2 d1d3 d1d4 d1d5 d1d6 d1d7 d1d8 d1c2 d1b3 d1a4 e1f1 e1d2 e1f2 Pseudo legal moves: d1a1 d1b1 d1c1 d1d2 d1d3 d1d4 d1d5 d1d6 d1d7 d1d8 e2f1 e2d3 e2f3 e2c4 e2g4 e2b5 e2h5 e2a6 d1c2 d1b3 d1a4 e1f1 e1d2 e1f2
应用移动
use chessgen::{ChessBoard, Move};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut board = ChessBoard::from_string(
"
- - - - q - - k
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - B - - -
- - - Q K - - -
",
)?;
board = board.validate_and_apply_move(&Move::from_string("d1d8")?)?;
println!("{}", board);
Ok(())
}
输出
a b c d e f g h 8 - - - Q q - - k 8 7 - - - - - - - - 7 6 - - - - - - - - 6 5 - - - - - - - - 5 4 - - - - - - - - 4 3 - - - - - - - - 3 2 - - - - B - - - 2 1 - - - - K - - - 1 a b c d e f g h
运行 PerfT
use chessgen::{ChessBoard, PerfT};
use std::time::Instant;
const CACHE_SIZE: usize = 64 * 1024 * 1024;
/// Run PerfT at specific board and depth.
fn main() -> Result<(), Box<dyn std::error::Error>> {
let board = ChessBoard::STANDARD;
let depth = 5;
let start = Instant::now();
let count = PerfT::new(CACHE_SIZE).perft_n(&board, depth);
let duration = start.elapsed();
println!("perfT finished:");
println!(" FEN: {}", board.to_fen());
println!(" depth: {}", depth);
println!(" count: {}", count);
println!(" time: {:?}", duration);
Ok(())
}
输出
perfT finished: FEN: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 depth: 5 count: 4865609 time: 1.363183325s
显示棋盘
您可以使用以下方式实现自定义的棋盘显示:
ChessBoard::piece_at()
use chessgen::{ChessBoard, Index};
/// Run PerfT at specific board and depth.
fn main() -> Result<(), Box<dyn std::error::Error>> {
let board = ChessBoard::STANDARD;
for i in Index::ALL_FIELDS {
if i.index > 0 && i.rank() != Index::new(i.index - 1).rank() {
println!();
}
let file = i.file();
let rank = i.rank();
// translate ranks for a display as rank 0 is the most bottom rank
let translated_i = Index::from_rank_and_file(7 - rank, file);
let output = match board.piece_at(translated_i) {
Some((color, piece)) => piece.to_char(color),
None => '-',
};
print!(" {} ", output);
}
println!();
Ok(())
}
输出
r n b q k b n r p p p p p p p p - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - P P P P P P P P R N B Q K B N R