puzzle_lib/
lib.rs

1use rand::Rng;
2use std::time::{SystemTime, UNIX_EPOCH};
3
4const DIRECTION_DIST: phf::Map<char, [i32; 2]> = phf::phf_map! {
5    'U' => [-1, 0],
6    'D' => [1, 0],
7    'L' => [0, -1],
8    'R' => [0, 1]
9};
10
11const DIRECTION_LIST: [char; 4] = ['U', 'D', 'L', 'R'];
12
13
14pub struct Puzzle {
15    pub cmds_str: String,
16    pub mode: usize,
17    pub puzzle: Vec<Vec<i32>>,
18    correct_puzzle: Vec<Vec<i32>>,
19    pub start_time: u128,
20    pub end_time: u128,
21}
22
23impl Puzzle {
24    pub fn new(mode: usize) -> Self {
25        let mut puzzle = vec![vec![0; mode]; mode];
26        let mut correct_puzzle = vec![vec![0; mode]; mode];
27        let mut num = 1;
28
29        for i in 0..mode {
30            for j in 0..mode {
31                puzzle[i][j] = num;
32                correct_puzzle[i][j] = num;
33                num += 1;
34            }
35        }
36
37        puzzle[mode - 1][mode - 1] = 0;
38        correct_puzzle[mode - 1][mode - 1] = 0;
39
40        let start_time = SystemTime::now()
41            .duration_since(UNIX_EPOCH)
42            .expect("Time went backwards")
43            .as_millis();
44
45        let mut instance = Puzzle {
46            cmds_str: String::new(),
47            mode,
48            puzzle,
49            correct_puzzle,
50            start_time,
51            end_time: 0,
52        };
53
54        instance.shuffle();
55        instance
56    }
57
58    fn find_0(&self) -> Option<(usize, usize)> {
59        for (i, row) in self.puzzle.iter().enumerate() {
60            for (j, &value) in row.iter().enumerate() {
61                if value == 0 {
62                    return Some((i, j));
63                }
64            }
65        }
66        None
67    }
68
69    pub fn move_tile(&mut self, direction: char) -> Option<char> {
70        self.cmds_str.push(direction);
71        if let Some((r, c)) = self.find_0() {
72            if (r == 0 && direction == 'U')
73                || (r == self.mode - 1 && direction == 'D')
74                || (c == 0 && direction == 'L')
75                || (c == self.mode - 1 && direction == 'R')
76            {
77                return None
78            }
79
80            if let Some(&[dr, dc]) = DIRECTION_DIST.get(&direction) {
81                let rr = (r as i32 + dr) as usize;
82                let cc = (c as i32 + dc) as usize;
83
84                let num1 = self.puzzle[rr][cc];
85                self.puzzle[r][c] = num1;
86                self.puzzle[rr][cc] = 0;
87                return Some(direction)
88            }
89        }
90        return None
91    }
92
93    pub fn check(&mut self) -> bool {
94        for i in 0..self.mode {
95            for j in 0..self.mode {
96                if self.puzzle[i][j] != self.correct_puzzle[i][j] {
97                    return false;
98                }
99            }
100        }
101        self.end_time = SystemTime::now()
102            .duration_since(UNIX_EPOCH)
103            .expect("Time went backwards")
104            .as_millis();
105        true
106    }
107
108    pub fn move_sequence(&mut self, sequence: &str) -> bool {
109        let uppercase = sequence.to_uppercase();
110        self.cmds_str.clear();
111        for command in uppercase.chars() {
112            self.move_tile(command);
113            if self.check() {
114                return true;
115            }
116        }
117        false
118    }
119
120    pub fn shuffle(&mut self) {
121        let mut rng = rand::thread_rng();
122        for _ in 0..1000 {
123            let random_direction = DIRECTION_LIST[rng.gen_range(0..4)];
124            self.move_tile(random_direction);
125        }
126    }
127
128    pub fn duration(&self) -> String {
129        let time_now = SystemTime::now()
130            .duration_since(UNIX_EPOCH)
131            .expect("Time went backwards")
132            .as_millis();
133        let duration = time_now - self.start_time;
134        self.format_duration(duration)
135    }
136
137    fn format_duration(&self, duration: u128) -> String {
138        let hours = duration / 3600000;
139        let minutes = (duration % 3600000) / 60000;
140        let seconds = (duration % 60000) / 1000;
141        format!("{}:{}:{}", hours, minutes, seconds)
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn it_works() {
151        let puzzle = Puzzle::new(4);
152        for i in 0..4{
153            println!("{:?}", puzzle.puzzle[i]);
154        }
155        println!();
156        assert_eq!(puzzle.puzzle.len(), 4);
157    }
158
159    #[test]
160    fn it_works_mode3() {
161        let mut puzzle = Puzzle::new(3);
162        for i in 0..3{
163            println!("{:?}", puzzle.puzzle[i]);
164        }
165        println!();
166        puzzle.move_tile('U');
167        for i in 0..3{
168            println!("{:?}", puzzle.puzzle[i]);
169        }
170        println!();
171        assert_eq!(puzzle.puzzle.len(), 3);
172    }
173}