Compare commits
26 commits
Author | SHA1 | Date | |
---|---|---|---|
dece | c5d07db007 | ||
dece | 94bd4ec01c | ||
dece | 53a96f8787 | ||
dece | 47cc483d9e | ||
dece | 54cfd43911 | ||
dece | 7ff52bc2b5 | ||
dece | 85d5ba1a62 | ||
dece | 3d5bbb8d2c | ||
dece | 97444db39c | ||
dece | 5a6893acc7 | ||
dece | 9595d0f435 | ||
dece | ce62d3ab3a | ||
dece | 5efdd5407b | ||
dece | 8b0f4c9255 | ||
dece | b62dd1c076 | ||
dece | d958e617d0 | ||
dece | fce38693bf | ||
dece | b4dd16d87d | ||
dece | 5b678dd595 | ||
dece | 4cea0e34e9 | ||
dece | 91e1fbbe21 | ||
dece | e4d2b20e23 | ||
dece | e114138a48 | ||
dece | 9c6327ec91 | ||
dece | ea58c72436 | ||
dece | 8485714a24 |
80
Cargo.lock
generated
80
Cargo.lock
generated
|
@ -1,13 +1,5 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
|
@ -31,11 +23,6 @@ name = "bitflags"
|
|||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.1"
|
||||
|
@ -50,44 +37,6 @@ dependencies = [
|
|||
"vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random-macro"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "3.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ahash 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.13"
|
||||
|
@ -101,20 +50,6 @@ name = "libc"
|
|||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
|
@ -138,7 +73,6 @@ name = "vatu"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dashmap 3.11.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -146,11 +80,6 @@ name = "vec_map"
|
|||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
|
@ -171,25 +100,16 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum ahash 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
|
||||
"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
|
||||
"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
|
||||
"checksum dashmap 3.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8cfcd41ae02d60edded204341d2798ba519c336c51a37330aa4b98a1128def32"
|
||||
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
"checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
|
||||
"checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
|
||||
"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
|
||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
|
|
@ -6,4 +6,3 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
dashmap = "3.11"
|
||||
|
|
18
README.md
18
README.md
|
@ -67,6 +67,8 @@ cp external/lichess-bot/config.yml.example /tmp/vatu-config/config.yml
|
|||
docker build -f res/docker/Dockerfile -t vatu .
|
||||
# Run with the config folder mounted at /config.
|
||||
docker run -v /tmp/vatu-config:/config -ti vatu
|
||||
# In the container, use the following command:
|
||||
python lichess-bot.py --config /config/config.yml
|
||||
```
|
||||
|
||||
|
||||
|
@ -74,10 +76,12 @@ docker run -v /tmp/vatu-config:/config -ti vatu
|
|||
TODO
|
||||
----
|
||||
|
||||
- Support time constraints
|
||||
- Proper unmake mechanism instead of allocating boards like there is no tomorrow
|
||||
- Precompute some pieces moves, maybe
|
||||
- Transposition table that does not actually slows search down
|
||||
- Check Zobrist hashes for previous point
|
||||
- Actual bitboard
|
||||
- Multithreading (never)
|
||||
- [X] Support time constraints
|
||||
- [X] Unmake mechanism instead of allocating nodes like there is no tomorrow
|
||||
- [X] Precompute some pieces moves, maybe (done for knights)
|
||||
- [ ] Transposition table that does not actually slows search down
|
||||
- [ ] Check Zobrist hashes for previous point
|
||||
- [X] Actual bitboard
|
||||
- [ ] Some kind of move ordering could be great
|
||||
- [ ] Multithreading (never)
|
||||
- [ ] Avoid 3-fold repetitions when winning
|
||||
|
|
30
res/scripts/gen_king_rays.py
Executable file
30
res/scripts/gen_king_rays.py
Executable file
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Pre-compute king ras bitboards for each square."""
|
||||
|
||||
TEMPLATE = """\
|
||||
/// Pre-computed king rays.
|
||||
pub const KING_RAYS: [Bitboard; 64] = [
|
||||
{}
|
||||
];
|
||||
"""
|
||||
|
||||
DIRS = [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)]
|
||||
|
||||
def bit_pos(square):
|
||||
return 1 << square
|
||||
|
||||
def get_rays():
|
||||
rays = []
|
||||
for f in range(8):
|
||||
for r in range(8):
|
||||
bitboard = 0
|
||||
for dir_f, dir_r in DIRS:
|
||||
ray_f = f + dir_f
|
||||
ray_r = r + dir_r
|
||||
if ray_f < 0 or ray_f > 7 or ray_r < 0 or ray_r > 7:
|
||||
continue
|
||||
bitboard |= bit_pos(ray_f * 8 + ray_r)
|
||||
rays.append(" 0b{:064b},".format(bitboard))
|
||||
return rays
|
||||
|
||||
print(TEMPLATE.format("\n".join(get_rays())))
|
30
res/scripts/gen_knight_rays.py
Executable file
30
res/scripts/gen_knight_rays.py
Executable file
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Pre-compute knight ray bitboards for each square."""
|
||||
|
||||
TEMPLATE = """\
|
||||
/// Pre-computed knight rays.
|
||||
pub const KNIGHT_RAYS: [Bitboard; 64] = [
|
||||
{}
|
||||
];
|
||||
"""
|
||||
|
||||
DIRS = [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1, 2)]
|
||||
|
||||
def bit_pos(square):
|
||||
return 1 << square
|
||||
|
||||
def get_rays():
|
||||
rays = []
|
||||
for f in range(8):
|
||||
for r in range(8):
|
||||
bitboard = 0
|
||||
for dir_f, dir_r in DIRS:
|
||||
ray_f = f + dir_f
|
||||
ray_r = r + dir_r
|
||||
if ray_f < 0 or ray_f > 7 or ray_r < 0 or ray_r > 7:
|
||||
continue
|
||||
bitboard |= bit_pos(ray_f * 8 + ray_r)
|
||||
rays.append(" 0b{:064b},".format(bitboard))
|
||||
return rays
|
||||
|
||||
print(TEMPLATE.format("\n".join(get_rays())))
|
39
res/scripts/gen_pawn_captures.py
Executable file
39
res/scripts/gen_pawn_captures.py
Executable file
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Pre-compute pawn captures bitboards for each square."""
|
||||
|
||||
TEMPLATE = """\
|
||||
/// Pre-computed pawn captures.
|
||||
pub const PAWN_CAPTURES: [[Bitboard; 64]; 2] = [
|
||||
[
|
||||
{}
|
||||
],
|
||||
[
|
||||
{}
|
||||
],
|
||||
];
|
||||
"""
|
||||
|
||||
def bit_pos(square):
|
||||
return 1 << square
|
||||
|
||||
def get_captures():
|
||||
both_captures = []
|
||||
for direction in [1, -1]:
|
||||
captures = []
|
||||
for f in range(8):
|
||||
for r in range(8):
|
||||
bitboard = 0
|
||||
prog_r = r + direction
|
||||
if 0 < prog_r < 7:
|
||||
prev_f = f - 1
|
||||
if prev_f >= 0:
|
||||
bitboard |= bit_pos(prev_f * 8 + prog_r)
|
||||
next_f = f + 1
|
||||
if next_f <= 7:
|
||||
bitboard |= bit_pos(next_f * 8 + prog_r)
|
||||
captures.append(" 0b{:064b},".format(bitboard))
|
||||
both_captures.append(captures)
|
||||
return both_captures
|
||||
|
||||
CAPTURES = get_captures()
|
||||
print(TEMPLATE.format("\n".join(CAPTURES[0]), "\n".join(CAPTURES[1])))
|
38
res/scripts/gen_pawn_progresses.py
Executable file
38
res/scripts/gen_pawn_progresses.py
Executable file
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Pre-compute pawn progress bitboards for each square."""
|
||||
|
||||
TEMPLATE = """\
|
||||
/// Pre-computed pawn progresses.
|
||||
pub const PAWN_PROGRESSES: [[Bitboard; 64]; 2] = [
|
||||
[
|
||||
{}
|
||||
],
|
||||
[
|
||||
{}
|
||||
],
|
||||
];
|
||||
"""
|
||||
|
||||
def bit_pos(square):
|
||||
return 1 << square
|
||||
|
||||
def get_progresses():
|
||||
both_progresses = []
|
||||
for direction in [1, -1]:
|
||||
progresses = []
|
||||
for f in range(8):
|
||||
for r in range(8):
|
||||
bitboard = 0
|
||||
if 0 < r < 7:
|
||||
prog_r = r + direction
|
||||
bitboard |= bit_pos(f * 8 + prog_r)
|
||||
if direction == 1 and r == 1:
|
||||
bitboard |= bit_pos(f * 8 + prog_r + 1)
|
||||
elif direction == -1 and r == 6:
|
||||
bitboard |= bit_pos(f * 8 + (prog_r - 1))
|
||||
progresses.append(" 0b{:064b},".format(bitboard))
|
||||
both_progresses.append(progresses)
|
||||
return both_progresses
|
||||
|
||||
PROGRESSES = get_progresses()
|
||||
print(TEMPLATE.format("\n".join(PROGRESSES[0]), "\n".join(PROGRESSES[1])))
|
5
res/scripts/gen_squares.py
Executable file
5
res/scripts/gen_squares.py
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
for f in range(8):
|
||||
for r in range(8):
|
||||
print("pub const {}{}: Pos = {};".format(chr(f + 65), r + 1, f * 8 + r))
|
|
@ -7,7 +7,6 @@ use crate::board;
|
|||
use crate::engine;
|
||||
use crate::movement::Move;
|
||||
use crate::node::Node;
|
||||
use crate::notation;
|
||||
use crate::rules;
|
||||
use crate::stats;
|
||||
|
||||
|
@ -50,6 +49,20 @@ pub struct AnalysisParams {
|
|||
pub black_time: i32,
|
||||
pub white_inc: i32,
|
||||
pub black_inc: i32,
|
||||
pub focus: Option<Move>,
|
||||
}
|
||||
|
||||
impl AnalysisParams {
|
||||
pub fn new() -> AnalysisParams {
|
||||
AnalysisParams {
|
||||
move_time: -1,
|
||||
white_time: -1,
|
||||
black_time: -1,
|
||||
white_inc: -1,
|
||||
black_inc: -1,
|
||||
focus: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Analysis info to report.
|
||||
|
@ -104,27 +117,27 @@ impl Analyzer {
|
|||
|
||||
if self.debug {
|
||||
self.log(format!("Analyzing node:\n{}", &self.node));
|
||||
let moves = self.node.get_player_moves(true);
|
||||
self.log(format!("Legal moves: {}", notation::move_list_to_string(&moves)));
|
||||
let moves = self.node.get_player_moves();
|
||||
self.log(format!("Legal moves: {}", Move::list_to_uci_string(&moves)));
|
||||
if let Some(focus) = &args.focus {
|
||||
self.log(format!("Focus on: {}", focus.to_uci_string()));
|
||||
}
|
||||
self.log(format!("Move time: {}", self.time_limit));
|
||||
}
|
||||
|
||||
self.start_time = Some(Instant::now());
|
||||
self.current_per_second_timer = Some(Instant::now());
|
||||
let (max_score, best_move) = self.negamax(&self.node.clone(), MIN_F32, MAX_F32, 0);
|
||||
let (max_score, best_move) = self.negamax(&mut self.node.clone(), MIN_F32, MAX_F32, 0);
|
||||
|
||||
if best_move.is_some() {
|
||||
let log_str = format!(
|
||||
"Best move {} evaluated {}",
|
||||
notation::move_to_string(&best_move.unwrap()), max_score
|
||||
);
|
||||
if let Some(m) = best_move {
|
||||
let log_str = format!("Best move {} evaluated {}", m.to_uci_string(), max_score);
|
||||
self.log(log_str);
|
||||
self.report_best_move(best_move);
|
||||
self.report_best_move(Some(m));
|
||||
} else {
|
||||
// If no best move could be found, checkmate is unavoidable; send the first legal move.
|
||||
self.log("Checkmate is unavoidable.".to_string());
|
||||
let moves = rules::get_player_moves(&self.node.board, &self.node.game_state, true);
|
||||
let m = if moves.len() > 0 { Some(moves[0]) } else { None };
|
||||
let moves = rules::get_player_moves(&mut self.node.board, &mut self.node.game_state);
|
||||
let m = if moves.len() > 0 { Some(moves[0].clone()) } else { None };
|
||||
self.report_best_move(m);
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +148,7 @@ impl Analyzer {
|
|||
self.time_limit = if args.move_time != -1 {
|
||||
args.move_time
|
||||
} else {
|
||||
let (time, inc) = if board::is_white(self.node.game_state.color) {
|
||||
let (time, inc) = if self.node.game_state.color == board::WHITE {
|
||||
(args.white_time, args.white_inc)
|
||||
} else {
|
||||
(args.black_time, args.black_inc)
|
||||
|
@ -162,7 +175,7 @@ impl Analyzer {
|
|||
/// lower score bound and `beta` the upper bound.
|
||||
fn negamax(
|
||||
&mut self,
|
||||
node: &Node,
|
||||
node: &mut Node,
|
||||
alpha: f32,
|
||||
beta: f32,
|
||||
depth: u32,
|
||||
|
@ -189,18 +202,18 @@ impl Analyzer {
|
|||
}
|
||||
|
||||
// Get negamax for playable moves.
|
||||
let moves = node.get_player_moves(true);
|
||||
let mut moves = node.get_player_moves();
|
||||
let mut alpha = alpha;
|
||||
let mut best_score = MIN_F32;
|
||||
let mut best_move = None;
|
||||
for m in moves {
|
||||
let mut sub_node = node.clone();
|
||||
sub_node.apply_move(&m);
|
||||
let result = self.negamax(&sub_node, -beta, -alpha, depth + 1);
|
||||
for m in &mut moves {
|
||||
node.apply_move(m);
|
||||
let result = self.negamax(node, -beta, -alpha, depth + 1);
|
||||
node.unmake_move(m);
|
||||
let score = -result.0;
|
||||
if score > best_score {
|
||||
best_score = score;
|
||||
best_move = Some(m);
|
||||
best_move = Some(m.to_owned());
|
||||
}
|
||||
if best_score > alpha {
|
||||
alpha = best_score;
|
||||
|
|
1009
src/board.rs
1009
src/board.rs
File diff suppressed because it is too large
Load diff
|
@ -1,19 +1,53 @@
|
|||
//! Castling flags.
|
||||
|
||||
pub const CASTLING_WH_K: u8 = 0b00000001;
|
||||
pub const CASTLING_WH_Q: u8 = 0b00000010;
|
||||
pub const CASTLING_WH_MASK: u8 = 0b00000011;
|
||||
pub const CASTLING_BL_K: u8 = 0b00000100;
|
||||
pub const CASTLING_BL_Q: u8 = 0b00001000;
|
||||
pub const CASTLING_BL_MASK: u8 = 0b00001100;
|
||||
pub const CASTLING_K_MASK: u8 = 0b00000101;
|
||||
pub const CASTLING_Q_MASK: u8 = 0b00001010;
|
||||
pub const CASTLING_MASK: u8 = 0b00001111;
|
||||
use crate::board::{Bitboard, RANK_1, RANK_8};
|
||||
|
||||
/// Castling sides parameters.
|
||||
pub type Castle = u8;
|
||||
|
||||
pub const CASTLE_WH_K: Castle = 0b00000001;
|
||||
pub const CASTLE_WH_Q: Castle = 0b00000010;
|
||||
pub const CASTLE_WH_MASK: Castle = 0b00000011;
|
||||
pub const CASTLE_BL_K: Castle = 0b00000100;
|
||||
pub const CASTLE_BL_Q: Castle = 0b00001000;
|
||||
pub const CASTLE_BL_MASK: Castle = 0b00001100;
|
||||
pub const CASTLE_K_MASK: Castle = 0b00000101;
|
||||
pub const CASTLE_Q_MASK: Castle = 0b00001010;
|
||||
pub const CASTLE_MASK: Castle = 0b00001111;
|
||||
|
||||
/// Index castling masks with their color.
|
||||
pub const CASTLE_MASK_BY_COLOR: [Castle; 2] = [CASTLE_WH_MASK, CASTLE_BL_MASK];
|
||||
|
||||
/// Index castling ranks with their color.
|
||||
pub const CASTLE_RANK_BY_COLOR: [i8; 2] = [RANK_1, RANK_8];
|
||||
|
||||
pub const CASTLE_SIDE_K: usize = 0;
|
||||
pub const CASTLE_SIDE_Q: usize = 1;
|
||||
pub const NUM_CASTLE_SIDES: usize = 2;
|
||||
|
||||
/// Index castling sides using CASTLE_SIDE_K and CASTLE_SIDE_Q.
|
||||
pub const CASTLE_SIDES: [Castle; 2] = [CASTLE_K_MASK, CASTLE_Q_MASK];
|
||||
|
||||
/// Castle paths that must not be under attack, by color and side.
|
||||
///
|
||||
/// For both sides, the 3-uple contains files that should be empty
|
||||
/// and not attacked, an optional file that should be empty for
|
||||
/// queen-side, and the castling side-mask.
|
||||
pub const CASTLING_SIDES: [([i8; 2], Option<i8>, u8); 2] =
|
||||
[([5i8, 6i8], None, CASTLING_K_MASK), ([3i8, 2i8], Some(1i8), CASTLING_Q_MASK)];
|
||||
/// This includes the original king position, its target square and
|
||||
/// the square in between.
|
||||
pub const CASTLE_LEGALITY_PATHS: [[Bitboard; 2]; 2] = [
|
||||
[
|
||||
0b00000000_00000001_00000001_00000001_00000000_00000000_00000000_00000000, // White Kside.
|
||||
0b00000000_00000000_00000000_00000001_00000001_00000001_00000000_00000000, // White Qside.
|
||||
], [
|
||||
0b00000000_10000000_10000000_10000000_00000000_00000000_00000000_00000000, // Black Kside.
|
||||
0b00000000_00000000_00000000_10000000_10000000_10000000_00000000_00000000, // Black Qside.
|
||||
]
|
||||
];
|
||||
|
||||
/// Castle paths that must be empty.
|
||||
pub const CASTLE_MOVE_PATHS: [[Bitboard; 2]; 2] = [
|
||||
[
|
||||
0b00000000_00000001_00000001_00000000_00000000_00000000_00000000_00000000, // White Kside.
|
||||
0b00000000_00000000_00000000_00000000_00000001_00000001_00000001_00000000, // White Qside.
|
||||
], [
|
||||
0b00000000_10000000_10000000_00000000_00000000_00000000_00000000_00000000, // Black Kside.
|
||||
0b00000000_00000000_00000000_00000000_10000000_10000000_10000000_00000000, // Black Qside.
|
||||
]
|
||||
];
|
||||
|
|
|
@ -11,9 +11,9 @@ use std::thread;
|
|||
use crate::analysis;
|
||||
use crate::board;
|
||||
use crate::castling;
|
||||
use crate::movement::{self, Move};
|
||||
use crate::fen;
|
||||
use crate::movement::Move;
|
||||
use crate::node::Node;
|
||||
use crate::notation;
|
||||
use crate::uci;
|
||||
|
||||
/// Analysis engine.
|
||||
|
@ -62,6 +62,10 @@ pub enum Cmd {
|
|||
WorkerInfo(Vec<analysis::AnalysisInfo>),
|
||||
/// Send best move found by analysis worker.
|
||||
WorkerBestMove(Option<Move>),
|
||||
/// Log current node.
|
||||
LogNode,
|
||||
/// Log debug information about a move.
|
||||
LogMove(Move),
|
||||
|
||||
// Commands that can be sent by the engine.
|
||||
|
||||
|
@ -123,7 +127,10 @@ impl Engine {
|
|||
// Workers commands.
|
||||
Cmd::Log(s) => self.reply(Cmd::Log(s.to_string())),
|
||||
Cmd::WorkerInfo(infos) => self.reply(Cmd::Info(infos.to_vec())),
|
||||
Cmd::WorkerBestMove(m) => self.reply(Cmd::BestMove(*m)),
|
||||
Cmd::WorkerBestMove(m) => self.reply(Cmd::BestMove(m.clone())),
|
||||
// Other commands.
|
||||
Cmd::LogNode => self.log_node(),
|
||||
Cmd::LogMove(m) => self.log_move(m),
|
||||
_ => eprintln!("Not an engine input command: {:?}", cmd),
|
||||
}
|
||||
}
|
||||
|
@ -141,29 +148,30 @@ impl Engine {
|
|||
/// Apply a FEN string to the engine state, replacing it.
|
||||
///
|
||||
/// For speed purposes, it assumes values are always valid.
|
||||
fn apply_fen(&mut self, fen: ¬ation::Fen) {
|
||||
fn apply_fen(&mut self, fen: &fen::Fen) {
|
||||
// Placement.
|
||||
self.node.board = board::new_from_fen(&fen.placement);
|
||||
self.node.board = board::Board::new_from_fen(&fen.placement);
|
||||
// Color.
|
||||
match fen.color.chars().next().unwrap() {
|
||||
'w' => self.node.game_state.color = board::SQ_WH,
|
||||
'b' => self.node.game_state.color = board::SQ_BL,
|
||||
'w' => self.node.game_state.color = board::WHITE,
|
||||
'b' => self.node.game_state.color = board::BLACK,
|
||||
_ => {}
|
||||
};
|
||||
// Castling.
|
||||
self.node.game_state.castling = 0;
|
||||
for c in fen.castling.chars() {
|
||||
match c {
|
||||
'K' => self.node.game_state.castling |= castling::CASTLING_WH_K,
|
||||
'Q' => self.node.game_state.castling |= castling::CASTLING_WH_Q,
|
||||
'k' => self.node.game_state.castling |= castling::CASTLING_BL_K,
|
||||
'q' => self.node.game_state.castling |= castling::CASTLING_BL_Q,
|
||||
'K' => self.node.game_state.castling |= castling::CASTLE_WH_K,
|
||||
'Q' => self.node.game_state.castling |= castling::CASTLE_WH_Q,
|
||||
'k' => self.node.game_state.castling |= castling::CASTLE_BL_K,
|
||||
'q' => self.node.game_state.castling |= castling::CASTLE_BL_Q,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// En passant.
|
||||
self.node.game_state.en_passant = match fen.en_passant.as_ref() {
|
||||
"-" => None,
|
||||
p => Some(board::pos(p)),
|
||||
s => Some(board::sq_from_string(s)),
|
||||
};
|
||||
// Half moves.
|
||||
self.node.game_state.halfmove = fen.halfmove.parse::<i32>().ok().unwrap();
|
||||
|
@ -172,13 +180,8 @@ impl Engine {
|
|||
}
|
||||
|
||||
/// Apply a series of moves to the current node.
|
||||
fn apply_moves(&mut self, moves: &Vec<Move>) {
|
||||
moves.iter().for_each(|m| self.apply_move(m));
|
||||
}
|
||||
|
||||
/// Apply a move to the current node.
|
||||
fn apply_move(&mut self, m: &Move) {
|
||||
movement::apply_move_to(&mut self.node.board, &mut self.node.game_state, m);
|
||||
fn apply_moves(&mut self, moves: &mut Vec<Move>) {
|
||||
moves.iter_mut().for_each(|m| m.apply_to(&mut self.node.board, &mut self.node.game_state));
|
||||
}
|
||||
|
||||
/// Start working on board, returning the best move found.
|
||||
|
@ -200,10 +203,7 @@ impl Engine {
|
|||
fn stop(&mut self) {
|
||||
self.working.store(false, atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
/// UCI commands management.
|
||||
impl Engine {
|
||||
/// Setup engine for UCI communication.
|
||||
pub fn setup_uci(&mut self, uci_s: mpsc::Sender<uci::Cmd>) {
|
||||
// Create a channel to receive commands from Uci.
|
||||
|
@ -221,11 +221,11 @@ impl Engine {
|
|||
self.apply_fen(&fen);
|
||||
},
|
||||
uci::PositionArgs::Startpos => {
|
||||
let fen = notation::parse_fen(notation::FEN_START).unwrap();
|
||||
let fen = fen::parse_fen(fen::FEN_START).unwrap();
|
||||
self.apply_fen(&fen);
|
||||
},
|
||||
uci::PositionArgs::Moves(moves) => {
|
||||
self.apply_moves(&moves);
|
||||
self.apply_moves(&mut moves.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -233,13 +233,7 @@ impl Engine {
|
|||
|
||||
/// Start working using parameters passed with a "go" command.
|
||||
fn uci_go(&mut self, g_args: &Vec<uci::GoArgs>) {
|
||||
let mut args = analysis::AnalysisParams {
|
||||
move_time: -1,
|
||||
white_time: -1,
|
||||
black_time: -1,
|
||||
white_inc: -1,
|
||||
black_inc: -1,
|
||||
};
|
||||
let mut args = analysis::AnalysisParams::new();
|
||||
for arg in g_args {
|
||||
match arg {
|
||||
uci::GoArgs::MoveTime(ms) => args.move_time = *ms,
|
||||
|
@ -253,4 +247,22 @@ impl Engine {
|
|||
}
|
||||
self.work(&args);
|
||||
}
|
||||
|
||||
/// Log current node information.
|
||||
fn log_node(&mut self) {
|
||||
let mut s = vec!();
|
||||
self.node.board.draw_to(&mut s);
|
||||
self.reply(Cmd::Log(format!(
|
||||
"Current node:\n{}{}",
|
||||
String::from_utf8_lossy(&s),
|
||||
self.node.game_state
|
||||
)));
|
||||
}
|
||||
|
||||
/// Log a move information.
|
||||
fn log_move(&mut self, m: &Move) {
|
||||
let mut args = analysis::AnalysisParams::new();
|
||||
args.focus = Some(m.clone());
|
||||
self.work(&args);
|
||||
}
|
||||
}
|
||||
|
|
55
src/fen.rs
Normal file
55
src/fen.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
//! Functions to parse FEN strings.
|
||||
|
||||
use crate::board;
|
||||
|
||||
pub const FEN_START: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
/// FEN notation for positions, split into fields.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Fen {
|
||||
pub placement: String,
|
||||
pub color: String,
|
||||
pub castling: String,
|
||||
pub en_passant: String,
|
||||
pub halfmove: String,
|
||||
pub fullmove: String,
|
||||
}
|
||||
|
||||
pub fn parse_fen(i: &str) -> Option<Fen> {
|
||||
let fields: Vec<&str> = i.split_whitespace().collect();
|
||||
parse_fen_fields(&fields)
|
||||
}
|
||||
|
||||
pub fn parse_fen_fields(fields: &[&str]) -> Option<Fen> {
|
||||
if fields.len() < 6 {
|
||||
return None
|
||||
}
|
||||
Some(Fen {
|
||||
placement: fields[0].to_string(),
|
||||
color: fields[1].to_string(),
|
||||
castling: fields[2].to_string(),
|
||||
en_passant: fields[3].to_string(),
|
||||
halfmove: fields[4].to_string(),
|
||||
fullmove: fields[5].to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn en_passant_to_string(ep: Option<board::Square>) -> String {
|
||||
ep.and_then(|p| Some(board::sq_to_string(p))).unwrap_or("-".to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_fen() {
|
||||
let fen_start = parse_fen(FEN_START).unwrap();
|
||||
assert_eq!(&fen_start.placement, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR");
|
||||
assert_eq!(&fen_start.color, "w");
|
||||
assert_eq!(&fen_start.castling, "KQkq");
|
||||
assert_eq!(&fen_start.en_passant, "-");
|
||||
assert_eq!(&fen_start.halfmove, "0");
|
||||
assert_eq!(&fen_start.fullmove, "1");
|
||||
}
|
||||
}
|
|
@ -4,9 +4,10 @@ pub mod analysis;
|
|||
pub mod board;
|
||||
pub mod castling;
|
||||
pub mod engine;
|
||||
pub mod fen;
|
||||
pub mod movement;
|
||||
pub mod node;
|
||||
pub mod notation;
|
||||
pub mod precomputed;
|
||||
pub mod rules;
|
||||
pub mod stats;
|
||||
pub mod uci;
|
||||
|
|
473
src/movement.rs
473
src/movement.rs
|
@ -1,230 +1,333 @@
|
|||
//! Move functions along with some castling helpers.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::board::*;
|
||||
use crate::castling::*;
|
||||
use crate::rules;
|
||||
|
||||
const START_WH_K_POS: Pos = pos("e1");
|
||||
const START_BL_K_POS: Pos = pos("e8");
|
||||
use crate::rules::GameState;
|
||||
|
||||
/// A movement, with before/after positions and optional promotion.
|
||||
pub type Move = (Pos, Pos, Option<u8>);
|
||||
|
||||
/// Apply a move `m` to copies to `board` and `game_state`.
|
||||
///
|
||||
/// Can be used for conveniance but it's better to write in existing
|
||||
/// instances as often as possible using `apply_move_to`.
|
||||
pub fn apply_move(
|
||||
board: &Board,
|
||||
game_state: &rules::GameState,
|
||||
m: &Move
|
||||
) -> (Board, rules::GameState) {
|
||||
let mut new_board = board.clone();
|
||||
let mut new_state = game_state.clone();
|
||||
apply_move_to(&mut new_board, &mut new_state, m);
|
||||
(new_board, new_state)
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Move {
|
||||
/// Square from which a piece moves.
|
||||
pub source: Square,
|
||||
/// Square to which a piece moves.
|
||||
pub dest: Square,
|
||||
/// Promotion piece for pawns reaching the last rank.
|
||||
pub promotion: Option<Piece>,
|
||||
/// Captured piece, if any.
|
||||
pub capture: Option<Piece>,
|
||||
/// Castle options before the move. This is set when the move is first applied.
|
||||
pub old_castles: Castle,
|
||||
}
|
||||
|
||||
/// Update `board` and `game_state` to reflect the move `m`.
|
||||
///
|
||||
/// The board is updated with correct piece placement.
|
||||
///
|
||||
/// The game state is updated with the new player turn and the new
|
||||
/// castling options.
|
||||
pub fn apply_move_to(
|
||||
board: &mut Board,
|
||||
game_state: &mut rules::GameState,
|
||||
m: &Move
|
||||
) {
|
||||
// If a rook is taken, remove its castling option. Needs to be checked before we update board.
|
||||
if m.1 == pos("a1") && get_square(board, &pos("a1")) == SQ_WH_R {
|
||||
game_state.castling &= !CASTLING_WH_Q;
|
||||
} else if m.1 == pos("h1") && get_square(board, &pos("h1")) == SQ_WH_R {
|
||||
game_state.castling &= !CASTLING_WH_K;
|
||||
} else if m.1 == pos("a8") && get_square(board, &pos("a8")) == SQ_BL_R {
|
||||
game_state.castling &= !CASTLING_BL_Q;
|
||||
} else if m.1 == pos("h8") && get_square(board, &pos("h8")) == SQ_BL_R {
|
||||
game_state.castling &= !CASTLING_BL_K;
|
||||
impl fmt::Debug for Move {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.to_uci_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Null move string in UCI exchanges.
|
||||
pub const UCI_NULL_MOVE_STR: &str = "0000";
|
||||
|
||||
impl Move {
|
||||
/// Build a move from `source` to `dest`, no promotion.
|
||||
pub const fn new(source: Square, dest: Square) -> Move {
|
||||
Move { source, dest, promotion: None, capture: None, old_castles: 0 }
|
||||
}
|
||||
|
||||
// Update board and game state.
|
||||
apply_move_to_board(board, m);
|
||||
/// Build a move from `source` to `dest`, with a promotion.
|
||||
pub const fn new_promotion(source: Square, dest: Square, promotion: Piece) -> Move {
|
||||
Move { source, dest, promotion: Some(promotion), capture: None, old_castles: 0 }
|
||||
}
|
||||
|
||||
/// Apply this move to `board` and `game_state`.
|
||||
///
|
||||
/// Set automatic queen promotion for pawns, register captured
|
||||
/// pieces and castle options.
|
||||
pub fn apply_to(&mut self, board: &mut Board, game_state: &mut GameState) {
|
||||
// Save current castling options to unmake later.
|
||||
self.old_castles = game_state.castling;
|
||||
|
||||
let piece = board.get_piece_on(self.source);
|
||||
// Handle king castling.
|
||||
if piece == KING {
|
||||
if let Some(castle) = self.get_castle() {
|
||||
match castle {
|
||||
CASTLE_WH_K => {
|
||||
board.move_square(E1, G1);
|
||||
board.move_square(H1, F1);
|
||||
game_state.castling &= !CASTLE_WH_MASK;
|
||||
}
|
||||
CASTLE_WH_Q => {
|
||||
board.move_square(E1, C1);
|
||||
board.move_square(A1, D1);
|
||||
game_state.castling &= !CASTLE_WH_MASK;
|
||||
}
|
||||
CASTLE_BL_K => {
|
||||
board.move_square(E8, G8);
|
||||
board.move_square(H8, F8);
|
||||
game_state.castling &= !CASTLE_BL_MASK;
|
||||
}
|
||||
CASTLE_BL_Q => {
|
||||
board.move_square(E8, C8);
|
||||
board.move_square(A8, D8);
|
||||
game_state.castling &= !CASTLE_BL_MASK;
|
||||
}
|
||||
_ => { panic!("Invalid castle.") }
|
||||
}
|
||||
game_state.color = opposite(game_state.color);
|
||||
return
|
||||
} else {
|
||||
// If the king moved from starting square, remove it from castling options.
|
||||
if self.source == E1 { game_state.castling &= !CASTLE_WH_MASK; }
|
||||
else if self.source == E8 { game_state.castling &= !CASTLE_BL_MASK; }
|
||||
}
|
||||
}
|
||||
// Record captured piece if any.
|
||||
if !board.is_empty(self.dest) {
|
||||
self.capture = Some(board.get_piece_on(self.dest));
|
||||
}
|
||||
|
||||
// If the move is a castle, remove it from castling options.
|
||||
if let Some(castle) = get_castle(m) {
|
||||
// Move the piece.
|
||||
board.move_square(self.source, self.dest);
|
||||
|
||||
// Apply promotion if any.
|
||||
if let Some(piece) = self.promotion {
|
||||
board.set_piece(self.dest, PAWN, piece);
|
||||
}
|
||||
|
||||
// If a rook moved, remove the castle side.
|
||||
if self.source == A1 || self.dest == A1 { game_state.castling &= !CASTLE_WH_Q; }
|
||||
else if self.source == H1 || self.dest == H1 { game_state.castling &= !CASTLE_WH_K; }
|
||||
else if self.source == A8 || self.dest == A8 { game_state.castling &= !CASTLE_BL_Q; }
|
||||
else if self.source == H8 || self.dest == H8 { game_state.castling &= !CASTLE_BL_K; }
|
||||
|
||||
// Finally, switch to the opposing player in the game state.
|
||||
game_state.color = opposite(game_state.color);
|
||||
}
|
||||
|
||||
/// Unmake a move.
|
||||
pub fn unmake(&self, board: &mut Board, game_state: &mut GameState) {
|
||||
// Always restore previous castle options.
|
||||
game_state.castling = self.old_castles;
|
||||
// If the move is a castle, unmake it properly.
|
||||
let piece = board.get_piece_on(self.dest);
|
||||
if piece == KING {
|
||||
if let Some(castle) = self.get_castle() {
|
||||
match castle {
|
||||
CASTLING_WH_K | CASTLING_WH_Q => game_state.castling &= !CASTLING_WH_MASK,
|
||||
CASTLING_BL_K | CASTLING_BL_Q => game_state.castling &= !CASTLING_BL_MASK,
|
||||
_ => {}
|
||||
};
|
||||
CASTLE_WH_K => { board.move_square(G1, E1); board.move_square(F1, H1); }
|
||||
CASTLE_WH_Q => { board.move_square(C1, E1); board.move_square(D1, A1); }
|
||||
CASTLE_BL_K => { board.move_square(G8, E8); board.move_square(F8, H8); }
|
||||
CASTLE_BL_Q => { board.move_square(C8, E8); board.move_square(D8, A8); }
|
||||
_ => { panic!("Invalid castle.") }
|
||||
}
|
||||
// Else, check if the king or a rook moved to update castling options.
|
||||
else {
|
||||
let piece = get_square(board, &m.1);
|
||||
if is_white(piece) && game_state.castling & CASTLING_WH_MASK != 0 {
|
||||
match get_type(piece) {
|
||||
SQ_K => {
|
||||
if m.0 == pos("e1") {
|
||||
game_state.castling &= !CASTLING_WH_MASK;
|
||||
game_state.color = opposite(game_state.color);
|
||||
return
|
||||
}
|
||||
}
|
||||
SQ_R => {
|
||||
if m.0 == pos("a1") {
|
||||
game_state.castling &= !CASTLING_WH_Q;
|
||||
} else if m.0 == pos("h1") {
|
||||
game_state.castling &= !CASTLING_WH_K;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if is_black(piece) && game_state.castling & CASTLING_BL_MASK != 0 {
|
||||
match get_type(piece) {
|
||||
SQ_K => {
|
||||
if m.0 == pos("e8") {
|
||||
game_state.castling &= !CASTLING_BL_MASK;
|
||||
}
|
||||
}
|
||||
SQ_R => {
|
||||
if m.0 == pos("a8") {
|
||||
game_state.castling &= !CASTLING_BL_Q;
|
||||
} else if m.0 == pos("h8") {
|
||||
game_state.castling &= !CASTLING_BL_K;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply a move `m` into `board`.
|
||||
pub fn apply_move_to_board(board: &mut Board, m: &Move) {
|
||||
if let Some(castle) = get_castle(m) {
|
||||
// Move the piece back.
|
||||
board.move_square(self.dest, self.source);
|
||||
|
||||
// Cancel the promotion.
|
||||
if let Some(piece) = self.promotion {
|
||||
board.set_piece(self.source, piece, PAWN);
|
||||
}
|
||||
|
||||
// Restore captured piece.
|
||||
if let Some(piece) = self.capture {
|
||||
board.set_square(self.dest, game_state.color, piece);
|
||||
}
|
||||
|
||||
// And switch back to previous player.
|
||||
game_state.color = opposite(game_state.color);
|
||||
}
|
||||
|
||||
/// Get the corresponding castling flag for this move.
|
||||
pub fn get_castle(&self) -> Option<Castle> {
|
||||
match (self.source, self.dest) {
|
||||
(E1, C1) => Some(CASTLE_WH_Q),
|
||||
(E1, G1) => Some(CASTLE_WH_K),
|
||||
(E8, C8) => Some(CASTLE_BL_Q),
|
||||
(E8, G8) => Some(CASTLE_BL_K),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the move for this castle.
|
||||
pub fn get_castle_move(castle: u8) -> Move {
|
||||
match castle {
|
||||
CASTLING_WH_K => {
|
||||
move_piece(board, &START_WH_K_POS, &pos("g1"));
|
||||
move_piece(board, &pos("h1"), &pos("f1"));
|
||||
}
|
||||
CASTLING_WH_Q => {
|
||||
move_piece(board, &START_WH_K_POS, &pos("c1"));
|
||||
move_piece(board, &pos("a1"), &pos("d1"));
|
||||
}
|
||||
CASTLING_BL_K => {
|
||||
move_piece(board, &START_BL_K_POS, &pos("g8"));
|
||||
move_piece(board, &pos("h8"), &pos("f8"));
|
||||
}
|
||||
CASTLING_BL_Q => {
|
||||
move_piece(board, &START_BL_K_POS, &pos("c8"));
|
||||
move_piece(board, &pos("a8"), &pos("d8"));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
move_piece(board, &m.0, &m.1);
|
||||
if let Some(prom_type) = m.2 {
|
||||
let color = get_color(get_square(board, &m.1));
|
||||
set_square(board, &m.1, color|prom_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the corresponding castling flag for this move.
|
||||
pub fn get_castle(m: &Move) -> Option<u8> {
|
||||
if m.0 == pos("e1") {
|
||||
if m.1 == pos("c1") {
|
||||
Some(CASTLING_WH_Q)
|
||||
} else if m.1 == pos("g1") {
|
||||
Some(CASTLING_WH_K)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if m.0 == pos("e8") {
|
||||
if m.1 == pos("c8") {
|
||||
Some(CASTLING_BL_Q)
|
||||
} else if m.1 == pos("g8") {
|
||||
Some(CASTLING_BL_K)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the move for this castle.
|
||||
pub fn get_castle_move(castle: u8) -> Move {
|
||||
match castle {
|
||||
CASTLING_WH_Q => (pos("e1"), pos("c1"), None),
|
||||
CASTLING_WH_K => (pos("e1"), pos("g1"), None),
|
||||
CASTLING_BL_Q => (pos("e8"), pos("c8"), None),
|
||||
CASTLING_BL_K => (pos("e8"), pos("g8"), None),
|
||||
CASTLE_WH_Q => Move::new(E1, C1),
|
||||
CASTLE_WH_K => Move::new(E1, G1),
|
||||
CASTLE_BL_Q => Move::new(E8, C8),
|
||||
CASTLE_BL_K => Move::new(E8, G8),
|
||||
_ => panic!("Illegal castling requested: {:08b}", castle),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an UCI move algebraic notation string to a Move.
|
||||
pub fn from_uci_string(m_str: &str) -> Move {
|
||||
Move {
|
||||
source: sq_from_string(&m_str[0..2]),
|
||||
dest: sq_from_string(&m_str[2..4]),
|
||||
promotion: if m_str.len() == 5 {
|
||||
Some(match m_str.as_bytes()[4] {
|
||||
b'b' => BISHOP,
|
||||
b'n' => KNIGHT,
|
||||
b'r' => ROOK,
|
||||
b'q' => QUEEN,
|
||||
_ => panic!("What is the opponent doing? This is illegal, I'm out."),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
capture: None,
|
||||
old_castles: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a string containing the UCI algebraic notation of this move.
|
||||
pub fn to_uci_string(&self) -> String {
|
||||
let mut move_string = String::new();
|
||||
move_string.push_str(&sq_to_string(self.source));
|
||||
move_string.push_str(&sq_to_string(self.dest));
|
||||
if let Some(piece) = self.promotion {
|
||||
move_string.push(match piece {
|
||||
QUEEN => 'q',
|
||||
BISHOP => 'b',
|
||||
KNIGHT => 'n',
|
||||
ROOK => 'r',
|
||||
_ => panic!("What are you doing? Promote to a legal piece.")
|
||||
});
|
||||
}
|
||||
move_string
|
||||
}
|
||||
|
||||
/// Debug only: create a space-separated string of moves.
|
||||
pub(crate) fn list_to_uci_string(moves: &Vec<Move>) -> String {
|
||||
moves.iter().map(|m| m.to_uci_string()).collect::<Vec<_>>().join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::notation::parse_move;
|
||||
|
||||
#[test]
|
||||
fn test_apply_move_to_board() {
|
||||
let mut b = new_empty();
|
||||
fn test_apply_to_board() {
|
||||
let mut b = Board::new_empty();
|
||||
let mut gs = GameState::new();
|
||||
|
||||
// Put 2 enemy knights on board.
|
||||
set_square(&mut b, &pos("d4"), SQ_WH_N);
|
||||
set_square(&mut b, &pos("f4"), SQ_BL_N);
|
||||
b.set_square(D4, WHITE, KNIGHT);
|
||||
b.set_square(F4, BLACK, KNIGHT);
|
||||
// Move white knight in a position attacked by black knight.
|
||||
apply_move_to_board(&mut b, &(pos("d4"), pos("e6"), None));
|
||||
assert_eq!(get_square(&b, &pos("d4")), SQ_E);
|
||||
assert_eq!(get_square(&b, &pos("e6")), SQ_WH_N);
|
||||
assert_eq!(num_pieces(&b), 2);
|
||||
let mut m = Move::new(D4, E6);
|
||||
m.apply_to(&mut b, &mut gs);
|
||||
assert!(b.is_empty(D4));
|
||||
assert_eq!(b.get_color_on(E6), WHITE);
|
||||
assert_eq!(b.get_piece_on(E6), KNIGHT);
|
||||
assert_eq!(count_bits(b.combined()), 2);
|
||||
assert!(m.capture.is_none());
|
||||
// Sack it with black knight
|
||||
apply_move_to_board(&mut b, &(pos("f4"), pos("e6"), None));
|
||||
assert_eq!(get_square(&b, &pos("e6")), SQ_BL_N);
|
||||
assert_eq!(num_pieces(&b), 1);
|
||||
let mut m = Move::new(F4, E6);
|
||||
m.apply_to(&mut b, &mut gs);
|
||||
assert_eq!(b.get_color_on(E6), BLACK);
|
||||
assert_eq!(b.get_piece_on(E6), KNIGHT);
|
||||
assert_eq!(count_bits(b.combined()), 1);
|
||||
assert_eq!(m.capture.unwrap(), KNIGHT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_move_to_castling() {
|
||||
let mut b = new();
|
||||
let mut gs = rules::GameState::new();
|
||||
assert_eq!(gs.castling, CASTLING_MASK);
|
||||
fn test_apply_to_castling() {
|
||||
let mut b = Board::new();
|
||||
let mut gs = GameState::new();
|
||||
assert_eq!(gs.castling, CASTLE_MASK);
|
||||
|
||||
// On a starting board, start by making place for all castles.
|
||||
clear_square(&mut b, &pos("b1"));
|
||||
clear_square(&mut b, &pos("c1"));
|
||||
clear_square(&mut b, &pos("d1"));
|
||||
clear_square(&mut b, &pos("f1"));
|
||||
clear_square(&mut b, &pos("g1"));
|
||||
clear_square(&mut b, &pos("b8"));
|
||||
clear_square(&mut b, &pos("c8"));
|
||||
clear_square(&mut b, &pos("d8"));
|
||||
clear_square(&mut b, &pos("f8"));
|
||||
clear_square(&mut b, &pos("g8"));
|
||||
b.clear_square(B1, WHITE, KNIGHT);
|
||||
b.clear_square(C1, WHITE, BISHOP);
|
||||
b.clear_square(D1, WHITE, QUEEN);
|
||||
b.clear_square(F1, WHITE, BISHOP);
|
||||
b.clear_square(G1, WHITE, KNIGHT);
|
||||
b.clear_square(B8, BLACK, KNIGHT);
|
||||
b.clear_square(C8, BLACK, BISHOP);
|
||||
b.clear_square(D8, BLACK, QUEEN);
|
||||
b.clear_square(F8, BLACK, BISHOP);
|
||||
b.clear_square(G8, BLACK, KNIGHT);
|
||||
// White queen-side castling.
|
||||
apply_move_to(&mut b, &mut gs, &parse_move("e1c1"));
|
||||
assert!(is_piece(get_square(&b, &pos("c1")), SQ_WH_K));
|
||||
assert!(is_piece(get_square(&b, &pos("d1")), SQ_WH_R));
|
||||
assert!(is_empty(&b, &pos("a1")));
|
||||
assert!(is_empty(&b, &pos("e1")));
|
||||
assert_eq!(gs.castling, CASTLING_BL_MASK);
|
||||
Move::new(E1, C1).apply_to(&mut b, &mut gs);
|
||||
assert_eq!(b.get_color_on(C1), WHITE);
|
||||
assert_eq!(b.get_piece_on(C1), KING);
|
||||
assert_eq!(b.get_color_on(D1), WHITE);
|
||||
assert_eq!(b.get_piece_on(D1), ROOK);
|
||||
assert!(b.is_empty(A1));
|
||||
assert!(b.is_empty(E1));
|
||||
assert_eq!(gs.castling, CASTLE_BL_MASK);
|
||||
// Black king-side castling.
|
||||
apply_move_to(&mut b, &mut gs, &parse_move("e8g8"));
|
||||
assert!(is_piece(get_square(&b, &pos("g8")), SQ_BL_K));
|
||||
assert!(is_piece(get_square(&b, &pos("f8")), SQ_BL_R));
|
||||
assert!(is_empty(&b, &pos("h8")));
|
||||
assert!(is_empty(&b, &pos("e8")));
|
||||
Move::new(E8, G8).apply_to(&mut b, &mut gs);
|
||||
assert_eq!(b.get_color_on(G8), BLACK);
|
||||
assert_eq!(b.get_piece_on(G8), KING);
|
||||
assert_eq!(b.get_color_on(F8), BLACK);
|
||||
assert_eq!(b.get_piece_on(F8), ROOK);
|
||||
assert!(b.is_empty(H8));
|
||||
assert!(b.is_empty(E8));
|
||||
// At the end, no more castling options for both sides.
|
||||
assert_eq!(gs.castling, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unmake() {
|
||||
let mut b = Board::new_empty();
|
||||
let mut gs = GameState::new();
|
||||
|
||||
// On unmaking a move, the white pawn is back to its original square.
|
||||
b.set_square(D4, WHITE, PAWN);
|
||||
let mut m = Move::new(D4, D5);
|
||||
m.apply_to(&mut b, &mut gs);
|
||||
m.unmake(&mut b, &mut gs);
|
||||
assert!(b.is_empty(D5));
|
||||
assert_eq!(b.get_color_on(D4), WHITE);
|
||||
assert_eq!(b.get_piece_on(D4), PAWN);
|
||||
|
||||
// Castle options should be properly unmade.
|
||||
b.set_square(E1, WHITE, KING);
|
||||
b.set_square(H1, WHITE, ROOK);
|
||||
let mut m = Move::new(E1, G1);
|
||||
m.apply_to(&mut b, &mut gs);
|
||||
assert!(!b.is_empty(G1));
|
||||
assert!(!b.is_empty(F1));
|
||||
assert_eq!(gs.castling, CASTLE_MASK ^ CASTLE_WH_MASK);
|
||||
m.unmake(&mut b, &mut gs);
|
||||
assert!(b.is_empty(G1));
|
||||
assert!(b.is_empty(F1));
|
||||
assert_eq!(b.get_piece_on(E1), KING);
|
||||
assert_eq!(b.get_piece_on(H1), ROOK);
|
||||
assert_eq!(gs.castling, CASTLE_MASK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_castle() {
|
||||
assert_eq!(get_castle(&parse_move("e1c1")), Some(CASTLING_WH_Q));
|
||||
assert_eq!(get_castle(&parse_move("e1g1")), Some(CASTLING_WH_K));
|
||||
assert_eq!(get_castle(&parse_move("e8c8")), Some(CASTLING_BL_Q));
|
||||
assert_eq!(get_castle(&parse_move("e8g8")), Some(CASTLING_BL_K));
|
||||
assert_eq!(get_castle(&parse_move("d2d4")), None);
|
||||
assert_eq!(Move::new(E1, C1).get_castle(), Some(CASTLE_WH_Q));
|
||||
assert_eq!(Move::new(E1, G1).get_castle(), Some(CASTLE_WH_K));
|
||||
assert_eq!(Move::new(E8, C8).get_castle(), Some(CASTLE_BL_Q));
|
||||
assert_eq!(Move::new(E8, G8).get_castle(), Some(CASTLE_BL_K));
|
||||
assert_eq!(Move::new(D2, D4).get_castle(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_uci_string() {
|
||||
assert_eq!(Move::new(A1, D4).to_uci_string(), "a1d4");
|
||||
assert_eq!(Move::new(H8, A8).to_uci_string(), "h8a8");
|
||||
assert_eq!(Move::new_promotion(H7, H8, QUEEN).to_uci_string(), "h7h8q");
|
||||
assert_eq!(Move::new_promotion(H7, H8, KNIGHT).to_uci_string(), "h7h8n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_uci_string() {
|
||||
assert_eq!(Move::from_uci_string("a1d4"), Move::new(A1, D4));
|
||||
assert_eq!(Move::from_uci_string("a7a8q"), Move::new_promotion(A7, A8, QUEEN));
|
||||
assert_eq!(Move::from_uci_string("a7a8r"), Move::new_promotion(A7, A8, ROOK));
|
||||
}
|
||||
}
|
||||
|
|
48
src/node.rs
48
src/node.rs
|
@ -1,13 +1,12 @@
|
|||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use crate::board;
|
||||
use crate::movement::{self, Move};
|
||||
use crate::movement::Move;
|
||||
use crate::rules;
|
||||
use crate::stats;
|
||||
|
||||
/// Analysis node: a board along with the game state.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Node {
|
||||
/// Board for this node.
|
||||
pub board: board::Board,
|
||||
|
@ -19,24 +18,28 @@ impl Node {
|
|||
/// Create a new node for an empty board and a new game state.
|
||||
pub fn new() -> Node {
|
||||
Node {
|
||||
board: board::new_empty(),
|
||||
board: board::Board::new_empty(),
|
||||
game_state: rules::GameState::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply a move to this node.
|
||||
pub fn apply_move(&mut self, m: &Move) {
|
||||
movement::apply_move_to(&mut self.board, &mut self.game_state, m);
|
||||
pub fn apply_move(&mut self, m: &mut Move) {
|
||||
m.apply_to(&mut self.board, &mut self.game_state);
|
||||
}
|
||||
|
||||
pub fn unmake_move(&mut self, m: &Move) {
|
||||
m.unmake(&mut self.board, &mut self.game_state);
|
||||
}
|
||||
|
||||
/// Return player moves from this node.
|
||||
pub fn get_player_moves(&self, commit: bool) -> Vec<Move> {
|
||||
rules::get_player_moves(&self.board, &self.game_state, commit)
|
||||
pub fn get_player_moves(&mut self) -> Vec<Move> {
|
||||
rules::get_player_moves(&mut self.board, &mut self.game_state)
|
||||
}
|
||||
|
||||
/// Compute stats for both players for this node.
|
||||
pub fn compute_stats(&self) -> (stats::BoardStats, stats::BoardStats) {
|
||||
stats::compute_stats(&self.board, &self.game_state)
|
||||
pub fn compute_stats(&mut self) -> (stats::BoardStats, stats::BoardStats) {
|
||||
stats::BoardStats::new_from(&mut self.board, &mut self.game_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,29 +56,8 @@ impl fmt::Debug for Node {
|
|||
impl fmt::Display for Node {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut s = vec!();
|
||||
board::draw(&self.board, &mut s);
|
||||
self.board.draw_to(&mut s);
|
||||
let board_drawing = String::from_utf8_lossy(&s).to_string();
|
||||
write!(
|
||||
f,
|
||||
"* Board:\n{}\n\
|
||||
* Game state:\n{}",
|
||||
board_drawing, self.game_state
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Node {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.board.iter().zip(other.board.iter()).all(|(a, b)| a == b)
|
||||
&& self.game_state == other.game_state
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Node {}
|
||||
|
||||
impl Hash for Node {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.board.iter().for_each(|square| state.write_u8(*square));
|
||||
self.game_state.hash(state);
|
||||
write!(f, "{}{}", board_drawing, self.game_state)
|
||||
}
|
||||
}
|
||||
|
|
111
src/notation.rs
111
src/notation.rs
|
@ -1,111 +0,0 @@
|
|||
//! Functions using various notations.
|
||||
|
||||
use crate::board::*;
|
||||
use crate::movement::Move;
|
||||
|
||||
pub const NULL_MOVE: &str = "0000";
|
||||
|
||||
/// Create a string containing the UCI algebraic notation of this move.
|
||||
pub fn move_to_string(m: &Move) -> String {
|
||||
let mut move_string = String::new();
|
||||
move_string.push_str(&pos_string(&m.0));
|
||||
move_string.push_str(&pos_string(&m.1));
|
||||
if let Some(prom) = m.2 {
|
||||
move_string.push(match prom {
|
||||
SQ_Q => 'q',
|
||||
SQ_B => 'b',
|
||||
SQ_N => 'n',
|
||||
SQ_R => 'r',
|
||||
_ => panic!("What are you doing? Promote to a legal piece.")
|
||||
});
|
||||
}
|
||||
move_string
|
||||
}
|
||||
|
||||
/// Parse an UCI move algebraic notation string to a Move.
|
||||
pub fn parse_move(m_str: &str) -> Move {
|
||||
let prom = if m_str.len() == 5 {
|
||||
Some(match m_str.as_bytes()[4] {
|
||||
b'b' => SQ_B,
|
||||
b'n' => SQ_N,
|
||||
b'r' => SQ_R,
|
||||
b'q' => SQ_Q,
|
||||
_ => panic!("What is the opponent doing? This is illegal, I'm out."),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(pos(&m_str[0..2]), pos(&m_str[2..4]), prom)
|
||||
}
|
||||
|
||||
/// Create a space-separated string of moves. Used for debugging.
|
||||
pub fn move_list_to_string(moves: &Vec<Move>) -> String {
|
||||
moves.iter().map(|m| move_to_string(m)).collect::<Vec<_>>().join(" ")
|
||||
}
|
||||
|
||||
pub const FEN_START: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
/// FEN notation for positions, split into fields.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Fen {
|
||||
pub placement: String,
|
||||
pub color: String,
|
||||
pub castling: String,
|
||||
pub en_passant: String,
|
||||
pub halfmove: String,
|
||||
pub fullmove: String,
|
||||
}
|
||||
|
||||
pub fn parse_fen(i: &str) -> Option<Fen> {
|
||||
let fields: Vec<&str> = i.split_whitespace().collect();
|
||||
parse_fen_fields(&fields)
|
||||
}
|
||||
|
||||
pub fn parse_fen_fields(fields: &[&str]) -> Option<Fen> {
|
||||
if fields.len() < 6 {
|
||||
return None
|
||||
}
|
||||
Some(Fen {
|
||||
placement: fields[0].to_string(),
|
||||
color: fields[1].to_string(),
|
||||
castling: fields[2].to_string(),
|
||||
en_passant: fields[3].to_string(),
|
||||
halfmove: fields[4].to_string(),
|
||||
fullmove: fields[5].to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn en_passant_to_string(ep: Option<Pos>) -> String {
|
||||
ep.and_then(|p| Some(pos_string(&p))).unwrap_or("-".to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_move_to_string() {
|
||||
assert_eq!(move_to_string(&((0, 0), (3, 3), None)), "a1d4");
|
||||
assert_eq!(move_to_string(&((7, 7), (0, 7), None)), "h8a8");
|
||||
assert_eq!(move_to_string(&((7, 6), (7, 7), Some(SQ_Q))), "h7h8q");
|
||||
assert_eq!(move_to_string(&((7, 6), (7, 7), Some(SQ_N))), "h7h8n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_move() {
|
||||
assert_eq!(parse_move("a1d4"), ((0, 0), (3, 3), None));
|
||||
assert_eq!(parse_move("a7a8q"), ((0, 6), (0, 7), Some(SQ_Q)));
|
||||
assert_eq!(parse_move("a7a8r"), ((0, 6), (0, 7), Some(SQ_R)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fen() {
|
||||
let fen_start = parse_fen(FEN_START).unwrap();
|
||||
assert_eq!(&fen_start.placement, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR");
|
||||
assert_eq!(&fen_start.color, "w");
|
||||
assert_eq!(&fen_start.castling, "KQkq");
|
||||
assert_eq!(&fen_start.en_passant, "-");
|
||||
assert_eq!(&fen_start.halfmove, "0");
|
||||
assert_eq!(&fen_start.fullmove, "1");
|
||||
}
|
||||
}
|
482
src/precomputed.rs
Normal file
482
src/precomputed.rs
Normal file
|
@ -0,0 +1,482 @@
|
|||
//! Ugly module with all kind of precomputed stuff.
|
||||
|
||||
use crate::board::{Square, Bitboard};
|
||||
|
||||
// Generated by gen_squares.py.
|
||||
pub const A1: Square = 0;
|
||||
pub const A2: Square = 1;
|
||||
pub const A3: Square = 2;
|
||||
pub const A4: Square = 3;
|
||||
pub const A5: Square = 4;
|
||||
pub const A6: Square = 5;
|
||||
pub const A7: Square = 6;
|
||||
pub const A8: Square = 7;
|
||||
pub const B1: Square = 8;
|
||||
pub const B2: Square = 9;
|
||||
pub const B3: Square = 10;
|
||||
pub const B4: Square = 11;
|
||||
pub const B5: Square = 12;
|
||||
pub const B6: Square = 13;
|
||||
pub const B7: Square = 14;
|
||||
pub const B8: Square = 15;
|
||||
pub const C1: Square = 16;
|
||||
pub const C2: Square = 17;
|
||||
pub const C3: Square = 18;
|
||||
pub const C4: Square = 19;
|
||||
pub const C5: Square = 20;
|
||||
pub const C6: Square = 21;
|
||||
pub const C7: Square = 22;
|
||||
pub const C8: Square = 23;
|
||||
pub const D1: Square = 24;
|
||||
pub const D2: Square = 25;
|
||||
pub const D3: Square = 26;
|
||||
pub const D4: Square = 27;
|
||||
pub const D5: Square = 28;
|
||||
pub const D6: Square = 29;
|
||||
pub const D7: Square = 30;
|
||||
pub const D8: Square = 31;
|
||||
pub const E1: Square = 32;
|
||||
pub const E2: Square = 33;
|
||||
pub const E3: Square = 34;
|
||||
pub const E4: Square = 35;
|
||||
pub const E5: Square = 36;
|
||||
pub const E6: Square = 37;
|
||||
pub const E7: Square = 38;
|
||||
pub const E8: Square = 39;
|
||||
pub const F1: Square = 40;
|
||||
pub const F2: Square = 41;
|
||||
pub const F3: Square = 42;
|
||||
pub const F4: Square = 43;
|
||||
pub const F5: Square = 44;
|
||||
pub const F6: Square = 45;
|
||||
pub const F7: Square = 46;
|
||||
pub const F8: Square = 47;
|
||||
pub const G1: Square = 48;
|
||||
pub const G2: Square = 49;
|
||||
pub const G3: Square = 50;
|
||||
pub const G4: Square = 51;
|
||||
pub const G5: Square = 52;
|
||||
pub const G6: Square = 53;
|
||||
pub const G7: Square = 54;
|
||||
pub const G8: Square = 55;
|
||||
pub const H1: Square = 56;
|
||||
pub const H2: Square = 57;
|
||||
pub const H3: Square = 58;
|
||||
pub const H4: Square = 59;
|
||||
pub const H5: Square = 60;
|
||||
pub const H6: Square = 61;
|
||||
pub const H7: Square = 62;
|
||||
pub const H8: Square = 63;
|
||||
pub const NUM_SQUARES: Square = 64;
|
||||
|
||||
// Generated by gen_pawn_progresses.py.
|
||||
/// Pre-computed pawn progress (for white and black).
|
||||
pub const PAWN_PROGRESSES: [[Bitboard; 64]; 2] = [
|
||||
[
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000001100,
|
||||
0b0000000000000000000000000000000000000000000000000000000000001000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000010000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000100000,
|
||||
0b0000000000000000000000000000000000000000000000000000000001000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000010000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000110000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000100000000000,
|
||||
0b0000000000000000000000000000000000000000000000000001000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000010000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000100000000000000,
|
||||
0b0000000000000000000000000000000000000000000000001000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000011000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000010000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000100000000000000000000,
|
||||
0b0000000000000000000000000000000000000000001000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000010000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000100000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000001100000000000000000000000000,
|
||||
0b0000000000000000000000000000000000001000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000010000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000100000000000000000000000000000,
|
||||
0b0000000000000000000000000000000001000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000010000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000110000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000100000000000000000000000000000000000,
|
||||
0b0000000000000000000000000001000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000010000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000100000000000000000000000000000000000000,
|
||||
0b0000000000000000000000001000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000011000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000010000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000100000000000000000000000000000000000000000000,
|
||||
0b0000000000000000001000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000010000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000100000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000001100000000000000000000000000000000000000000000000000,
|
||||
0b0000000000001000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000010000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000100000000000000000000000000000000000000000000000000000,
|
||||
0b0000000001000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000010000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000110000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000100000000000000000000000000000000000000000000000000000000000,
|
||||
0b0001000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0010000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0100000000000000000000000000000000000000000000000000000000000000,
|
||||
0b1000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
],
|
||||
[
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000001,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000010,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000100,
|
||||
0b0000000000000000000000000000000000000000000000000000000000001000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000010000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000110000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000100000000,
|
||||
0b0000000000000000000000000000000000000000000000000000001000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000010000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000100000000000,
|
||||
0b0000000000000000000000000000000000000000000000000001000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000011000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000010000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000100000000000000000,
|
||||
0b0000000000000000000000000000000000000000000001000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000010000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000100000000000000000000,
|
||||
0b0000000000000000000000000000000000000000001100000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000001000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000010000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000100000000000000000000000000,
|
||||
0b0000000000000000000000000000000000001000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000010000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000110000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000100000000000000000000000000000000,
|
||||
0b0000000000000000000000000000001000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000010000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000100000000000000000000000000000000000,
|
||||
0b0000000000000000000000000001000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000011000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000010000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000100000000000000000000000000000000000000000,
|
||||
0b0000000000000000000001000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000010000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000100000000000000000000000000000000000000000000,
|
||||
0b0000000000000000001100000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000001000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000010000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000100000000000000000000000000000000000000000000000000,
|
||||
0b0000000000001000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000010000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000110000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000100000000000000000000000000000000000000000000000000000000,
|
||||
0b0000001000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000010000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000100000000000000000000000000000000000000000000000000000000000,
|
||||
0b0001000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0011000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
],
|
||||
];
|
||||
|
||||
// Generated by gen_pawn_captures.py.
|
||||
/// Pre-computed pawn captures (for white and black).
|
||||
pub const PAWN_CAPTURES: [[Bitboard; 64]; 2] = [
|
||||
[
|
||||
0b0000000000000000000000000000000000000000000000000000001000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000010000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000100000000000,
|
||||
0b0000000000000000000000000000000000000000000000000001000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000010000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000100000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000100000000000000010,
|
||||
0b0000000000000000000000000000000000000000000001000000000000000100,
|
||||
0b0000000000000000000000000000000000000000000010000000000000001000,
|
||||
0b0000000000000000000000000000000000000000000100000000000000010000,
|
||||
0b0000000000000000000000000000000000000000001000000000000000100000,
|
||||
0b0000000000000000000000000000000000000000010000000000000001000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000010000000000000001000000000,
|
||||
0b0000000000000000000000000000000000000100000000000000010000000000,
|
||||
0b0000000000000000000000000000000000001000000000000000100000000000,
|
||||
0b0000000000000000000000000000000000010000000000000001000000000000,
|
||||
0b0000000000000000000000000000000000100000000000000010000000000000,
|
||||
0b0000000000000000000000000000000001000000000000000100000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000001000000000000000100000000000000000,
|
||||
0b0000000000000000000000000000010000000000000001000000000000000000,
|
||||
0b0000000000000000000000000000100000000000000010000000000000000000,
|
||||
0b0000000000000000000000000001000000000000000100000000000000000000,
|
||||
0b0000000000000000000000000010000000000000001000000000000000000000,
|
||||
0b0000000000000000000000000100000000000000010000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000100000000000000010000000000000000000000000,
|
||||
0b0000000000000000000001000000000000000100000000000000000000000000,
|
||||
0b0000000000000000000010000000000000001000000000000000000000000000,
|
||||
0b0000000000000000000100000000000000010000000000000000000000000000,
|
||||
0b0000000000000000001000000000000000100000000000000000000000000000,
|
||||
0b0000000000000000010000000000000001000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000010000000000000001000000000000000000000000000000000,
|
||||
0b0000000000000100000000000000010000000000000000000000000000000000,
|
||||
0b0000000000001000000000000000100000000000000000000000000000000000,
|
||||
0b0000000000010000000000000001000000000000000000000000000000000000,
|
||||
0b0000000000100000000000000010000000000000000000000000000000000000,
|
||||
0b0000000001000000000000000100000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000001000000000000000100000000000000000000000000000000000000000,
|
||||
0b0000010000000000000001000000000000000000000000000000000000000000,
|
||||
0b0000100000000000000010000000000000000000000000000000000000000000,
|
||||
0b0001000000000000000100000000000000000000000000000000000000000000,
|
||||
0b0010000000000000001000000000000000000000000000000000000000000000,
|
||||
0b0100000000000000010000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000010000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000100000000000000000000000000000000000000000000000000,
|
||||
0b0000000000001000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000010000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000100000000000000000000000000000000000000000000000000000,
|
||||
0b0000000001000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
],
|
||||
[
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000001000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000010000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000100000000000,
|
||||
0b0000000000000000000000000000000000000000000000000001000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000010000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000100000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000100000000000000010,
|
||||
0b0000000000000000000000000000000000000000000001000000000000000100,
|
||||
0b0000000000000000000000000000000000000000000010000000000000001000,
|
||||
0b0000000000000000000000000000000000000000000100000000000000010000,
|
||||
0b0000000000000000000000000000000000000000001000000000000000100000,
|
||||
0b0000000000000000000000000000000000000000010000000000000001000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000010000000000000001000000000,
|
||||
0b0000000000000000000000000000000000000100000000000000010000000000,
|
||||
0b0000000000000000000000000000000000001000000000000000100000000000,
|
||||
0b0000000000000000000000000000000000010000000000000001000000000000,
|
||||
0b0000000000000000000000000000000000100000000000000010000000000000,
|
||||
0b0000000000000000000000000000000001000000000000000100000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000001000000000000000100000000000000000,
|
||||
0b0000000000000000000000000000010000000000000001000000000000000000,
|
||||
0b0000000000000000000000000000100000000000000010000000000000000000,
|
||||
0b0000000000000000000000000001000000000000000100000000000000000000,
|
||||
0b0000000000000000000000000010000000000000001000000000000000000000,
|
||||
0b0000000000000000000000000100000000000000010000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000100000000000000010000000000000000000000000,
|
||||
0b0000000000000000000001000000000000000100000000000000000000000000,
|
||||
0b0000000000000000000010000000000000001000000000000000000000000000,
|
||||
0b0000000000000000000100000000000000010000000000000000000000000000,
|
||||
0b0000000000000000001000000000000000100000000000000000000000000000,
|
||||
0b0000000000000000010000000000000001000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000010000000000000001000000000000000000000000000000000,
|
||||
0b0000000000000100000000000000010000000000000000000000000000000000,
|
||||
0b0000000000001000000000000000100000000000000000000000000000000000,
|
||||
0b0000000000010000000000000001000000000000000000000000000000000000,
|
||||
0b0000000000100000000000000010000000000000000000000000000000000000,
|
||||
0b0000000001000000000000000100000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000001000000000000000100000000000000000000000000000000000000000,
|
||||
0b0000010000000000000001000000000000000000000000000000000000000000,
|
||||
0b0000100000000000000010000000000000000000000000000000000000000000,
|
||||
0b0001000000000000000100000000000000000000000000000000000000000000,
|
||||
0b0010000000000000001000000000000000000000000000000000000000000000,
|
||||
0b0100000000000000010000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000010000000000000000000000000000000000000000000000000,
|
||||
0b0000000000000100000000000000000000000000000000000000000000000000,
|
||||
0b0000000000001000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000010000000000000000000000000000000000000000000000000000,
|
||||
0b0000000000100000000000000000000000000000000000000000000000000000,
|
||||
0b0000000001000000000000000000000000000000000000000000000000000000,
|
||||
],
|
||||
];
|
||||
|
||||
// Generated by gen_king_rays.py.
|
||||
/// Pre-computed king rays.
|
||||
pub const KING_RAYS: [Bitboard; 64] = [
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00000011_00000010,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00000111_00000101,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00001110_00001010,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00011100_00010100,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00111000_00101000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_01110000_01010000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_11100000_10100000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_11000000_01000000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000011_00000010_00000011,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000111_00000101_00000111,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00001110_00001010_00001110,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00011100_00010100_00011100,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00111000_00101000_00111000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_01110000_01010000_01110000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_11100000_10100000_11100000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_11000000_01000000_11000000,
|
||||
0b00000000_00000000_00000000_00000000_00000011_00000010_00000011_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000111_00000101_00000111_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00001110_00001010_00001110_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00011100_00010100_00011100_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00111000_00101000_00111000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_01110000_01010000_01110000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_11100000_10100000_11100000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_11000000_01000000_11000000_00000000,
|
||||
0b00000000_00000000_00000000_00000011_00000010_00000011_00000000_00000000,
|
||||
0b00000000_00000000_00000000_00000111_00000101_00000111_00000000_00000000,
|
||||
0b00000000_00000000_00000000_00001110_00001010_00001110_00000000_00000000,
|
||||
0b00000000_00000000_00000000_00011100_00010100_00011100_00000000_00000000,
|
||||
0b00000000_00000000_00000000_00111000_00101000_00111000_00000000_00000000,
|
||||
0b00000000_00000000_00000000_01110000_01010000_01110000_00000000_00000000,
|
||||
0b00000000_00000000_00000000_11100000_10100000_11100000_00000000_00000000,
|
||||
0b00000000_00000000_00000000_11000000_01000000_11000000_00000000_00000000,
|
||||
0b00000000_00000000_00000011_00000010_00000011_00000000_00000000_00000000,
|
||||
0b00000000_00000000_00000111_00000101_00000111_00000000_00000000_00000000,
|
||||
0b00000000_00000000_00001110_00001010_00001110_00000000_00000000_00000000,
|
||||
0b00000000_00000000_00011100_00010100_00011100_00000000_00000000_00000000,
|
||||
0b00000000_00000000_00111000_00101000_00111000_00000000_00000000_00000000,
|
||||
0b00000000_00000000_01110000_01010000_01110000_00000000_00000000_00000000,
|
||||
0b00000000_00000000_11100000_10100000_11100000_00000000_00000000_00000000,
|
||||
0b00000000_00000000_11000000_01000000_11000000_00000000_00000000_00000000,
|
||||
0b00000000_00000011_00000010_00000011_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00000111_00000101_00000111_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00001110_00001010_00001110_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00011100_00010100_00011100_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00111000_00101000_00111000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_01110000_01010000_01110000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_11100000_10100000_11100000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_11000000_01000000_11000000_00000000_00000000_00000000_00000000,
|
||||
0b00000011_00000010_00000011_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000111_00000101_00000111_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00001110_00001010_00001110_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00011100_00010100_00011100_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00111000_00101000_00111000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b01110000_01010000_01110000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b11100000_10100000_11100000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b11000000_01000000_11000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000010_00000011_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000101_00000111_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00001010_00001110_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00010100_00011100_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00101000_00111000_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b01010000_01110000_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b10100000_11100000_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b01000000_11000000_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
];
|
||||
|
||||
// Generated by gen_knight_rays.py.
|
||||
/// Pre-computed knight rays.
|
||||
pub const KNIGHT_RAYS: [Bitboard; 64] = [
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000010_00000100_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000101_00001000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00001010_00010001_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00010100_00100010_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00101000_01000100_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_01010000_10001000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_10100000_00010000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_01000000_00100000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000010_00000100_00000000_00000100,
|
||||
0b00000000_00000000_00000000_00000000_00000101_00001000_00000000_00001000,
|
||||
0b00000000_00000000_00000000_00000000_00001010_00010001_00000000_00010001,
|
||||
0b00000000_00000000_00000000_00000000_00010100_00100010_00000000_00100010,
|
||||
0b00000000_00000000_00000000_00000000_00101000_01000100_00000000_01000100,
|
||||
0b00000000_00000000_00000000_00000000_01010000_10001000_00000000_10001000,
|
||||
0b00000000_00000000_00000000_00000000_10100000_00010000_00000000_00010000,
|
||||
0b00000000_00000000_00000000_00000000_01000000_00100000_00000000_00100000,
|
||||
0b00000000_00000000_00000000_00000010_00000100_00000000_00000100_00000010,
|
||||
0b00000000_00000000_00000000_00000101_00001000_00000000_00001000_00000101,
|
||||
0b00000000_00000000_00000000_00001010_00010001_00000000_00010001_00001010,
|
||||
0b00000000_00000000_00000000_00010100_00100010_00000000_00100010_00010100,
|
||||
0b00000000_00000000_00000000_00101000_01000100_00000000_01000100_00101000,
|
||||
0b00000000_00000000_00000000_01010000_10001000_00000000_10001000_01010000,
|
||||
0b00000000_00000000_00000000_10100000_00010000_00000000_00010000_10100000,
|
||||
0b00000000_00000000_00000000_01000000_00100000_00000000_00100000_01000000,
|
||||
0b00000000_00000000_00000010_00000100_00000000_00000100_00000010_00000000,
|
||||
0b00000000_00000000_00000101_00001000_00000000_00001000_00000101_00000000,
|
||||
0b00000000_00000000_00001010_00010001_00000000_00010001_00001010_00000000,
|
||||
0b00000000_00000000_00010100_00100010_00000000_00100010_00010100_00000000,
|
||||
0b00000000_00000000_00101000_01000100_00000000_01000100_00101000_00000000,
|
||||
0b00000000_00000000_01010000_10001000_00000000_10001000_01010000_00000000,
|
||||
0b00000000_00000000_10100000_00010000_00000000_00010000_10100000_00000000,
|
||||
0b00000000_00000000_01000000_00100000_00000000_00100000_01000000_00000000,
|
||||
0b00000000_00000010_00000100_00000000_00000100_00000010_00000000_00000000,
|
||||
0b00000000_00000101_00001000_00000000_00001000_00000101_00000000_00000000,
|
||||
0b00000000_00001010_00010001_00000000_00010001_00001010_00000000_00000000,
|
||||
0b00000000_00010100_00100010_00000000_00100010_00010100_00000000_00000000,
|
||||
0b00000000_00101000_01000100_00000000_01000100_00101000_00000000_00000000,
|
||||
0b00000000_01010000_10001000_00000000_10001000_01010000_00000000_00000000,
|
||||
0b00000000_10100000_00010000_00000000_00010000_10100000_00000000_00000000,
|
||||
0b00000000_01000000_00100000_00000000_00100000_01000000_00000000_00000000,
|
||||
0b00000010_00000100_00000000_00000100_00000010_00000000_00000000_00000000,
|
||||
0b00000101_00001000_00000000_00001000_00000101_00000000_00000000_00000000,
|
||||
0b00001010_00010001_00000000_00010001_00001010_00000000_00000000_00000000,
|
||||
0b00010100_00100010_00000000_00100010_00010100_00000000_00000000_00000000,
|
||||
0b00101000_01000100_00000000_01000100_00101000_00000000_00000000_00000000,
|
||||
0b01010000_10001000_00000000_10001000_01010000_00000000_00000000_00000000,
|
||||
0b10100000_00010000_00000000_00010000_10100000_00000000_00000000_00000000,
|
||||
0b01000000_00100000_00000000_00100000_01000000_00000000_00000000_00000000,
|
||||
0b00000100_00000000_00000100_00000010_00000000_00000000_00000000_00000000,
|
||||
0b00001000_00000000_00001000_00000101_00000000_00000000_00000000_00000000,
|
||||
0b00010001_00000000_00010001_00001010_00000000_00000000_00000000_00000000,
|
||||
0b00100010_00000000_00100010_00010100_00000000_00000000_00000000_00000000,
|
||||
0b01000100_00000000_01000100_00101000_00000000_00000000_00000000_00000000,
|
||||
0b10001000_00000000_10001000_01010000_00000000_00000000_00000000_00000000,
|
||||
0b00010000_00000000_00010000_10100000_00000000_00000000_00000000_00000000,
|
||||
0b00100000_00000000_00100000_01000000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00000100_00000010_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00001000_00000101_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00010001_00001010_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00100010_00010100_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_01000100_00101000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_10001000_01010000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00010000_10100000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00100000_01000000_00000000_00000000_00000000_00000000_00000000,
|
||||
];
|
874
src/rules.rs
874
src/rules.rs
File diff suppressed because it is too large
Load diff
264
src/stats.rs
264
src/stats.rs
|
@ -1,7 +1,7 @@
|
|||
//! Board statistics used for heuristics.
|
||||
|
||||
use crate::board::*;
|
||||
use crate::rules;
|
||||
use crate::rules::{GameState, get_player_moves};
|
||||
|
||||
/// Storage for board pieces stats.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -27,6 +27,20 @@ impl BoardStats {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create two new BoardStats objects from the board, for both sides.
|
||||
///
|
||||
/// The playing color will have its stats filled in the first
|
||||
/// BoardStats object, its opponent in the second.
|
||||
pub fn new_from(board: &mut Board, game_state: &mut GameState) -> (BoardStats, BoardStats) {
|
||||
let mut stats = (BoardStats::new(), BoardStats::new());
|
||||
let mut gs = game_state.clone();
|
||||
stats.0.compute(board, &mut gs);
|
||||
gs.color = opposite(gs.color);
|
||||
stats.1.compute(board, &mut gs);
|
||||
stats
|
||||
}
|
||||
|
||||
/// Reset all stats to 0.
|
||||
pub fn reset(&mut self) {
|
||||
self.num_pawns = 0;
|
||||
self.num_bishops = 0;
|
||||
|
@ -39,6 +53,88 @@ impl BoardStats {
|
|||
self.num_isolated_pawns = 0;
|
||||
self.mobility = 0;
|
||||
}
|
||||
|
||||
/// Fill `stats` from given `board` and `game_state`.
|
||||
///
|
||||
/// Only the current playing side stats are created,
|
||||
/// prepare the game_state accordingly.
|
||||
pub fn compute(&mut self, board: &mut Board, game_state: &mut GameState) {
|
||||
self.reset();
|
||||
let color = game_state.color;
|
||||
// Compute mobility for all pieces.
|
||||
self.mobility = get_player_moves(board, game_state).len() as i32;
|
||||
// Compute amount of each piece.
|
||||
for file in 0..8 {
|
||||
for rank in 0..8 {
|
||||
let square = sq(file, rank);
|
||||
if board.is_empty(square) || board.get_color_on(square) != color {
|
||||
continue
|
||||
}
|
||||
match board.get_piece_on(square) {
|
||||
ROOK => self.num_rooks += 1,
|
||||
KNIGHT => self.num_knights += 1,
|
||||
BISHOP => self.num_bishops += 1,
|
||||
QUEEN => self.num_queens += 1,
|
||||
KING => self.num_kings += 1,
|
||||
PAWN => {
|
||||
self.num_pawns += 1;
|
||||
let pawn_bb = board.by_color_and_piece(color, PAWN);
|
||||
|
||||
// Check for doubled pawns.
|
||||
let file_bb = FILES[file as usize];
|
||||
if (pawn_bb ^ bit_pos(square)) & file_bb != 0 {
|
||||
self.num_doubled_pawns += 1;
|
||||
}
|
||||
|
||||
// Check for isolated and backward pawns.
|
||||
let (iso_on_prev_file, bw_on_prev_file) = if file > FILE_A {
|
||||
self.find_isolated_and_backward(pawn_bb, square, color, file - 1)
|
||||
} else {
|
||||
(true, true)
|
||||
};
|
||||
let (iso_on_next_file, bw_on_next_file) = if file < FILE_H {
|
||||
self.find_isolated_and_backward(pawn_bb, square, color, file + 1)
|
||||
} else {
|
||||
(true, true)
|
||||
};
|
||||
if iso_on_prev_file && iso_on_next_file {
|
||||
self.num_isolated_pawns += 1;
|
||||
}
|
||||
if bw_on_prev_file && bw_on_next_file {
|
||||
self.num_backward_pawns += 1;
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Find isolated and backward pawns from `square` perspective.
|
||||
///
|
||||
/// `bb` is the bitboard of `color`. `square` is only used to have
|
||||
/// the reference rank. `file` is the file to inspect. To detect
|
||||
/// isolated and backward pawns, `bb` should be the bitboard of
|
||||
/// pawns of `color`.
|
||||
fn find_isolated_and_backward(
|
||||
&mut self,
|
||||
bb: Bitboard,
|
||||
square: Square,
|
||||
color: Color,
|
||||
file: i8
|
||||
) -> (bool, bool) {
|
||||
if bb & FILES[file as usize] == 0 {
|
||||
// If the piece is isolated for this file, it's backward as well.
|
||||
(true, true)
|
||||
} else {
|
||||
let backward_file_bb = if color == WHITE {
|
||||
before_on_file(file, sq_rank(square)) | bit_pos(sq(file, sq_rank(square)))
|
||||
} else {
|
||||
after_on_file(file, sq_rank(square)) | bit_pos(sq(file, sq_rank(square)))
|
||||
};
|
||||
(false, bb & backward_file_bb == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BoardStats {
|
||||
|
@ -54,126 +150,6 @@ impl std::fmt::Display for BoardStats {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create two new BoardStats objects from the board, for both sides.
|
||||
///
|
||||
/// See `compute_stats_into` for details.
|
||||
pub fn compute_stats(board: &Board, game_state: &rules::GameState) -> (BoardStats, BoardStats) {
|
||||
let mut stats = (BoardStats::new(), BoardStats::new());
|
||||
compute_stats_into(board, game_state, &mut stats);
|
||||
stats
|
||||
}
|
||||
|
||||
/// Compute stats for both the current player and its opponent.
|
||||
///
|
||||
/// The playing color will have its stats filled in the first
|
||||
/// BoardStats object, its opponent in the second.
|
||||
pub fn compute_stats_into(
|
||||
board: &Board,
|
||||
game_state: &rules::GameState,
|
||||
stats: &mut (BoardStats, BoardStats)
|
||||
) {
|
||||
let mut gs = game_state.clone();
|
||||
compute_color_stats_into(board, &gs, &mut stats.0);
|
||||
gs.color = opposite(gs.color);
|
||||
compute_color_stats_into(board, &gs, &mut stats.1);
|
||||
}
|
||||
|
||||
/// Fill `stats` from given `board` and `game_state`.
|
||||
///
|
||||
/// Only the current playing side stats are created,
|
||||
/// prepare the game_state accordingly.
|
||||
pub fn compute_color_stats_into(
|
||||
board: &Board,
|
||||
game_state: &rules::GameState,
|
||||
stats: &mut BoardStats,
|
||||
) {
|
||||
stats.reset();
|
||||
let color = game_state.color;
|
||||
// Compute mobility for all pieces.
|
||||
stats.mobility = rules::get_player_moves(board, game_state, true).len() as i32;
|
||||
// Compute amount of each piece.
|
||||
for (piece, p) in get_piece_iterator(board) {
|
||||
let (pos_f, pos_r) = p;
|
||||
if piece == SQ_E || !is_color(piece, color) {
|
||||
continue
|
||||
}
|
||||
match get_type(piece) {
|
||||
SQ_R => stats.num_rooks += 1,
|
||||
SQ_N => stats.num_knights += 1,
|
||||
SQ_B => stats.num_bishops += 1,
|
||||
SQ_Q => stats.num_queens += 1,
|
||||
SQ_K => stats.num_kings += 1,
|
||||
SQ_P => {
|
||||
stats.num_pawns += 1;
|
||||
let mut doubled = false;
|
||||
let mut isolated = true;
|
||||
let mut backward = true;
|
||||
for r in 0..8 {
|
||||
// Check for doubled pawns.
|
||||
if
|
||||
!doubled &&
|
||||
is_piece(get_square(board, &(pos_f, r)), color|SQ_P) && r != pos_r
|
||||
{
|
||||
doubled = true;
|
||||
}
|
||||
// Check for isolated pawns.
|
||||
if
|
||||
isolated &&
|
||||
(
|
||||
// Check on the left file if not on a-file...
|
||||
(
|
||||
pos_f > POS_MIN &&
|
||||
is_piece(get_square(board, &(pos_f - 1, r)), color|SQ_P)
|
||||
) ||
|
||||
// Check on the right file if not on h-file...
|
||||
(
|
||||
pos_f < POS_MAX &&
|
||||
is_piece(get_square(board, &(pos_f + 1, r)), color|SQ_P)
|
||||
)
|
||||
)
|
||||
{
|
||||
isolated = false;
|
||||
}
|
||||
// Check for backward pawns.
|
||||
if backward {
|
||||
if color == SQ_WH && r <= pos_r {
|
||||
if (
|
||||
pos_f > POS_MIN &&
|
||||
is_type(get_square(board, &(pos_f - 1, r)), SQ_P)
|
||||
) || (
|
||||
pos_f < POS_MAX &&
|
||||
is_type(get_square(board, &(pos_f + 1, r)), SQ_P)
|
||||
) {
|
||||
backward = false;
|
||||
}
|
||||
} else if color == SQ_BL && r >= pos_r {
|
||||
if (
|
||||
pos_f > POS_MIN &&
|
||||
is_type(get_square(board, &(pos_f - 1, r)), SQ_P)
|
||||
) || (
|
||||
pos_f < POS_MAX &&
|
||||
is_type(get_square(board, &(pos_f + 1, r)), SQ_P)
|
||||
) {
|
||||
backward = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if doubled {
|
||||
stats.num_doubled_pawns += 1;
|
||||
}
|
||||
if isolated {
|
||||
stats.num_isolated_pawns += 1;
|
||||
}
|
||||
if backward {
|
||||
stats.num_backward_pawns += 1;
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -181,8 +157,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_compute_stats() {
|
||||
// Check that initial stats are correct.
|
||||
let b = new();
|
||||
let gs = rules::GameState::new();
|
||||
let mut b = Board::new();
|
||||
let mut gs = GameState::new();
|
||||
let initial_stats = BoardStats {
|
||||
num_pawns: 8,
|
||||
num_bishops: 2,
|
||||
|
@ -195,56 +171,54 @@ mod tests {
|
|||
num_isolated_pawns: 0,
|
||||
mobility: 20,
|
||||
};
|
||||
let mut stats = compute_stats(&b, &gs);
|
||||
eprintln!("{}", stats.0);
|
||||
eprintln!("{}", stats.1);
|
||||
let mut stats = BoardStats::new_from(&mut b, &mut gs);
|
||||
assert!(stats.0 == stats.1);
|
||||
assert!(stats.0 == initial_stats);
|
||||
|
||||
// Check that doubled pawns are correctly counted.
|
||||
let mut b = new_empty();
|
||||
set_square(&mut b, &pos("d4"), SQ_WH_P);
|
||||
set_square(&mut b, &pos("d6"), SQ_WH_P);
|
||||
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||
let mut b = Board::new_empty();
|
||||
b.set_square(D4, WHITE, PAWN);
|
||||
b.set_square(D6, WHITE, PAWN);
|
||||
stats.0.compute(&mut b, &mut gs);
|
||||
assert_eq!(stats.0.num_doubled_pawns, 2);
|
||||
// Add a pawn on another file, no changes expected.
|
||||
set_square(&mut b, &pos("e6"), SQ_WH_P);
|
||||
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||
b.set_square(E6, WHITE, PAWN);
|
||||
stats.0.compute(&mut b, &mut gs);
|
||||
assert_eq!(stats.0.num_doubled_pawns, 2);
|
||||
// Add a pawn backward in the d-file: there are now 3 doubled pawns.
|
||||
set_square(&mut b, &pos("d2"), SQ_WH_P);
|
||||
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||
b.set_square(D2, WHITE, PAWN);
|
||||
stats.0.compute(&mut b, &mut gs);
|
||||
assert_eq!(stats.0.num_doubled_pawns, 3);
|
||||
|
||||
// Check that isolated and backward pawns are correctly counted.
|
||||
assert_eq!(stats.0.num_isolated_pawns, 0);
|
||||
assert_eq!(stats.0.num_backward_pawns, 2); // A bit weird?
|
||||
// Protect d4 pawn with a friend in e3: it is not isolated nor backward anymore.
|
||||
set_square(&mut b, &pos("e3"), SQ_WH_P);
|
||||
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||
b.set_square(E3, WHITE, PAWN);
|
||||
stats.0.compute(&mut b, &mut gs);
|
||||
assert_eq!(stats.0.num_doubled_pawns, 5);
|
||||
assert_eq!(stats.0.num_isolated_pawns, 0);
|
||||
assert_eq!(stats.0.num_backward_pawns, 1);
|
||||
// Add an adjacent friend to d2 pawn: no pawns are left isolated or backward.
|
||||
set_square(&mut b, &pos("c2"), SQ_WH_P);
|
||||
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||
b.set_square(C2, WHITE, PAWN);
|
||||
stats.0.compute(&mut b, &mut gs);
|
||||
assert_eq!(stats.0.num_doubled_pawns, 5);
|
||||
assert_eq!(stats.0.num_isolated_pawns, 0);
|
||||
assert_eq!(stats.0.num_backward_pawns, 0);
|
||||
// Add an isolated/backward white pawn in a far file.
|
||||
set_square(&mut b, &pos("a2"), SQ_WH_P);
|
||||
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||
b.set_square(A2, WHITE, PAWN);
|
||||
stats.0.compute(&mut b, &mut gs);
|
||||
assert_eq!(stats.0.num_doubled_pawns, 5);
|
||||
assert_eq!(stats.0.num_isolated_pawns, 1);
|
||||
assert_eq!(stats.0.num_backward_pawns, 1);
|
||||
|
||||
// Check for pawns that are backward but not isolated.
|
||||
let mut b = new_empty();
|
||||
let mut b = Board::new_empty();
|
||||
// Here, d4 pawn protects both e5 and e3, but it is backward.
|
||||
set_square(&mut b, &pos("d4"), SQ_WH_P);
|
||||
set_square(&mut b, &pos("e5"), SQ_WH_P);
|
||||
set_square(&mut b, &pos("e3"), SQ_WH_P);
|
||||
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||
b.set_square(D4, WHITE, PAWN);
|
||||
b.set_square(E5, WHITE, PAWN);
|
||||
b.set_square(E3, WHITE, PAWN);
|
||||
stats.0.compute(&mut b, &mut gs);
|
||||
assert_eq!(stats.0.num_doubled_pawns, 2);
|
||||
assert_eq!(stats.0.num_isolated_pawns, 0);
|
||||
assert_eq!(stats.0.num_backward_pawns, 1);
|
||||
|
|
39
src/uci.rs
39
src/uci.rs
|
@ -7,8 +7,8 @@ use std::thread;
|
|||
|
||||
use crate::analysis::AnalysisInfo;
|
||||
use crate::engine;
|
||||
use crate::movement::Move;
|
||||
use crate::notation;
|
||||
use crate::fen;
|
||||
use crate::movement::{Move, UCI_NULL_MOVE_STR};
|
||||
|
||||
const VATU_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
const VATU_AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
||||
|
@ -57,6 +57,11 @@ pub enum UciCmd {
|
|||
Position(Vec<PositionArgs>),
|
||||
Go(Vec<GoArgs>),
|
||||
Quit,
|
||||
|
||||
// Unofficial commands mostly for debugging.
|
||||
VatuNode,
|
||||
VatuMove(String),
|
||||
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
|
@ -64,7 +69,7 @@ pub enum UciCmd {
|
|||
#[derive(Debug, Clone)]
|
||||
pub enum PositionArgs {
|
||||
Startpos,
|
||||
Fen(notation::Fen),
|
||||
Fen(fen::Fen),
|
||||
Moves(Vec<Move>),
|
||||
}
|
||||
|
||||
|
@ -198,6 +203,12 @@ impl Uci {
|
|||
self.send_engine_command(engine::Cmd::Stop);
|
||||
},
|
||||
UciCmd::Quit => return false,
|
||||
UciCmd::VatuNode => {
|
||||
self.send_engine_command(engine::Cmd::LogNode);
|
||||
}
|
||||
UciCmd::VatuMove(m) => {
|
||||
self.send_engine_command(engine::Cmd::LogMove(Move::from_uci_string(m)));
|
||||
}
|
||||
UciCmd::Unknown(c) => { self.log(format!("Unknown command: {}", c)); }
|
||||
}
|
||||
true
|
||||
|
@ -271,7 +282,7 @@ impl Uci {
|
|||
s.push_str(&format!(" nps {}", n));
|
||||
}
|
||||
AnalysisInfo::CurrentMove(m) => {
|
||||
s.push_str(&format!(" currmove {}", notation::move_to_string(m)));
|
||||
s.push_str(&format!(" currmove {}", m.to_uci_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -280,11 +291,10 @@ impl Uci {
|
|||
|
||||
/// Send best move.
|
||||
fn send_bestmove(&mut self, m: &Option<Move>) {
|
||||
let move_str = match m {
|
||||
Some(m) => notation::move_to_string(m),
|
||||
None => notation::NULL_MOVE.to_string(),
|
||||
};
|
||||
self.send(&format!("bestmove {}", move_str));
|
||||
self.send(&format!(
|
||||
"bestmove {}",
|
||||
if let Some(m) = m { m.to_uci_string() } else { UCI_NULL_MOVE_STR.to_string() }
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,6 +302,9 @@ impl Uci {
|
|||
// UCI command parsers
|
||||
|
||||
/// Parse an UCI command.
|
||||
///
|
||||
/// Handle main UCI commands: position, go, etc. The command "p" is an
|
||||
/// alias to "position".
|
||||
fn parse_command(s: &str) -> UciCmd {
|
||||
if s.len() == 0 {
|
||||
return UciCmd::Unknown("Empty command.".to_string());
|
||||
|
@ -303,9 +316,11 @@ fn parse_command(s: &str) -> UciCmd {
|
|||
"isready" => UciCmd::IsReady,
|
||||
"ucinewgame" => UciCmd::UciNewGame,
|
||||
"stop" => UciCmd::Stop,
|
||||
"position" => parse_position_command(&fields[1..]),
|
||||
"position" | "p" => parse_position_command(&fields[1..]),
|
||||
"go" => parse_go_command(&fields[1..]),
|
||||
"quit" => UciCmd::Quit,
|
||||
"vatunode" => UciCmd::VatuNode,
|
||||
"vatumove" => UciCmd::VatuMove(fields[1].to_string()),
|
||||
c => UciCmd::Unknown(c.to_string()),
|
||||
}
|
||||
}
|
||||
|
@ -319,7 +334,7 @@ fn parse_position_command(fields: &[&str]) -> UciCmd {
|
|||
match fields[i] {
|
||||
// Subcommand "fen" is followed by a FEN string.
|
||||
"fen" => {
|
||||
if let Some(fen) = notation::parse_fen_fields(&fields[i + 1 .. i + 7]) {
|
||||
if let Some(fen) = fen::parse_fen_fields(&fields[i + 1 .. i + 7]) {
|
||||
subcommands.push(PositionArgs::Fen(fen))
|
||||
} else {
|
||||
return UciCmd::Unknown(format!("Bad format for position fen"))
|
||||
|
@ -332,7 +347,7 @@ fn parse_position_command(fields: &[&str]) -> UciCmd {
|
|||
"moves" => {
|
||||
let mut moves = vec!();
|
||||
while i + 1 < num_fields {
|
||||
moves.push(notation::parse_move(fields[i + 1]));
|
||||
moves.push(Move::from_uci_string(fields[i + 1]));
|
||||
i += 1;
|
||||
}
|
||||
subcommands.push(PositionArgs::Moves(moves));
|
||||
|
|
Reference in a new issue