summaryrefslogtreecommitdiff
path: root/2022/rust/day10/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to '2022/rust/day10/src/main.rs')
-rw-r--r--2022/rust/day10/src/main.rs179
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(())
+}