New account since lemmyrs.org went down, other @Deebsters are available.

  • 56 Posts
  • 985 Comments
Joined 2 years ago
cake
Cake day: October 16th, 2023

help-circle


  • Mild spoilers ahead, but you’re reading the solutions thread.

    I was just doing some preliminary checking of the data on my phone with Nushell (to see how much my ageing laptop would suffer) when I discovered there weren’t any non trivial cases.

    Normally I get the test data working before trying the input data, this is definitely teaching me the value of investigating the data before heading down into the code mines.

    Unfortunately I can’t get the second star yet because I missed a few days.







  • nushell

    I’m still travelling, so another phone attempt. Jet lag says sleep, so just part 1 for now:

    def part1 [filename: string] {
      mut input = open $filename | lines |
        each { parse '{index}: {children}' | update children { split row " " } | first } |
        insert paths { null }
      print $"Data loaded, ($input | length) devices"
    
      $input = explore-path $input you
      $input | where index == you | get 0.paths
    }
    
    def explore-path [devices, start: string] {
      print $"Exploring ($start)"
      let dev = $devices | where index == $start | first
      if ($dev | get paths) != null {
        print "Already explored"
        return $devices
      }
    
      # Shadow with mutable version
      mut devices = $devices
      mut paths = 0
      let is_out = $dev | get children | where ($it == out) | is-not-empty
      if $is_out {
        print $"Found an out device: ($start)"
        $paths = 1
      } else {
        for child in ($dev | get children ) {
          $devices = explore-path $devices $child
          $paths += $devices | where index == $child | get 0.paths
        }
      }
    
      # Shadow with immutable... wtf
      let paths = $paths
      print $"Setting paths for ($start) to ($paths)"
      $devices = $devices | update paths { |row| if $row.index == $start {  
    $paths } else {} }
       $devices
    }
    






  • For part one and likely part 2 you don’t need to do all 499500 comparisons, you could split the grid into boxes and only search adjacent ones. E.g. z / 10 = which decile and look at z, z-1, z+1 (and the same for x and y). Less work for the processor, more work for you, and of course, sqrt (which you can also probably skip) is so efficient on modern chips that the overhead of this eats a chunk of the benefits (depending on how low-level your language is).


  • Rust

    Part 1 took a decent while, partly life getting in the way, partly as I was struggling with some Rust things like floats not being sortable without mucking around, plus some weird bugs trying to get collect to do everything that I eventually just rewrote to avoid.

    I found the noisy_float crate which let me wrap f64 as a “real” r64 which meant no NaN which meant Ord which meant sort_by_cached_key() and the rest worked.

    I’d planned how to partition the closest neighbour search, but the whole thing runs in 24ms so I didn’t bother.

    other types
    type Id = usize;
    type Connection = (Id, Id);
    type Circuit = HashSet<Id>;
    
    #[derive(PartialEq)]
    struct Point {
        x: usize,
        y: usize,
        z: usize,
    }
    
    impl FromStr for Point {
        type Err = Report;
    
        fn from_str(s: &str) -> Result<Self> {
            let mut parts = s.split(',');
            Ok(Point {
                x: parts.next().ok_or_eyre("missing x")?.parse()?,
                y: parts.next().ok_or_eyre("missing y")?.parse()?,
                z: parts.next().ok_or_eyre("missing z")?.parse()?,
            })
        }
    }
    
    impl Point {
        fn distance(&self, other: &Point) -> R64 {
            let dist = ((
                self.x.abs_diff(other.x).pow(2) +
                self.y.abs_diff(other.y).pow(2) +
                self.z.abs_diff(other.z).pow(2)) as f64)
                .sqrt();
            r64(dist)
        }
    }
    
    struct Boxes(Vec<Point>);
    
    impl Boxes {
        fn closest(&self) -> Vec<Connection> {
            let mut closest = (0..self.0.len())
                .flat_map(|a| ((a + 1)..self.0.len()).map(move |b| (a, b)))
                .collect::<Vec<_>>();
    
            closest.sort_by_cached_key(|&(a, b)| self.0[a].distance(&self.0[b]));
            closest
        }
    
        fn connect_all(&self, p1_threshold: usize) -> Result<(usize, usize)> {
            let mut circuits: Vec<Circuit> = (0..self.0.len())
                .map(|id| HashSet::from_iter(iter::once(id)))
                .collect();
    
            let mut closest = self.closest().into_iter();
            let mut p1 = 0;
            let mut n = 0;
            loop {
                n += 1;
                let (a, b) = closest.next().ok_or_eyre("All connected already")?;
                let a_circ = circuits.iter().position(|c| c.contains(&a));
                let b_circ = circuits.iter().position(|c| c.contains(&b));
                match (a_circ, b_circ) {
                    (None, None) => {
                        circuits.push(vec![a, b].into_iter().collect());
                    }
                    (None, Some(idx)) => {
                        circuits[idx].insert(a);
                    }
                    (Some(idx), None) => {
                        circuits[idx].insert(b);
                    }
                    (Some(a_idx), Some(b_idx)) if a_idx != b_idx => {
                        let keep_idx = a_idx.min(b_idx);
                        let rem_idx = a_idx.max(b_idx);
    
                        let drained = circuits.swap_remove(rem_idx);
                        // this position is still valid since we removed the later set
                        circuits[keep_idx].extend(drained);
                    }
                    _ => { /* already connected to same circuit */ }
                };
    
                if n == p1_threshold {
                    circuits.sort_by_key(|set| set.len());
                    circuits.reverse();
                    p1 = circuits.iter().take(3).map(|set| set.len()).product();
                }
                if circuits.len() == 1 {
                    let p2 = self.0[a].x * self.0[b].x;
                    return Ok((p1, p2));
                }
            }
        }
    }
    




  • QIR (Quantum Intermediate Representation)

    Nah, just kidding - I used Rust. The only tricky part seemed to be finding time on a Sunday to get it coded!

    Part 1 I swept down with a bool array for beams and part 2 I swept up with a score array and summed when it split (joined).

    struct Teleporter(String);
    
    impl Teleporter {
        fn new(s: String) -> Self {
            Self(s)
        }
    
        fn start_pos(line: &str) -> Result<usize> {
            line.find('S').ok_or_eyre("Start not found")
        }
    
        fn splits(&self) -> Result<usize> {
            let mut input = self.0.lines();
            let start_line = input.next().ok_or_eyre("No start line")?;
            let start = Self::start_pos(start_line)?;
    
            let mut beams = vec![false; start_line.len()];
            beams[start] = true;
    
            let mut splits = 0;
            for line in input {
                for (i, ch) in line.bytes().enumerate() {
                    if beams[i] && ch == b'^' {
                        splits += 1;
                        beams[i] = false;
                        beams[i - 1] = true;
                        beams[i + 1] = true;
                    }
                }
            }
            Ok(splits)
        }
    
        fn timelines(&self) -> Result<usize> {
            let mut input = self.0.lines();
            let start_line = input.next().ok_or_eyre("No start line")?;
            let start = Self::start_pos(start_line)?;
    
            let mut num_paths = vec![1; start_line.len()];
            for line in input.rev() {
                for (i, c) in line.bytes().enumerate() {
                    if c == b'^' || c == b'S' {
                        num_paths[i] = num_paths[i - 1] + num_paths[i + 1];
                    }
                }
            }
            Ok(num_paths[start])
        }
    }