board.rs (4252B)
1 use rand::RngExt; 2 3 pub const GRID_SIZE: usize = 9; 4 5 #[derive(Debug, Clone, Copy, PartialEq)] 6 pub struct Cell { 7 pub value: Option<u8>, 8 pub is_fixed: bool, 9 } 10 11 impl Cell { 12 pub fn new(value: Option<u8>, is_fixed: bool) -> Self { 13 Self { value, is_fixed } 14 } 15 } 16 17 pub struct Board { 18 pub cells: [[Cell; GRID_SIZE]; GRID_SIZE], 19 } 20 21 impl Board { 22 pub fn new() -> Self { 23 let cells = [[Cell::new(None, false); GRID_SIZE]; GRID_SIZE]; 24 Self { cells } 25 } 26 27 pub fn set_value(&mut self, row: usize, col: usize, value: Option<u8>) { 28 if !self.cells[row][col].is_fixed { 29 self.cells[row][col].value = value; 30 } 31 } 32 33 pub fn is_valid(&self, row: usize, col: usize, val: u8) -> bool { 34 // Check row 35 for c in 0..GRID_SIZE { 36 if c != col && self.cells[row][c].value == Some(val) { 37 return false; 38 } 39 } 40 41 // Check column 42 for r in 0..GRID_SIZE { 43 if r != row && self.cells[r][col].value == Some(val) { 44 return false; 45 } 46 } 47 48 // Check 3x3 box 49 let start_row = (row / 3) * 3; 50 let start_col = (col / 3) * 3; 51 for r in start_row..start_row + 3 { 52 for c in start_col..start_col + 3 { 53 if (r != row || c != col) && self.cells[r][c].value == Some(val) { 54 return false; 55 } 56 } 57 } 58 59 true 60 } 61 62 pub fn is_conflict(&self, row: usize, col: usize) -> bool { 63 if let Some(val) = self.cells[row][col].value { 64 !self.is_valid(row, col, val) 65 } else { 66 false 67 } 68 } 69 70 pub fn is_full(&self) -> bool { 71 self.cells 72 .iter() 73 .all(|row| row.iter().all(|c| c.value.is_some())) 74 } 75 76 pub fn is_solved(&self) -> bool { 77 if !self.is_full() { 78 return false; 79 } 80 for r in 0..GRID_SIZE { 81 for c in 0..GRID_SIZE { 82 if self.is_conflict(r, c) { 83 return false; 84 } 85 } 86 } 87 true 88 } 89 90 pub fn solve(&mut self) -> bool { 91 let mut empty_cell = None; 92 for r in 0..GRID_SIZE { 93 for c in 0..GRID_SIZE { 94 if self.cells[r][c].value.is_none() { 95 empty_cell = Some((r, c)); 96 break; 97 } 98 } 99 if empty_cell.is_some() { 100 break; 101 } 102 } 103 104 let (r, c) = match empty_cell { 105 Some(pos) => pos, 106 None => return true, // Solved 107 }; 108 109 for val in 1..=9 { 110 if self.is_valid(r, c, val) { 111 self.cells[r][c].value = Some(val); 112 if self.solve() { 113 return true; 114 } 115 self.cells[r][c].value = None; 116 } 117 } 118 119 false 120 } 121 122 pub fn generate(&mut self) { 123 // Clear board 124 *self = Board::new(); 125 126 // Fill diagonal 3x3 boxes 127 for i in (0..GRID_SIZE).step_by(3) { 128 self.fill_box(i, i); 129 } 130 131 // Solve the rest 132 self.solve(); 133 134 // Set all as fixed for now (as the base) 135 for r in 0..GRID_SIZE { 136 for c in 0..GRID_SIZE { 137 self.cells[r][c].is_fixed = true; 138 } 139 } 140 141 // Randomly remove cells 142 let mut rng = rand::rng(); 143 let cells_to_remove = 40; 144 let mut count = 0; 145 while count < cells_to_remove { 146 let r = rng.random_range(0..GRID_SIZE); 147 let c = rng.random_range(0..GRID_SIZE); 148 if self.cells[r][c].value.is_some() { 149 self.cells[r][c].value = None; 150 self.cells[r][c].is_fixed = false; 151 count += 1; 152 } 153 } 154 } 155 156 fn fill_box(&mut self, row: usize, col: usize) { 157 use rand::seq::SliceRandom; 158 let mut nums: Vec<u8> = (1..=9).collect(); 159 let mut rng = rand::rng(); 160 nums.shuffle(&mut rng); 161 for r in 0..3 { 162 for c in 0..3 { 163 self.cells[row + r][col + c].value = Some(nums[r * 3 + c]); 164 } 165 } 166 } 167 }