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

  • hades@lemm.ee
    link
    fedilink
    arrow-up
    1
    ·
    edit-2
    3 months 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
    link
    fedilink
    arrow-up
    0
    ·
    11 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");