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 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 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 let row = 0b0101_0000_0101_1100;
90
91 let updated_row = set_value_in_row(row, 2, 8);
93
94 assert_eq!(0b0101_0000_1000_1100, updated_row);
96 }
97
98 #[test]
99 fn should_get_left_move() {
100 let row = 0b0101_0000_0101_1100;
102
103 let left_moved = get_left_move(row);
105
106 assert_eq!(0b0110_1100_0000_0000, left_moved);
108 }
109
110 #[test]
111 fn should_get_right_move() {
112 let row = 0b0101_0000_0101_1100;
114
115 let left_moved = get_right_move(row);
117
118 assert_eq!(0b0000_0000_0110_1100, left_moved);
120 }
121
122 #[test]
123 fn should_invert_row() {
124 let row = 0b0101_0000_0101_1100;
126
127 let inverted_row = invert_row(row);
129
130 assert_eq!(0b1100_0101_0000_0101, inverted_row);
132 }
133}