rustoku_lib/core/
board.rs
1use crate::error::RustokuError;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub struct Board {
11 pub(crate) cells: [[u8; 9]; 9],
13}
14
15impl Board {
16 pub fn new(initial_board: [[u8; 9]; 9]) -> Self {
17 Board {
18 cells: initial_board,
19 }
20 }
21
22 pub fn get(&self, r: usize, c: usize) -> u8 {
24 self.cells[r][c]
25 }
26
27 pub(super) fn set(&mut self, r: usize, c: usize, value: u8) {
29 self.cells[r][c] = value;
30 }
31
32 pub fn is_empty(&self, r: usize, c: usize) -> bool {
34 self.cells[r][c] == 0
35 }
36
37 pub fn iter_cells(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
39 (0..9).flat_map(move |r| (0..9).map(move |c| (r, c)))
40 }
41
42 pub fn iter_empty_cells(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
44 (0..9).flat_map(move |r| {
45 (0..9).filter_map(move |c| {
46 if self.is_empty(r, c) {
47 Some((r, c))
48 } else {
49 None
50 }
51 })
52 })
53 }
54}
55
56impl TryFrom<[u8; 81]> for Board {
57 type Error = RustokuError;
58
59 fn try_from(bytes: [u8; 81]) -> Result<Self, Self::Error> {
60 let mut board = [[0u8; 9]; 9];
61 for i in 0..81 {
62 if bytes[i] > 9 {
65 return Err(RustokuError::InvalidInputCharacter); }
67 board[i / 9][i % 9] = bytes[i];
68 }
69 Ok(Board::new(board)) }
71}
72
73impl TryFrom<&str> for Board {
74 type Error = RustokuError;
75
76 fn try_from(s: &str) -> Result<Self, Self::Error> {
77 if s.len() != 81 {
78 return Err(RustokuError::InvalidInputLength);
79 }
80 let mut bytes = [0u8; 81];
81 for (i, ch) in s.bytes().enumerate() {
82 match ch {
83 b'0'..=b'9' => bytes[i] = ch - b'0',
84 b'.' | b'_' => bytes[i] = 0, _ => return Err(RustokuError::InvalidInputCharacter),
86 }
87 }
88 bytes.try_into()
90 }
91}