diff --git a/2022/day10-cathode-ray_tube/src/lib.rs b/2022/day10-cathode-ray_tube/src/lib.rs new file mode 100644 index 0000000..e2248a3 --- /dev/null +++ b/2022/day10-cathode-ray_tube/src/lib.rs @@ -0,0 +1,121 @@ +use core::fmt::Display; +use std::num::ParseIntError; + +#[derive(Debug, PartialEq, Eq)] +pub enum ParseError<'a> { + ParseIntError(std::num::ParseIntError), + UnknownInstruction(&'a str), +} + +impl From for ParseError<'_> { + fn from(value: ParseIntError) -> Self { + Self::ParseIntError(value) + } +} + +impl Display for ParseError<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ParseIntError(e) => write!(f, "Unable to parse into integer: {e}"), + Self::UnknownInstruction(i) => write!(f, "Tried to parse unknown instruction {i}"), + } + } +} + +struct Cpu { + states: Vec, +} + +impl <'a> TryFrom<&'a str> for Cpu { + type Error = ParseError<'a>; + + fn try_from(value: &'a str) -> Result { + let mut cpu = Cpu { states: vec![1, 1] }; + for instruction in value.lines() { + match &instruction[0..4] { + "noop" => cpu.noop(), + "addx" => cpu.addx(instruction[5..].parse()?), + instr => return Err(Self::Error::UnknownInstruction(instr)), + } + } + Ok(cpu) + } +} + +impl Cpu { + fn addx(&mut self, x: i32) { + let old_state = *self.states.last().unwrap(); + self.states.push(old_state); + self.states.push(old_state + x); + } + + fn noop(&mut self) { + let old_state = *self.states.last().unwrap(); + self.states.push(old_state); + } + + fn get_rendering(&self) -> String { + let mut rendering = String::new(); + (0..self.states.len()/40).for_each(|line_number| { + if line_number != 0 { + rendering.push('\n'); + } + let mut this_line = String::new(); + (0..40).for_each(|col_number| { + let clock_cycle = 40*line_number+col_number; + if (clock_cycle as i32 % 40 - self.states[clock_cycle + 1]).abs() < 2 { + this_line += "#"; + } else { + this_line += "."; + } + }); + rendering.push_str(&this_line); + }); + rendering + } +} + +pub fn run(input: &str) -> Result<(i32, String), ParseError> { + let cpu = Cpu::try_from(input)?; + let first = [20, 60, 100, 140, 180, 220].iter() + .map(|&i| i as i32 * cpu.states[i]) + .sum(); + let second = cpu.get_rendering(); + Ok((first, second)) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs::read_to_string; + + fn read_file(name: &str) -> String { + read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string() + } + + #[test] + fn test_sample() { + let sample_input = read_file("tests/sample_input"); + let expected = &r#" +##..##..##..##..##..##..##..##..##..##.. +###...###...###...###...###...###...###. +####....####....####....####....####.... +#####.....#####.....#####.....#####..... +######......######......######......#### +#######.......#######.......#######....."#[1..]; + assert_eq!(run(&sample_input), Ok((13140, expected.to_string()))); + } + + #[test] + fn test_challenge() { + let challenge_input = read_file("tests/challenge_input"); + let expected = &r#" +####.####.###..###..###..####.####.####. +#.......#.#..#.#..#.#..#.#.......#.#.... +###....#..###..#..#.###..###....#..###.. +#.....#...#..#.###..#..#.#.....#...#.... +#....#....#..#.#....#..#.#....#....#.... +#....####.###..#....###..#....####.#...."#[1..]; + assert_eq!(run(&challenge_input), Ok((14720, expected.to_string()))); + } +} diff --git a/2022/day10-cathode-ray_tube/src/main.rs b/2022/day10-cathode-ray_tube/src/main.rs deleted file mode 100644 index 2e0cbbe..0000000 --- a/2022/day10-cathode-ray_tube/src/main.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::fs; - -struct Cpu { - states: Vec, -} - -impl Cpu { - fn addx(&mut self, x: i32) { - let old_state = *self.states.last().unwrap(); - self.states.push(old_state); - self.states.push(old_state + x); - } - - fn noop(&mut self) { - let old_state = *self.states.last().unwrap(); - self.states.push(old_state); - } - - fn parse(&mut self, instruction: &str) { - match &instruction[0..4] { - "noop" => self.noop(), - "addx" => self.addx(instruction[5..].parse().unwrap()), - _ => panic!("Unknown instruction"), - } - } - - fn get_rendering(&self) -> String { - let mut rendering = String::new(); - (0..self.states.len()/40).for_each(|line_number| { - if line_number != 0 { - rendering.push('\n'); - } - let mut this_line = String::new(); - (0..40).for_each(|col_number| { - let clock_cycle = 40*line_number+col_number; - if (clock_cycle as i32 % 40 - self.states[clock_cycle + 1]).abs() < 2 { - this_line += "#"; - } else { - this_line += "."; - } - }); - rendering.push_str(&this_line); - }); - rendering - } - - fn render(&self) { - for line_number in 0..self.states.len()/40 { - let mut line_string = String::new(); - for col_number in 0..40 { - let clock_cycle = 40*line_number+col_number; - if (clock_cycle as i32 % 40 - self.states[clock_cycle + 1]).abs() < 2 { - line_string += "#"; - } else { - line_string += "."; - } - } - println!("{line_string}"); - } - } -} - -fn read_file(path: &str) -> String { - fs::read_to_string(path) - .expect("File not Found") -} - -fn main() { - //let program = read_file("sample_input"); - let program = read_file("input"); - - let mut cpu = Cpu { states: vec![1, 1], }; - for instruction in program.lines() { - cpu.parse(instruction); - } - let sum_of_relevant_strengths: i32 = [20, 60, 100, 140, 180, 220].iter() - .map(|&i| i as i32 * cpu.states[i]) - .sum(); - - println!("The relevant signal strengths sum up to {sum_of_relevant_strengths}."); - cpu.render(); -} - -#[test] -fn sample_input() { - let program = read_file("tests/sample_input"); - - let mut cpu = Cpu { states: vec![1, 1], }; - for instruction in program.lines() { - cpu.parse(instruction); - } - let sum_of_relevant_strengths: i32 = [20, 60, 100, 140, 180, 220].iter() - .map(|&i| i as i32 * cpu.states[i]) - .sum(); - - assert_eq!(sum_of_relevant_strengths, 13140); - assert_eq!(cpu.get_rendering(), r#"##..##..##..##..##..##..##..##..##..##.. -###...###...###...###...###...###...###. -####....####....####....####....####.... -#####.....#####.....#####.....#####..... -######......######......######......#### -#######.......#######.......#######....."#); -} - -#[test] -fn challenge_input() { - let program = read_file("tests/input"); - - let mut cpu = Cpu { states: vec![1, 1], }; - for instruction in program.lines() { - cpu.parse(instruction); - } - let sum_of_relevant_strengths: i32 = [20, 60, 100, 140, 180, 220].iter() - .map(|&i| i as i32 * cpu.states[i]) - .sum(); - - assert_eq!(sum_of_relevant_strengths, 14720); - assert_eq!(cpu.get_rendering(), r#"####.####.###..###..###..####.####.####. -#.......#.#..#.#..#.#..#.#.......#.#.... -###....#..###..#..#.###..###....#..###.. -#.....#...#..#.###..#..#.#.....#...#.... -#....#....#..#.#....#..#.#....#....#.... -#....####.###..#....###..#....####.#...."#); -} diff --git a/2022/day10-cathode-ray_tube/tests/input b/2022/day10-cathode-ray_tube/tests/challenge_input similarity index 100% rename from 2022/day10-cathode-ray_tube/tests/input rename to 2022/day10-cathode-ray_tube/tests/challenge_input