Skip to main content

holomaze/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(missing_docs)]
3
4use Cell::*;
5
6pub mod games;
7
8/// Represents a cell in the map.
9#[derive(Copy, Clone, Debug, PartialEq, Eq)]
10pub enum Cell {
11    /// An indetermine state.
12    Unknown,
13    /// The player.
14    Player,
15    /// A previous position of the player.
16    ///
17    /// This is intended to make visualization easier.
18    Follower,
19    /// An observable.
20    ///
21    /// This is either `true` or `false`.
22    Val(bool),
23}
24
25/// Represents the map of a game.
26#[derive(Clone)]
27pub struct Map {
28    /// The current state of cells.
29    pub cells: [[Cell; 4]; 4],
30    /// The "laws of nature".
31    pub f: fn(&mut u16),
32    /// The current player position.
33    pub player_pos: [usize; 2],
34}
35
36impl Map {
37    /// Creates a new map with default initial configuration.
38    ///
39    /// One can modify this state to e.g. create observables.
40    pub fn new(f: fn(&mut u16)) -> Map {
41        Map {
42            cells: [
43                [Player, Unknown, Unknown, Unknown],
44                [Unknown; 4],
45                [Unknown; 4],
46                [Unknown; 4],
47            ],
48            f,
49            player_pos: [0, 0],
50        }
51    }
52
53    /// Returns `true` if the player has reached the goal, `false` otherwise.
54    pub fn has_won(&self) -> bool {
55        if let Player = self.cells[3][3] {true} else {false}
56    }
57
58    /// Returns `true` if the player is dead, `false` otherwise.
59    pub fn has_lost(&self) -> bool {
60        for i in 0..4 {
61            for j in 0..4 {
62                if let Player = self.cells[i][j] {return false}
63            }
64        }
65        true
66    }
67
68    /// Gets the amount of information in the map.
69    pub fn information(&self) -> u8 {
70        let mut sum = 0;
71        for i in 0..4 {
72            for j in 0..4 {
73                if let Val(_) = self.cells[i][j] {sum += 1}
74            }
75        }
76        sum
77    }
78
79    /// Gets the amount of followers in the map.
80    pub fn followers(&self) -> u8 {
81        let mut sum = 0;
82        for i in 0..4 {
83            for j in 0..4 {
84                if let Follower = self.cells[i][j] {sum += 1}
85            }
86        }
87        sum 
88    }
89
90    /// Performs a move.
91    /// Returns `true` if it was valid, `false` otherwise.
92    pub fn mov(&mut self, pos: [usize; 2]) -> bool {
93        if pos == [0, 0] {return false}
94        let information = self.information();
95
96        let old_pos = self.player_pos;
97        self.cells[self.player_pos[1]][self.player_pos[0]] = Follower;
98        self.cells[pos[1]][pos[0]] = Player;
99        self.player_pos = pos;
100
101        let mut valid = true;
102        let mut filter1: u16 = u16::MAX;
103        let mut filter2: u16 = 0;
104        'state: for s in 0..=u16::MAX {
105            for i in 0..4 {
106                for j in 0..4 {
107                    let b = i * 4 + j;
108                    let v = (s >> b) & 1 == 1;
109                    match self.cells[i][j] {
110                        Player | Follower | Unknown => {}
111                        Val(a) => if v != a {continue 'state}
112                    }
113                }
114            }
115
116            let b = old_pos[1] * 4 + old_pos[0];
117            let old_player_state = (s >> b) & 1 == 1;
118            let mut t = s;
119            (self.f)(&mut t);
120            let b2 = pos[1] * 4 + pos[0];
121            let new_player_state = (t >> b2) & 1 == 1;
122            if new_player_state != old_player_state {
123                valid = false;
124                break;
125            }
126            filter1 &= t;
127            filter2 |= t;
128        }
129        if !valid {
130            self.cells[self.player_pos[1]][self.player_pos[0]] = Follower;
131        }
132
133        // Update cells with new observables.
134        let old_cells = self.cells.clone();
135        for i in 0..4 {
136            for j in 0..4 {
137                let b = i * 4 + j;
138                let set_to_1 = (filter1 >> b) & 1 == 1;
139                let set_to_0 = (filter2 >> b) & 1 == 0;
140                match (set_to_1, set_to_0) {
141                    (true, true) | (false, false) => match self.cells[i][j] {
142                        Player | Follower | Unknown => {}
143                        Val(_) => self.cells[i][j] = Unknown,
144                    },
145                    (true, false) => self.cells[i][j] = Val(true),
146                    (false, true) => self.cells[i][j] = Val(false),
147                }
148            }
149        }
150        if self.information() != information {return false}
151
152        // Check that no other states outside possible worlds
153        // can result in a match against this new world.
154        'other_state: for s2 in 0..=u16::MAX {
155            let mut match_all = true;
156            for i in 0..4 {
157                for j in 0..4 {
158                    let b = i * 4 + j;
159                    let v = (s2 >> b) & 1 == 1;
160                    match old_cells[i][j] {
161                        Player | Follower | Unknown => {}
162                        Val(a) => if v != a {match_all = false}
163                    }
164                }
165            }
166            if match_all {continue 'other_state}
167
168            let mut t2 = s2;
169            (self.f)(&mut t2);
170            let mut match_all = true;
171            for i in 0..4 {
172                for j in 0..4 {
173                    let b = i * 4 + j;
174                    let v = (t2 >> b) & 1 == 1;
175                    match self.cells[i][j] {
176                        Player | Follower | Unknown => {}
177                        Val(a) => if v != a {match_all = false}
178                    }
179                }
180            }
181            if match_all {
182                valid = false;
183                break 'other_state;
184            }
185        }
186
187        valid
188    }
189}
190
191/// Represents a game.
192pub struct Game {
193    /// The name of the game.
194    pub name: String,
195    /// Laws of nature.
196    pub f: fn(&mut u16),
197    /// Configurate the map.
198    pub config: fn(&mut Map),
199}
200
201/// Copy bit from one location to another.
202pub fn move_bit(from: [usize; 2], to: [usize; 2], s: &mut u16) {
203    let b = from[1] * 4 + from[0];
204    let b2 = to[1] * 4 + to[0];
205    if (*s >> b) & 1 == 1 {*s |= 1 << b2} else {*s &= !(1 << b2)}
206}
207
208/// Chain moves together.
209pub fn snake_bits(ps: &[[usize; 2]], s: &mut u16) {
210    for i in (1..ps.len()).rev() {
211        let j = i - 1;
212        move_bit(ps[j], ps[i], s);
213    }
214}
215
216/// Swap bits from one location with another.
217pub fn swap_bits(from: [usize; 2], to: [usize; 2], s: &mut u16) {
218    let a = get_bit(from, *s);
219    set_bit(from, get_bit(to, *s), s);
220    set_bit(to, a, s);
221}
222
223/// Toggles bit at some location.
224pub fn toggle_bit(at: [usize; 2], s: &mut u16) {
225    set_bit(at, !get_bit(at, *s), s);
226}
227
228/// Set bit in `u16` state.
229pub fn set_bit(a: [usize; 2], val: bool, s: &mut u16) {
230    let b = a[1] * 4 + a[0];
231    if val {*s |= 1 << b} else {*s &= !(1 << b)}
232}
233
234/// Get bit from `u16` state.
235pub fn get_bit(a: [usize; 2], s: u16) -> bool {
236    let b = a[1] * 4 + a[0];
237    (s >> b) & 1 == 1
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243
244    /// Sets the output bit directly from the input bit.
245    pub fn direct_win(s: &mut u16) {
246        move_bit([0, 0], [3, 3], s);
247    }
248
249    /// Sets the bit at `(1, 0)` to `1`.
250    pub fn set_1_0_to_1(s: &mut u16) {
251        set_bit([1, 0], true, s);
252    }
253
254    /// Allows moving diagonally.
255    pub fn diagonal(s: &mut u16) {
256        move_bit([2, 2], [3, 3], s);
257        move_bit([1, 1], [2, 2], s);
258        move_bit([0, 0], [1, 1], s);
259    }
260
261    /// Allows moving anywhere.
262    pub fn anywhere(s: &mut u16) {
263        for i in 0..4 {
264            for j in 0..4 {
265                let pos = [i, j];
266                if pos == [0, 0] {continue}
267                move_bit([0, 0], pos, s);
268            }
269        }
270    }
271
272    /// Allow moving to either corner,
273    /// but only allow reaching goal from top right corner.
274    pub fn fake_choice(s: &mut u16) {
275        move_bit([3, 0], [3, 3], s);
276        move_bit([0, 0], [3, 0], s);
277        move_bit([0, 0], [0, 3], s);
278    }
279
280    /// Allow moving to either corner,
281    /// but provides no way to reach the goal from either corner.
282    ///
283    /// This is because there is some possible worlds where corners are not equal.
284    pub fn fake_choice2(s: &mut u16) {
285        if get_bit([3, 0], *s) == get_bit([0, 3], *s) {
286            move_bit([3, 0], [3, 3], s);
287            assert_eq!(get_bit([0, 3], *s), get_bit([3, 0], *s));
288        }
289        move_bit([0, 0], [3, 0], s);
290        move_bit([0, 0], [0, 3], s);
291    }
292
293    /// Sets goal to zero.
294    pub fn set_goal_to_zero(s: &mut u16) {
295        set_bit([3, 3], false, s);
296    }
297
298    #[test]
299    fn test_direct_win() {
300        let mut map = Map::new(direct_win);
301        assert!(!map.has_won());
302        assert!(!map.has_lost());
303
304        assert!(map.mov([3, 3]));
305        assert!(map.has_won());
306    }
307
308    #[test]
309    fn test_set_1_0_to_1() {
310        let mut map = Map::new(set_1_0_to_1);
311        assert_eq!(map.cells[0][1], Unknown);
312
313        assert!(!map.mov([1, 1]));
314        assert_eq!(map.cells[0][1], Val(true));
315    }
316
317    #[test]
318    fn test_diagonal() {
319        let mut map = Map::new(diagonal);
320        assert!(map.mov([1, 1]));
321        assert!(map.mov([2, 2]));
322        assert!(map.mov([3, 3]));
323        assert!(map.has_won());
324    }
325
326    #[test]
327    fn test_anywhere() {
328        let mut map = Map::new(anywhere);
329        assert!(map.mov([2, 3]));
330        // You have to try reach the goal immediately.
331        assert!(!map.mov([3, 3]));
332        assert!(!map.has_won());
333    }
334
335    #[test]
336    fn test_fake_choice() {
337        let mut map = Map::new(fake_choice);
338        assert!(map.mov([3, 0]));
339        assert!(map.mov([3, 3]));
340
341        let mut map = Map::new(fake_choice);
342        assert!(map.mov([0, 3]));
343        assert!(!map.mov([3, 3]));
344    }
345
346    #[test]
347    fn test_fake_choice2() {
348        let mut map = Map::new(fake_choice2);
349        assert!(map.mov([3, 0]));
350        assert!(!map.mov([3, 3]));
351
352        let mut map = Map::new(fake_choice2);
353        assert!(map.mov([0, 3]));
354        assert!(!map.mov([3, 3]));
355    }
356
357    #[test]
358    fn test_set_goal_to_zero() {
359        let mut map = Map::new(set_goal_to_zero);
360        assert!(!map.mov([3, 3]));
361        assert!(map.has_lost());
362    }
363}
364