Quest 17: Deadline-Driven Development

  • 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

Link to participate: https://everybody.codes/

  • hadesOPM
    link
    fedilink
    arrow-up
    1
    ·
    10 days ago

    Rust

    use std::{
        cmp::Reverse,
        collections::{HashMap, HashSet},
        f64::consts::PI,
    };
    
    use itertools::Itertools;
    use libm::atan2;
    use priority_queue::PriorityQueue;
    
    fn l2(i: usize, j: usize) -> usize {
        i * i + j * j
    }
    
    fn erupt(data: &[Vec<char>], vi: usize, vj: usize, r: usize) -> usize {
        let r2 = r * r;
        data.iter()
            .enumerate()
            .flat_map(|(i, l)| {
                l.iter()
                    .enumerate()
                    .filter(move |&(j, v)| *v != '@' && l2(vi.abs_diff(i), vj.abs_diff(j)) <= r2)
            })
            .map(|(_, v)| (*v as u8 - b'0') as usize)
            .sum::<usize>()
    }
    
    fn find(data: &[Vec<char>], c: char) -> (usize, usize) {
        data.iter()
            .enumerate()
            .flat_map(|(i, l)| {
                l.iter()
                    .enumerate()
                    .filter(|&(_, v)| *v == c)
                    .map(move |(j, _)| (i, j))
            })
            .exactly_one()
            .unwrap()
    }
    
    pub fn solve_part_1(input: &str) -> String {
        let data = input
            .lines()
            .map(|l| l.chars().collect::<Vec<_>>())
            .collect::<Vec<_>>();
        let (vi, vj) = find(&data, '@');
        erupt(&data, vi, vj, 10).to_string()
    }
    
    pub fn solve_part_2(input: &str) -> String {
        let data = input
            .lines()
            .map(|l| l.chars().collect::<Vec<_>>())
            .collect::<Vec<_>>();
        let (vi, vj) = find(&data, '@');
        let h = data.len();
        let w = data[0].len();
        let max_r = vi.max(vj).max(h - vi - 1).max(w - vj - 1);
        let mut last_eruption = 0;
        let mut max_eruption = 0;
        let mut max_eruption_r = 0;
        for r in 1..=max_r {
            let eruption = erupt(&data, vi, vj, r);
            let de = eruption - last_eruption;
            if de > max_eruption {
                max_eruption = de;
                max_eruption_r = r;
            }
            last_eruption = eruption;
        }
        (max_eruption_r * max_eruption).to_string()
    }
    
    pub fn solve_part_3(input: &str) -> String {
        let data = input
            .lines()
            .map(|l| l.chars().collect::<Vec<_>>())
            .collect::<Vec<_>>();
        let width = data[0].len();
        let height = data.len();
        let (vi, vj) = find(&data, '@');
        let (si, sj) = find(&data, 'S');
    
        let azimuth = |i: usize, j: usize| atan2(i as f64 - vi as f64, j as f64 - vj as f64);
    
        let small_rot = |az1: f64, az2: f64| {
            let d = az1 - az2;
            if d > PI {
                d - 2. * PI
            } else if d < -PI {
                d + 2. * PI
            } else {
                d
            }
        };
    
        let solve = |radius: usize| {
            let r2 = radius * radius;
            let time_limit = ((radius + 1) * 30) as i64;
            let mut queue = PriorityQueue::new();
            let mut rotations = HashMap::new();
            rotations.insert((si, sj, false), 0f64);
            let mut visited = HashSet::new();
            queue.push((si, sj, false), Reverse(0));
            while let Some(((i, j, rotated), Reverse(time))) = queue.pop() {
                if time >= time_limit {
                    break;
                }
                visited.insert((i, j, rotated));
                let az = azimuth(i, j);
                let rotation = rotations[&(i, j, rotated)];
                for (di, dj) in [(-1, 0), (1, 0), (0, -1), (0, 1)] {
                    let (ni, nj) = (i.wrapping_add_signed(di), j.wrapping_add_signed(dj));
                    if ni >= height || nj >= width {
                        continue;
                    }
                    if l2(ni.abs_diff(vi), nj.abs_diff(vj)) <= r2 {
                        continue;
                    }
                    let is_rotated = if let Some(previous_rotation) = rotations.get(&(ni, nj, false)) {
                        let rotation = rotation + small_rot(azimuth(ni, nj), az);
                        (rotation - previous_rotation).abs() > 6.
                    } else {
                        false
                    };
                    if (ni, nj, is_rotated) == (si, sj, true) {
                        return Some(time);
                    }
                    if visited.contains(&(ni, nj, is_rotated)) {
                        continue;
                    }
                    let new_time: i64 = time + (data[ni][nj] as i8 - '0' as i8) as i64;
                    let should_update =
                        match queue.push_increase((ni, nj, is_rotated), Reverse(new_time)) {
                            None => true,
                            Some(Reverse(t)) => t > new_time,
                        };
                    if should_update {
                        rotations.insert(
                            (ni, nj, is_rotated),
                            rotation + small_rot(azimuth(ni, nj), az),
                        );
                    };
                }
            }
            None
        };
        let (radius, time) = (1..(width.min(height) / 2))
            .map(|radius| (radius, solve(radius)))
            .filter(|(_, s)| s.is_some())
            .min()
            .unwrap();
        (radius as i64 * time.unwrap()).to_string()
    }