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/


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() }