1use crate::utils::{build_left_moves_table, build_right_moves_table, get_exponent};
2use lazy_static::lazy_static;
3use std::fmt::{Debug, Display, Formatter};
4use termion::color;
5
6#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
20pub struct Board {
21 state: u64,
22}
23
24#[derive(Clone, Copy, PartialEq, Eq, Debug)]
26pub enum Direction {
27 Left,
28 Right,
29 Up,
30 Down,
31}
32
33impl Direction {
34 pub fn all() -> &'static [Direction; 4] {
35 &[
36 Direction::Left,
37 Direction::Right,
38 Direction::Up,
39 Direction::Down,
40 ]
41 }
42}
43
44lazy_static! {
45 static ref LEFT_MOVES_TABLE: Vec<u16> = build_left_moves_table();
46 static ref RIGHT_MOVES_TABLE: Vec<u16> = build_right_moves_table();
47}
48
49impl Board {
50 pub fn get_value(self, tile_idx: u8) -> u16 {
53 let exponent = self.get_exponent_value(tile_idx);
54 if exponent == 0 {
55 return 0;
56 }
57 2 << (exponent - 1) as u16
58 }
59
60 pub fn get_exponent_value(self, tile_idx: u8) -> u8 {
64 ((self.state >> (4 * (15 - tile_idx as u64))) & 0xF) as u8
65 }
66
67 pub fn set_value(self, tile_idx: u8, tile_value: u16) -> Self {
69 let exponent = get_exponent(tile_value);
70 self.set_value_by_exponent(tile_idx, exponent)
71 }
72
73 pub fn set_value_by_exponent(self, tile_idx: u8, value_exponent: u64) -> Self {
77 let bits_shift = ((15 - tile_idx) * 4) as u64;
78 let clear_mask = !(0xF << bits_shift);
80 let update_mask = value_exponent << bits_shift;
81 let new_state = (self.state & clear_mask) | update_mask;
82 Board { state: new_state }
83 }
84
85 pub fn rows(self) -> [u16; 4] {
87 let row1 = ((self.state & 0xFFFF_0000_0000_0000) >> 48) as u16;
88 let row2 = ((self.state & 0x0000_FFFF_0000_0000) >> 32) as u16;
89 let row3 = ((self.state & 0x0000_0000_FFFF_0000) >> 16) as u16;
90 let row4 = (self.state & 0x0000_0000_0000_FFFF) as u16;
91 [row1, row2, row3, row4]
92 }
93
94 pub fn columns(self) -> [u16; 4] {
96 self.transpose().rows()
97 }
98
99 pub fn max_value(self) -> u16 {
101 let exponent = self.into_iter().max().unwrap();
102 1 << exponent as u16
103 }
104
105 pub fn empty_tiles_indices(self) -> impl Iterator<Item = u8> {
107 self.into_empty_tiles_iter()
108 }
109
110 pub fn count_empty_tiles(self) -> usize {
112 self.empty_tiles_indices().fold(0, |mut acc, _| {
113 acc += 1;
114 acc
115 })
116 }
117
118 pub fn count_distinct_tiles(self) -> usize {
120 let mut bitset: u16 = 0;
121 let mut state = self.state;
122 while state != 0 {
123 bitset |= 1 << (state & 0b1111) as u16;
124 state >>= 4;
125 }
126 bitset >>= 1;
128 let mut count: usize = 0;
129 while bitset != 0 {
130 bitset &= bitset - 1;
131 count += 1;
132 }
133 count
134 }
135
136 pub fn move_to(self, direction: Direction) -> Self {
138 match direction {
139 Direction::Left => self.into_left(),
140 Direction::Right => self.into_right(),
141 Direction::Up => self.into_up(),
142 Direction::Down => self.into_down(),
143 }
144 }
145
146 fn transpose(self) -> Self {
147 let x = self.state;
150 let a1 = x & 0xF0F0_0F0F_F0F0_0F0F;
151 let a2 = x & 0x0000_F0F0_0000_F0F0;
152 let a3 = x & 0x0F0F_0000_0F0F_0000;
153 let a = a1 | (a2 << 12) | (a3 >> 12);
154 let b1 = a & 0xFF00_FF00_00FF_00FF;
155 let b2 = a & 0x00FF_00FF_0000_0000;
156 let b3 = a & 0x0000_0000_FF00_FF00;
157 let ret = b1 | (b2 >> 24) | (b3 << 24);
158 Self { state: ret }
159 }
160
161 fn into_left(self) -> Self {
162 self.rows()
163 .iter()
164 .enumerate()
165 .fold(Board::default(), |mut acc, (row_idx, row)| {
166 acc.state |=
167 (LEFT_MOVES_TABLE[*row as usize] as u64) << (16 * (3 - row_idx) as u64);
168 acc
169 })
170 }
171
172 fn into_right(self) -> Self {
173 self.rows()
174 .iter()
175 .enumerate()
176 .fold(Board::default(), |mut acc, (row_idx, row)| {
177 acc.state |=
178 (RIGHT_MOVES_TABLE[*row as usize] as u64) << (16 * (3 - row_idx) as u64);
179 acc
180 })
181 }
182
183 fn into_up(self) -> Self {
184 self.transpose().rows().iter().enumerate().fold(
185 Board::default(),
186 |mut acc, (col_idx, col)| {
187 let up_col = LEFT_MOVES_TABLE[*col as usize] as u64;
188 let col_shift = 4 * (3 - col_idx) as u64;
189 acc.state |= (up_col & 0xF000) << (36 + col_shift);
190 acc.state |= (up_col & 0xF00) << (24 + col_shift);
191 acc.state |= (up_col & 0xF0) << (12 + col_shift);
192 acc.state |= (up_col & 0xF) << col_shift;
193 acc
194 },
195 )
196 }
197
198 fn into_down(self) -> Self {
199 self.transpose().rows().iter().enumerate().fold(
200 Board::default(),
201 |mut acc, (col_idx, col)| {
202 let up_col = RIGHT_MOVES_TABLE[*col as usize] as u64;
203 let col_shift = 4 * (3 - col_idx) as u64;
204 acc.state |= (up_col & 0xF000) << (36 + col_shift);
205 acc.state |= (up_col & 0xF00) << (24 + col_shift);
206 acc.state |= (up_col & 0xF0) << (12 + col_shift);
207 acc.state |= (up_col & 0xF) << col_shift;
208 acc
209 },
210 )
211 }
212}
213
214impl IntoIterator for Board {
215 type Item = u8;
216 type IntoIter = BoardIntoIterator;
217
218 fn into_iter(self) -> Self::IntoIter {
219 BoardIntoIterator {
220 state: self.state,
221 index: 0,
222 }
223 }
224}
225
226pub struct BoardIntoIterator {
227 state: u64,
228 index: u8,
229}
230
231impl Iterator for BoardIntoIterator {
232 type Item = u8;
233
234 fn next(&mut self) -> Option<Self::Item> {
235 match self.index {
236 16 => None,
237 _ => {
238 let exponent = self.state >> 60;
239 self.state <<= 4;
240 self.index += 1;
241 Some(exponent as u8)
242 }
243 }
244 }
245}
246
247impl Board {
248 pub fn into_empty_tiles_iter(self) -> EmptyTilesIterator {
249 EmptyTilesIterator {
250 state: self.state,
251 index: 0,
252 }
253 }
254}
255
256pub struct EmptyTilesIterator {
257 state: u64,
258 index: u8,
259}
260
261impl Iterator for EmptyTilesIterator {
262 type Item = u8;
263
264 fn next(&mut self) -> Option<Self::Item> {
265 loop {
266 match self.index {
267 16 => return None,
268 _ => {
269 let empty_tile_index = if self.state.leading_zeros() >= 4 {
270 Some(self.index)
271 } else {
272 None
273 };
274 self.state <<= 4;
275 self.index += 1;
276 if empty_tile_index.is_some() {
277 return empty_tile_index;
278 }
279 }
280 }
281 }
282 }
283}
284
285impl From<Vec<u16>> for Board {
286 fn from(tiles: Vec<u16>) -> Self {
287 let mut state: u64 = 0;
288 for tile_value in tiles.into_iter() {
289 state <<= 4;
290 state |= get_exponent(tile_value);
291 }
292 Self { state }
293 }
294}
295
296impl From<Board> for Vec<u16> {
297 fn from(board: Board) -> Self {
298 board
299 .into_iter()
300 .map(|tile_exponent| {
301 if tile_exponent == 0 {
302 0
303 } else {
304 2 << (tile_exponent - 1) as u16
305 }
306 })
307 .collect()
308 }
309}
310
311impl Board {
312 fn display(self, f: &mut Formatter<'_>, debug: bool) -> Result<(), std::fmt::Error> {
313 let mut display = String::new();
314 let line_break = if debug { "\n" } else { "\n\r" };
315 display.push_str(&*format!(
316 "{b}╔═══════╦═══════╦═══════╦═══════╗{b}",
317 b = line_break
318 ));
319 for (i, tile) in Vec::from(self).into_iter().enumerate() {
320 if tile == 0 {
321 display.push_str("║ ");
322 } else if debug {
323 display.push_str(&*format!(
324 "║{prefix}{tile} ",
325 prefix = get_spaces_prefix(tile),
326 tile = tile,
327 ));
328 } else {
329 display.push_str(&*format!(
330 "║{prefix}{color}{tile}{reset} ",
331 prefix = get_spaces_prefix(tile),
332 color = get_color(tile),
333 tile = tile,
334 reset = color::Fg(color::Reset)
335 ));
336 }
337 if i % 4 == 3 {
338 display.push_str(&*format!("║{b}", b = line_break));
339 if i == 15 {
340 display.push_str(&*format!(
341 "╚═══════╩═══════╩═══════╩═══════╝{b}",
342 b = line_break
343 ));
344 } else {
345 display.push_str(&*format!(
346 "╠═══════╬═══════╬═══════╬═══════╣{b}",
347 b = line_break
348 ));
349 }
350 }
351 }
352 write!(f, "{}", display)
353 }
354}
355
356impl Display for Board {
357 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
358 self.display(f, false)
359 }
360}
361
362impl Debug for Board {
363 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
364 self.display(f, true)
365 }
366}
367
368fn get_spaces_prefix(tile: u16) -> &'static str {
369 if tile < 10 {
370 " "
371 } else if tile < 100 {
372 " "
373 } else if tile < 1000 {
374 " "
375 } else if tile < 10000 {
376 " "
377 } else {
378 " "
379 }
380}
381
382fn get_color(tile: u16) -> color::Fg<color::Rgb> {
383 match tile {
384 2 => color::Fg(color::Rgb(238, 228, 218)),
385 4 => color::Fg(color::Rgb(237, 224, 200)),
386 8 => color::Fg(color::Rgb(242, 177, 121)),
387 16 => color::Fg(color::Rgb(245, 149, 99)),
388 32 => color::Fg(color::Rgb(246, 124, 95)),
389 64 => color::Fg(color::Rgb(246, 94, 59)),
390 128 => color::Fg(color::Rgb(237, 207, 114)),
391 256 => color::Fg(color::Rgb(237, 204, 97)),
392 512 => color::Fg(color::Rgb(237, 200, 80)),
393 1024 => color::Fg(color::Rgb(237, 197, 63)),
394 2048 => color::Fg(color::Rgb(237, 194, 46)),
395 4096 => color::Fg(color::Rgb(129, 214, 154)),
396 8192 => color::Fg(color::Rgb(129, 214, 154)),
397 16384 => color::Fg(color::Rgb(129, 214, 154)),
398 32768 => color::Fg(color::Rgb(129, 214, 154)),
399 _ => panic!("Invalid tile value: {}", tile),
400 }
401}
402
403#[cfg(test)]
404mod tests {
405 use super::*;
406
407 #[test]
408 fn should_convert_vec_to_board() {
409 #[rustfmt::skip]
411 let vec_board: Vec<u16> = vec![
412 0, 2, 0, 0,
413 32768, 0, 0, 2,
414 0, 0, 16, 4,
415 8, 2, 16, 64
416 ];
417
418 let board = Board::from(vec_board.clone());
420
421 let into_vec_board: Vec<u16> = board.into();
423 assert_eq!(vec_board, into_vec_board);
424 }
425
426 #[test]
427 fn should_iterate_over_exponents() {
428 #[rustfmt::skip]
430 let vec_board: Vec<u16> = vec![
431 0, 2, 0, 0,
432 32768, 0, 0, 2,
433 0, 0, 16, 4,
434 8, 2, 16, 64
435 ];
436 let board = Board::from(vec_board.clone());
437
438 let exponents: Vec<_> = board.into_iter().collect();
440
441 #[rustfmt::skip]
443 let expected_exponents = vec![
444 0, 1, 0, 0,
445 15, 0, 0, 1,
446 0, 0, 4, 2,
447 3, 1, 4, 6
448 ];
449 assert_eq!(expected_exponents, exponents);
450 }
451
452 #[test]
453 fn should_use_binary_representation() {
454 #[rustfmt::skip]
456 let board_values = vec![
457 0, 0, 0, 0,
458 0, 0, 0, 0,
459 0, 0, 4, 0,
460 0, 16, 0, 8
461 ];
462
463 let board = Board::from(board_values);
465
466 let board_repr: u64 =
468 2u64.pow(0 + 0) + 2u64.pow(1 + 0) + 2u64.pow(2 + 8) + 2u64.pow(1 + 20);
469 assert_eq!(board_repr as u64, board.state);
470 }
471
472 #[test]
473 fn should_get_value() {
474 #[rustfmt::skip]
476 let board = Board::from(vec![
477 0, 4, 0, 2,
478 2, 0, 4, 0,
479 4, 2, 0, 512,
480 16, 8, 32, 32,
481 ]);
482
483 assert_eq!(512, board.get_value(11));
485 }
486
487 #[test]
488 fn should_get_exponent_value() {
489 #[rustfmt::skip]
491 let board = Board::from(vec![
492 0, 4, 0, 2,
493 2, 0, 4, 0,
494 4, 2, 0, 512,
495 16, 8, 32, 32,
496 ]);
497
498 assert_eq!(9, board.get_exponent_value(11));
500 }
501
502 #[test]
503 fn should_get_rows() {
504 #[rustfmt::skip]
506 let board = Board::from(vec![
507 0, 4, 0, 2,
508 2, 0, 4, 0,
509 4, 2, 0, 512,
510 16, 8, 32, 32,
511 ]);
512
513 let rows = board.rows();
515
516 let expected_rows: [u16; 4] = [0x0201, 0x1020, 0x2109, 0x4355];
518 assert_eq!(expected_rows, rows);
519 }
520
521 #[test]
522 fn should_get_columns() {
523 #[rustfmt::skip]
525 let board = Board::from(vec![
526 0, 2, 4, 16,
527 4, 0, 2, 8,
528 0, 4, 0, 32,
529 2, 0, 512, 32,
530 ]);
531
532 let columns = board.columns();
534
535 let expected_rows: [u16; 4] = [0x0201, 0x1020, 0x2109, 0x4355];
537 assert_eq!(expected_rows, columns);
538 }
539
540 #[test]
541 fn should_set_value() {
542 #[rustfmt::skip]
544 let board = Board::from(vec![
545 0, 4, 0, 2,
546 2, 0, 4, 0,
547 4, 2, 0, 512,
548 16, 8, 32, 32,
549 ]);
550
551 let board = board.set_value(5, 32).set_value(8, 64);
553
554 #[rustfmt::skip]
556 let expected_board = Board::from(vec![
557 0, 4, 0, 2,
558 2, 32, 4, 0,
559 64, 2, 0, 512,
560 16, 8, 32, 32,
561 ]);
562 assert_eq!(expected_board, board);
563 }
564
565 #[test]
566 fn should_set_value_by_exponent() {
567 #[rustfmt::skip]
569 let board = Board::from(vec![
570 0, 4, 0, 2,
571 2, 0, 4, 0,
572 4, 2, 0, 512,
573 16, 8, 32, 32,
574 ]);
575
576 let board = board
578 .set_value_by_exponent(5, 5)
579 .set_value_by_exponent(8, 6);
580
581 #[rustfmt::skip]
583 let expected_board = Board::from(vec![
584 0, 4, 0, 2,
585 2, 32, 4, 0,
586 64, 2, 0, 512,
587 16, 8, 32, 32,
588 ]);
589 assert_eq!(expected_board, board);
590 }
591
592 #[test]
593 fn should_move_left() {
594 #[rustfmt::skip]
596 let board = Board::from(vec![
597 0, 0, 0, 2,
598 2, 2, 4, 0,
599 4, 2, 8, 512,
600 16, 16, 32, 32,
601 ]);
602
603 let left_board = board.into_left();
605
606 #[rustfmt::skip]
608 let expected_board = Board::from(vec![
609 2, 0, 0, 0,
610 4, 4, 0, 0,
611 4, 2, 8, 512,
612 32, 64, 0, 0,
613 ]);
614 assert_eq!(expected_board, left_board);
615 }
616
617 #[test]
618 fn should_move_right() {
619 #[rustfmt::skip]
621 let board = Board::from(vec![
622 512, 8, 2, 4,
623 2, 0, 0, 0,
624 0, 4, 2, 2,
625 32, 32, 16, 16,
626 ]);
627
628 let right_board = board.into_right();
630
631 #[rustfmt::skip]
633 let expected_board = Board::from(vec![
634 512, 8, 2, 4,
635 0, 0, 0, 2,
636 0, 0, 4, 4,
637 0, 0, 64, 32,
638 ]);
639 assert_eq!(expected_board, right_board);
640 }
641
642 #[test]
643 fn should_move_up() {
644 #[rustfmt::skip]
646 let board = Board::from(vec![
647 0, 2, 512, 16,
648 0, 2, 8, 16,
649 0, 4, 2, 32,
650 2, 0, 4, 32,
651 ]);
652
653 let up_board = board.into_up();
655
656 #[rustfmt::skip]
658 let expected_board = Board::from(vec![
659 2, 4, 512, 32,
660 0, 4, 8, 64,
661 0, 0, 2, 0,
662 0, 0, 4, 0,
663 ]);
664 assert_eq!(expected_board, up_board);
665 }
666
667 #[test]
668 fn should_move_down() {
669 #[rustfmt::skip]
671 let board = Board::from(vec![
672 2, 0, 512, 32,
673 0, 4, 8, 32,
674 0, 2, 2, 16,
675 0, 2, 4, 16,
676 ]);
677
678 let down_board = board.into_down();
680
681 #[rustfmt::skip]
683 let expected_board = Board::from(vec![
684 0, 0, 512, 0,
685 0, 0, 8, 0,
686 0, 4, 2, 64,
687 2, 4, 4, 32,
688 ]);
689 assert_eq!(expected_board, down_board);
690 }
691
692 #[test]
693 fn should_move_with_high_values() {
694 #[rustfmt::skip]
696 let board = Board::from(vec![
697 0, 0, 0, 0,
698 0, 0, 16384, 0,
699 0, 0, 16384, 0,
700 0, 0, 0, 0,
701 ]);
702
703 let down_board = board.into_down();
705
706 #[rustfmt::skip]
708 let expected_board = Board::from(vec![
709 0, 0, 0, 0,
710 0, 0, 0, 0,
711 0, 0, 0, 0,
712 0, 0, 32768, 0,
713 ]);
714 assert_eq!(expected_board, down_board);
715 }
716
717 #[test]
718 fn should_get_max_value() {
719 #[rustfmt::skip]
721 let vec_board = vec![
722 0, 2, 0, 2048,
723 0, 256, 0, 512,
724 0, 0, 1024, 4,
725 8, 2, 16, 64
726 ];
727 let board = Board::from(vec_board);
728
729 let max_value = board.max_value();
731
732 assert_eq!(2048, max_value);
734 }
735
736 #[test]
737 fn should_get_empty_tiles() {
738 #[rustfmt::skip]
740 let vec_board = vec![
741 0, 2, 0, 2048,
742 0, 256, 0, 512,
743 0, 0, 1024, 4,
744 8, 2, 16, 64
745 ];
746 let board = Board::from(vec_board);
747
748 let empty_tiles: Vec<_> = board.empty_tiles_indices().collect();
750 assert_eq!(vec![0, 2, 4, 6, 8, 9], empty_tiles);
751 }
752
753 #[test]
754 fn should_count_empty_tiles() {
755 #[rustfmt::skip]
757 let vec_board = vec![
758 0, 2, 0, 2048,
759 0, 256, 0, 512,
760 0, 0, 1024, 4,
761 8, 2, 16, 64
762 ];
763 let board = Board::from(vec_board);
764
765 let nb_empty_tiles = board.count_empty_tiles();
767 assert_eq!(6, nb_empty_tiles);
768 }
769
770 #[test]
771 fn should_count_distinct_tiles() {
772 #[rustfmt::skip]
774 let vec_board = vec![
775 0, 2, 0, 2048,
776 0, 16, 0, 512,
777 0, 0, 8, 4,
778 8, 2, 16, 64
779 ];
780 let board = Board::from(vec_board);
781
782 let distinct_tiles = board.count_distinct_tiles();
784 assert_eq!(7, distinct_tiles);
785 }
786
787 #[test]
788 fn should_display_board_for_debug() {
789 #[rustfmt::skip]
791 let vec_board = vec![
792 8192, 32, 16384, 32768,
793 4096, 256, 0, 512,
794 2048, 128, 1024, 4,
795 8, 2, 16, 64
796 ];
797 let board = Board::from(vec_board);
798
799 let display = format!("{:?}", board);
801
802 let expected_display = r#"
804╔═══════╦═══════╦═══════╦═══════╗
805║ 8192 ║ 32 ║ 16384 ║ 32768 ║
806╠═══════╬═══════╬═══════╬═══════╣
807║ 4096 ║ 256 ║ ║ 512 ║
808╠═══════╬═══════╬═══════╬═══════╣
809║ 2048 ║ 128 ║ 1024 ║ 4 ║
810╠═══════╬═══════╬═══════╬═══════╣
811║ 8 ║ 2 ║ 16 ║ 64 ║
812╚═══════╩═══════╩═══════╩═══════╝
813"#;
814 assert_eq!(expected_display, display);
815 }
816
817 #[test]
818 fn should_display_board() {
819 #[rustfmt::skip]
821 let vec_board = vec![
822 8192, 32, 16384, 32768,
823 4096, 256, 0, 512,
824 2048, 128, 1024, 4,
825 8, 2, 16, 64
826 ];
827 let board = Board::from(vec_board);
828
829 format!("{}", board);
831 }
832}