diff --git a/Cargo.lock b/Cargo.lock index a27552a..c6a6045 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -459,6 +459,8 @@ dependencies = [ "futures", "image", "isocountry", + "multimap", + "pathfinding", "radians", "regex", "reqwest", @@ -714,6 +716,18 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +[[package]] +name = "deprecate-until" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3767f826efbbe5a5ae093920b58b43b01734202be697e1354914e862e8e704" +dependencies = [ + "proc-macro2", + "quote", + "semver", + "syn 2.0.39", +] + [[package]] name = "der" version = "0.7.8" @@ -858,6 +872,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.28" @@ -1480,6 +1500,15 @@ dependencies = [ "hashbrown 0.14.3", ] +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + [[package]] name = "ipnet" version = "2.9.0" @@ -1741,6 +1770,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "multimap" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1a5d38b9b352dbd913288736af36af41c48d61b1a8cd34bcecd727561b7d511" + [[package]] name = "native-tls" version = "0.2.11" @@ -2078,6 +2113,21 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pathfinding" +version = "4.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f4a3f5089b981000cb50ec24320faf7a19649a45e8730e4adf49f78f066528" +dependencies = [ + "deprecate-until", + "fixedbitset", + "indexmap 2.1.0", + "integer-sqrt", + "num-traits", + "rustc-hash", + "thiserror", +] + [[package]] name = "pem" version = "3.0.2" @@ -3298,18 +3348,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 600d986..1d1459c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ country-boundaries = "1.2.0" futures = "0.3.29" image = "0.24.7" isocountry = "0.3.2" +multimap = { version = "0.9.1", default-features = false } +pathfinding = "4.8.0" radians = "0.3.1" regex = "1.10.2" reqwest = { version = "0.11.22", features = ["json"] } diff --git a/src/cal/day22.rs b/src/cal/day22.rs new file mode 100644 index 0000000..4d37f12 --- /dev/null +++ b/src/cal/day22.rs @@ -0,0 +1,85 @@ +use axum::{http::StatusCode, response::IntoResponse, routing::post, Router}; + +pub(crate) fn router() -> Router { + Router::new() + .route("/22/integers", post(integers)) + .route("/22/rocket", post(rocket)) +} + +async fn integers(nums: String) -> Result { + let mut nums = nums + .lines() + .map(|s| s.parse::().expect("All lines should be valid u64s")) + .collect::>(); + nums.sort_unstable(); + for i in (0..nums.len()).step_by(2) { + if i == (nums.len() - 1) || nums[i] != nums[i + 1] { + return Ok("🎁".repeat(nums[i] as usize)); + } + } + Err(StatusCode::BAD_REQUEST) +} + +async fn rocket(input: String) -> Result { + fn star_loc(s: &str) -> (i32, i32, i32) { + let mut s = s.split_ascii_whitespace().take(3); + ( + s.next().expect("Exists").parse().expect("Valid i32"), + s.next().expect("Exists").parse().expect("Valid i32"), + s.next().expect("Exists").parse().expect("Valid i32"), + ) + } + + fn star_dist(a: (i32, i32, i32), b: (i32, i32, i32)) -> f32 { + let total = (a.0 - b.0).pow(2) + (a.1 - b.1).pow(2) + (a.2 - b.2).pow(2); + (total as f32).sqrt() + } + + fn portal_path(s: &str) -> (u32, u32) { + let mut s = s.split_ascii_whitespace().take(3); + ( + s.next().expect("Exists").parse().expect("Valid u32"), + s.next().expect("Exists").parse().expect("Valid u32"), + ) + } + + let mut input = input.lines(); + let s_cnt = input + .next() + .expect("Line exists") + .parse::() + .expect("In 2..=100"); + + let mut stars = vec![]; + for _ in 0..s_cnt { + let s = input.next().expect("Line exists"); + stars.push(star_loc(s)); + } + + let p_cnt = input + .next() + .expect("Line exists") + .parse::() + .expect("In 1..=100"); + + let mut portals = multimap::MultiMap::::new(); + for _ in 0..p_cnt { + let (s, e) = portal_path(input.next().expect("Line exists")); + portals.insert(s, e); + } + + let Some(path) = pathfinding::directed::bfs::bfs( + &0u32, + |n| portals.get_vec(n).cloned().unwrap_or_default(), + |&n| n == s_cnt - 1, + ) else { + return Err(StatusCode::INTERNAL_SERVER_ERROR); + }; + + let d = path + .windows(2) + .map(|p| star_dist(stars[p[0] as usize], stars[p[1] as usize])) + .sum::(); + + Ok(format!("{} {d:.3}", path.len() - 1)) +} diff --git a/src/cal/mod.rs b/src/cal/mod.rs index 31c0f2b..dd030f0 100644 --- a/src/cal/mod.rs +++ b/src/cal/mod.rs @@ -14,6 +14,7 @@ mod day18; mod day19; mod day20; mod day21; +mod day22; pub(crate) fn router(pool: sqlx::PgPool) -> axum::Router { axum::Router::new() @@ -33,4 +34,5 @@ pub(crate) fn router(pool: sqlx::PgPool) -> axum::Router { .nest("/", day19::router()) .nest("/", day20::router()) .nest("/", day21::router()) + .nest("/", day22::router()) }