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
113
114
115
116
117
118
use takparse::{Color, Piece};
use crate::{
colors::Colors,
error::{StackError, TakeError},
};
#[derive(Clone, Copy, Debug, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Stack {
piece: Piece,
colors: Colors,
}
impl Stack {
/// Create a new stack with a single piece.
#[must_use]
pub const fn new(piece: Piece, color: Color) -> Self {
Self {
piece,
colors: Colors::of_one(color),
}
}
/// Create a new stack with the given colors.
///
/// # Panics
///
/// Panics if the colors are empty.
#[must_use]
pub fn exact(piece: Piece, colors: Colors) -> Self {
assert!(!colors.is_empty());
Self { piece, colors }
}
/// Check if anything is in this stack. If false, it means the square is
/// empty.
#[must_use]
pub const fn is_empty(&self) -> bool {
self.colors.is_empty()
}
/// Get the size of the stack.
#[must_use]
pub const fn size(&self) -> u32 {
self.colors.len()
}
/// Get the top piece and color of this stack.
#[must_use]
pub fn top(&self) -> Option<(Piece, Color)> {
self.colors.top().map(|color| (self.piece, color))
}
#[must_use]
pub const fn colors(&self) -> Colors {
self.colors
}
/// Check if this stack contributes to roads for the given color.
#[must_use]
pub fn road(&self, color: Color) -> bool {
matches!(self.top(), Some((Piece::Flat | Piece::Cap, c)) if c == color)
}
/// Try to put a piece on top of this stack.
///
/// # Errors
///
/// You cannot stack on top of capstones or walls with the exception
/// that capstones can flatten walls.
pub fn stack(&mut self, piece: Piece, color: Color) -> Result<(), StackError> {
// Only allow stacking on top of flats, or flattening walls.
match self.piece {
Piece::Flat => Ok(()),
Piece::Wall => {
if matches!(piece, Piece::Cap) {
Ok(())
} else {
Err(StackError::Wall)
}
}
Piece::Cap => Err(StackError::Cap),
}?;
self.piece = piece;
self.colors.push(color);
Ok(())
}
/// Try taking the top `amount` pieces from this tile.
///
/// # Errors
///
/// Trying to take 0, more than the carry limit, or more than the stack size
/// will result in an error.
///
/// # Panics
///
/// Should not panic because we check the amount beforehand.
pub fn take<const N: usize>(&mut self, amount: u32) -> Result<(Piece, Colors), TakeError> {
if amount == 0 {
return Err(TakeError::Zero);
} else if amount as usize > N {
return Err(TakeError::CarryLimit);
} else if amount > self.size() {
return Err(TakeError::StackSize);
}
let piece = self.piece;
self.piece = Piece::Flat;
Ok((
piece,
self.colors
.take(amount)
.expect("The amount should be small enough since we checked that above."),
))
}
}