Skip to main content

play_2048/
utils.rs

1pub fn get_exponent(value: u16) -> u64 {
2    match value {
3        0 => 0,
4        2 => 1,
5        4 => 2,
6        8 => 3,
7        16 => 4,
8        32 => 5,
9        64 => 6,
10        128 => 7,
11        256 => 8,
12        512 => 9,
13        1024 => 10,
14        2048 => 11,
15        4096 => 12,
16        8192 => 13,
17        16384 => 14,
18        32768 => 15,
19        _ => panic!("Invalid tile value {}", value),
20    }
21}
22
23pub fn build_left_moves_table() -> Vec<u16> {
24    (0..(std::u16::MAX as usize + 1))
25        .map(|x| get_left_move(x as u16))
26        .collect()
27}
28
29pub fn build_right_moves_table() -> Vec<u16> {
30    (0..(std::u16::MAX as usize + 1))
31        .map(|x| get_right_move(x as u16))
32        .collect()
33}
34
35fn get_left_move(row: u16) -> u16 {
36    let mut result = row;
37    let mut prev_value = std::u8::MAX;
38    let mut new_value_idx = 0;
39    // whether or not tiles have been moved in this row
40    let mut moved = false;
41    for i in 0..4 {
42        let value: u8 = ((row & (0xF << (4 * (3 - i)))) >> (4 * (3 - i))) as u8;
43        if value == 0 {
44            moved = true;
45        } else if value == prev_value {
46            result = set_value_in_row(result, new_value_idx - 1, value + 1);
47            result = set_value_in_row(result, i as u8, 0);
48            prev_value = std::u8::MAX;
49            moved = true;
50        } else {
51            if moved {
52                result = set_value_in_row(result, new_value_idx, value);
53                result = set_value_in_row(result, i as u8, 0);
54            }
55            prev_value = value;
56            new_value_idx += 1;
57        }
58    }
59    result
60}
61
62fn get_right_move(row: u16) -> u16 {
63    invert_row(get_left_move(invert_row(row)))
64}
65
66fn invert_row(row: u16) -> u16 {
67    let mut inverted_row: u16 = 0;
68    for i in 0..4 {
69        let value = (row >> (4 * i)) & 0xF;
70        inverted_row = set_value_in_row(inverted_row, i as u8, value as u8);
71    }
72    inverted_row
73}
74
75fn set_value_in_row(row: u16, idx: u8, value: u8) -> u16 {
76    // bitmask with 0000 at the corresponding tile_idx and 1s everywhere else
77    let clear_mask: u16 = !(0xF << (4 * (3 - idx) as u16));
78    let update_mask: u16 = (value as u16) << (4 * (3 - idx) as u16);
79    (row & clear_mask) | update_mask
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn should_set_value_in_row() {
88        // Given
89        let row = 0b0101_0000_0101_1100;
90
91        // When
92        let updated_row = set_value_in_row(row, 2, 8);
93
94        // Then
95        assert_eq!(0b0101_0000_1000_1100, updated_row);
96    }
97
98    #[test]
99    fn should_get_left_move() {
100        // Given
101        let row = 0b0101_0000_0101_1100;
102
103        // When
104        let left_moved = get_left_move(row);
105
106        // Then
107        assert_eq!(0b0110_1100_0000_0000, left_moved);
108    }
109
110    #[test]
111    fn should_get_right_move() {
112        // Given
113        let row = 0b0101_0000_0101_1100;
114
115        // When
116        let left_moved = get_right_move(row);
117
118        // Then
119        assert_eq!(0b0000_0000_0110_1100, left_moved);
120    }
121
122    #[test]
123    fn should_invert_row() {
124        // Given
125        let row = 0b0101_0000_0101_1100;
126
127        // When
128        let inverted_row = invert_row(row);
129
130        // Then
131        assert_eq!(0b1100_0101_0000_0101, inverted_row);
132    }
133}