Cleanup for 2022 day 10: Turned into a lib and introduced parse errors

This commit is contained in:
Burnus 2023-05-14 14:38:05 +02:00
parent 6e1656f7ee
commit 68802e6959
3 changed files with 121 additions and 124 deletions

View file

@ -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<ParseIntError> 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<i32>,
}
impl <'a> TryFrom<&'a str> for Cpu {
type Error = ParseError<'a>;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
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())));
}
}

View file

@ -1,124 +0,0 @@
use std::fs;
struct Cpu {
states: Vec<i32>,
}
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#"####.####.###..###..###..####.####.####.
#.......#.#..#.#..#.#..#.#.......#.#....
###....#..###..#..#.###..###....#..###..
#.....#...#..#.###..#..#.#.....#...#....
#....#....#..#.#....#..#.#....#....#....
#....####.###..#....###..#....####.#...."#);
}