use aoc_runner_derive::{aoc, aoc_generator}; #[derive(Debug, Clone, Copy)] enum Instruction { AddX(i32), NoOp, } #[aoc_generator(day10)] fn generator(input: &str) -> Vec { input .lines() .map(|l| { if l == "noop" { Instruction::NoOp } else if let Some(l) = l.strip_prefix("addx ") { Instruction::AddX(l.parse().unwrap()) } else { panic!() } }) .collect() } struct CpuState { cycle: i32, x: i32, } fn run_simulation(input: &[Instruction], mut during_cycle_handler: impl FnMut(CpuState)) { let mut cycle = 1; let mut x = 1; let mut iter = input.iter().copied(); let mut current_instruction = iter.next().unwrap(); let mut current_instruction_cycle = 0; // god i hate fenceposts loop { // START OF CYCLE let need_new_instruction = match (current_instruction, current_instruction_cycle) { (_, 0) => false, (Instruction::NoOp, 1) => true, (Instruction::AddX(_), 1) => false, (Instruction::AddX(v), 2) => { x += v; true } _ => unreachable!(), }; if need_new_instruction { let Some(tmp) = iter.next() else { break }; current_instruction = tmp; current_instruction_cycle = 0; } // DURING CYCLE during_cycle_handler(CpuState { cycle, x }); // END OF CYCLE current_instruction_cycle += 1; cycle += 1; } } #[aoc(day10, part1)] fn part1(input: &[Instruction]) -> i32 { let mut res = 0; run_simulation(input, |CpuState { cycle, x }| { if (cycle + 20) % 40 == 0 { res += cycle * x; } }); res } #[aoc(day10, part2)] fn part2(input: &[Instruction]) -> String { let mut res = "\n".to_owned(); // newline so that `cargo aoc` formats this nicely run_simulation(input, |CpuState { cycle, x }| { let crt_x_pos = (cycle - 1) % 40; if (crt_x_pos - x).abs() <= 1 { res.push('#'); } else { res.push(' '); // the puzzle says `.` but ` ` is more readable } if cycle % 40 == 0 { res.push('\n'); } }); res }