diff --git a/Cargo.lock b/Cargo.lock index 08638a6..0292ee2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "backtrace" version = "0.3.40" @@ -48,6 +53,24 @@ dependencies = [ "shared 0.1.0", ] +[[package]] +name = "day12" +version = "0.1.0" +dependencies = [ + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "day13" +version = "0.1.0" +dependencies = [ + "shared 0.1.0", +] + +[[package]] +name = "day14" +version = "0.1.0" + [[package]] name = "day2" version = "0.1.0" @@ -131,6 +154,23 @@ name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "1.0.6" @@ -186,6 +226,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" "checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" "checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" @@ -195,6 +236,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" diff --git a/Cargo.toml b/Cargo.toml index 81ada48..5f6075e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,8 @@ members = [ "day9", "day10", "day11", + "day12", + "day13", + "day14", "shared", ] diff --git a/day12/Cargo.toml b/day12/Cargo.toml new file mode 100644 index 0000000..a4e9894 --- /dev/null +++ b/day12/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day12" +version = "0.1.0" +authors = ["Stefan Bühler "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +num-integer = "0.1.41" diff --git a/day12/src/input.txt b/day12/src/input.txt new file mode 100644 index 0000000..e86abf8 --- /dev/null +++ b/day12/src/input.txt @@ -0,0 +1,4 @@ + + + + diff --git a/day12/src/main.rs b/day12/src/main.rs new file mode 100644 index 0000000..a74df00 --- /dev/null +++ b/day12/src/main.rs @@ -0,0 +1,166 @@ +fn strip_prefix<'a>(prefix: &str, input: &'a str) -> &'a str { + assert!(input.starts_with(prefix)); + &input[prefix.len()..] +} + +fn strip_suffix<'a>(suffix: &str, input: &'a str) -> &'a str { + assert!(input.ends_with(suffix)); + &input[..input.len() - suffix.len()] +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +pub struct Position { + pub x: i32, + pub y: i32, + pub z: i32, +} + +impl Position { + pub fn parse(input: &str) -> Self { + let input = strip_suffix(">", strip_prefix("<", input.trim())); + let mut parts = input.split(","); + let x = strip_prefix("x=", parts.next().expect("need x component").trim()).parse().unwrap(); + let y = strip_prefix("y=", parts.next().expect("need y component").trim()).parse().unwrap(); + let z = strip_prefix("z=", parts.next().expect("need z component").trim()).parse().unwrap(); + assert!(parts.next().is_none(), "unexpected data"); + Position { x, y, z } + } + + pub fn potential_energy(self) -> u32 { + self.x.abs() as u32 + self.y.abs() as u32 + self.z.abs() as u32 + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +pub struct Velocity { + pub dx: i32, + pub dy: i32, + pub dz: i32, +} + +impl Velocity { + pub fn kinetic_energy(self) -> u32 { + self.dx.abs() as u32 + self.dy.abs() as u32 + self.dz.abs() as u32 + } +} + +impl std::ops::AddAssign for Position { + fn add_assign(&mut self, rhs: Velocity) { + self.x += rhs.dx; + self.y += rhs.dy; + self.z += rhs.dz; + } +} + +fn parse_moons(input: &str) -> Vec<(Position, Velocity)> { + input.lines().map(Position::parse).map(|p| (p, Velocity::default())).collect() +} + +fn step(moons: &mut [(Position, Velocity)]) { + for i in 0..moons.len() - 1 { + // change velocity based on "gravity" + for j in i + 1..moons.len() { + let dx = (moons[j].0.x - moons[i].0.x).signum(); + moons[i].1.dx += dx; moons[j].1.dx -= dx; + let dy = (moons[j].0.y - moons[i].0.y).signum(); + moons[i].1.dy += dy; moons[j].1.dy -= dy; + let dz = (moons[j].0.z - moons[i].0.z).signum(); + moons[i].1.dz += dz; moons[j].1.dz -= dz; + } + } + for moon in moons { + moon.0 += moon.1; + } +} + +fn energy(moons: &[(Position, Velocity)]) -> u32 { + moons.iter().map(|(p, v)| p.potential_energy() * v.kinetic_energy()).sum() +} + +fn energy_after_steps(mut moons: Vec<(Position, Velocity)>, steps: u32) -> u32 { + for _ in 0..steps { + step(&mut moons); + } + energy(&moons) +} + +type ExtractedAxis = Vec<(i32, i32)>; +fn extract_x(moons: &[(Position, Velocity)]) -> ExtractedAxis { + moons.iter().map(|(p, v)| (p.x, v.dx)).collect() +} +fn extract_y(moons: &[(Position, Velocity)]) -> ExtractedAxis { + moons.iter().map(|(p, v)| (p.y, v.dy)).collect() +} +fn extract_z(moons: &[(Position, Velocity)]) -> ExtractedAxis { + moons.iter().map(|(p, v)| (p.z, v.dz)).collect() +} + +fn repeats_after_steps(mut moons: Vec<(Position, Velocity)>) -> u64 { + // as `step` is reversible the first repeated state will always be the initial state + + // find cycle per axis + let mut result_x = None; + let initial_x = extract_x(&moons); + let mut result_y = None; + let initial_y = extract_y(&moons); + let mut result_z = None; + let initial_z = extract_z(&moons); + + for current in 1u64.. { + step(&mut moons); + if result_x.is_none() && initial_x == extract_x(&moons) { + result_x = Some(current); + } + if result_y.is_none() && initial_y == extract_y(&moons) { + result_y = Some(current); + } + if result_z.is_none() && initial_z == extract_z(&moons) { + result_z = Some(current); + } + if let (Some(x), Some(y), Some(z)) = (result_x, result_y, result_z) { + return num_integer::lcm(num_integer::lcm(x, y), z); + } + } + unreachable!() +} + +fn main() { + let moons = parse_moons(include_str!("input.txt")); + let energy = energy_after_steps(moons.clone(), 1000); + println!("Energy after 1000 steps: {}", energy); + println!("Repeats initial state after {} steps", repeats_after_steps(moons)); +} + +#[cfg(test)] +mod day12test { + use super::*; + + #[test] + fn examples1() { + assert_eq!(179, energy_after_steps(parse_moons( + " + + +"), 10)); + assert_eq!(1940, energy_after_steps(parse_moons( + " + + + +"), 100)); + } + + #[test] + fn examples2() { + assert_eq!(2772, repeats_after_steps(parse_moons( + " + + +"))); + assert_eq!(4686774924, repeats_after_steps(parse_moons( + " + + +"))); + } +} \ No newline at end of file diff --git a/day13/Cargo.toml b/day13/Cargo.toml new file mode 100644 index 0000000..52cd6be --- /dev/null +++ b/day13/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day13" +version = "0.1.0" +authors = ["Stefan Bühler "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +shared = { path = "../shared" } diff --git a/day13/src/input.txt b/day13/src/input.txt new file mode 100644 index 0000000..7bc4efe --- /dev/null +++ b/day13/src/input.txt @@ -0,0 +1 @@ +1,380,379,385,1008,2607,501667,381,1005,381,12,99,109,2608,1101,0,0,383,1101,0,0,382,20102,1,382,1,20102,1,383,2,21101,37,0,0,1106,0,578,4,382,4,383,204,1,1001,382,1,382,1007,382,41,381,1005,381,22,1001,383,1,383,1007,383,24,381,1005,381,18,1006,385,69,99,104,-1,104,0,4,386,3,384,1007,384,0,381,1005,381,94,107,0,384,381,1005,381,108,1105,1,161,107,1,392,381,1006,381,161,1102,-1,1,384,1105,1,119,1007,392,39,381,1006,381,161,1102,1,1,384,20102,1,392,1,21102,22,1,2,21102,0,1,3,21102,138,1,0,1105,1,549,1,392,384,392,21001,392,0,1,21102,1,22,2,21102,3,1,3,21101,0,161,0,1106,0,549,1102,0,1,384,20001,388,390,1,20102,1,389,2,21102,1,180,0,1105,1,578,1206,1,213,1208,1,2,381,1006,381,205,20001,388,390,1,20102,1,389,2,21102,1,205,0,1105,1,393,1002,390,-1,390,1102,1,1,384,20101,0,388,1,20001,389,391,2,21102,228,1,0,1105,1,578,1206,1,261,1208,1,2,381,1006,381,253,20102,1,388,1,20001,389,391,2,21102,1,253,0,1106,0,393,1002,391,-1,391,1102,1,1,384,1005,384,161,20001,388,390,1,20001,389,391,2,21102,1,279,0,1105,1,578,1206,1,316,1208,1,2,381,1006,381,304,20001,388,390,1,20001,389,391,2,21101,304,0,0,1106,0,393,1002,390,-1,390,1002,391,-1,391,1101,0,1,384,1005,384,161,21002,388,1,1,20102,1,389,2,21101,0,0,3,21102,338,1,0,1105,1,549,1,388,390,388,1,389,391,389,20102,1,388,1,20101,0,389,2,21101,4,0,3,21101,0,365,0,1106,0,549,1007,389,23,381,1005,381,75,104,-1,104,0,104,0,99,0,1,0,0,0,0,0,0,420,18,19,1,1,20,109,3,21201,-2,0,1,22102,1,-1,2,21101,0,0,3,21101,0,414,0,1106,0,549,22101,0,-2,1,22102,1,-1,2,21101,0,429,0,1106,0,601,1201,1,0,435,1,386,0,386,104,-1,104,0,4,386,1001,387,-1,387,1005,387,451,99,109,-3,2106,0,0,109,8,22202,-7,-6,-3,22201,-3,-5,-3,21202,-4,64,-2,2207,-3,-2,381,1005,381,492,21202,-2,-1,-1,22201,-3,-1,-3,2207,-3,-2,381,1006,381,481,21202,-4,8,-2,2207,-3,-2,381,1005,381,518,21202,-2,-1,-1,22201,-3,-1,-3,2207,-3,-2,381,1006,381,507,2207,-3,-4,381,1005,381,540,21202,-4,-1,-1,22201,-3,-1,-3,2207,-3,-4,381,1006,381,529,22102,1,-3,-7,109,-8,2106,0,0,109,4,1202,-2,41,566,201,-3,566,566,101,639,566,566,2101,0,-1,0,204,-3,204,-2,204,-1,109,-4,2106,0,0,109,3,1202,-1,41,594,201,-2,594,594,101,639,594,594,20101,0,0,-2,109,-3,2105,1,0,109,3,22102,24,-2,1,22201,1,-1,1,21102,1,499,2,21101,766,0,3,21102,984,1,4,21102,630,1,0,1106,0,456,21201,1,1623,-2,109,-3,2106,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,2,2,0,0,2,2,2,0,2,2,0,2,2,0,2,0,2,2,2,2,0,0,0,2,2,2,2,0,2,2,2,2,0,2,2,2,0,1,1,0,2,2,0,0,0,2,2,2,0,0,2,2,2,0,0,0,0,0,2,2,2,2,2,0,2,0,2,2,2,2,2,2,0,0,2,2,0,0,1,1,0,2,2,2,2,2,2,2,0,2,2,2,2,2,0,2,2,0,0,2,2,2,0,2,2,2,0,0,2,0,0,2,2,2,2,2,0,2,0,1,1,0,2,2,2,2,2,0,2,2,2,0,2,2,2,2,0,0,0,0,2,2,2,2,2,2,0,2,2,0,0,0,2,2,2,0,0,0,2,0,1,1,0,2,2,2,0,2,2,2,2,2,2,2,2,0,0,0,0,2,0,2,0,2,2,2,0,2,2,2,2,2,2,2,2,0,2,0,2,2,0,1,1,0,2,2,2,2,2,2,0,0,2,0,0,0,2,2,2,0,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,0,1,1,0,0,2,2,2,2,0,2,2,2,0,2,2,2,2,2,2,2,2,0,2,2,2,2,2,0,0,2,2,2,2,2,0,0,0,2,0,2,0,1,1,0,0,2,2,0,2,2,0,2,2,0,0,2,2,2,2,2,0,2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,0,1,1,0,2,0,2,0,0,2,0,0,2,2,2,0,2,2,0,2,2,2,2,2,2,2,2,0,2,2,2,2,2,0,2,2,2,2,2,2,0,0,1,1,0,2,2,2,2,2,2,2,2,2,0,2,2,0,2,2,0,0,0,2,2,0,2,2,2,2,2,2,2,0,2,2,0,0,2,0,2,0,0,1,1,0,2,0,0,0,2,2,0,2,0,2,2,0,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,1,0,2,2,2,0,2,2,2,0,0,2,2,2,0,2,2,0,2,2,2,2,2,2,0,2,2,2,2,2,0,0,0,2,0,2,2,2,0,0,1,1,0,2,2,2,2,0,2,0,2,0,0,2,2,2,2,0,0,0,2,2,2,2,0,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,0,1,1,0,0,2,2,0,0,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,0,2,2,2,0,2,2,2,0,0,2,2,2,0,2,2,0,1,1,0,2,2,0,0,0,2,2,2,2,0,2,0,0,2,2,0,2,2,2,2,0,0,2,2,2,0,2,2,2,0,2,2,2,0,2,2,2,0,1,1,0,2,2,2,2,2,0,0,2,0,2,2,2,0,2,2,2,2,2,2,0,2,0,2,2,2,2,0,2,0,0,2,0,2,2,0,2,2,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,68,30,70,17,8,95,58,65,89,47,70,26,8,7,65,13,54,62,14,76,19,56,81,92,65,56,46,12,32,48,52,92,70,30,90,26,97,53,52,49,43,93,5,20,81,91,23,4,60,46,14,90,66,33,56,57,89,29,17,45,81,11,39,9,97,68,86,36,92,25,41,66,92,50,25,96,70,82,96,45,47,26,21,97,16,55,93,90,14,24,86,65,19,44,66,36,60,86,79,95,47,37,82,8,34,17,89,74,17,74,40,97,43,92,26,94,14,58,9,68,48,32,42,60,31,35,96,88,71,93,80,78,66,32,76,13,45,86,53,31,82,77,11,69,67,63,88,16,94,77,48,11,90,53,54,12,92,27,66,77,86,31,36,91,50,55,98,72,53,92,33,64,61,23,52,31,25,69,89,18,94,1,80,83,30,13,47,71,91,35,55,19,89,57,92,77,46,54,77,59,9,13,20,4,57,81,22,61,33,83,18,38,24,41,83,48,70,82,33,7,8,41,56,47,85,89,85,65,93,40,95,73,47,87,24,42,10,64,71,77,57,18,21,8,30,83,55,10,94,19,52,80,89,67,40,1,80,60,36,71,80,77,62,16,23,40,68,50,17,81,26,22,65,92,47,46,43,21,81,20,50,40,84,90,97,73,95,39,12,76,9,41,92,59,8,10,32,54,34,59,32,26,74,63,90,13,46,96,40,98,52,34,65,95,16,73,54,74,28,73,2,36,69,19,68,71,33,24,44,53,56,7,58,25,15,49,77,40,30,59,87,29,36,65,71,92,21,14,44,5,4,34,87,81,71,36,42,59,20,76,36,39,27,68,62,85,80,96,66,56,96,86,53,60,52,7,65,77,8,51,88,10,26,77,74,57,78,22,19,6,86,33,91,66,15,81,15,37,36,98,17,48,82,5,37,82,76,36,65,17,18,54,47,73,5,54,84,77,4,73,16,54,10,12,6,76,97,45,63,30,26,54,97,60,44,12,80,94,33,16,43,88,85,52,5,73,30,23,41,76,10,92,79,13,52,95,67,4,41,10,96,7,92,80,33,2,60,25,83,49,20,42,83,31,49,71,25,74,52,48,83,12,50,26,13,86,21,21,50,7,31,71,77,12,91,2,18,93,22,15,28,40,27,41,84,10,85,93,65,67,13,80,36,10,52,79,2,29,18,48,47,42,4,12,12,75,44,41,21,75,69,6,63,61,29,51,59,58,9,70,25,57,39,67,83,6,90,63,56,2,58,96,5,94,85,22,92,14,58,91,16,1,55,58,24,77,74,41,70,49,90,23,26,54,74,70,40,65,38,31,2,80,93,21,60,56,3,94,87,53,73,59,73,26,21,76,66,94,81,60,43,39,14,18,89,33,73,47,2,96,50,76,84,27,43,1,29,45,59,37,81,82,56,19,71,20,90,48,67,21,16,16,40,77,22,96,32,47,15,87,74,42,98,97,52,83,96,9,51,95,34,29,16,44,3,32,65,86,25,93,1,20,95,26,6,22,58,33,46,3,38,94,95,85,57,52,11,14,12,28,86,92,55,45,26,60,57,21,3,84,7,12,57,17,86,41,46,37,89,4,91,1,12,46,71,5,84,21,83,7,56,95,40,20,26,65,51,90,2,64,33,69,4,92,58,88,8,58,46,31,19,24,35,28,40,58,52,4,56,28,38,6,89,73,74,94,16,70,59,93,8,66,8,50,89,56,5,5,71,30,86,20,70,64,35,90,54,59,1,36,3,40,31,37,77,21,74,38,7,15,5,43,14,67,38,96,90,36,84,81,66,8,33,77,73,64,3,35,96,12,91,71,60,43,30,44,87,61,21,37,68,43,24,29,26,57,75,31,76,36,32,92,95,39,54,75,79,90,98,49,34,38,79,55,53,36,47,35,3,79,89,70,84,43,58,7,92,57,96,96,23,35,59,56,78,9,4,42,35,46,86,61,34,36,89,33,5,51,56,88,34,10,44,86,95,95,20,97,15,41,85,42,37,1,8,29,48,10,6,51,61,53,97,72,83,8,41,15,27,38,20,70,59,70,66,95,31,46,22,73,68,27,45,31,61,51,10,5,81,37,27,34,30,95,83,67,10,52,26,87,56,64,70,78,14,86,76,94,15,82,70,18,26,48,94,15,52,39,47,51,15,51,20,14,23,45,29,8,9,47,9,30,27,76,57,98,57,73,72,13,35,26,45,70,30,84,91,65,12,6,91,98,78,40,501667 diff --git a/day13/src/main.rs b/day13/src/main.rs new file mode 100644 index 0000000..7416b2f --- /dev/null +++ b/day13/src/main.rs @@ -0,0 +1,147 @@ +use shared::intcode::{IntCode, SimulateStep, CELL}; +use std::collections::HashSet; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Tile { + Empty, + Wall, + Block, + HorizontalPaddle, + Ball, +} + +impl Tile { + pub fn new(t: CELL) -> Self { + match t { + 0 => Self::Empty, + 1 => Self::Wall, + 2 => Self::Block, + 3 => Self::HorizontalPaddle, + 4 => Self::Ball, + _ => panic!("Unknown tile {}", t), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Position { + pub x: CELL, + pub y: CELL, +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Update { + Tile { + position: Position, + tile: Tile, + }, + Score { + score: CELL, + }, +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[repr(i8)] +pub enum Joystick { + Left = -1, + Neutral = 0, + Right = 1, +} + +fn view_game(mut ic: IntCode) -> impl Iterator { + std::iter::from_fn(move || { + let x = match ic.simulate_step() { + SimulateStep::Finished => return None, + SimulateStep::WaitForInput => panic!("no input for game"), + SimulateStep::Output(n) => n, + }; + let y = ic.simulate_step().expect_output(); + let p = Position { x, y }; + let tile = Tile::new(ic.simulate_step().expect_output()); + Some((p, tile)) + }) +} + +fn find_blocks(tiles: I) -> HashSet +where + I: Iterator, +{ + let mut blocks = HashSet::new(); + for (pos, tile) in tiles { + match tile { + Tile::Block => { blocks.insert(pos); } + _ => { blocks.remove(&pos); } + } + } + blocks +} + +fn get_update(ic: &mut IntCode, joystick: Joystick) -> Option { + let joystick = joystick as i8 as CELL; + ic.next_input = Some(joystick); + let x = match ic.simulate_step() { + SimulateStep::Finished => { + ic.next_input = None; + return None; + }, + SimulateStep::WaitForInput => panic!("no input for game"), + SimulateStep::Output(n) => n, + }; + let y = ic.simulate_step().expect_output(); + if x == -1 && y == 0 { + Some(Update::Score { score: ic.simulate_step().expect_output() }) + } else { + let position = Position { x, y }; + let tile = Tile::new(ic.simulate_step().expect_output()); + Some(Update::Tile { position, tile }) + } +} + +fn break_all_blocks(mut ic: IntCode) -> CELL { + ic.data[0] = 2; // "play for free" + let mut last_score = None; + let mut last_paddle = None; + let mut last_ball = None; + let mut blocks = HashSet::new(); + let mut had_blocks = false; + let mut joystick = Joystick::Neutral; + while let Some(update) = get_update(&mut ic, joystick) { + match update { + Update::Score { score } => last_score = Some(score), + Update::Tile { position, tile } => { + blocks.remove(&position); + match tile { + Tile::Empty => (), + Tile::Wall => (), + Tile::Block => { blocks.insert(position); had_blocks = true; }, + Tile::HorizontalPaddle => last_paddle = Some(position), + Tile::Ball => last_ball = Some(position), + } + } + } + if let (Some(paddle), Some(ball)) = (&last_paddle, &last_ball) { + let diff_x = ball.x - paddle.x; + joystick = if diff_x > 0 { + Joystick::Right + } else if diff_x < 0 { + Joystick::Left + } else { + Joystick::Neutral + }; + } + } + if had_blocks && blocks.is_empty() { + println!("Won game, all blocks destroyed"); + } else { + println!("Lost game"); + } + last_score.expect("no score written") +} + +fn main() { + let ic = include_str!("input.txt").parse::().unwrap(); + let blocks = find_blocks(view_game(ic.clone())); + println!("Number of blocks: {}", blocks.len()); + let score = break_all_blocks(ic); + println!("Score: {}", score); +} diff --git a/day14/Cargo.toml b/day14/Cargo.toml new file mode 100644 index 0000000..a0a5db3 --- /dev/null +++ b/day14/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "day14" +version = "0.1.0" +authors = ["Stefan Bühler "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/day14/src/input.txt b/day14/src/input.txt new file mode 100644 index 0000000..a2ff15f --- /dev/null +++ b/day14/src/input.txt @@ -0,0 +1,63 @@ +12 VJWQR, 1 QTBC => 6 BGXJV +12 BGTMN, 2 DRKQR => 2 TVSF +2 FTFK => 2 THNDN +13 LKRTN, 7 MDPN, 12 NZKQZ => 5 LPWPD +1 HDKX, 3 DWZS, 1 RCBQS => 1 DCRK +14 ZCMF => 6 ZKHLC +3 ZFVH, 2 ZCMF, 1 SCJG, 1 LQWJ, 4 BGBJ, 1 NHPR, 3 VKZFJ, 7 FWFXZ => 4 QVJMP +11 TNMLB => 7 NVDCR +1 LPWPD, 1 BGBJ => 2 SCJG +3 DFCVF, 1 QGSN => 6 PQXG +1 BGXJV, 1 THNDN => 4 BCQN +3 LKRTN => 9 MDPN +2 THNDN, 13 RCKZ, 10 FQSLN => 8 VKZFJ +4 LBCZ, 9 LWHS => 1 FQSLN +6 WSRVZ => 9 TNMLB +8 FQSLN, 14 JQRF, 4 BGTMN => 5 QGSN +4 ZCMF, 4 PLSM, 2 ZHTX => 8 TDHPM +2 RSKC, 10 SHBC, 8 MDPN => 6 FMSZ +2 VJWQR => 2 FPTV +12 DRKQR => 6 NHPR +35 QJLF, 22 BGTMN, 11 VJWTR, 1 QVJMP, 8 LQWJ, 1 TWLC, 16 NXZCH, 18 THKF, 42 JBLM => 1 FUEL +2 BGTMN, 4 XJKN => 8 ZCMF +4 TVSF => 3 RSKC +7 HRWS, 1 TVSF => 3 ZHTX +134 ORE => 4 WSRVZ +1 VKZFJ, 1 TWLC, 4 ZHTX, 5 THNDN, 12 PLVN, 1 ZFXNP, 1 PQXG, 6 CWHV => 7 VJWTR +20 XJKN, 1 LCKW, 3 NZKQZ => 7 HDKX +1 LPWPD => 8 RCKZ +4 RCBQS, 1 NVDCR => 5 BGBJ +8 BGXJV => 4 BGTMN +13 QBDX, 16 BGXJV => 6 NZKQZ +2 LPWPD => 3 DRKQR +4 QBDX => 7 XJKN +12 LCKW, 9 NVDCR => 3 RCBQS +142 ORE => 3 QBDX +1 WXHJF => 1 XKDJ +2 RSKC => 2 CWHV +2 ZHTX, 1 ZFXNP => 6 JQRF +1 FTFK, 1 TVSF, 1 QBDX => 2 JBLM +1 TDHPM, 14 NHPR, 3 QPSF => 5 ZFVH +3 GDTPC, 1 ZKHLC => 8 ZFXNP +5 DWZS => 3 LQWJ +1 FTFK, 4 LBCZ, 13 NHPR => 1 FWFXZ +1 RCBQS, 12 SHBC => 9 FTFK +1 WSRVZ, 1 XKDJ => 5 LKRTN +2 BGTMN, 1 MDPN => 5 PLSM +2 BGXJV, 17 XKDJ, 4 FPTV => 9 LCKW +148 ORE => 2 QTBC +110 ORE => 2 VJWQR +42 ZFXNP, 15 RCKZ, 8 GDTPC => 3 QJLF +13 HRWS => 4 GDTPC +34 HRWS => 4 DFCVF +2 VKZFJ, 2 NHPR, 16 PLVN, 1 QPSF, 13 LBCZ, 4 DCRK, 10 LWHS => 7 NXZCH +3 CWHV, 1 THNDN => 7 LWHS +1 BGXJV, 2 QBDX => 5 DWZS +9 LQWJ => 8 QPSF +21 BCQN, 3 FMSZ, 1 RSKC => 5 THKF +118 ORE => 6 WXHJF +11 FMSZ => 9 TWLC +28 PLSM => 5 SHBC +1 ZKHLC, 23 SCJG => 7 LBCZ +17 DWZS, 16 THNDN => 9 PLVN +7 HDKX => 9 HRWS diff --git a/day14/src/main.rs b/day14/src/main.rs new file mode 100644 index 0000000..a2dd29d --- /dev/null +++ b/day14/src/main.rs @@ -0,0 +1,195 @@ +use std::collections::HashMap; + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct ChemicalAmount<'a> { + pub amount: u64, + pub name: &'a str, +} + +impl<'a> ChemicalAmount<'a> { + pub fn parse(input: &'a str) -> Self { + let input = input.trim(); + let mut elems = input.split_whitespace(); + let amount = elems.next().expect("need amount").trim().parse::().expect("number for amount"); + let name = elems.next().expect("need amount").trim(); + assert!(elems.next().is_none(), "unexpected data"); + ChemicalAmount { + amount, + name, + } + } +} + +pub type Inputs<'a> = Vec>; + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Reaction<'a> { + pub inputs: Inputs<'a>, + pub output: ChemicalAmount<'a>, +} + +impl<'a> Reaction<'a> { + pub fn parse(input: &'a str) -> Self { + let mut parts = input.split("=>"); + let inputs = parts.next().expect("need inputs").trim().split(",").map(ChemicalAmount::parse).collect(); + let output = ChemicalAmount::parse(parts.next().expect("need output").trim()); + assert!(parts.next().is_none(), "unexpected data"); + Reaction { + inputs, + output, + } + } + + pub fn runs_for(&self, required_output: u64) -> u64 { + (required_output + self.output.amount - 1) / self.output.amount + } +} + +#[derive(Clone, Debug)] +pub struct Reactions<'a>(pub HashMap<&'a str, Reaction<'a>>); + +impl<'a> Reactions<'a> { + pub fn parse(input: &'a str) -> Self { + Self(input.lines().map(Reaction::parse).map(|reaction| (reaction.output.name.into(), reaction)).collect()) + } + + fn gather_ore(&self, needs: &mut HashMap<&'a str, u64>, name: &'a str, additional: u64) -> u64 { + if name == "ORE" { return additional; } + let previous_need; + let updated_need; + { + let need = needs.entry(&name).or_default(); + previous_need = *need; + updated_need = previous_need + additional; + *need = updated_need; + } + let reaction = &self.0[name]; + let previous_runs = reaction.runs_for(previous_need); + let updated_runs = reaction.runs_for(updated_need); + let mut ore = 0; + if updated_runs > previous_runs { + let new_runs = updated_runs - previous_runs; + for input in &reaction.inputs { + ore += self.gather_ore(needs, input.name, input.amount * new_runs); + } + } + ore + } + + fn ore_for_fuel(&self, fuel: u64) -> u64 { + let mut needs = HashMap::new(); + self.gather_ore(&mut needs, "FUEL", fuel) + } +} + +// check must be monotone, there must be a `n` such that for all `i`: `i <= n <=> check(i)` +// returns `None` if this `n` is less than 0 (i.e. check is never true) +fn binary_search_max(check: F, start: u64, max_step: u64) -> Option +where + F: Fn(u64) -> bool, +{ + let mut upper; + let mut lower; + + // find boundaries + if !check(start) { + // find lower limit + if start == 0 { return None; } + upper = start - 1; + loop { + lower = start / 2; + if check(lower) { break; } + if lower == 0 { return None; } + upper = lower - 1; + } + } else { + lower = start; + // find upper limit + upper = start; + loop { + upper += max_step; + if !check(upper) { + upper -= 1; + break; + } + } + } + + // invariant: check(lower) && !check(upper + 1) + while lower != upper { + debug_assert!(lower < upper); + let mid = (lower + upper + 1) / 2; + debug_assert!(mid > lower); + if check(mid) { + lower = mid; + } else { + upper = mid - 1; + } + } + Some(lower) +} + +fn main() { + let reactions = Reactions::parse(include_str!("input.txt")); + let ore_one_fuel; + { + ore_one_fuel = reactions.ore_for_fuel(1); + println!("Need {} ORE for one FUEL", ore_one_fuel); + } + + let have_ore = 1000000000000u64; + let max_fuel = binary_search_max(|fuel| { + reactions.ore_for_fuel(fuel) <= have_ore + }, have_ore / ore_one_fuel, have_ore / ore_one_fuel).unwrap(); + println!("Can get {} fuel for {} ore", max_fuel, have_ore); +} + +#[cfg(test)] +mod day14test { + use super::*; + + fn calculate_needed_ore(input: &str) -> u64 { + let reactions = Reactions::parse(input); + reactions.ore_for_fuel(1) + } + + #[test] + fn examples1() { + assert_eq!(165, calculate_needed_ore( + "9 ORE => 2 A +8 ORE => 3 B +7 ORE => 5 C +3 A, 4 B => 1 AB +5 B, 7 C => 1 BC +4 C, 1 A => 1 CA +2 AB, 3 BC, 4 CA => 1 FUEL")); + assert_eq!(13312, calculate_needed_ore( + "157 ORE => 5 NZVS +165 ORE => 6 DCFZ +44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL +12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ +179 ORE => 7 PSHF +177 ORE => 5 HKGWZ +7 DCFZ, 7 PSHF => 2 XJWVT +165 ORE => 2 GPVTF +3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT")); + assert_eq!(2210736, calculate_needed_ore( + "171 ORE => 8 CNZTR +7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL +114 ORE => 4 BHXH +14 VRPVC => 6 BMBT +6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL +6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT +15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW +13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW +5 BMBT => 4 WPTQ +189 ORE => 9 KTJDG +1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP +12 VRPVC, 27 CNZTR => 2 XDBXC +15 KTJDG, 12 BHXH => 5 XCVML +3 BHXH, 2 VRPVC => 7 MZWV +121 ORE => 7 VRPVC +7 XCVML => 6 RJRHP +5 BHXH, 4 VRPVC => 5 LTCX")); + } +} \ No newline at end of file