numberplace_core/
normal_game.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4pub mod cell;
5pub mod group;
6pub mod remove_answer;
7pub mod setting;
8pub mod shuffle;
9pub mod solve;
10
11pub struct NormalGame {
12    setting: setting::GameSetting,
13    cells: Vec<Rc<RefCell<cell::Cell>>>,
14    groups: Vec<Rc<RefCell<group::Group>>>,
15    answered_count: u32,
16}
17
18impl NormalGame {
19    pub fn new(setting: setting::GameSetting) -> NormalGame {
20        let cells = cell::create_cells(&setting);
21        let groups = group::create_groups(&cells, &setting);
22        NormalGame {
23            setting,
24            cells,
25            groups,
26            answered_count: 0,
27        }
28    }
29
30    pub fn setting(&self) -> &setting::GameSetting {
31        &self.setting
32    }
33    pub fn cells(&self) -> &Vec<Rc<RefCell<cell::Cell>>> {
34        &self.cells
35    }
36    pub fn groups(&self) -> &Vec<Rc<RefCell<group::Group>>> {
37        &self.groups
38    }
39    pub fn answered_count(&self) -> u32 {
40        self.answered_count
41    }
42    /// ' 7     6 |6   1   3|  54 87  |  8   4  | 1  3  5 |  9   1  |  35 12  |7   2   8| 5     9 '
43    pub fn load(&mut self, issue: &str) {
44        let answer_columns: Vec<&str> = issue.split("|").collect();
45        for (y, horizontal_line) in answer_columns.iter().enumerate() {
46            let horizontal_line = horizontal_line.to_string();
47            let answers: Vec<String> = if horizontal_line.contains(',') {
48                horizontal_line.split(',').map(|s| s.to_string()).collect()
49            } else {
50                horizontal_line.chars().map(|c| format!("{}", c)).collect()
51            };
52            for (x, answer) in answers.iter().enumerate() {
53                if *answer == " " || *answer == "" {
54                    continue;
55                }
56                let answer: u8 = answer.parse().expect("issue is wrong.");
57                self.set_answer(cell::Position::new(x as u8, y as u8), answer);
58            }
59        }
60    }
61
62    pub fn set_answer(&mut self, pos: cell::Position, answer: u8) {
63        let cell = self.find_cell(pos).unwrap();
64        if cell.borrow().answer().is_some() {
65            return;
66        }
67        cell.borrow_mut().set_answer(answer);
68        self.groups()
69            .iter()
70            .filter(|g| g.borrow().cells().iter().any(|c| c.borrow().pos() == pos))
71            .for_each(|g| g.borrow_mut().remove_answer_candidate(answer));
72        self.answered_count += 1;
73    }
74
75    pub fn check_status(&mut self) -> GameState {
76        if self
77            .cells()
78            .iter()
79            .filter(|c| c.borrow().answer() == None)
80            .any(|c| c.borrow().answer_candidate_count() == 0)
81        {
82            return GameState::Failure;
83        }
84
85        if self
86            .groups()
87            .iter()
88            .any(|g| g.borrow().is_duplicate_answer())
89        {
90            return GameState::Failure;
91        }
92
93        if (self.setting.side_size() as u32 * self.setting.side_size() as u32)
94            == self.answered_count
95        {
96            return if self.is_all_clear_groups_answer_candidate() {
97                GameState::Complete
98            } else {
99                GameState::Failure
100            };
101        }
102        return GameState::Solving;
103    }
104
105    fn is_all_clear_groups_answer_candidate(&self) -> bool {
106        self.groups()
107            .iter()
108            .all(|g| g.borrow().is_all_clear_answer_candidate())
109    }
110
111    pub fn find_cell(&self, pos: cell::Position) -> Option<&Rc<RefCell<cell::Cell>>> {
112        self.cells.iter().find(|c| c.borrow().pos() == pos)
113    }
114
115    pub fn to_string(&self) -> String {
116        let mut str = String::new();
117        for y in 0..self.setting.side_size() {
118            for x in 0..self.setting.side_size() {
119                let str2 = match self
120                    .find_cell(cell::Position::new(x, y))
121                    .unwrap()
122                    .borrow()
123                    .answer()
124                {
125                    Some(a) => a.to_string(),
126                    None => " ".to_string(),
127                };
128                str = format!("{}{}", str, str2);
129            }
130            str = format!("{}{}", str, '|');
131        }
132        str.pop();
133        return str;
134    }
135    pub fn to_string_with_comma(&self) -> String {
136        let mut str = String::new();
137        for y in 0..self.setting.side_size() {
138            for x in 0..self.setting.side_size() {
139                let str2 = match self
140                    .find_cell(cell::Position::new(x, y))
141                    .unwrap()
142                    .borrow()
143                    .answer()
144                {
145                    Some(a) => a.to_string(),
146                    None => " ".to_string(),
147                };
148                str = format!("{}{}{}", str, if x == 0 { "" } else { "," }, str2);
149            }
150            str = format!("{}{}", str, '|');
151        }
152        str.pop();
153        return str;
154    }
155    pub fn to_string_with_newline(&self) -> String {
156        let mut str = String::new();
157        for y in 0..self.setting.side_size() {
158            for x in 0..self.setting.side_size() {
159                let str2 = match self
160                    .find_cell(cell::Position::new(x, y))
161                    .unwrap()
162                    .borrow()
163                    .answer()
164                {
165                    Some(a) => a.to_string(),
166                    None => " ".to_string(),
167                };
168                str = format!("{}{}{}", str, if x == 0 { "" } else { "," }, str2);
169            }
170            str = format!("{}{}", str, '\n');
171        }
172        str.pop();
173        return str;
174    }
175}
176
177impl PartialEq for NormalGame {
178    fn eq(&self, other: &Self) -> bool {
179        self.setting().block_size() == other.setting().block_size()
180            && self.to_string_with_comma() == other.to_string_with_comma()
181    }
182}
183
184impl Clone for NormalGame {
185    fn clone(&self) -> Self {
186        let mut new_game = NormalGame::new(self.setting.clone());
187        self.cells()
188            .iter()
189            .filter(|c| c.borrow().answer() != None)
190            .for_each(|c| new_game.set_answer(c.borrow().pos(), c.borrow().answer().unwrap()));
191        new_game
192    }
193}
194
195#[derive(Debug, PartialEq, Copy, Clone)]
196pub enum GameState {
197    Solving,
198    Complete,
199    Failure,
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205    use crate::normal_game::setting::BlockSize;
206    use crate::normal_game::setting::GameSetting;
207    fn setting() -> GameSetting {
208        GameSetting::new(BlockSize {
209            height: 2,
210            width: 3,
211        })
212    }
213
214    #[cfg(test)]
215    mod _2_3 {
216        use super::*;
217
218        #[test]
219        fn it_answer_candidate_is_1_to_6() {
220            assert_eq!(setting().answer_candidate(), vec![1, 2, 3, 4, 5, 6]);
221        }
222        #[test]
223        fn it_has_36_cells() {
224            let game = NormalGame::new(setting());
225            assert_eq!(game.cells.len(), 36);
226        }
227        #[test]
228        fn it_has_18_groups() {
229            let game = NormalGame::new(setting());
230            assert_eq!(game.groups.len(), 18);
231        }
232    }
233    mod _3_3 {
234        use super::*;
235        fn setting() -> GameSetting {
236            GameSetting::new(BlockSize {
237                height: 3,
238                width: 3,
239            })
240        }
241
242        #[test]
243        fn it_answer_candidate_is_1_to_9() {
244            assert_eq!(
245                setting().answer_candidate(),
246                vec![1, 2, 3, 4, 5, 6, 7, 8, 9]
247            );
248        }
249        #[test]
250        fn it_has_81_cells() {
251            let game = NormalGame::new(setting());
252            assert_eq!(game.cells.len(), 81);
253        }
254        #[test]
255        fn it_has_27_groups() {
256            let game = NormalGame::new(setting());
257            assert_eq!(game.groups.len(), 27);
258        }
259    }
260    mod test_load {
261        use super::*;
262        const GAME_STRING:&str = " 7     6 |6   1   3|  54 87  |  8   4  | 1  3  5 |  9   1  |  35 12  |7   2   8| 5     9 ";
263        fn game() -> NormalGame {
264            let mut game = NormalGame::new(setting::GameSetting::new(BlockSize {
265                height: 3,
266                width: 3,
267            }));
268            game.load(GAME_STRING);
269            game
270        }
271        mod load_9_9 {
272            use super::*;
273            #[test]
274            fn has_81_cells() {
275                assert_eq!(game().cells.len(), 81);
276            }
277
278            #[test]
279            fn none_0_0() {
280                assert_eq!(
281                    game()
282                        .cells
283                        .iter()
284                        .find(|c| c.borrow().pos() == cell::Position::new(0, 0))
285                        // .find_by_position(&cell::Position::new(0, 0))
286                        .unwrap()
287                        .borrow()
288                        .answer(),
289                    None
290                );
291            }
292            #[test]
293            fn some_1_0() {
294                assert_eq!(
295                    game()
296                        .cells
297                        .iter()
298                        .find(|c| c.borrow().pos() == cell::Position::new(1, 0))
299                        // .find_by_position(&cell::Position::new(1, 0))
300                        .unwrap()
301                        .borrow()
302                        .answer(),
303                    Some(7)
304                );
305            }
306            #[test]
307            fn some_2_0() {
308                assert_eq!(
309                    game()
310                        .cells
311                        .iter()
312                        .find(|c| c.borrow().pos() == cell::Position::new(2, 0))
313                        // .find_by_position(&cell::Position::new(2, 0))
314                        .unwrap()
315                        .borrow()
316                        .answer(),
317                    None
318                );
319            }
320            #[test]
321            fn some_7_0() {
322                assert_eq!(
323                    game()
324                        .cells
325                        .iter()
326                        .find(|c| c.borrow().pos() == cell::Position::new(7, 0))
327                        // .find_by_position(&cell::Position::new(7, 0))
328                        .unwrap()
329                        .borrow()
330                        .answer(),
331                    Some(6)
332                );
333            }
334        }
335        #[test]
336        fn test_to_string() {
337            assert_eq!(game().to_string(), GAME_STRING.to_string());
338        }
339        #[test]
340        fn test_load_9_9_2() {
341            let mut game = NormalGame::new(setting::GameSetting::new(BlockSize {
342                height: 3,
343                width: 3,
344            }));
345            game.load(
346                "4       1| 5   1 4 |  8 476  | 79|  3 7 2|      59|  681 9| 4 9   7|2       5",
347            );
348            assert_eq!(game.to_string(), "4       1| 5   1 4 |  8 476  | 79      |  3 7 2  |      59 |  681 9  | 4 9   7 |2       5")
349        }
350        #[test]
351        fn test_load_12x12() {
352            let mut game = NormalGame::new(setting::GameSetting::new(BlockSize {
353                height: 3,
354                width: 4,
355            }));
356            game.load(
357                " , , ,6, , , , ,8| , , , ,12,10,5,11| , ,10,4, ,9,7, ,1,11|10, ,3, , , , , , ,7, ,12| ,5, , , ,12,10, , , ,9| ,7,8, ,9, , ,2, ,5,10| ,1,7, ,8, , ,6, ,3,4,| ,10, , , ,5,1, , , ,2|11, ,4, , , , , , ,12, ,7| , ,9,10, ,8,4, ,3,6,| , , , ,2,1,6,9,| , , ,11, , , , ,9",
358            );
359            assert_eq!(game.to_string_with_comma(), " , , ,6, , , , ,8, , , | , , , ,12,10,5,11, , , , | , ,10,4, ,9,7, ,1,11, , |10, ,3, , , , , , ,7, ,12| ,5, , , ,12,10, , , ,9, | ,7,8, ,9, , ,2, ,5,10, | ,1,7, ,8, , ,6, ,3,4, | ,10, , , ,5,1, , , ,2, |11, ,4, , , , , , ,12, ,7| , ,9,10, ,8,4, ,3,6, , | , , , ,2,1,6,9, , , , | , , ,11, , , , ,9, , , ")
360        }
361    }
362    mod set_answer {
363        use super::*;
364        #[test]
365        fn test() {
366            let mut game = NormalGame::new(setting());
367            game.set_answer(cell::Position::new(0, 0), 2);
368            assert_eq!(game.cells()[0].borrow().answer(), Some(2));
369        }
370        #[test]
371        fn remove_unanswerd_candidate_from_groups() {
372            let mut game = NormalGame::new(setting());
373            game.set_answer(cell::Position::new(0, 0), 2);
374            assert_eq!(game.cells()[1].borrow().has_answer_candidate(2), false);
375        }
376    }
377}