Skip to main content

play_2048/
board.rs

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/// `Board` is the main object of the 2048 game. It represents the state of the 16 tiles.
7///
8/// The representation relies on a single u64 value which encode the 16 values by leveraging the
9/// fact that each value is a power of 2. This allows to represent values from `0` to `2^15`.
10///
11/// As an example, to encode `32` we take its binary decomposition which is `2^5`. Then, the binary
12/// representation of `5` is computed, and is `"101"`. To make sure that all 16 representations
13/// have 4 bits, a prefix consisting of 0s is added: `"101"` is transformed into `"0101"`.
14///
15/// `0` is actually not a power of 2, hence the previous example cannot be applied to find its
16/// representation. However, an important detail is that the specific value `1` is not part of
17/// the game, its representation would have been `"0000"`. This value is thus the one we use to
18/// represent `0`.
19#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
20pub struct Board {
21    state: u64,
22}
23
24/// The four directions in which the tiles can be moved
25#[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    /// Returns the value at the corresponding index
51    /// The underlying vector representation is used here
52    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    /// Returns the exponent of the value at the corresponding index
61    /// For example, if `get_value(3)` returns `512`, then `get_exponent_value(3)` will return `9`
62    /// because 512 = 2^9
63    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    /// Sets the value `tile_value` at the index `tile_idx`
68    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    /// Sets the value `tile_value` at the index `tile_idx` by specifying the exponent directly.
74    /// For example, `set_value_by_exponent(3, 9)` is equivalent to `set_value(3, 512)`
75    /// because 512 = 2^9
76    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        // bitmask with 0000 at the corresponding tile_idx and 1s everywhere else
79        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    /// Returns the rows
86    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    /// Returns the columns
95    pub fn columns(self) -> [u16; 4] {
96        self.transpose().rows()
97    }
98
99    /// Returns the maximum value of the board
100    pub fn max_value(self) -> u16 {
101        let exponent = self.into_iter().max().unwrap();
102        1 << exponent as u16
103    }
104
105    /// Returns the indices of empty tiles
106    pub fn empty_tiles_indices(self) -> impl Iterator<Item = u8> {
107        self.into_empty_tiles_iter()
108    }
109
110    /// Returns the number of empty tiles
111    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    /// Returns the number of distinct tiles, excluding empty tiles
119    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        // exclude empty tiles from the count
127        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    /// Moves the tiles in the provided `Direction` and returns the resulting `Board`
137    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        // Credit to nneonneo for this fast tranpose implementation
148        // https://github.com/nneonneo/2048-ai/blob/master/2048.cpp
149        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        // Given
410        #[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        // When
419        let board = Board::from(vec_board.clone());
420
421        // Then
422        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        // Given
429        #[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        // When
439        let exponents: Vec<_> = board.into_iter().collect();
440
441        // Then
442        #[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        // Given
455        #[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        // When
464        let board = Board::from(board_values);
465
466        // Then
467        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        // Given
475        #[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        // When / Then
484        assert_eq!(512, board.get_value(11));
485    }
486
487    #[test]
488    fn should_get_exponent_value() {
489        // Given
490        #[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        // When / Then
499        assert_eq!(9, board.get_exponent_value(11));
500    }
501
502    #[test]
503    fn should_get_rows() {
504        // Given
505        #[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        // When
514        let rows = board.rows();
515
516        // Then
517        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        // Given
524        #[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        // When
533        let columns = board.columns();
534
535        // Then
536        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        // Given
543        #[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        // When
552        let board = board.set_value(5, 32).set_value(8, 64);
553
554        // Then
555        #[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        // Given
568        #[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        // When
577        let board = board
578            .set_value_by_exponent(5, 5)
579            .set_value_by_exponent(8, 6);
580
581        // Then
582        #[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        // Given
595        #[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        // When
604        let left_board = board.into_left();
605
606        // Then
607        #[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        // Given
620        #[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        // When
629        let right_board = board.into_right();
630
631        // Then
632        #[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        // Given
645        #[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        // When
654        let up_board = board.into_up();
655
656        // Then
657        #[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        // Given
670        #[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        // When
679        let down_board = board.into_down();
680
681        // Then
682        #[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        // Given
695        #[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        // When
704        let down_board = board.into_down();
705
706        // Then
707        #[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        // Given
720        #[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        // When
730        let max_value = board.max_value();
731
732        // Then
733        assert_eq!(2048, max_value);
734    }
735
736    #[test]
737    fn should_get_empty_tiles() {
738        // Given
739        #[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        // When
749        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        // Given
756        #[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        // When
766        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        // Given
773        #[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        // When
783        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        // Given
790        #[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        // When
800        let display = format!("{:?}", board);
801
802        // Then
803        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        // Given
820        #[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        // When / Then
830        format!("{}", board);
831    }
832}