|
@@ -0,0 +1,127 @@
|
|
|
+use std::collections::HashSet;
|
|
|
+
|
|
|
+use aoc2022_niels_overkamp::common::{self, AOCResult};
|
|
|
+
|
|
|
+use itertools::Itertools;
|
|
|
+use nom::{
|
|
|
+ bytes::complete::tag,
|
|
|
+ character::complete::u64 as u64_parser,
|
|
|
+ combinator::all_consuming,
|
|
|
+ error::Error,
|
|
|
+ multi::separated_list1,
|
|
|
+ sequence::{terminated, tuple},
|
|
|
+ Finish, Parser,
|
|
|
+};
|
|
|
+
|
|
|
+const DAY: &str = "day14";
|
|
|
+
|
|
|
+fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
+ common::run(DAY, &run)
|
|
|
+}
|
|
|
+
|
|
|
+pub fn run(input: &Vec<String>) -> AOCResult {
|
|
|
+ let point_parser = tuple((terminated(u64_parser, tag(",")), u64_parser));
|
|
|
+ let wall_parser = separated_list1(tag(" -> "), point_parser);
|
|
|
+ let mut wall_parser = all_consuming(wall_parser);
|
|
|
+
|
|
|
+ let mut map: HashSet<(u64, u64)> = HashSet::new();
|
|
|
+
|
|
|
+ for line in input {
|
|
|
+ let result: Result<Vec<(u64, u64)>, Error<&str>> =
|
|
|
+ wall_parser.parse(line.as_str()).finish().map(|(_, l)| l);
|
|
|
+ let wall = result.map_err(|e| e.to_string())?;
|
|
|
+ for line in wall.windows(2) {
|
|
|
+ let (p1, p2) = if let &[p1, p2] = line {
|
|
|
+ (p1, p2)
|
|
|
+ } else {
|
|
|
+ panic!("windows returned not 2 length");
|
|
|
+ };
|
|
|
+ let (hori, iter) = if p1.0 == p2.0 {
|
|
|
+ (false, p1.1.min(p2.1)..=p1.1.max(p2.1))
|
|
|
+ } else if p1.1 == p2.1 {
|
|
|
+ (true, p1.0.min(p2.0)..=p1.0.max(p2.0))
|
|
|
+ } else {
|
|
|
+ return Err(format!("Found non straight line: {:?} -> {:?}", p1, p2).into());
|
|
|
+ };
|
|
|
+ println!("{:?} -> {:?} hori:{}", p1, p2, hori);
|
|
|
+ for v in iter.into_iter() {
|
|
|
+ let p = if hori { (v, p1.1) } else { (p1.0, v) };
|
|
|
+ map.insert(p);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let death_plane = *map.iter().map(|(_, y)| y).max().unwrap();
|
|
|
+ println!("plane: {}", death_plane);
|
|
|
+
|
|
|
+ let floor = death_plane + 2;
|
|
|
+
|
|
|
+ let (min_x, max_x) = map.iter().map(|(x, _)| x).minmax().into_option().unwrap();
|
|
|
+ let (min_x, max_x) = (*min_x, *max_x);
|
|
|
+
|
|
|
+ for y in 0..=death_plane {
|
|
|
+ let s: String = (min_x..=max_x)
|
|
|
+ .into_iter()
|
|
|
+ .map(|x| if map.contains(&(x, y)) { '#' } else { ' ' })
|
|
|
+ .collect();
|
|
|
+ println!("{}", s);
|
|
|
+ }
|
|
|
+
|
|
|
+ let wall_map = map.clone();
|
|
|
+
|
|
|
+ let (mut x, mut y) = (500, 0);
|
|
|
+ let mut count = 0;
|
|
|
+ let mut count1 = None;
|
|
|
+ let count2;
|
|
|
+ loop {
|
|
|
+ if y > death_plane && count1.is_none() {
|
|
|
+ count1 = Some(count);
|
|
|
+ }
|
|
|
+ match (
|
|
|
+ y + 1 == floor,
|
|
|
+ map.contains(&(x, y + 1)),
|
|
|
+ map.contains(&(x - 1, y + 1)),
|
|
|
+ map.contains(&(x + 1, y + 1)),
|
|
|
+ ) {
|
|
|
+ (false, false, _, _) => (x, y) = (x, y + 1),
|
|
|
+ (false, _, false, _) => (x, y) = (x - 1, y + 1),
|
|
|
+ (false, _, _, false) => (x, y) = (x + 1, y + 1),
|
|
|
+ _ => {
|
|
|
+ map.insert((x, y));
|
|
|
+ count += 1;
|
|
|
+ if (x, y) == (500, 0) {
|
|
|
+ count2 = count;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ (x, y) = (500, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println!("");
|
|
|
+
|
|
|
+ let (min_x, max_x) = map.iter().map(|(x, _)| x).minmax().into_option().unwrap();
|
|
|
+ let (min_x, max_x) = (*min_x, *max_x);
|
|
|
+
|
|
|
+ for y in 0..=death_plane {
|
|
|
+ let s: String = (min_x..=max_x)
|
|
|
+ .into_iter()
|
|
|
+ .map(|x| {
|
|
|
+ if wall_map.contains(&(x, y)) {
|
|
|
+ '#'
|
|
|
+ } else if map.contains(&(x, y)) {
|
|
|
+ '.'
|
|
|
+ } else {
|
|
|
+ ' '
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .collect();
|
|
|
+ println!("{}", s);
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok([Some(count1.unwrap().to_string()), Some(count2.to_string())])
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+pub fn test_day14() {
|
|
|
+ assert!(common::run_test(DAY, &run))
|
|
|
+}
|