From 6c0083e094bedc92a44d9f4bddc98236284c83a4 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 26 Nov 2022 16:09:32 -0500 Subject: [PATCH] Sudoku through the end of chapter 8 --- piston-tutorials/sudoku/src/gameboard.rs | 54 +++++++++++++- piston-tutorials/sudoku/src/gameboard_view.rs | 71 ++++++++++++++----- piston-tutorials/sudoku/src/main.rs | 5 +- .../sudoku/static/puzzle-almost-solved.sdm | 1 + 4 files changed, 110 insertions(+), 21 deletions(-) create mode 100644 piston-tutorials/sudoku/static/puzzle-almost-solved.sdm diff --git a/piston-tutorials/sudoku/src/gameboard.rs b/piston-tutorials/sudoku/src/gameboard.rs index ec06e3f..8611167 100644 --- a/piston-tutorials/sudoku/src/gameboard.rs +++ b/piston-tutorials/sudoku/src/gameboard.rs @@ -8,17 +8,20 @@ const SIZE: usize = 9; 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, } } @@ -29,6 +32,7 @@ impl Gameboard { ret.cells[i][j] = Cell { value: col, loaded: col != 0, + invalid: false, }; } } @@ -52,8 +56,15 @@ impl Gameboard { 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 { @@ -71,12 +82,53 @@ impl Gameboard { cells[row][col] = Cell { value, loaded: value != 0, + invalid: false, }; col += 1; } } - Self { cells } + 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; } } diff --git a/piston-tutorials/sudoku/src/gameboard_view.rs b/piston-tutorials/sudoku/src/gameboard_view.rs index d6beb0e..40f2622 100644 --- a/piston-tutorials/sudoku/src/gameboard_view.rs +++ b/piston-tutorials/sudoku/src/gameboard_view.rs @@ -20,6 +20,9 @@ pub struct GameboardViewSettings { pub selected_cell_background_color: Color, pub text_color: Color, pub loaded_cell_background_color: Color, + pub invalid_cell_background_color: Color, + pub invalid_selected_cell_background_color: Color, + pub completed_background_color: Color, } impl GameboardViewSettings { @@ -38,6 +41,9 @@ impl GameboardViewSettings { selected_cell_background_color: [0.9, 0.9, 1.0, 1.0], text_color: [0.0, 0.0, 0.1, 1.0], loaded_cell_background_color: [1.0, 1.0, 1.0, 1.0], + invalid_cell_background_color: [1.0, 0.0, 0.0, 1.0], + invalid_selected_cell_background_color: [1.0, 0.0, 0.5, 1.0], + completed_background_color: [0.0, 1.0, 0.0, 1.0], } } } @@ -70,29 +76,56 @@ impl GameboardView { settings.size, ]; - Rectangle::new(settings.background_color).draw(board_rect, &c.draw_state, c.transform, g); + if controller.gameboard.completed { + Rectangle::new(settings.completed_background_color).draw( + board_rect, + &c.draw_state, + c.transform, + g, + ); + } else { + Rectangle::new(settings.background_color).draw( + board_rect, + &c.draw_state, + c.transform, + g, + ); - for i in 0..9 { - for j in 0..9 { - if controller.gameboard.cells[i][j].loaded { - color_cell( - settings, - [j, i], - settings.loaded_cell_background_color, - c, - g, - ); + for i in 0..9 { + for j in 0..9 { + if controller.gameboard.cells[i][j].loaded { + color_cell( + settings, + [j, i], + settings.loaded_cell_background_color, + c, + g, + ); + } else if controller.gameboard.cells[i][j].invalid { + color_cell( + settings, + [j, i], + settings.invalid_cell_background_color, + c, + g, + ); + } } } - } - if let Some(ind) = controller.selected_cell { - let color = if !controller.gameboard.cells[ind[1]][ind[0]].loaded { - settings.selected_cell_background_color - } else { - settings.loaded_cell_background_color - }; - color_cell(settings, ind, color, c, g); + if let Some(ind) = controller.selected_cell { + let cell = controller.gameboard.cells[ind[1]][ind[0]]; + let color = if !cell.loaded { + if !cell.invalid { + settings.selected_cell_background_color + } else { + settings.invalid_selected_cell_background_color + } + } else { + settings.loaded_cell_background_color + }; + color_cell(settings, ind, color, c, g); + } } let text_image = Image::new_color(settings.text_color); diff --git a/piston-tutorials/sudoku/src/main.rs b/piston-tutorials/sudoku/src/main.rs index 7391dfb..3e3da24 100644 --- a/piston-tutorials/sudoku/src/main.rs +++ b/piston-tutorials/sudoku/src/main.rs @@ -27,7 +27,10 @@ fn main() { let mut events = Events::new(EventSettings::new().lazy(true)); let mut gl = GlGraphics::new(opengl); - let gameboard = Gameboard::load_sdm("static/puzzle.sdm"); + let args: Vec<_> = std::env::args().collect(); + let infile = args.get(1).expect("usage: sudoku "); + + let gameboard = Gameboard::load_sdm(infile); let mut gameboard_controller = GameboardController::new(gameboard); let gameboard_view_settings = GameboardViewSettings::new(); let gameboard_view = GameboardView::new(gameboard_view_settings); diff --git a/piston-tutorials/sudoku/static/puzzle-almost-solved.sdm b/piston-tutorials/sudoku/static/puzzle-almost-solved.sdm new file mode 100644 index 0000000..6f09bfa --- /dev/null +++ b/piston-tutorials/sudoku/static/puzzle-almost-solved.sdm @@ -0,0 +1 @@ +517962483236847915498351762371695248654218397829734156765129834142583679983476520