3 个版本

0.9.4 2023年9月19日
0.9.3 2023年9月17日
0.9.2 2023年9月17日
0.9.1 2023年9月17日

游戏开发 中排名 #317

MIT 许可证

170KB
2.5K SLoC

chessgen

License Cargo Documentation

一个简单快速的棋子移动生成器。

这是我将棋子移动生成器用新的编程语言重写的下一版,也可能是最后一版。

自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 

依赖项