1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
pub const SCREEN_WIDTH: usize = 64;
pub const SCREEN_HEIGHT: usize = 32;

pub type Pixels = [bool; SCREEN_WIDTH * SCREEN_HEIGHT];

#[derive(Debug, Clone)]
pub struct Display {
  pub pixels: Pixels,
}

impl Display {
  pub fn new() -> Display {
    Display {
      pixels: [false; SCREEN_WIDTH * SCREEN_HEIGHT],
    }
  }

  pub fn clear(&mut self) {
    self.pixels = [false; SCREEN_WIDTH * SCREEN_HEIGHT];
  }

  fn set_pixel(&mut self, x: usize, y: usize, value: bool) {
    self.pixels[x + y * SCREEN_WIDTH] = value;
  }

  fn get_pixel(&self, x: usize, y: usize) -> bool {
    self.pixels[x + y * SCREEN_WIDTH]
  }

  fn xor_pixel(&mut self, x: usize, y: usize, new_value: bool) -> bool {
    let current = self.get_pixel(x, y);
    let new_pixel = current ^ new_value;
    self.set_pixel(x, y, new_pixel);
    current && !new_pixel
  }

  pub fn draw(&mut self, x: usize, y: usize, sprite: &[u8]) -> u8 {
    let mut new_vf = 0;

    sprite.iter().enumerate().for_each(|(line_number, line)| {
      if line_number + y < SCREEN_HEIGHT {
        format!("{:08b}", line)
          .chars()
          .map(|char| char == '1')
          .enumerate()
          .for_each(|(column_number, pixel)| {
            if column_number + x < SCREEN_WIDTH {
              if self.xor_pixel(x + column_number, y + line_number, pixel) {
                new_vf = 1;
              }
            }
          });
      }
    });

    new_vf
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn get_pixel() {
    let mut display = Display::new();
    display.pixels[1] = true;
    assert_eq!(display.get_pixel(1, 0), true);
    assert_eq!(display.get_pixel(0, 0), false);
  }

  #[test]
  fn set_pixel() {
    let mut display = Display::new();
    display.set_pixel(1, 1, true);
    assert_eq!(display.get_pixel(1, 1), true);
    display.set_pixel(1, 1, false);
    assert_eq!(display.get_pixel(1, 1), false);
  }

  #[test]
  fn clear_screen() {
    let mut display = Display::new();
    display.set_pixel(1, 3, true);
    display.set_pixel(5, 15, true);
    display.clear();
    assert!(display.pixels.iter().all(|pixel| { !*pixel }));
  }

  #[test]
  fn draw() {
    let lines: [u8; 4] = [0b01101100, 0b00011000, 0b00011000, 0b00111100];
    let mut display = Display::new();
    display.draw(0, 0, &lines);
    for i in 0..4 {
      for j in 0..8 {
        if i == 0 {
          assert_eq!(display.get_pixel(j, i), [1, 2, 4, 5].contains(&j));
        } else if i < 3 {
          assert_eq!(display.get_pixel(j, i), [3, 4].contains(&j));
        } else {
          assert_eq!(display.get_pixel(j, i), [2, 3, 4, 5].contains(&j));
        }
      }
    }
  }

  #[test]
  fn draw_erases() {
    let lines: [u8; 4] = [0b01101100, 0b00011000, 0b00011000, 0b00111100];
    let mut display = Display::new();
    display.draw(0, 0, &lines);
    display.draw(0, 0, &lines);
    assert!(display.pixels.iter().all(|pixel| { !*pixel }));
  }
}