1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use chess::{Color, Piece, NUM_COLORS};
use crate::error::*;
use std::str::FromStr;
pub const NUM_HELD_PIECE_TYPES: usize = 5;
type HeldArray = [[u8; NUM_HELD_PIECE_TYPES]; NUM_COLORS];
fn empty() -> HeldArray {
[[0; NUM_HELD_PIECE_TYPES]; NUM_COLORS]
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Holdings {
holdings: HeldArray,
}
impl Holdings {
pub fn new(holdings: &HeldArray) -> Self {
Holdings {
holdings: *holdings,
}
}
pub fn has_piece(&self, color: Color, piece: Piece) -> bool {
self.holdings[color.to_index()][piece.to_index()] > 0
}
pub fn drop(&mut self, color: Color, piece: Piece) -> Result<(), Error> {
let color_idx = color.to_index();
let piece_idx = piece.to_index();
let cur_val = self.holdings[color_idx][piece_idx];
if cur_val > 0 {
self.holdings[color_idx][piece_idx] = cur_val - 1;
return Ok(());
}
return Err(Error::UnheldDrop(color, piece));
}
pub fn add(&mut self, color: Color, piece: Piece) {
let cidx = color.to_index();
let pidx = piece.to_index();
self.holdings[cidx][pidx] += 1;
}
}
impl Default for Holdings {
#[inline]
fn default() -> Self {
Holdings { holdings: empty() }
}
}
impl FromStr for Holdings {
type Err = Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let mut bfen_holdings = empty();
let black_idx = Color::Black.to_index();
let white_idx = Color::White.to_index();
for p in value.trim().chars() {
match p {
'p' => bfen_holdings[black_idx][Piece::Pawn.to_index()] += 1,
'n' => bfen_holdings[black_idx][Piece::Knight.to_index()] += 1,
'b' => bfen_holdings[black_idx][Piece::Bishop.to_index()] += 1,
'r' => bfen_holdings[black_idx][Piece::Rook.to_index()] += 1,
'q' => bfen_holdings[black_idx][Piece::Queen.to_index()] += 1,
'P' => bfen_holdings[white_idx][Piece::Pawn.to_index()] += 1,
'N' => bfen_holdings[white_idx][Piece::Knight.to_index()] += 1,
'B' => bfen_holdings[white_idx][Piece::Bishop.to_index()] += 1,
'R' => bfen_holdings[white_idx][Piece::Rook.to_index()] += 1,
'Q' => bfen_holdings[white_idx][Piece::Queen.to_index()] += 1,
_ => {
return Err(Error::HoldingsParseError(value.to_string()));
}
}
}
Ok(Holdings {
holdings: bfen_holdings,
})
}
}
#[test]
fn empty_position() {
let res = Holdings::from_str("").unwrap();
assert!(res == Holdings::default());
}
#[test]
fn random_position() {
let res = Holdings::from_str("BrpBBqppN").unwrap();
let expected_holdings = [
[0, 1, 3, 0, 0],
[3, 0, 0, 1, 1],
];
assert!(res == Holdings::new(&expected_holdings));
}
#[test]
fn kings_dont_make_sense() {
assert!(Holdings::from_str("k").is_err());
}