diff --git a/Cargo.lock b/Cargo.lock index e70b6ec..356992e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,350 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "aoc-2023" version = "0.1.0" +dependencies = [ + "rustworkx-core", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-deque" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", + "rayon", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "rayon", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.1.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "priority-queue" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff39edfcaec0d64e8d0da38564fad195d2d51b680940295fcc307366e101e61" +dependencies = [ + "autocfg", + "indexmap 1.9.3", +] + +[[package]] +name = "proc-macro2" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-cond" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac2a28c5317e6d26ac87a8629c0eb362690ed1d739f4040e21cfaafdf04e6f8" +dependencies = [ + "either", + "itertools", + "rayon", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rustworkx-core" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72abf7976bc09a30391248b3c6509338b235c02b0e9b0bf8af686c289cad3f45" +dependencies = [ + "ahash", + "fixedbitset", + "hashbrown 0.14.3", + "indexmap 2.1.0", + "num-traits", + "petgraph", + "priority-queue", + "rand", + "rand_pcg", + "rayon", + "rayon-cond", +] + +[[package]] +name = "syn" +version = "2.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eae3c679c56dc214320b67a1bc04ef3dfbd6411f6443974b5e4893231298e66" +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" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 1cacbd9..707b0d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,5 @@ categories = ["Algorithms", "Command line utilities"] overflow-checks = true lto = "fat" -[dependencies] \ No newline at end of file +[dependencies] +rustworkx-core = "0.13.2" \ No newline at end of file diff --git a/aoc-py/solutions/day24.py b/aoc-py/solutions/day24.py index f00f3d2..12aea94 100644 --- a/aoc-py/solutions/day24.py +++ b/aoc-py/solutions/day24.py @@ -10,6 +10,7 @@ import z3 import numpy as np +from sympy import Symbol, solve from ..solution import Solution @@ -110,11 +111,49 @@ def part_one(self, inp: str) -> int: total += 1 return total + def part_two_sympy(self, inp: str) -> int: + """Part 2 solved algebraically using sympy""" + hailstones = [ + Hailstone.from_str(line) for line in inp.splitlines() + ] + # Declare symbols: + # They are all integers, and positions have to also be non-negative + # + # rock x, y, z starting positions + x_pos = Symbol('x_pos', integer=True, nonnegative=True) + y_pos = Symbol('y_pos', integer=True, nonnegative=True) + z_pos = Symbol('z_pos', integer=True, nonnegative=True) + # rock x, y, z velocities + x_vel = Symbol('x_vel', integer=True) + y_vel = Symbol('y_vel', integer=True) + z_vel = Symbol('z_vel', integer=True) + + equations = [] + + for n_eqs, hailstone in enumerate(hailstones, 1): + equations.append( + (x_pos - hailstone.x_pos) * (hailstone.y_vel - y_vel) # type: ignore (cannot recognize arithmetic between `int` and `Symbol`) + - (y_pos - hailstone.y_pos) * (hailstone.x_vel - x_vel)) # type: ignore + equations.append( + (y_pos - hailstone.y_pos) * (hailstone.z_vel - z_vel) # type: ignore + - (z_pos - hailstone.z_pos) * (hailstone.y_vel - y_vel) # type: ignore + ) + # we have 3 or more equations -> try solve & solution exists + if n_eqs >= 3 and (solutions := solve(equations)): + solution = solutions[0] + # solution = add up values of (x, y, z) for the rock's position + return int(solution[x_pos] + solution[y_pos] + solution[z_pos]) + raise ValueError('No solution found for the system of equations') + def part_two_linalg(self, inp: str) -> int: """Part 2 solved using linear algebra""" hs1, hs2, hs3, *_ = [Hailstone.from_str(line) for line in inp.splitlines()] # solving for `x` in Ax + b where (A = a) + # + # coeffcient matrix (A) of 6x6 + # 6 variables & 6 equations generated from positions and velocities + # of 3 hailstone vectors that are linearly independent a = np.array( [ [hs2.y_vel - hs1.y_vel, hs1.x_vel - hs2.x_vel, 0, hs1.y_pos - hs2.y_pos, hs2.x_pos - hs1.x_pos, 0], @@ -181,8 +220,9 @@ def part_two(self, inp: str) -> int: def run(self, inp: str) -> None: print('Part 1:', p1 := self.part_one(inp)) - print('Part 2:', p2 := self.part_two_linalg(inp)) + print('Part 2:', p2 := self.part_two(inp)) + assert p2 == self.part_two_sympy(inp) assert p2 == self.part_two_linalg(inp) assert p1 == 14672 diff --git a/aoc-py/solutions/day25.py b/aoc-py/solutions/day25.py index 3bbffda..7cb17b8 100644 --- a/aoc-py/solutions/day25.py +++ b/aoc-py/solutions/day25.py @@ -17,17 +17,21 @@ class Day25(Solution): def part_one(self, inp: str) -> int: graph = nx.Graph() + # generate graph for line in inp.splitlines(): left, right = line.split(':') for node in right.strip().split(): graph.add_edge(left, node) graph.add_edge(node, left) + # remove the edges that are of the cut graph.remove_edges_from( nx.minimum_edge_cut(graph) ) + # get 2 partitioned groups after the cut (`a` and `b`) a, b = nx.connected_components(graph) + # multiply sizes of 2 groups together return len(a) * len(b) def part_two(self, _: str) -> None: diff --git a/src/bin/day18.rs b/src/bin/day18.rs index 7f15256..f9c8ef9 100644 --- a/src/bin/day18.rs +++ b/src/bin/day18.rs @@ -20,8 +20,9 @@ impl Day18 { (points .iter() .zip( - points[1..] + points .iter() + .skip(1) .chain(once(&points[0])) ) .map(|((x1, y1), (x2, y2))| diff --git a/src/bin/day22.rs b/src/bin/day22.rs index 3e9663b..47f1cca 100644 --- a/src/bin/day22.rs +++ b/src/bin/day22.rs @@ -109,9 +109,11 @@ impl Day22 { .lines() .filter_map(|line| line.parse::().ok()) .collect::>(); + + let n_bricks = bricks.len(); bricks.sort(); - for i in 0..bricks.len() { + for i in 0..n_bricks { let brick = &bricks[i]; let mut z = 1; @@ -128,8 +130,8 @@ impl Day22 { } bricks.sort(); - let mut supports = HashMap::new(); - let mut supported_by = HashMap::new(); + let mut supports = HashMap::with_capacity(n_bricks); + let mut supported_by = HashMap::with_capacity(n_bricks); for (a, brick) in bricks .iter() diff --git a/src/bin/day23.rs b/src/bin/day23.rs index 459ea67..719e4dd 100644 --- a/src/bin/day23.rs +++ b/src/bin/day23.rs @@ -24,11 +24,12 @@ impl Day23 { (b'^', (row.wrapping_sub(1), col)), ] .into_iter() - .filter(|&(_, (i, j))| grid - .get(i) - .and_then(|row| row.get(j)) - .map(|&tile| tile != b'#') - .unwrap_or_default() + .filter(|&(_, (i, j))| + grid + .get(i) + .and_then(|row| row.get(j)) + .map(|&tile| tile != b'#') + .unwrap_or_default() ) .collect::>() } @@ -104,7 +105,7 @@ impl Day23 { ) ); - let mut graph = HashMap::new(); + let mut graph = HashMap::with_capacity(nodes.len()); for starting_node in &nodes { let mut to_check = VecDeque::from([ diff --git a/src/bin/day24.rs b/src/bin/day24.rs index 832b3e6..f1874e8 100644 --- a/src/bin/day24.rs +++ b/src/bin/day24.rs @@ -7,8 +7,6 @@ use std::{ }; use aoc_2023::Solution; -pub struct Day24; - #[derive(Debug, Clone, PartialEq, Eq)] struct ParseHailstoneError; @@ -22,6 +20,19 @@ struct Hailstone { z_vel: f64, } +macro_rules! parse_part { + ($parts:expr) => { + $parts + .next() + .and_then(|raw| raw + .trim() + .parse::() + .ok() + ) + .ok_or(ParseHailstoneError)? + } +} + impl FromStr for Hailstone { type Err = ParseHailstoneError; @@ -33,31 +44,13 @@ impl FromStr for Hailstone { let mut vel = vel.split(','); Ok(Self { - x_pos: pos - .next() - .and_then(|x_pos| x_pos.parse::().ok()) - .ok_or(ParseHailstoneError)?, - y_pos: pos - .next() - .and_then(|y_pos| y_pos.parse::().ok()) - .ok_or(ParseHailstoneError)?, - z_pos: pos - .next() - .and_then(|z_pos| z_pos.parse::().ok()) - .ok_or(ParseHailstoneError)?, - - x_vel: vel - .next() - .and_then(|x_vel| x_vel.parse::().ok()) - .ok_or(ParseHailstoneError)?, - y_vel: vel - .next() - .and_then(|y_vel| y_vel.parse::().ok()) - .ok_or(ParseHailstoneError)?, - z_vel: vel - .next() - .and_then(|z_vel| z_vel.parse::().ok()) - .ok_or(ParseHailstoneError)?, + x_pos: parse_part!(pos), + y_pos: parse_part!(pos), + z_pos: parse_part!(pos), + + x_vel: parse_part!(vel), + y_vel: parse_part!(vel), + z_vel: parse_part!(vel), }) } } @@ -100,6 +93,8 @@ impl Hailstone { } } +pub struct Day24; + impl Day24 { pub fn part_one(&self, inp: T) -> usize { let hailstones = inp @@ -144,7 +139,7 @@ impl Solution for Day24 { println!("Part 1: {p1}"); println!("Part 2: {p2}"); - //assert_eq!(p1, 14672); + assert_eq!(p1, 14672); //assert_eq!(p2, 646_810_057_104_753); } } diff --git a/src/bin/day25.rs b/src/bin/day25.rs new file mode 100644 index 0000000..0faa13e --- /dev/null +++ b/src/bin/day25.rs @@ -0,0 +1,72 @@ +//! Day 25: Snowverload +//! +//! +use std::fmt::Display; +use rustworkx_core::{ + petgraph::graphmap::GraphMap, + connectivity::stoer_wagner_min_cut as min_cut, +}; +use aoc_2023::Solution; + +pub struct Day25; + +impl Day25 { + pub fn part_one(&self, inp: T) -> usize { + let mut graph = GraphMap::new(); + let inp = inp + .to_string(); + + for line in inp.lines() { + let (left, right) = line + .split_once(':') + .unwrap(); + for node in right + .trim() + .split_whitespace() + { + graph.add_edge(left, node, 1); + graph.add_edge(node, left, 1); + } + } + + if let Ok(Some((_, partition_1))) = min_cut( + &graph, + |_| Ok::(1)) + { + let size_1 = partition_1.len(); + let size_2 = graph.node_count() - size_1; + + size_1 * size_2 + } else { + panic!("Could not perform a minimum cut on the graph"); + } + } + + pub fn part_two(&self, _inp: T) -> usize { + unimplemented!() + } +} + +impl Solution for Day25 { + const NAME: &'static str = "Snowverload"; + + fn run(&self, inp: String) { + let p1 = self.part_one(&inp); + + println!("Part 1: {p1}"); + + assert_eq!(p1, 554064); + } +} + +fn main() { + aoc_2023::run_day(25, &Day25); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { main(); } +} \ No newline at end of file