3 个版本
0.1.2 | 2021 年 9 月 27 日 |
---|---|
0.1.1 | 2021 年 9 月 27 日 |
0.1.0 | 2021 年 1 月 27 日 |
158 在 游戏 中
每月 38 次下载
在 dune 中使用
2MB
2K SLoC
包含 (WOFF 字体, 99KB) fontawesome-webfont.woff, (WOFF 字体, 78KB) fontawesome-webfont.woff2, (WOFF 字体, 45KB) open-sans-v17-all-charsets-300.woff2, (WOFF 字体, 41KB) open-sans-v17-all-charsets-300italic.woff2, (WOFF 字体, 45KB) open-sans-v17-all-charsets-600.woff2, (WOFF 字体, 43KB) open-sans-v17-all-charsets-600italic.woff2 等 更多.
为什么编写一个棋引擎?
首先,Tom7 的这个视频 是这个项目的灵感来源。他非常出色,我强烈建议您观看他的内容。
我非常喜欢棋。它绝对是我最喜欢的游戏之一。然而,当我尝试编写数字下棋的程序(尤其是在编译型语言中)时,我总是感到失望。尽管存在几个惊人的引擎,但几乎找不到一个整洁的库来用于棋类编程,并且能在所有设备上运行。
chess-engine 是我对这个问题的解决方案。如果您需要一个可以在嵌入式设备、终端、桌面(带图形界面) 和 网络 上运行的棋引擎,这可能就是您的最佳选择。
它是如何工作的?
这个特定的 AI(以及大多数其他棋类 AI)使用 Minimax 算法,并结合 Alpha-Beta 剪枝 进行优化。
现在,让我们来分解一下。
Minimax 算法本质上会递归地遍历所有可能的走法,并在走法执行后评估所有棋盘。如果一个棋盘更倾向于积极,它将 鼓励 执行其父节点走法,但如果一个棋盘倾向于消极,它将 反对 执行给定的走法。
此外,当AI试图看到除了当前棋盘之外的棋局时,它将假设人类玩家总是做出最佳走法。因此,电脑几乎从不犯错。这使得电脑几乎总是比玩家做出更客观的走法。
嵌入网页
因为它没有依赖项,所以使用wasm将其嵌入网页浏览器非常简单。试试自己玩吧!
用法
Board
结构有几个不同的方法,允许用户从给定位置生成走法,包括get_best_next_move
、get_worst_next_move
和get_legal_moves
。这些方法特别适用于编写与AI对战的棋类AI。
fn main() {
let board = Board::default();
// Get the best move with 4 moves of lookahead
let best_move = board.get_best_next_move(4);
// Get the worst move with 3 moves of lookahead
let worst_move = board.get_worst_next_move(3);
// Get all of the possible legal moves for the given player
let legal_moves = board.get_legal_moves();
// Print the board
println!("{}", board);
print!("CPU chose to ");
match best_move {
Move::Piece(from, to) => println!("move {} to {}", from, to),
Move::KingSideCastle => println!("castle kingside"),
Move::QueenSideCastle => println!("castle queenside"),
Move::Resign => println!("resign")
}
}
为了增加变化或更高级的玩法,可以考虑编写一个AI,它首先使用已知的开局来构建更好的位置,然后再使用get_best_next_move
方法!
自定义棋盘
此外,用户可以创建自己的自定义Board
对象,而不仅仅是默认对象。这是通过使用BoardBuilder
结构来完成的。BoardBuilder
结构支持启用和禁用王车易位,放置行和列的棋子,以及放置单个棋子。
请注意,在使用BoardBuilder
时,王车易位默认是禁用的!
fn main() {
// `BoardBuilder::new()` returns an empty board
// with castling disabled.
// Creating a board builder from another board
// structure will preserve
// all settings from the board (such as castling
// and the last en-passant move).
// This BoardBuilder constructs the "Horde" chess variant!
let board = BoardBuilder::from(Board::default())
.row(Piece::Pawn(WHITE, A1))
.row(Piece::Pawn(WHITE, A2))
.row(Piece::Pawn(WHITE, A3))
.row(Piece::Pawn(WHITE, A4))
.piece(Piece::Pawn(WHITE, F5))
.piece(Piece::Pawn(WHITE, G5))
.piece(Piece::Pawn(WHITE, B5))
.piece(Piece::Pawn(WHITE, C5))
.build();
// The CPU can also play variants!
let cpu_move = board.get_best_next_move(3);
match board.play_move(cpu_move) {
GameResult::Continuing(next_board) => {
println!("{}", next_board);
}
GameResult::Victory(winner) => {
// You can use the ! operator on a player's
// color to invert.
println!("{} loses. {} is victorious.",
!winner, winner
);
}
GameResult::IllegalMove(x) => {
eprintln!("{} is an illegal move.", x);
}
GameResult::Stalemate => {
println!("Drawn game.");
}
}
}