Day 18: Lavaduct Lagoon
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
Rust
Code
use std::fs; use std::path::PathBuf; use clap::Parser; #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Cli { /// A file containing the input input_file: PathBuf, #[arg(short, long)] part: Option, } fn main() { // Parse CLI arguments let cli = Cli::parse(); // Read file let input_text = fs::read_to_string(&cli.input_file) .expect(format!("File \"{}\" not found", cli.input_file.display()).as_str()); let (run_part_1, run_part_2) = if let Some(part) = cli.part { match part { 1 => (true, false), 2 => (false, true), _ => unimplemented!(), } } else { (true, true) }; let (it1, it2) = preprocess(&input_text); if run_part_1 { let solution = solve(it1); println!("Part 1: {solution}"); } if run_part_2 { let solution = solve(it2); println!("Part 2: {solution}"); } } /// Preprocessing for solving /// Extracts important information from the input /// `part` specifies which part to preprocess for. /// Returns a vector for each part containing a direction and amount fn preprocess(input: &str) -> (Vec<((isize, isize), isize)>, Vec<((isize, isize), isize)>) { let it = input.lines().map(|l| { let line: Vec<_> = l.split(' ').collect(); let direction: char = line[0].chars().nth(0).unwrap(); let amount: isize = line[1].parse().unwrap(); let color: &str = &line[2][2..8]; let direction = match direction { 'R' => (1, 0), 'L' => (-1, 0), 'U' => (0, 1), 'D' => (0, -1), _ => unreachable!(), }; ((direction, amount), { let amount: isize = isize::from_str_radix(&color[..5], 16).unwrap(); let direction = match &color[5..] { "0" => (1, 0), "1" => (0, -1), "2" => (-1, 0), "3" => (0, 1), _ => unreachable!(), }; (direction, amount) }) }); let it1 = it.clone().map(|(i1, _i2)| i1).collect(); let it2 = it.map(|(_i1, i2)| i2).collect(); (it1, it2) } /// Finds the area using the shoelace formula fn solve(it: Vec<((isize, isize), isize)>) -> isize { // Get coordinates from it let mut coords: Vec<(isize, isize)> = Vec::with_capacity(it.len() + 1); // Start at (0, 0) coords.push((0, 0)); // Needs to be at both begining and end let (mut x, mut y) = (0, 0); let mut perimeter_length = 0; // Compute and add the coords for (direction, amount) in it { let translation = (direction.0 * amount, direction.1 * amount); x += translation.0; y += translation.1; coords.push((x, y)); perimeter_length += amount; } // Should end where it started assert_eq!(coords.last().unwrap(), &(0, 0)); // Shoelace formula let a = coords .iter() .zip(coords.iter().skip(1)) .map(|((x1, y1), (x2, y2))| (x1 * y2) - (x2 * y1)) .sum::() / 2; // Found by drawing, then trial and error // Shoelace area missing 1/2 of perimeter // Inside and outside corners cancel out except for one a.abs() + perimeter_length / 2 + 1 }
Yay math!