fast_tak/
board.rs

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
20// https://godbolt.org/z/434j733oW
21impl<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        // Horizontal roads
75        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        // Vertical roads
84        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}