From 9892e3ebde304726903a1e5c358d05c2e343ea5e Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Tue, 19 Apr 2022 20:26:36 +0200 Subject: Refile for merging repos --- 2019/src/bin/day_7.rs | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 2019/src/bin/day_7.rs (limited to '2019/src/bin/day_7.rs') diff --git a/2019/src/bin/day_7.rs b/2019/src/bin/day_7.rs new file mode 100644 index 0000000..9b9177a --- /dev/null +++ b/2019/src/bin/day_7.rs @@ -0,0 +1,175 @@ +use aoc2019::*; +use rpds::list; +use rpds::list::List; +use rpds::vector::Vector; +use std::io; +use std::io::prelude::*; +use std::iter; +use std::process; +use structopt::StructOpt; + +#[derive(Debug, StructOpt)] +#[structopt(name = "Day 7: Amplification Circuit")] +/// Executes an Intcode program on 5 amplifiers, and finds the input that gives the max output +/// +/// See https://adventofcode.com/2019/day/7 for details. +struct Opt { + #[structopt(short = "f", long = "feedback-loop")] + feedback_loop_mode: bool, +} + +fn main() { + let stdin = io::stdin(); + let opt = Opt::from_args(); + + let program: IntcodeProgram = stdin + .lock() + .split(b',') + .map(|x| exit_on_failed_assertion(x, "Error reading input")) + .map(|x| exit_on_failed_assertion(String::from_utf8(x), "Input was not valid UTF-8")) + .map(|x| exit_on_failed_assertion(x.trim().parse::(), "Invalid number")) + .collect::(); + + let result = exit_on_failed_assertion( + find_max_power(&program, opt.feedback_loop_mode), + "Program errored", + ); + println!("{}", result); +} + +fn exit_on_failed_assertion(data: Result, message: &str) -> A { + match data { + Ok(data) => data, + Err(e) => { + eprintln!("{}: {}", message, e); + process::exit(1); + } + } +} + +fn find_max_power( + program: &IntcodeProgram, + feedback_loop_mode: bool, +) -> Result { + PhaseSetting::all(feedback_loop_mode) + .map(|phase| AmplifierArray::new(program, &phase).execute()) + .collect::, _>>() + .map(|powers| powers.into_iter().max().unwrap_or(Intcode::from(0))) +} + +#[derive(Debug, Clone)] +struct PhaseSetting([Intcode; 5]); + +impl PhaseSetting { + fn all(feedback_loop_mode: bool) -> impl Iterator { + if feedback_loop_mode { + PhaseSetting::permute(5, 10) + } else { + PhaseSetting::permute(0, 5) + } + } + + fn permute(min: i32, max: i32) -> impl Iterator { + // This is an absolutely atrocious way to do the permutation, + // but luckily it's only 5 elements long. + (min..max) + .flat_map(move |a| { + (min..max).flat_map(move |b| { + (min..max).flat_map(move |c| { + (min..max).flat_map(move |d| { + (min..max).map(move |e| { + PhaseSetting([a.into(), b.into(), c.into(), d.into(), e.into()]) + }) + }) + }) + }) + }) + .filter(move |phase| (min..max).all(|x| phase.0.contains(&x.into()))) + } +} + +#[derive(Debug, Clone)] +struct AmplifierArray { + amplifiers: Vector, +} + +impl AmplifierArray { + fn new(program: &IntcodeProgram, phase: &PhaseSetting) -> AmplifierArray { + AmplifierArray { + amplifiers: (0..5) + .map(|n| AmplifierArray::new_amp(program, phase, n)) + .collect(), + } + } + + fn new_amp(program: &IntcodeProgram, phase: &PhaseSetting, n: usize) -> IntcodeProgram { + if n == 0 { + program.with_input(list![phase.0[n].clone(), Intcode::from(0)]) + } else { + program.with_input(list![phase.0[n].clone()]) + } + } + + fn execute(&self) -> Result { + self.run_to_termination().output_into_result() + } + + fn run_to_termination(&self) -> AmplifierArray { + iter::successors(Some(self.clone()), |p| Some(p.next())) + .find(|p| p.is_terminated()) + .unwrap() // successors doesn't terminate, so this will never be none. + } + + fn output_into_result(&self) -> Result { + self.amplifiers + .first() + .and_then(|amp| amp.input.first().cloned()) + .ok_or(IntcodeProgramError::Unknown) + } + + fn is_terminated(&self) -> bool { + self.amplifiers.last().map(|amp| amp.halted).unwrap_or(true) + } + + fn next(&self) -> AmplifierArray { + self.run_amplifiers().update_inputs() + } + + fn run_amplifiers(&self) -> AmplifierArray { + AmplifierArray { + amplifiers: self + .amplifiers + .iter() + .map(|amp| amp.run_to_termination_or_input()) + .collect(), + } + } + + fn update_inputs(&self) -> AmplifierArray { + AmplifierArray { + amplifiers: self + .amplifiers + .iter() + .fold( + ( + Vector::new(), + self.amplifiers + .last() + .map(|a| a.output.iter().cloned().collect::>()) + .unwrap_or(List::new()), + ), + |(amps, piped_input), next_amp| { + ( + amps.push_back( + next_amp + .with_additional_input(piped_input) + .with_cleared_output(), + ), + next_amp.output.iter().cloned().collect::>(), + ) + }, + ) + .0, + } + } +} -- cgit v1.2.3