#[macro_use] extern crate lazy_static; extern crate regex; use std::str::FromStr; use std::io::{self, BufRead}; use std::fmt; struct Screen { screen: Vec> } impl Screen { fn new(width: usize, height: usize) -> Self { Screen { screen: vec![vec![false; width]; height] } } fn set(&mut self, display: &str) { for (y, line) in display.lines().enumerate() { for (x, c) in line.chars().enumerate() { self.screen[y][x] = match c { '#' => true, '.' => false, _ => panic!("invalid char!") }; } } } fn get(&self) -> String { self.screen.iter() .map(|row| { row.iter() .map(|c| if *c { '#' } else { '.' }) .collect::() }) .collect::>() .join("\n") } fn fill_rect(&mut self, width: usize, height: usize) { for y in 0..height { for x in 0..width { self.screen[y][x] = true; } } } fn rotate_column(&mut self, column: usize, step: usize) { let swaps = self.screen.len(); for _ in 0..step { for y in (1..swaps).rev() { let tmp = self.screen[y - 1][column]; self.screen[y - 1][column] = self.screen[y][column]; self.screen[y][column] = tmp; } } } fn rotate_row(&mut self, row: usize, step: usize) { let mut vec = &mut self.screen[row]; let swaps = vec.len(); for _ in 0..step { for x in (1..swaps).rev() { vec.swap(x, x - 1); } } } } impl fmt::Display for Screen { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.get()) } } #[derive(Debug, PartialEq)] enum Instruction { Rect { x: usize, y: usize }, RotateColumn { x: usize, step: usize }, RotateRow { y: usize, step: usize } } impl FromStr for Instruction { type Err = (); fn from_str(s: &str) -> Result { use regex::Regex; lazy_static! { static ref RECT: Regex = Regex::new(r"^rect (\d+)x(\d+)$").unwrap(); static ref R_COL: Regex = Regex::new(r"^rotate column x=(\d+) by (\d+)$").unwrap(); static ref R_ROW: Regex = Regex::new(r"^rotate row y=(\d+) by (\d+)$").unwrap(); } if let Some(caps) = RECT.captures(s) { let x = caps.at(1).unwrap().parse::().unwrap(); let y = caps.at(2).unwrap().parse::().unwrap(); return Ok(Instruction::Rect { x: x, y: y }); } if let Some(caps) = R_COL.captures(s) { let x = caps.at(1).unwrap().parse::().unwrap(); let step = caps.at(2).unwrap().parse::().unwrap(); return Ok(Instruction::RotateColumn { x: x, step: step }); } if let Some(caps) = R_ROW.captures(s) { let y = caps.at(1).unwrap().parse::().unwrap(); let step = caps.at(2).unwrap().parse::().unwrap(); return Ok(Instruction::RotateRow { y: y, step: step }); } Err(()) } } fn main() { let stdin = io::stdin(); let instructions = stdin.lock().lines() .filter_map(|line| line.ok()) .filter_map(|line| Instruction::from_str(&line).ok()); let mut screen = Screen::new(50, 6); for instruction in instructions { use Instruction::*; match instruction { Rect { x, y } => screen.fill_rect(x, y), RotateColumn { x, step } => screen.rotate_column(x, step), RotateRow { y, step } => screen.rotate_row(y, step) } } let count = screen.get().chars() .fold(0, |acc, c| if c == '#' { acc + 1 } else { acc }); println!("{}", screen); println!("lit={}", count); } #[cfg(test)] mod tests { use std::fmt::Write; use std::str::FromStr; use super::{Screen, Instruction}; #[test] fn test_rect() { let mut screen = Screen::new(7, 3); screen.fill_rect(3, 2); let mut w = String::new(); write!(&mut w, "{}", screen).unwrap(); assert_eq!(w, "###....\n\ ###....\n\ ......."); } #[test] fn test_rotate_column() { let mut screen = Screen::new(7, 3); screen.set("###....\n\ ###....\n\ ......."); screen.rotate_column(1, 1); let mut w = String::new(); write!(&mut w, "{}", screen).unwrap(); assert_eq!(w, "#.#....\n\ ###....\n\ .#....."); } #[test] fn test_rotate_row() { let mut screen = Screen::new(7, 3); screen.set("#.#....\n\ ###....\n\ .#....."); screen.rotate_row(0, 4); let mut w = String::new(); write!(&mut w, "{}", screen).unwrap(); assert_eq!(w, "....#.#\n\ ###....\n\ .#....."); } #[test] fn test_parse_rect() { assert_eq!(Ok(Instruction::Rect { x: 1, y: 1 }), Instruction::from_str("rect 1x1")); } #[test] fn test_parse_rotate_column() { assert_eq!(Ok(Instruction::RotateColumn { x: 0, step: 1 }), Instruction::from_str("rotate column x=0 by 1")); } #[test] fn test_parse_rotate_row() { assert_eq!(Ok(Instruction::RotateRow { y: 2, step: 15 }), Instruction::from_str("rotate row y=2 by 15")); } }