//! Game board logic. use std::fs::read_to_string; const SIZE: usize = 9; #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct Cell { pub value: u8, pub loaded: bool, pub invalid: bool, } #[derive(Debug, PartialEq)] pub struct Gameboard { pub cells: [[Cell; SIZE]; SIZE], pub completed: bool, } impl Gameboard { pub fn new() -> Gameboard { Gameboard { cells: [[Cell::default(); SIZE]; SIZE], completed: false, } } pub fn from_cells(cells: [[u8; SIZE]; SIZE]) -> Gameboard { let mut ret = Gameboard::new(); for (i, row) in cells.iter().enumerate() { for (j, &col) in row.iter().enumerate() { ret.cells[i][j] = Cell { value: col, loaded: col != 0, invalid: false, }; } } ret } pub fn char(&self, ind: [usize; 2]) -> Option { Some(match self.cells[ind[1]][ind[0]].value { 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', _ => return None, }) } pub fn set(&mut self, ind: [usize; 2], val: u8) { if !self.cells[ind[1]][ind[0]].loaded { self.validate(ind, val); self.cells[ind[1]][ind[0]].value = val; } self.completed = self .cells .iter() .flatten() .all(|cell| !cell.invalid && cell.value != 0); } pub fn load_sdm(filename: &str) -> Self { let data = read_to_string(filename).expect("failed to read SDM file"); let mut cells = [[Cell::default(); SIZE]; SIZE]; let mut row = 0; let mut col = 0; for c in data.chars() { if col == SIZE { col = 0; row += 1; } if let Some(v) = c.to_digit(10) { let value = v as u8; cells[row][col] = Cell { value, loaded: value != 0, invalid: false, }; col += 1; } } Self { cells, completed: false, } } fn validate(&mut self, ind: [usize; 2], val: u8) { let [b, a] = ind; for i in 0..SIZE { if i == a { continue; } if self.cells[a][i].value == val { self.cells[a][b].invalid = true; return; } } for i in 0..SIZE { if i == b { continue; } if self.cells[i][b].value == val { self.cells[a][b].invalid = true; return; } } let (row, col) = (a / 3, b / 3); for i in 3 * row..3 * row + 3 { for j in 3 * col..3 * col + 3 { if i == a && j == b { continue; } if self.cells[i][j].value == val { self.cells[a][b].invalid = true; return; } } } self.cells[a][b].invalid = false; } } #[cfg(test)] mod tests { use super::*; #[test] fn load_sdm() { let got = Gameboard::load_sdm("static/puzzle.sdm"); let want = Gameboard::from_cells([ [0, 1, 6, 4, 0, 0, 0, 0, 0], [2, 0, 0, 0, 0, 9, 0, 0, 0], [4, 0, 0, 0, 0, 0, 0, 6, 2], [0, 7, 0, 2, 3, 0, 1, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 3], [0, 0, 3, 0, 8, 7, 0, 4, 0], [9, 6, 0, 0, 0, 0, 0, 0, 5], [0, 0, 0, 8, 0, 0, 0, 0, 7], [0, 0, 0, 0, 0, 6, 8, 2, 0], ]); assert_eq!(got, want); } }