1use std::array;
2
3use takparse::{Color, Piece, Square, Stack as TpsStack};
4
5use crate::stack::Stack;
6
7#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
8pub struct Board<const N: usize> {
9 data: [[Stack; N]; N],
10}
11
12impl<const N: usize> Default for Board<N> {
13 fn default() -> Self {
14 Self {
15 data: [[Stack::default(); N]; N],
16 }
17 }
18}
19
20impl<const N: usize> Clone for Board<N> {
22 #[inline]
23 fn clone(&self) -> Self {
24 unsafe { std::mem::transmute_copy(self) }
25 }
26}
27
28impl<const N: usize> Board<N> {
29 #[inline]
30 pub fn iter(&self) -> impl Iterator<Item = impl Iterator<Item = &Stack>> {
31 self.data.iter().map(|row| row.iter())
32 }
33
34 #[inline]
35 #[must_use]
36 pub fn get(&self, square: Square) -> Option<&Stack> {
37 self.data
38 .get(square.row() as usize)
39 .and_then(|r| r.get(square.column() as usize))
40 }
41
42 #[inline]
43 pub fn get_mut(&mut self, square: Square) -> Option<&mut Stack> {
44 self.data
45 .get_mut(square.row() as usize)
46 .and_then(|r| r.get_mut(square.column() as usize))
47 }
48
49 #[must_use]
50 pub fn full(&self) -> bool {
51 !self.data.iter().any(|row| row.iter().any(Stack::is_empty))
52 }
53
54 #[must_use]
55 pub fn flat_diff(&self) -> i8 {
56 self.data
57 .iter()
58 .flat_map(|row| row.iter())
59 .map(|tile| match tile.top() {
60 Some((Piece::Flat, Color::White)) => 1,
61 Some((Piece::Flat, Color::Black)) => -1,
62 _ => 0,
63 })
64 .sum()
65 }
66
67 #[must_use]
68 pub fn has_road(&self, color: Color) -> bool {
69 let road = self.data.map(|row| row.map(|s| s.road(color)));
70 let mut seen = [[false; N]; N];
71 let mut reached = [[false; N]; N];
72 let mut stack = Vec::with_capacity(N * N);
73
74 for row in 0..N {
76 stack.push((row, 0));
77 }
78 Self::flood_fill(&road, &mut stack, &mut seen, &mut reached);
79 if reached.iter().any(|row| row[N - 1]) {
80 return true;
81 }
82
83 stack.clear();
85 reached = [[false; N]; N];
86 seen = [[false; N]; N];
87 for col in 0..N {
88 stack.push((0, col));
89 }
90 Self::flood_fill(&road, &mut stack, &mut seen, &mut reached);
91 reached[N - 1].iter().any(|b| *b)
92 }
93
94 fn flood_fill(
95 road: &[[bool; N]; N],
96 stack: &mut Vec<(usize, usize)>,
97 seen: &mut [[bool; N]; N],
98 reached: &mut [[bool; N]; N],
99 ) {
100 while let Some((row, col)) = stack.pop() {
101 seen[row][col] = true;
102 if road[row][col] {
103 reached[row][col] = true;
104
105 if let Some(r) = row.checked_sub(1) {
106 if !seen[r][col] {
107 stack.push((r, col));
108 }
109 }
110 if let Some(c) = col.checked_sub(1) {
111 if !seen[row][c] {
112 stack.push((row, c));
113 }
114 }
115 if row < N - 1 {
116 let r = row + 1;
117 if !seen[r][col] {
118 stack.push((r, col));
119 }
120 }
121 if col < N - 1 {
122 let c = col + 1;
123 if !seen[row][c] {
124 stack.push((row, c));
125 }
126 }
127 }
128 }
129 }
130}
131
132impl<'a, const N: usize> FromIterator<Option<&'a TpsStack>> for Board<N> {
133 fn from_iter<T: IntoIterator<Item = Option<&'a TpsStack>>>(iter: T) -> Self {
134 let mut iter = iter.into_iter().map(|square| {
135 square.map_or_else(Stack::default, |stack| {
136 Stack::exact(stack.top(), stack.colors().collect())
137 })
138 });
139 let mut data = array::from_fn(|_| array::from_fn(|_| iter.next().unwrap_or_default()));
140 data.reverse();
141 Self { data }
142 }
143}