rustoku_lib/core/
board.rs1use crate::error::RustokuError;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct Board {
11 pub 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 empty() -> Self {
24 Board {
25 cells: [[0u8; 9]; 9],
26 }
27 }
28
29 pub fn get(&self, r: usize, c: usize) -> u8 {
31 self.cells[r][c]
32 }
33
34 pub fn set(&mut self, r: usize, c: usize, value: u8) {
36 self.cells[r][c] = value;
37 }
38
39 pub fn is_empty(&self, r: usize, c: usize) -> bool {
41 self.cells[r][c] == 0
42 }
43
44 pub fn iter_cells(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
46 (0..9).flat_map(move |r| (0..9).map(move |c| (r, c)))
47 }
48
49 pub fn iter_empty_cells(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
51 (0..9).flat_map(move |r| {
52 (0..9).filter_map(move |c| {
53 if self.is_empty(r, c) {
54 Some((r, c))
55 } else {
56 None
57 }
58 })
59 })
60 }
61}
62
63impl TryFrom<[u8; 81]> for Board {
64 type Error = RustokuError;
65
66 fn try_from(bytes: [u8; 81]) -> Result<Self, Self::Error> {
67 let mut board = [[0u8; 9]; 9];
68 for i in 0..81 {
69 if bytes[i] > 9 {
72 return Err(RustokuError::InvalidInputCharacter); }
74 board[i / 9][i % 9] = bytes[i];
75 }
76 Ok(Board::new(board)) }
78}
79
80impl TryFrom<&str> for Board {
81 type Error = RustokuError;
82
83 fn try_from(s: &str) -> Result<Self, Self::Error> {
84 if s.len() != 81 {
85 return Err(RustokuError::InvalidInputLength);
86 }
87 let mut bytes = [0u8; 81];
88 for (i, ch) in s.bytes().enumerate() {
89 match ch {
90 b'0'..=b'9' => bytes[i] = ch - b'0',
91 b'.' | b'_' => bytes[i] = 0, _ => return Err(RustokuError::InvalidInputCharacter),
93 }
94 }
95 bytes.try_into()
97 }
98}