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_19.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 2019/src/bin/day_19.rs (limited to '2019/src/bin/day_19.rs') diff --git a/2019/src/bin/day_19.rs b/2019/src/bin/day_19.rs new file mode 100644 index 0000000..73c8374 --- /dev/null +++ b/2019/src/bin/day_19.rs @@ -0,0 +1,102 @@ +use aoc2019::*; +use cached::cached_key; +use cached::UnboundCache; +use rpds::list; +use rpds::list::List; +use rpds::vector; +use std::io; +use std::io::prelude::*; +use std::process; +use structopt::StructOpt; + +#[derive(Debug, StructOpt)] +#[structopt(name = "Day 19: Tractor Beam")] +/// Finds the effective area of a tractor beam in an n x n square, and +/// how far away you need to be to capture Santa's ship. +/// +/// See https://adventofcode.com/2019/day/19 for details. +struct Opt { + /// The size for a diagnostics scan. + #[structopt(long = "diagnostics-size")] + diagnostics_size: Option, + /// The size of Santa's ship + #[structopt(long = "ship-size")] + ship_size: Option, +} + +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::(); + + if let Some(size) = opt.diagnostics_size { + println!("{}", count_active_in_area(program.clone(), 0, 0, size)); + } + if let Some(size) = opt.ship_size { + println!("{:?}", find_closest_ship_space(program, size)) + } +} + +fn exit_on_failed_assertion(data: Result, message: &str) -> A { + match data { + Ok(data) => data, + Err(e) => { + eprintln!("{}: {}", message, e); + process::exit(1); + } + } +} + +fn count_active_in_area(program: IntcodeProgram, left: usize, top: usize, size: usize) -> usize { + (left..left + size) + .flat_map(|x| (top..top + size).map(move |y| (x, y))) + .filter(|(x, y)| tractor_beam_is_active(program.clone(), *x, *y)) + .count() +} + +fn area_is_all_full(program: IntcodeProgram, left: usize, top: usize, size: usize) -> bool { + // This check with a grid that's aligned to 10 gives an early exit + // for most, that will have the program executions shared. This + // makes the memoized tractor function more effective at cutting + // down on execution, even though you need to do the whole lot + // again to verify if this passes. + (left..left + size) + .flat_map(|x| (top..top + size).map(move |y| (x, y))) + .filter(|(x, y)| x % 10 == 0 && y % 10 == 0) + .all(|(x, y)| tractor_beam_is_active(program.clone(), x, y)) + && (left..left + size) + .flat_map(|x| (top..top + size).map(move |y| (x, y))) + .all(|(x, y)| tractor_beam_is_active(program.clone(), x, y)) +} + +fn find_closest_ship_space(program: IntcodeProgram, size: usize) -> (usize, usize) { + (0..) + .flat_map(|radius| { + (0..radius) + .flat_map(move |x| (0..radius).map(move |y| (x, y))) + .filter(move |(x, y)| { + (radius - 1) * (radius - 1) < x * x + y * y && x * x + y * y <= radius * radius + }) + }) + .find(|(x, y)| area_is_all_full(program.clone(), *x, *y, size)) + .unwrap() +} + +cached_key! { + TRACTOR_BEAM_IS_ACTIVE: UnboundCache<(usize, usize), bool> = UnboundCache::new(); + Key = { (x, y) }; + fn tractor_beam_is_active(program: IntcodeProgram, x: usize, y: usize) -> bool = { + program + .with_input(list![Intcode::from(x), Intcode::from(y)]) + .execute() + == Ok(vector![Intcode::from(1)]) + + } +} -- cgit v1.2.3