diff options
Diffstat (limited to '2022/rust/day10/src/main.rs')
-rw-r--r-- | 2022/rust/day10/src/main.rs | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/2022/rust/day10/src/main.rs b/2022/rust/day10/src/main.rs new file mode 100644 index 0000000..426be03 --- /dev/null +++ b/2022/rust/day10/src/main.rs @@ -0,0 +1,179 @@ +use { + anyhow::{ + anyhow, + Context, + Result, + }, + regex::Regex, + std::{ + env::args, + fs::File, + io::{ + BufRead, + BufReader, + }, + path::Path, + }, +}; + +#[derive(Clone,Copy)] +enum Instruction { + Addx(isize), + Noop, +} + +impl Instruction { + fn cycles(&self) -> usize { + match self { + Instruction::Addx(_) => 2, + Instruction::Noop => 1, + } + } +} + +#[derive(Clone)] +struct Program { + inner: Vec<Instruction>, +} + +impl Program { + fn instruction(&self, cycle: usize) -> Option<Instruction> { + let mut counter = 0; + + for instruction in self.inner.iter() { + counter += instruction.cycles(); + match counter { + c if c == cycle => return Some(*instruction), + c if c > cycle => return None, + _ => { }, + } + } + None + } +} + +struct Registers { + x: isize, +} + +impl Registers { + fn new() -> Self { + Self { + x: 1, + } + } +} + +struct Cpu { + cycle: usize, + program: Program, + registers: Registers, +} + +impl Cpu { + fn new(program: Program) -> Self { + let registers = Registers::new(); + + Self { + cycle: 0, + program, + registers, + } + } + + fn step(&mut self) { + if let Some(instruction) = self.program.instruction(self.cycle) { + match instruction { + Instruction::Addx(argument) => self.registers.x += argument, + Instruction::Noop => { }, + } + } + self.cycle += 1; + } + + fn signal_strength(&self) -> isize { + self.registers.x * self.cycle as isize + } + + fn pixel(&self) -> String { + if ((self.registers.x - 1)..=(self.registers.x + 1)) + .contains(&((self.cycle - 1) as isize % 40)) + { + String::from("#") + } else { + String::from(".") + } + } +} + +fn read_input<T: AsRef<Path>>(filename: T) -> Result<Program> { + let reader = BufReader::new(File::open(filename)?); + let re = Regex::new(r#"(?x)^ + (?P<instruction>(addx)|(noop)) + ( + \s + (?P<argument>[0-9-]+) + )? + $"#)?; + + let instructions = reader.lines().map( + |v| { + let s = v?; + let caps = re.captures(&s).ok_or_else(|| anyhow!("Could not parse: {s}"))?; + let instruction = caps.name("instruction").unwrap_or_else(|| unreachable!()).as_str(); + let argument = caps.name("argument").map(|a| a.as_str()).unwrap_or_default(); + match instruction { + "addx" => Ok(Instruction::Addx(argument.parse()?)), + "noop" => Ok(Instruction::Noop), + _ => unreachable!(), + } + } + ).collect::<Result<Vec<_>>>()?; + + Ok(Program { inner: instructions }) +} + +fn part1(input: &Program) -> Result<isize> { + let mut cpu = Cpu::new(input.clone()); + let mut sum = 0; + + for i in 0..=220 { + if [20, 60, 100, 140, 180, 220].contains(&i) { + sum += cpu.signal_strength(); + } + cpu.step(); + } + + Ok(sum) +} + +fn part2(input: &Program) -> Result<String> { + let mut cpu = Cpu::new(input.clone()); + let mut output = String::default(); + + for i in 0..240 { + cpu.step(); + if i % 40 == 0 { + output += "\n"; + } + output += &cpu.pixel(); + } + + Ok(output) +} + +fn main() -> Result<()> { + let ( do_part_1, do_part_2 ) = aoc::do_parts(); + + let filename = args().nth(1).ok_or_else(|| anyhow!("Missing input filename"))?; + let input = read_input(filename).context("Could not read input")?; + if do_part_1 { + let solution = part1(&input).context("No solution for part 1")?; + println!("Part1, solution found to be: {}", solution); + } + if do_part_2 { + let solution = part2(&input).context("No solution for part 2")?; + println!("Part2, solution found to be: {}", solution); + } + Ok(()) +} |