diff --git a/24/Cargo.lock b/24/Cargo.lock new file mode 100644 index 0000000..2e519f1 --- /dev/null +++ b/24/Cargo.lock @@ -0,0 +1,134 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "day24" +version = "0.1.0" +dependencies = [ + "itertools", + "lazy-regex", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + +[[package]] +name = "lazy-regex" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "proc-macro2" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "syn" +version = "2.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/24/Cargo.toml b/24/Cargo.toml new file mode 100644 index 0000000..1b063bd --- /dev/null +++ b/24/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day24" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itertools = "0.12.0" +lazy-regex = "3.1.0" diff --git a/24/src/main.rs b/24/src/main.rs new file mode 100644 index 0000000..2c6ed13 --- /dev/null +++ b/24/src/main.rs @@ -0,0 +1,205 @@ +use itertools::Itertools; +use std::fmt::Debug; +use std::fs::File; +use std::io::{BufRead, BufReader, Lines}; +use std::ops::Index; +use std::time::Instant; + +// BOILERPLATE +type InputIter = Lines>; + +fn get_input() -> InputIter { + let f = File::open("input").unwrap(); + let br = BufReader::new(f); + br.lines() +} + +fn main() { + let start = Instant::now(); + let ans1 = problem1(get_input()); + let duration = start.elapsed(); + println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64()); + + let start = Instant::now(); + let ans2 = problem2(get_input()); + let duration = start.elapsed(); + println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64()); +} + +// Parse + +#[derive(Clone, Copy)] +struct Vec3 { + x: f64, + y: f64, + z: f64, +} +impl Debug for Vec3 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}, {}, {}", self.x, self.y, self.z) + } +} + +#[derive(Clone, Copy)] +struct Vec2 { + x: f64, + y: f64, +} +impl Debug for Vec2 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}, {}", self.x, self.y) + } +} + + +#[derive(Clone)] + +struct Hailstone { + pos: Vec3, + vel: Vec3, +} + +impl Hailstone { + fn xy_vel_angle(&self) -> f64 { + (self.vel.y / self.vel.x).atan() + } + fn xy_vel_abs(&self) -> f64 { + (self.vel.x.powi(2) + self.vel.y.powi(2)).sqrt() + } + fn xy_slope(&self) -> f64 { + self.vel.y / self.vel.x + } + fn y_crossing(&self) -> f64 { + self.pos.y - self.xy_slope() * self.pos.x + } + fn xy_poi(&self, other: &Hailstone) -> Option { + let our_slope = self.xy_slope(); + let other_slope = other.xy_slope(); + if our_slope == other_slope || our_slope * other_slope == -1. { + None + } else { + let our_yint = self.y_crossing(); + let other_yint = other.y_crossing(); + let ratio = (other_yint - our_yint) / (our_slope - other_slope); + Some(Vec2 { + x: ratio, + y: our_slope * ratio + our_yint, + }) + } + } + fn xy_point_future(&self, point: &Vec2) -> bool { + // a point will be in the past if the difference between its 'new' position and its 'start' position has a + // different sign than the velocity for any component + let diffs = [point.x - self.pos.x, point.y - self.pos.y]; + // for (diff, vel) in diffs.iter().zip([self.vel.x, self.vel.y].iter()) { + // println!(" diff: {:?} vel: {:?} mul: {:?}", diff, vel, diff * vel > 0.); + // } + diffs.iter().zip([self.vel.x, self.vel.y].iter()).any(|(diff, vel)| diff * vel > 0.) + } +} + +impl From<&str> for Hailstone { + fn from(value: &str) -> Self { + let (pos, vel) = value.split_once("@").unwrap(); + let [px, py, pz] = pos.split(",").map(|s| s.trim().parse::().unwrap()).collect_vec()[..] else { + panic!() + }; + let [vx, vy, vz] = vel.split(",").map(|s| s.trim().parse::().unwrap()).collect_vec()[..] else { + panic!() + }; + + Self { + pos: Vec3 { x: px, y: py, z: pz }, + vel: Vec3 { x: vx, y: vy, z: vz }, + } + } +} + +impl Debug for Hailstone { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?} @ {:?}", self.pos, self.vel) + } +} + +#[derive(Debug)] +struct Hailstones { + stones: Vec, +} + +impl Hailstones { + fn count_xy_pois(&self, bounds: (f64, f64)) -> u64 { + assert!(bounds.0 < bounds.1); + let mut stones = self.stones.clone(); + + let mut count = 0; + while let Some(stone) = &stones.pop() { + for other in &stones { + let Some(poi) = stone.xy_poi(other) else { continue }; + // println!("intersection: {:?} / {:?} @ {:?}", stone, other, poi); + if poi.x >= bounds.0 && poi.x <= bounds.1 && poi.y >= bounds.0 && poi.y <= bounds.1 && stone.xy_point_future(&poi) && other.xy_point_future(&poi) { + count += 1; + } + } + } + + count + } +} + +impl Index for Hailstones { + type Output = Hailstone; + fn index(&self, index: usize) -> &Self::Output { + &self.stones[index] + } +} + +impl From> for Hailstones { + fn from(value: Lines) -> Self { + let mut stones = Vec::new(); + for line in value { + stones.push(Hailstone::from(line.unwrap().as_str())); + } + Hailstones { stones } + } +} + +// PROBLEM 1 solution + +fn problem1(input: Lines) -> u64 { + problem1_impl(input, (200000000000000., 400000000000000.)) +} + +fn problem1_impl(input: Lines, bounds: (f64, f64)) -> u64 { + let stones = Hailstones::from(input); + + stones.count_xy_pois(bounds) +} + +// PROBLEM 2 solution +fn problem2(input: Lines) -> u64 { + 0 +} + +#[cfg(test)] +mod tests { + use crate::*; + use std::io::Cursor; + + const EXAMPLE: &str = &"19, 13, 30 @ -2, 1, -2 +18, 19, 22 @ -1, -1, -2 +20, 25, 34 @ -2, -2, -4 +12, 31, 28 @ -1, -2, -1 +20, 19, 15 @ 1, -5, -3"; + + #[test] + fn problem1_example() { + let c = Cursor::new(EXAMPLE); + assert_eq!(problem1_impl(c.lines(), (7., 27.)), 2); + } + + #[test] + fn problem2_example() { + let c = Cursor::new(EXAMPLE); + assert_eq!(problem2(c.lines()), 0); + } +}