this post was submitted on 18 Dec 2023
0 points (NaN% liked)

Advent Of Code

767 readers
1 users here now

An unofficial home for the advent of code community on programming.dev!

Advent of Code is an annual Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

AoC 2023

Solution Threads

M T W T F S S
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25

Rules/Guidelines

Relevant Communities

Relevant Links

Credits

Icon base by Lorc under CC BY 3.0 with modifications to add a gradient

console.log('Hello World')

founded 1 year ago
MODERATORS
 

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

top 2 comments
sorted by: hot top controversial new old
[โ€“] hades@lemm.ee 1 points 8 months ago* (last edited 1 month ago)

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
[โ€“] Gobbel2000@feddit.de 0 points 8 months ago

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");