Compare commits
No commits in common. "log_move" and "master" have entirely different histories.
80
Cargo.lock
generated
80
Cargo.lock
generated
|
@ -1,5 +1,13 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# 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]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
@ -23,6 +31,11 @@ name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.1"
|
version = "2.33.1"
|
||||||
|
@ -37,6 +50,44 @@ dependencies = [
|
||||||
"vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
|
@ -50,6 +101,20 @@ name = "libc"
|
||||||
version = "0.2.71"
|
version = "0.2.71"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
@ -73,6 +138,7 @@ name = "vatu"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
|
@ -80,6 +146,11 @@ name = "vec_map"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
@ -100,16 +171,25 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[metadata]
|
[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 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 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 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 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 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 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 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 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 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 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 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-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"
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
|
@ -6,3 +6,4 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
|
dashmap = "3.11"
|
||||||
|
|
18
README.md
18
README.md
|
@ -67,8 +67,6 @@ cp external/lichess-bot/config.yml.example /tmp/vatu-config/config.yml
|
||||||
docker build -f res/docker/Dockerfile -t vatu .
|
docker build -f res/docker/Dockerfile -t vatu .
|
||||||
# Run with the config folder mounted at /config.
|
# Run with the config folder mounted at /config.
|
||||||
docker run -v /tmp/vatu-config:/config -ti vatu
|
docker run -v /tmp/vatu-config:/config -ti vatu
|
||||||
# In the container, use the following command:
|
|
||||||
python lichess-bot.py --config /config/config.yml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,12 +74,10 @@ python lichess-bot.py --config /config/config.yml
|
||||||
TODO
|
TODO
|
||||||
----
|
----
|
||||||
|
|
||||||
- [X] Support time constraints
|
- Support time constraints
|
||||||
- [X] Unmake mechanism instead of allocating nodes like there is no tomorrow
|
- Proper unmake mechanism instead of allocating boards like there is no tomorrow
|
||||||
- [X] Precompute some pieces moves, maybe (done for knights)
|
- Precompute some pieces moves, maybe
|
||||||
- [ ] Transposition table that does not actually slows search down
|
- Transposition table that does not actually slows search down
|
||||||
- [ ] Check Zobrist hashes for previous point
|
- Check Zobrist hashes for previous point
|
||||||
- [X] Actual bitboard
|
- Actual bitboard
|
||||||
- [ ] Some kind of move ordering could be great
|
- Multithreading (never)
|
||||||
- [ ] Multithreading (never)
|
|
||||||
- [ ] Avoid 3-fold repetitions when winning
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
#!/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())))
|
|
|
@ -1,30 +0,0 @@
|
||||||
#!/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())))
|
|
|
@ -1,39 +0,0 @@
|
||||||
#!/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])))
|
|
|
@ -1,38 +0,0 @@
|
||||||
#!/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])))
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/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,6 +7,7 @@ use crate::board;
|
||||||
use crate::engine;
|
use crate::engine;
|
||||||
use crate::movement::Move;
|
use crate::movement::Move;
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
use crate::notation;
|
||||||
use crate::rules;
|
use crate::rules;
|
||||||
use crate::stats;
|
use crate::stats;
|
||||||
|
|
||||||
|
@ -49,20 +50,6 @@ pub struct AnalysisParams {
|
||||||
pub black_time: i32,
|
pub black_time: i32,
|
||||||
pub white_inc: i32,
|
pub white_inc: i32,
|
||||||
pub black_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.
|
/// Analysis info to report.
|
||||||
|
@ -117,27 +104,27 @@ impl Analyzer {
|
||||||
|
|
||||||
if self.debug {
|
if self.debug {
|
||||||
self.log(format!("Analyzing node:\n{}", &self.node));
|
self.log(format!("Analyzing node:\n{}", &self.node));
|
||||||
let moves = self.node.get_player_moves();
|
let moves = self.node.get_player_moves(true);
|
||||||
self.log(format!("Legal moves: {}", Move::list_to_uci_string(&moves)));
|
self.log(format!("Legal moves: {}", notation::move_list_to_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.log(format!("Move time: {}", self.time_limit));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.start_time = Some(Instant::now());
|
self.start_time = Some(Instant::now());
|
||||||
self.current_per_second_timer = Some(Instant::now());
|
self.current_per_second_timer = Some(Instant::now());
|
||||||
let (max_score, best_move) = self.negamax(&mut self.node.clone(), MIN_F32, MAX_F32, 0);
|
let (max_score, best_move) = self.negamax(&self.node.clone(), MIN_F32, MAX_F32, 0);
|
||||||
|
|
||||||
if let Some(m) = best_move {
|
if best_move.is_some() {
|
||||||
let log_str = format!("Best move {} evaluated {}", m.to_uci_string(), max_score);
|
let log_str = format!(
|
||||||
|
"Best move {} evaluated {}",
|
||||||
|
notation::move_to_string(&best_move.unwrap()), max_score
|
||||||
|
);
|
||||||
self.log(log_str);
|
self.log(log_str);
|
||||||
self.report_best_move(Some(m));
|
self.report_best_move(best_move);
|
||||||
} else {
|
} else {
|
||||||
// If no best move could be found, checkmate is unavoidable; send the first legal move.
|
// If no best move could be found, checkmate is unavoidable; send the first legal move.
|
||||||
self.log("Checkmate is unavoidable.".to_string());
|
self.log("Checkmate is unavoidable.".to_string());
|
||||||
let moves = rules::get_player_moves(&mut self.node.board, &mut self.node.game_state);
|
let moves = rules::get_player_moves(&self.node.board, &self.node.game_state, true);
|
||||||
let m = if moves.len() > 0 { Some(moves[0].clone()) } else { None };
|
let m = if moves.len() > 0 { Some(moves[0]) } else { None };
|
||||||
self.report_best_move(m);
|
self.report_best_move(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,7 +135,7 @@ impl Analyzer {
|
||||||
self.time_limit = if args.move_time != -1 {
|
self.time_limit = if args.move_time != -1 {
|
||||||
args.move_time
|
args.move_time
|
||||||
} else {
|
} else {
|
||||||
let (time, inc) = if self.node.game_state.color == board::WHITE {
|
let (time, inc) = if board::is_white(self.node.game_state.color) {
|
||||||
(args.white_time, args.white_inc)
|
(args.white_time, args.white_inc)
|
||||||
} else {
|
} else {
|
||||||
(args.black_time, args.black_inc)
|
(args.black_time, args.black_inc)
|
||||||
|
@ -175,7 +162,7 @@ impl Analyzer {
|
||||||
/// lower score bound and `beta` the upper bound.
|
/// lower score bound and `beta` the upper bound.
|
||||||
fn negamax(
|
fn negamax(
|
||||||
&mut self,
|
&mut self,
|
||||||
node: &mut Node,
|
node: &Node,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
beta: f32,
|
beta: f32,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
|
@ -202,18 +189,18 @@ impl Analyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get negamax for playable moves.
|
// Get negamax for playable moves.
|
||||||
let mut moves = node.get_player_moves();
|
let moves = node.get_player_moves(true);
|
||||||
let mut alpha = alpha;
|
let mut alpha = alpha;
|
||||||
let mut best_score = MIN_F32;
|
let mut best_score = MIN_F32;
|
||||||
let mut best_move = None;
|
let mut best_move = None;
|
||||||
for m in &mut moves {
|
for m in moves {
|
||||||
node.apply_move(m);
|
let mut sub_node = node.clone();
|
||||||
let result = self.negamax(node, -beta, -alpha, depth + 1);
|
sub_node.apply_move(&m);
|
||||||
node.unmake_move(m);
|
let result = self.negamax(&sub_node, -beta, -alpha, depth + 1);
|
||||||
let score = -result.0;
|
let score = -result.0;
|
||||||
if score > best_score {
|
if score > best_score {
|
||||||
best_score = score;
|
best_score = score;
|
||||||
best_move = Some(m.to_owned());
|
best_move = Some(m);
|
||||||
}
|
}
|
||||||
if best_score > alpha {
|
if best_score > alpha {
|
||||||
alpha = best_score;
|
alpha = best_score;
|
||||||
|
|
1011
src/board.rs
1011
src/board.rs
File diff suppressed because it is too large
Load diff
|
@ -1,53 +1,19 @@
|
||||||
//! Castling flags.
|
//! Castling flags.
|
||||||
|
|
||||||
use crate::board::{Bitboard, RANK_1, RANK_8};
|
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;
|
||||||
|
|
||||||
pub type Castle = u8;
|
/// Castling sides parameters.
|
||||||
|
|
||||||
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.
|
|
||||||
///
|
///
|
||||||
/// This includes the original king position, its target square and
|
/// For both sides, the 3-uple contains files that should be empty
|
||||||
/// the square in between.
|
/// and not attacked, an optional file that should be empty for
|
||||||
pub const CASTLE_LEGALITY_PATHS: [[Bitboard; 2]; 2] = [
|
/// queen-side, and the castling side-mask.
|
||||||
[
|
pub const CASTLING_SIDES: [([i8; 2], Option<i8>, u8); 2] =
|
||||||
0b00000000_00000001_00000001_00000001_00000000_00000000_00000000_00000000, // White Kside.
|
[([5i8, 6i8], None, CASTLING_K_MASK), ([3i8, 2i8], Some(1i8), CASTLING_Q_MASK)];
|
||||||
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::analysis;
|
||||||
use crate::board;
|
use crate::board;
|
||||||
use crate::castling;
|
use crate::castling;
|
||||||
use crate::fen;
|
use crate::movement::{self, Move};
|
||||||
use crate::movement::Move;
|
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
use crate::notation;
|
||||||
use crate::uci;
|
use crate::uci;
|
||||||
|
|
||||||
/// Analysis engine.
|
/// Analysis engine.
|
||||||
|
@ -62,10 +62,6 @@ pub enum Cmd {
|
||||||
WorkerInfo(Vec<analysis::AnalysisInfo>),
|
WorkerInfo(Vec<analysis::AnalysisInfo>),
|
||||||
/// Send best move found by analysis worker.
|
/// Send best move found by analysis worker.
|
||||||
WorkerBestMove(Option<Move>),
|
WorkerBestMove(Option<Move>),
|
||||||
/// Log current node.
|
|
||||||
LogNode,
|
|
||||||
/// Log debug information about a move.
|
|
||||||
LogMove(Move),
|
|
||||||
|
|
||||||
// Commands that can be sent by the engine.
|
// Commands that can be sent by the engine.
|
||||||
|
|
||||||
|
@ -127,10 +123,7 @@ impl Engine {
|
||||||
// Workers commands.
|
// Workers commands.
|
||||||
Cmd::Log(s) => self.reply(Cmd::Log(s.to_string())),
|
Cmd::Log(s) => self.reply(Cmd::Log(s.to_string())),
|
||||||
Cmd::WorkerInfo(infos) => self.reply(Cmd::Info(infos.to_vec())),
|
Cmd::WorkerInfo(infos) => self.reply(Cmd::Info(infos.to_vec())),
|
||||||
Cmd::WorkerBestMove(m) => self.reply(Cmd::BestMove(m.clone())),
|
Cmd::WorkerBestMove(m) => self.reply(Cmd::BestMove(*m)),
|
||||||
// Other commands.
|
|
||||||
Cmd::LogNode => self.log_node(),
|
|
||||||
Cmd::LogMove(m) => self.log_move(m),
|
|
||||||
_ => eprintln!("Not an engine input command: {:?}", cmd),
|
_ => eprintln!("Not an engine input command: {:?}", cmd),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,30 +141,29 @@ impl Engine {
|
||||||
/// Apply a FEN string to the engine state, replacing it.
|
/// Apply a FEN string to the engine state, replacing it.
|
||||||
///
|
///
|
||||||
/// For speed purposes, it assumes values are always valid.
|
/// For speed purposes, it assumes values are always valid.
|
||||||
fn apply_fen(&mut self, fen: &fen::Fen) {
|
fn apply_fen(&mut self, fen: ¬ation::Fen) {
|
||||||
// Placement.
|
// Placement.
|
||||||
self.node.board = board::Board::new_from_fen(&fen.placement);
|
self.node.board = board::new_from_fen(&fen.placement);
|
||||||
// Color.
|
// Color.
|
||||||
match fen.color.chars().next().unwrap() {
|
match fen.color.chars().next().unwrap() {
|
||||||
'w' => self.node.game_state.color = board::WHITE,
|
'w' => self.node.game_state.color = board::SQ_WH,
|
||||||
'b' => self.node.game_state.color = board::BLACK,
|
'b' => self.node.game_state.color = board::SQ_BL,
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
// Castling.
|
// Castling.
|
||||||
self.node.game_state.castling = 0;
|
|
||||||
for c in fen.castling.chars() {
|
for c in fen.castling.chars() {
|
||||||
match c {
|
match c {
|
||||||
'K' => self.node.game_state.castling |= castling::CASTLE_WH_K,
|
'K' => self.node.game_state.castling |= castling::CASTLING_WH_K,
|
||||||
'Q' => self.node.game_state.castling |= castling::CASTLE_WH_Q,
|
'Q' => self.node.game_state.castling |= castling::CASTLING_WH_Q,
|
||||||
'k' => self.node.game_state.castling |= castling::CASTLE_BL_K,
|
'k' => self.node.game_state.castling |= castling::CASTLING_BL_K,
|
||||||
'q' => self.node.game_state.castling |= castling::CASTLE_BL_Q,
|
'q' => self.node.game_state.castling |= castling::CASTLING_BL_Q,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// En passant.
|
// En passant.
|
||||||
self.node.game_state.en_passant = match fen.en_passant.as_ref() {
|
self.node.game_state.en_passant = match fen.en_passant.as_ref() {
|
||||||
"-" => None,
|
"-" => None,
|
||||||
s => Some(board::sq_from_string(s)),
|
p => Some(board::pos(p)),
|
||||||
};
|
};
|
||||||
// Half moves.
|
// Half moves.
|
||||||
self.node.game_state.halfmove = fen.halfmove.parse::<i32>().ok().unwrap();
|
self.node.game_state.halfmove = fen.halfmove.parse::<i32>().ok().unwrap();
|
||||||
|
@ -180,8 +172,13 @@ impl Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a series of moves to the current node.
|
/// Apply a series of moves to the current node.
|
||||||
fn apply_moves(&mut self, moves: &mut Vec<Move>) {
|
fn apply_moves(&mut self, moves: &Vec<Move>) {
|
||||||
moves.iter_mut().for_each(|m| m.apply_to(&mut self.node.board, &mut self.node.game_state));
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start working on board, returning the best move found.
|
/// Start working on board, returning the best move found.
|
||||||
|
@ -203,7 +200,10 @@ impl Engine {
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
self.working.store(false, atomic::Ordering::SeqCst);
|
self.working.store(false, atomic::Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UCI commands management.
|
||||||
|
impl Engine {
|
||||||
/// Setup engine for UCI communication.
|
/// Setup engine for UCI communication.
|
||||||
pub fn setup_uci(&mut self, uci_s: mpsc::Sender<uci::Cmd>) {
|
pub fn setup_uci(&mut self, uci_s: mpsc::Sender<uci::Cmd>) {
|
||||||
// Create a channel to receive commands from Uci.
|
// Create a channel to receive commands from Uci.
|
||||||
|
@ -221,11 +221,11 @@ impl Engine {
|
||||||
self.apply_fen(&fen);
|
self.apply_fen(&fen);
|
||||||
},
|
},
|
||||||
uci::PositionArgs::Startpos => {
|
uci::PositionArgs::Startpos => {
|
||||||
let fen = fen::parse_fen(fen::FEN_START).unwrap();
|
let fen = notation::parse_fen(notation::FEN_START).unwrap();
|
||||||
self.apply_fen(&fen);
|
self.apply_fen(&fen);
|
||||||
},
|
},
|
||||||
uci::PositionArgs::Moves(moves) => {
|
uci::PositionArgs::Moves(moves) => {
|
||||||
self.apply_moves(&mut moves.clone());
|
self.apply_moves(&moves);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,13 @@ impl Engine {
|
||||||
|
|
||||||
/// Start working using parameters passed with a "go" command.
|
/// Start working using parameters passed with a "go" command.
|
||||||
fn uci_go(&mut self, g_args: &Vec<uci::GoArgs>) {
|
fn uci_go(&mut self, g_args: &Vec<uci::GoArgs>) {
|
||||||
let mut args = analysis::AnalysisParams::new();
|
let mut args = analysis::AnalysisParams {
|
||||||
|
move_time: -1,
|
||||||
|
white_time: -1,
|
||||||
|
black_time: -1,
|
||||||
|
white_inc: -1,
|
||||||
|
black_inc: -1,
|
||||||
|
};
|
||||||
for arg in g_args {
|
for arg in g_args {
|
||||||
match arg {
|
match arg {
|
||||||
uci::GoArgs::MoveTime(ms) => args.move_time = *ms,
|
uci::GoArgs::MoveTime(ms) => args.move_time = *ms,
|
||||||
|
@ -247,22 +253,4 @@ impl Engine {
|
||||||
}
|
}
|
||||||
self.work(&args);
|
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
55
src/fen.rs
|
@ -1,55 +0,0 @@
|
||||||
//! 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,10 +4,9 @@ pub mod analysis;
|
||||||
pub mod board;
|
pub mod board;
|
||||||
pub mod castling;
|
pub mod castling;
|
||||||
pub mod engine;
|
pub mod engine;
|
||||||
pub mod fen;
|
|
||||||
pub mod movement;
|
pub mod movement;
|
||||||
pub mod node;
|
pub mod node;
|
||||||
pub mod precomputed;
|
pub mod notation;
|
||||||
pub mod rules;
|
pub mod rules;
|
||||||
pub mod stats;
|
pub mod stats;
|
||||||
pub mod uci;
|
pub mod uci;
|
||||||
|
|
469
src/movement.rs
469
src/movement.rs
|
@ -1,333 +1,230 @@
|
||||||
//! Move functions along with some castling helpers.
|
//! Move functions along with some castling helpers.
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use crate::board::*;
|
use crate::board::*;
|
||||||
use crate::castling::*;
|
use crate::castling::*;
|
||||||
use crate::rules::GameState;
|
use crate::rules;
|
||||||
|
|
||||||
|
const START_WH_K_POS: Pos = pos("e1");
|
||||||
|
const START_BL_K_POS: Pos = pos("e8");
|
||||||
|
|
||||||
/// A movement, with before/after positions and optional promotion.
|
/// A movement, with before/after positions and optional promotion.
|
||||||
#[derive(Clone, PartialEq)]
|
pub type Move = (Pos, Pos, Option<u8>);
|
||||||
pub struct Move {
|
|
||||||
/// Square from which a piece moves.
|
/// Apply a move `m` to copies to `board` and `game_state`.
|
||||||
pub source: Square,
|
///
|
||||||
/// Square to which a piece moves.
|
/// Can be used for conveniance but it's better to write in existing
|
||||||
pub dest: Square,
|
/// instances as often as possible using `apply_move_to`.
|
||||||
/// Promotion piece for pawns reaching the last rank.
|
pub fn apply_move(
|
||||||
pub promotion: Option<Piece>,
|
board: &Board,
|
||||||
/// Captured piece, if any.
|
game_state: &rules::GameState,
|
||||||
pub capture: Option<Piece>,
|
m: &Move
|
||||||
/// Castle options before the move. This is set when the move is first applied.
|
) -> (Board, rules::GameState) {
|
||||||
pub old_castles: Castle,
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Move {
|
/// Update `board` and `game_state` to reflect the move `m`.
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
///
|
||||||
write!(f, "{}", self.to_uci_string())
|
/// The board is updated with correct piece placement.
|
||||||
}
|
///
|
||||||
}
|
/// The game state is updated with the new player turn and the new
|
||||||
|
/// castling options.
|
||||||
/// Null move string in UCI exchanges.
|
pub fn apply_move_to(
|
||||||
pub const UCI_NULL_MOVE_STR: &str = "0000";
|
board: &mut Board,
|
||||||
|
game_state: &mut rules::GameState,
|
||||||
impl Move {
|
m: &Move
|
||||||
/// Build a move from `source` to `dest`, no promotion.
|
) {
|
||||||
pub const fn new(source: Square, dest: Square) -> Move {
|
// If a rook is taken, remove its castling option. Needs to be checked before we update board.
|
||||||
Move { source, dest, promotion: None, capture: None, old_castles: 0 }
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a move from `source` to `dest`, with a promotion.
|
// Update board and game state.
|
||||||
pub const fn new_promotion(source: Square, dest: Square, promotion: Piece) -> Move {
|
apply_move_to_board(board, m);
|
||||||
Move { source, dest, promotion: Some(promotion), capture: None, old_castles: 0 }
|
game_state.color = opposite(game_state.color);
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply this move to `board` and `game_state`.
|
// If the move is a castle, remove it from castling options.
|
||||||
///
|
if let Some(castle) = get_castle(m) {
|
||||||
/// 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
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.") }
|
|
||||||
}
|
|
||||||
game_state.color = opposite(game_state.color);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
match castle {
|
||||||
CASTLE_WH_Q => Move::new(E1, C1),
|
CASTLING_WH_K | CASTLING_WH_Q => game_state.castling &= !CASTLING_WH_MASK,
|
||||||
CASTLE_WH_K => Move::new(E1, G1),
|
CASTLING_BL_K | CASTLING_BL_Q => game_state.castling &= !CASTLING_BL_MASK,
|
||||||
CASTLE_BL_Q => Move::new(E8, C8),
|
_ => {}
|
||||||
CASTLE_BL_K => Move::new(E8, G8),
|
};
|
||||||
_ => panic!("Illegal castling requested: {:08b}", 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse an UCI move algebraic notation string to a Move.
|
/// Apply a move `m` into `board`.
|
||||||
pub fn from_uci_string(m_str: &str) -> Move {
|
pub fn apply_move_to_board(board: &mut Board, m: &Move) {
|
||||||
Move {
|
if let Some(castle) = get_castle(m) {
|
||||||
source: sq_from_string(&m_str[0..2]),
|
match castle {
|
||||||
dest: sq_from_string(&m_str[2..4]),
|
CASTLING_WH_K => {
|
||||||
promotion: if m_str.len() == 5 {
|
move_piece(board, &START_WH_K_POS, &pos("g1"));
|
||||||
Some(match m_str.as_bytes()[4] {
|
move_piece(board, &pos("h1"), &pos("f1"));
|
||||||
b'b' => BISHOP,
|
}
|
||||||
b'n' => KNIGHT,
|
CASTLING_WH_Q => {
|
||||||
b'r' => ROOK,
|
move_piece(board, &START_WH_K_POS, &pos("c1"));
|
||||||
b'q' => QUEEN,
|
move_piece(board, &pos("a1"), &pos("d1"));
|
||||||
_ => panic!("What is the opponent doing? This is illegal, I'm out."),
|
}
|
||||||
})
|
CASTLING_BL_K => {
|
||||||
} else {
|
move_piece(board, &START_BL_K_POS, &pos("g8"));
|
||||||
None
|
move_piece(board, &pos("h8"), &pos("f8"));
|
||||||
},
|
}
|
||||||
capture: None,
|
CASTLING_BL_Q => {
|
||||||
old_castles: 0,
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a string containing the UCI algebraic notation of this move.
|
/// Get the corresponding castling flag for this move.
|
||||||
pub fn to_uci_string(&self) -> String {
|
pub fn get_castle(m: &Move) -> Option<u8> {
|
||||||
let mut move_string = String::new();
|
if m.0 == pos("e1") {
|
||||||
move_string.push_str(&sq_to_string(self.source));
|
if m.1 == pos("c1") {
|
||||||
move_string.push_str(&sq_to_string(self.dest));
|
Some(CASTLING_WH_Q)
|
||||||
if let Some(piece) = self.promotion {
|
} else if m.1 == pos("g1") {
|
||||||
move_string.push(match piece {
|
Some(CASTLING_WH_K)
|
||||||
QUEEN => 'q',
|
} else {
|
||||||
BISHOP => 'b',
|
None
|
||||||
KNIGHT => 'n',
|
|
||||||
ROOK => 'r',
|
|
||||||
_ => panic!("What are you doing? Promote to a legal piece.")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
move_string
|
} 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
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Debug only: create a space-separated string of moves.
|
/// Get the move for this castle.
|
||||||
pub(crate) fn list_to_uci_string(moves: &Vec<Move>) -> String {
|
pub fn get_castle_move(castle: u8) -> Move {
|
||||||
moves.iter().map(|m| m.to_uci_string()).collect::<Vec<_>>().join(" ")
|
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),
|
||||||
|
_ => panic!("Illegal castling requested: {:08b}", castle),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::notation::parse_move;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_to_board() {
|
fn test_apply_move_to_board() {
|
||||||
let mut b = Board::new_empty();
|
let mut b = new_empty();
|
||||||
let mut gs = GameState::new();
|
|
||||||
|
|
||||||
// Put 2 enemy knights on board.
|
// Put 2 enemy knights on board.
|
||||||
b.set_square(D4, WHITE, KNIGHT);
|
set_square(&mut b, &pos("d4"), SQ_WH_N);
|
||||||
b.set_square(F4, BLACK, KNIGHT);
|
set_square(&mut b, &pos("f4"), SQ_BL_N);
|
||||||
// Move white knight in a position attacked by black knight.
|
// Move white knight in a position attacked by black knight.
|
||||||
let mut m = Move::new(D4, E6);
|
apply_move_to_board(&mut b, &(pos("d4"), pos("e6"), None));
|
||||||
m.apply_to(&mut b, &mut gs);
|
assert_eq!(get_square(&b, &pos("d4")), SQ_E);
|
||||||
assert!(b.is_empty(D4));
|
assert_eq!(get_square(&b, &pos("e6")), SQ_WH_N);
|
||||||
assert_eq!(b.get_color_on(E6), WHITE);
|
assert_eq!(num_pieces(&b), 2);
|
||||||
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
|
// Sack it with black knight
|
||||||
let mut m = Move::new(F4, E6);
|
apply_move_to_board(&mut b, &(pos("f4"), pos("e6"), None));
|
||||||
m.apply_to(&mut b, &mut gs);
|
assert_eq!(get_square(&b, &pos("e6")), SQ_BL_N);
|
||||||
assert_eq!(b.get_color_on(E6), BLACK);
|
assert_eq!(num_pieces(&b), 1);
|
||||||
assert_eq!(b.get_piece_on(E6), KNIGHT);
|
|
||||||
assert_eq!(count_bits(b.combined()), 1);
|
|
||||||
assert_eq!(m.capture.unwrap(), KNIGHT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_to_castling() {
|
fn test_apply_move_to_castling() {
|
||||||
let mut b = Board::new();
|
let mut b = new();
|
||||||
let mut gs = GameState::new();
|
let mut gs = rules::GameState::new();
|
||||||
assert_eq!(gs.castling, CASTLE_MASK);
|
assert_eq!(gs.castling, CASTLING_MASK);
|
||||||
|
|
||||||
// On a starting board, start by making place for all castles.
|
// On a starting board, start by making place for all castles.
|
||||||
b.clear_square(B1, WHITE, KNIGHT);
|
clear_square(&mut b, &pos("b1"));
|
||||||
b.clear_square(C1, WHITE, BISHOP);
|
clear_square(&mut b, &pos("c1"));
|
||||||
b.clear_square(D1, WHITE, QUEEN);
|
clear_square(&mut b, &pos("d1"));
|
||||||
b.clear_square(F1, WHITE, BISHOP);
|
clear_square(&mut b, &pos("f1"));
|
||||||
b.clear_square(G1, WHITE, KNIGHT);
|
clear_square(&mut b, &pos("g1"));
|
||||||
b.clear_square(B8, BLACK, KNIGHT);
|
clear_square(&mut b, &pos("b8"));
|
||||||
b.clear_square(C8, BLACK, BISHOP);
|
clear_square(&mut b, &pos("c8"));
|
||||||
b.clear_square(D8, BLACK, QUEEN);
|
clear_square(&mut b, &pos("d8"));
|
||||||
b.clear_square(F8, BLACK, BISHOP);
|
clear_square(&mut b, &pos("f8"));
|
||||||
b.clear_square(G8, BLACK, KNIGHT);
|
clear_square(&mut b, &pos("g8"));
|
||||||
// White queen-side castling.
|
// White queen-side castling.
|
||||||
Move::new(E1, C1).apply_to(&mut b, &mut gs);
|
apply_move_to(&mut b, &mut gs, &parse_move("e1c1"));
|
||||||
assert_eq!(b.get_color_on(C1), WHITE);
|
assert!(is_piece(get_square(&b, &pos("c1")), SQ_WH_K));
|
||||||
assert_eq!(b.get_piece_on(C1), KING);
|
assert!(is_piece(get_square(&b, &pos("d1")), SQ_WH_R));
|
||||||
assert_eq!(b.get_color_on(D1), WHITE);
|
assert!(is_empty(&b, &pos("a1")));
|
||||||
assert_eq!(b.get_piece_on(D1), ROOK);
|
assert!(is_empty(&b, &pos("e1")));
|
||||||
assert!(b.is_empty(A1));
|
assert_eq!(gs.castling, CASTLING_BL_MASK);
|
||||||
assert!(b.is_empty(E1));
|
|
||||||
assert_eq!(gs.castling, CASTLE_BL_MASK);
|
|
||||||
// Black king-side castling.
|
// Black king-side castling.
|
||||||
Move::new(E8, G8).apply_to(&mut b, &mut gs);
|
apply_move_to(&mut b, &mut gs, &parse_move("e8g8"));
|
||||||
assert_eq!(b.get_color_on(G8), BLACK);
|
assert!(is_piece(get_square(&b, &pos("g8")), SQ_BL_K));
|
||||||
assert_eq!(b.get_piece_on(G8), KING);
|
assert!(is_piece(get_square(&b, &pos("f8")), SQ_BL_R));
|
||||||
assert_eq!(b.get_color_on(F8), BLACK);
|
assert!(is_empty(&b, &pos("h8")));
|
||||||
assert_eq!(b.get_piece_on(F8), ROOK);
|
assert!(is_empty(&b, &pos("e8")));
|
||||||
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);
|
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]
|
#[test]
|
||||||
fn test_get_castle() {
|
fn test_get_castle() {
|
||||||
assert_eq!(Move::new(E1, C1).get_castle(), Some(CASTLE_WH_Q));
|
assert_eq!(get_castle(&parse_move("e1c1")), Some(CASTLING_WH_Q));
|
||||||
assert_eq!(Move::new(E1, G1).get_castle(), Some(CASTLE_WH_K));
|
assert_eq!(get_castle(&parse_move("e1g1")), Some(CASTLING_WH_K));
|
||||||
assert_eq!(Move::new(E8, C8).get_castle(), Some(CASTLE_BL_Q));
|
assert_eq!(get_castle(&parse_move("e8c8")), Some(CASTLING_BL_Q));
|
||||||
assert_eq!(Move::new(E8, G8).get_castle(), Some(CASTLE_BL_K));
|
assert_eq!(get_castle(&parse_move("e8g8")), Some(CASTLING_BL_K));
|
||||||
assert_eq!(Move::new(D2, D4).get_castle(), None);
|
assert_eq!(get_castle(&parse_move("d2d4")), 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,12 +1,13 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use crate::board;
|
use crate::board;
|
||||||
use crate::movement::Move;
|
use crate::movement::{self, Move};
|
||||||
use crate::rules;
|
use crate::rules;
|
||||||
use crate::stats;
|
use crate::stats;
|
||||||
|
|
||||||
/// Analysis node: a board along with the game state.
|
/// Analysis node: a board along with the game state.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
/// Board for this node.
|
/// Board for this node.
|
||||||
pub board: board::Board,
|
pub board: board::Board,
|
||||||
|
@ -18,28 +19,24 @@ impl Node {
|
||||||
/// Create a new node for an empty board and a new game state.
|
/// Create a new node for an empty board and a new game state.
|
||||||
pub fn new() -> Node {
|
pub fn new() -> Node {
|
||||||
Node {
|
Node {
|
||||||
board: board::Board::new_empty(),
|
board: board::new_empty(),
|
||||||
game_state: rules::GameState::new(),
|
game_state: rules::GameState::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a move to this node.
|
/// Apply a move to this node.
|
||||||
pub fn apply_move(&mut self, m: &mut Move) {
|
pub fn apply_move(&mut self, m: &Move) {
|
||||||
m.apply_to(&mut self.board, &mut self.game_state);
|
movement::apply_move_to(&mut self.board, &mut self.game_state, m);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unmake_move(&mut self, m: &Move) {
|
|
||||||
m.unmake(&mut self.board, &mut self.game_state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return player moves from this node.
|
/// Return player moves from this node.
|
||||||
pub fn get_player_moves(&mut self) -> Vec<Move> {
|
pub fn get_player_moves(&self, commit: bool) -> Vec<Move> {
|
||||||
rules::get_player_moves(&mut self.board, &mut self.game_state)
|
rules::get_player_moves(&self.board, &self.game_state, commit)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute stats for both players for this node.
|
/// Compute stats for both players for this node.
|
||||||
pub fn compute_stats(&mut self) -> (stats::BoardStats, stats::BoardStats) {
|
pub fn compute_stats(&self) -> (stats::BoardStats, stats::BoardStats) {
|
||||||
stats::BoardStats::new_from(&mut self.board, &mut self.game_state)
|
stats::compute_stats(&self.board, &self.game_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +53,29 @@ impl fmt::Debug for Node {
|
||||||
impl fmt::Display for Node {
|
impl fmt::Display for Node {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let mut s = vec!();
|
let mut s = vec!();
|
||||||
self.board.draw_to(&mut s);
|
board::draw(&self.board, &mut s);
|
||||||
let board_drawing = String::from_utf8_lossy(&s).to_string();
|
let board_drawing = String::from_utf8_lossy(&s).to_string();
|
||||||
write!(f, "{}{}", board_drawing, self.game_state)
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
111
src/notation.rs
Normal file
111
src/notation.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
//! 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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,482 +0,0 @@
|
||||||
//! 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,
|
|
||||||
];
|
|
866
src/rules.rs
866
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.
|
//! Board statistics used for heuristics.
|
||||||
|
|
||||||
use crate::board::*;
|
use crate::board::*;
|
||||||
use crate::rules::{GameState, get_player_moves};
|
use crate::rules;
|
||||||
|
|
||||||
/// Storage for board pieces stats.
|
/// Storage for board pieces stats.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -27,20 +27,6 @@ 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) {
|
pub fn reset(&mut self) {
|
||||||
self.num_pawns = 0;
|
self.num_pawns = 0;
|
||||||
self.num_bishops = 0;
|
self.num_bishops = 0;
|
||||||
|
@ -53,88 +39,6 @@ impl BoardStats {
|
||||||
self.num_isolated_pawns = 0;
|
self.num_isolated_pawns = 0;
|
||||||
self.mobility = 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 {
|
impl std::fmt::Display for BoardStats {
|
||||||
|
@ -150,6 +54,126 @@ 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -157,8 +181,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_compute_stats() {
|
fn test_compute_stats() {
|
||||||
// Check that initial stats are correct.
|
// Check that initial stats are correct.
|
||||||
let mut b = Board::new();
|
let b = new();
|
||||||
let mut gs = GameState::new();
|
let gs = rules::GameState::new();
|
||||||
let initial_stats = BoardStats {
|
let initial_stats = BoardStats {
|
||||||
num_pawns: 8,
|
num_pawns: 8,
|
||||||
num_bishops: 2,
|
num_bishops: 2,
|
||||||
|
@ -171,54 +195,56 @@ mod tests {
|
||||||
num_isolated_pawns: 0,
|
num_isolated_pawns: 0,
|
||||||
mobility: 20,
|
mobility: 20,
|
||||||
};
|
};
|
||||||
let mut stats = BoardStats::new_from(&mut b, &mut gs);
|
let mut stats = compute_stats(&b, &gs);
|
||||||
|
eprintln!("{}", stats.0);
|
||||||
|
eprintln!("{}", stats.1);
|
||||||
assert!(stats.0 == stats.1);
|
assert!(stats.0 == stats.1);
|
||||||
assert!(stats.0 == initial_stats);
|
assert!(stats.0 == initial_stats);
|
||||||
|
|
||||||
// Check that doubled pawns are correctly counted.
|
// Check that doubled pawns are correctly counted.
|
||||||
let mut b = Board::new_empty();
|
let mut b = new_empty();
|
||||||
b.set_square(D4, WHITE, PAWN);
|
set_square(&mut b, &pos("d4"), SQ_WH_P);
|
||||||
b.set_square(D6, WHITE, PAWN);
|
set_square(&mut b, &pos("d6"), SQ_WH_P);
|
||||||
stats.0.compute(&mut b, &mut gs);
|
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||||
assert_eq!(stats.0.num_doubled_pawns, 2);
|
assert_eq!(stats.0.num_doubled_pawns, 2);
|
||||||
// Add a pawn on another file, no changes expected.
|
// Add a pawn on another file, no changes expected.
|
||||||
b.set_square(E6, WHITE, PAWN);
|
set_square(&mut b, &pos("e6"), SQ_WH_P);
|
||||||
stats.0.compute(&mut b, &mut gs);
|
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||||
assert_eq!(stats.0.num_doubled_pawns, 2);
|
assert_eq!(stats.0.num_doubled_pawns, 2);
|
||||||
// Add a pawn backward in the d-file: there are now 3 doubled pawns.
|
// Add a pawn backward in the d-file: there are now 3 doubled pawns.
|
||||||
b.set_square(D2, WHITE, PAWN);
|
set_square(&mut b, &pos("d2"), SQ_WH_P);
|
||||||
stats.0.compute(&mut b, &mut gs);
|
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||||
assert_eq!(stats.0.num_doubled_pawns, 3);
|
assert_eq!(stats.0.num_doubled_pawns, 3);
|
||||||
|
|
||||||
// Check that isolated and backward pawns are correctly counted.
|
// Check that isolated and backward pawns are correctly counted.
|
||||||
assert_eq!(stats.0.num_isolated_pawns, 0);
|
assert_eq!(stats.0.num_isolated_pawns, 0);
|
||||||
assert_eq!(stats.0.num_backward_pawns, 2); // A bit weird?
|
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.
|
// Protect d4 pawn with a friend in e3: it is not isolated nor backward anymore.
|
||||||
b.set_square(E3, WHITE, PAWN);
|
set_square(&mut b, &pos("e3"), SQ_WH_P);
|
||||||
stats.0.compute(&mut b, &mut gs);
|
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||||
assert_eq!(stats.0.num_doubled_pawns, 5);
|
assert_eq!(stats.0.num_doubled_pawns, 5);
|
||||||
assert_eq!(stats.0.num_isolated_pawns, 0);
|
assert_eq!(stats.0.num_isolated_pawns, 0);
|
||||||
assert_eq!(stats.0.num_backward_pawns, 1);
|
assert_eq!(stats.0.num_backward_pawns, 1);
|
||||||
// Add an adjacent friend to d2 pawn: no pawns are left isolated or backward.
|
// Add an adjacent friend to d2 pawn: no pawns are left isolated or backward.
|
||||||
b.set_square(C2, WHITE, PAWN);
|
set_square(&mut b, &pos("c2"), SQ_WH_P);
|
||||||
stats.0.compute(&mut b, &mut gs);
|
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||||
assert_eq!(stats.0.num_doubled_pawns, 5);
|
assert_eq!(stats.0.num_doubled_pawns, 5);
|
||||||
assert_eq!(stats.0.num_isolated_pawns, 0);
|
assert_eq!(stats.0.num_isolated_pawns, 0);
|
||||||
assert_eq!(stats.0.num_backward_pawns, 0);
|
assert_eq!(stats.0.num_backward_pawns, 0);
|
||||||
// Add an isolated/backward white pawn in a far file.
|
// Add an isolated/backward white pawn in a far file.
|
||||||
b.set_square(A2, WHITE, PAWN);
|
set_square(&mut b, &pos("a2"), SQ_WH_P);
|
||||||
stats.0.compute(&mut b, &mut gs);
|
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||||
assert_eq!(stats.0.num_doubled_pawns, 5);
|
assert_eq!(stats.0.num_doubled_pawns, 5);
|
||||||
assert_eq!(stats.0.num_isolated_pawns, 1);
|
assert_eq!(stats.0.num_isolated_pawns, 1);
|
||||||
assert_eq!(stats.0.num_backward_pawns, 1);
|
assert_eq!(stats.0.num_backward_pawns, 1);
|
||||||
|
|
||||||
// Check for pawns that are backward but not isolated.
|
// Check for pawns that are backward but not isolated.
|
||||||
let mut b = Board::new_empty();
|
let mut b = new_empty();
|
||||||
// Here, d4 pawn protects both e5 and e3, but it is backward.
|
// Here, d4 pawn protects both e5 and e3, but it is backward.
|
||||||
b.set_square(D4, WHITE, PAWN);
|
set_square(&mut b, &pos("d4"), SQ_WH_P);
|
||||||
b.set_square(E5, WHITE, PAWN);
|
set_square(&mut b, &pos("e5"), SQ_WH_P);
|
||||||
b.set_square(E3, WHITE, PAWN);
|
set_square(&mut b, &pos("e3"), SQ_WH_P);
|
||||||
stats.0.compute(&mut b, &mut gs);
|
compute_color_stats_into(&b, &gs, &mut stats.0);
|
||||||
assert_eq!(stats.0.num_doubled_pawns, 2);
|
assert_eq!(stats.0.num_doubled_pawns, 2);
|
||||||
assert_eq!(stats.0.num_isolated_pawns, 0);
|
assert_eq!(stats.0.num_isolated_pawns, 0);
|
||||||
assert_eq!(stats.0.num_backward_pawns, 1);
|
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::analysis::AnalysisInfo;
|
||||||
use crate::engine;
|
use crate::engine;
|
||||||
use crate::fen;
|
use crate::movement::Move;
|
||||||
use crate::movement::{Move, UCI_NULL_MOVE_STR};
|
use crate::notation;
|
||||||
|
|
||||||
const VATU_NAME: &str = env!("CARGO_PKG_NAME");
|
const VATU_NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
const VATU_AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
const VATU_AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
||||||
|
@ -57,11 +57,6 @@ pub enum UciCmd {
|
||||||
Position(Vec<PositionArgs>),
|
Position(Vec<PositionArgs>),
|
||||||
Go(Vec<GoArgs>),
|
Go(Vec<GoArgs>),
|
||||||
Quit,
|
Quit,
|
||||||
|
|
||||||
// Unofficial commands mostly for debugging.
|
|
||||||
VatuNode,
|
|
||||||
VatuMove(String),
|
|
||||||
|
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +64,7 @@ pub enum UciCmd {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum PositionArgs {
|
pub enum PositionArgs {
|
||||||
Startpos,
|
Startpos,
|
||||||
Fen(fen::Fen),
|
Fen(notation::Fen),
|
||||||
Moves(Vec<Move>),
|
Moves(Vec<Move>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,12 +198,6 @@ impl Uci {
|
||||||
self.send_engine_command(engine::Cmd::Stop);
|
self.send_engine_command(engine::Cmd::Stop);
|
||||||
},
|
},
|
||||||
UciCmd::Quit => return false,
|
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)); }
|
UciCmd::Unknown(c) => { self.log(format!("Unknown command: {}", c)); }
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -282,7 +271,7 @@ impl Uci {
|
||||||
s.push_str(&format!(" nps {}", n));
|
s.push_str(&format!(" nps {}", n));
|
||||||
}
|
}
|
||||||
AnalysisInfo::CurrentMove(m) => {
|
AnalysisInfo::CurrentMove(m) => {
|
||||||
s.push_str(&format!(" currmove {}", m.to_uci_string()));
|
s.push_str(&format!(" currmove {}", notation::move_to_string(m)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,10 +280,11 @@ impl Uci {
|
||||||
|
|
||||||
/// Send best move.
|
/// Send best move.
|
||||||
fn send_bestmove(&mut self, m: &Option<Move>) {
|
fn send_bestmove(&mut self, m: &Option<Move>) {
|
||||||
self.send(&format!(
|
let move_str = match m {
|
||||||
"bestmove {}",
|
Some(m) => notation::move_to_string(m),
|
||||||
if let Some(m) = m { m.to_uci_string() } else { UCI_NULL_MOVE_STR.to_string() }
|
None => notation::NULL_MOVE.to_string(),
|
||||||
));
|
};
|
||||||
|
self.send(&format!("bestmove {}", move_str));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,9 +292,6 @@ impl Uci {
|
||||||
// UCI command parsers
|
// UCI command parsers
|
||||||
|
|
||||||
/// Parse an UCI command.
|
/// 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 {
|
fn parse_command(s: &str) -> UciCmd {
|
||||||
if s.len() == 0 {
|
if s.len() == 0 {
|
||||||
return UciCmd::Unknown("Empty command.".to_string());
|
return UciCmd::Unknown("Empty command.".to_string());
|
||||||
|
@ -316,11 +303,9 @@ fn parse_command(s: &str) -> UciCmd {
|
||||||
"isready" => UciCmd::IsReady,
|
"isready" => UciCmd::IsReady,
|
||||||
"ucinewgame" => UciCmd::UciNewGame,
|
"ucinewgame" => UciCmd::UciNewGame,
|
||||||
"stop" => UciCmd::Stop,
|
"stop" => UciCmd::Stop,
|
||||||
"position" | "p" => parse_position_command(&fields[1..]),
|
"position" => parse_position_command(&fields[1..]),
|
||||||
"go" => parse_go_command(&fields[1..]),
|
"go" => parse_go_command(&fields[1..]),
|
||||||
"quit" => UciCmd::Quit,
|
"quit" => UciCmd::Quit,
|
||||||
"vatunode" => UciCmd::VatuNode,
|
|
||||||
"vatumove" => UciCmd::VatuMove(fields[1].to_string()),
|
|
||||||
c => UciCmd::Unknown(c.to_string()),
|
c => UciCmd::Unknown(c.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,7 +319,7 @@ fn parse_position_command(fields: &[&str]) -> UciCmd {
|
||||||
match fields[i] {
|
match fields[i] {
|
||||||
// Subcommand "fen" is followed by a FEN string.
|
// Subcommand "fen" is followed by a FEN string.
|
||||||
"fen" => {
|
"fen" => {
|
||||||
if let Some(fen) = fen::parse_fen_fields(&fields[i + 1 .. i + 7]) {
|
if let Some(fen) = notation::parse_fen_fields(&fields[i + 1 .. i + 7]) {
|
||||||
subcommands.push(PositionArgs::Fen(fen))
|
subcommands.push(PositionArgs::Fen(fen))
|
||||||
} else {
|
} else {
|
||||||
return UciCmd::Unknown(format!("Bad format for position fen"))
|
return UciCmd::Unknown(format!("Bad format for position fen"))
|
||||||
|
@ -347,7 +332,7 @@ fn parse_position_command(fields: &[&str]) -> UciCmd {
|
||||||
"moves" => {
|
"moves" => {
|
||||||
let mut moves = vec!();
|
let mut moves = vec!();
|
||||||
while i + 1 < num_fields {
|
while i + 1 < num_fields {
|
||||||
moves.push(Move::from_uci_string(fields[i + 1]));
|
moves.push(notation::parse_move(fields[i + 1]));
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
subcommands.push(PositionArgs::Moves(moves));
|
subcommands.push(PositionArgs::Moves(moves));
|
||||||
|
|
Reference in a new issue