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
You must log in or register to comment.
Python
0.09 line-seconds (third simplest after days 6 and 2).
from .solver import Solver class Day18(Solver): def __init__(self): super().__init__(18) def presolve(self, input: str): self.lines = input.splitlines() def solve_first_star(self): commands = [] for line in self.lines: direction, distance, *_ = line.split(' ') commands.append((direction, int(distance))) return self._solve(commands) def solve_second_star(self): commands = [] for line in self.lines: _, _, command = line.split(' ') distance = int(command[2:-2], 16) direction = ('R', 'D', 'L', 'U')[int(command[-2])] commands.append((direction, distance)) return self._solve(commands) def _solve(self, commands: list[tuple[str, int]]): points: list[tuple[int, int]] = [(0, 0)] perimeter_integer_points = 1 x, y = 0, 0 for direction, distance in commands: dx, dy = {'R': (1, 0), 'L': (-1, 0), 'U': (0, -1), 'D': (0, 1)}[direction] x, y = x + dx * distance, y + dy * distance perimeter_integer_points += distance points.append((x, y)) last_x, last_y = points[-1] perimeter_integer_points += abs(last_x) + abs(last_y) - 1 area_x2 = sum((points[i][1] + points[(i+1) % len(points)][1]) * (points[i][0] - points[(i+1) % len(points)][0]) for i in range(len(points))) interior_integer_points = (area_x2 - perimeter_integer_points) // 2 + 1 return interior_integer_points + perimeter_integer_points
Rust
I originally went with a flooding solution similar to Day 10 with the pipe ring. But that was highly unlikely to work in part 2. So I added
length * horizontal_pos
when going down, and subtracted the same when going up. After fiddling with a bunch of off-by-one-errors, that gave me the result I was looking for.use std::str::FromStr; enum Dir { N, E, S, W, } impl FromStr for Dir { type Err = String; // Part 1 fn from_str(s: &str) -> Result<Self, Self::Err> { match s { "U" => Ok(Dir::N), "R" => Ok(Dir::E), "D" => Ok(Dir::S), "L" => Ok(Dir::W), _ => Err(format!("Invalid direction char {s}")), } } } impl TryFrom<u32> for Dir { type Error = String; // Part 2 fn try_from(n: u32) -> Result<Self, Self::Error> { match n { 0 => Ok(Dir::E), 1 => Ok(Dir::S), 2 => Ok(Dir::W), 3 => Ok(Dir::N), _ => Err(format!("Invalid direction number {n}")) } } } struct Edge { dir: Dir, length: u32, } impl Edge { fn from_str_part1(s: &str) -> Option<Self> { let mut parts = s.split_whitespace(); let dir = parts.next()?.to_string().parse().ok()?; let length = parts.next()?.parse::<u32>().ok()?; Some(Edge { dir, length, }) } fn from_str_part2(s: &str) -> Option<Self> { let mut parts = s.split_whitespace(); let not_color = parts.nth(2)?; let hex = not_color.trim_start_matches("(#").trim_end_matches(')'); let num = u32::from_str_radix(hex, 16).ok()?; Some(Edge { dir: Dir::try_from(num & 0xf).ok()?, length: num >> 4, }) } } fn area(edges: &[Edge]) -> u64 { // Initialize with 1 for starting point let mut area: i64 = 1; // Horizontal position let mut pos = 0; for edge in edges { let l = edge.length as i64; match edge.dir { Dir::S => area += l * (pos + 1), Dir::N => area -= l * pos, Dir::E => { area += l; pos += l }, Dir::W => pos -= l, } } // If the path went counter-clockwise, area would end up negative area.unsigned_abs() } fn part1(input: String) { let inputs: Vec<Edge> = input.lines() .map(|l| Edge::from_str_part1(l).unwrap()) .collect(); println!("{}", area(&inputs)); } fn part2(input: String) { let inputs: Vec<Edge> = input.lines() .map(|l| Edge::from_str_part2(l).unwrap()) .collect(); println!("{}", area(&inputs)); } util::aoc_main!("day18.txt");