From b85d5bcf0a40747fcfec2042fac5de02fca68c26 Mon Sep 17 00:00:00 2001 From: Wesley Moore Date: Thu, 12 Dec 2019 21:19:23 +1100 Subject: [PATCH] Day 12 part 1 --- 2019/Cargo.lock | 35 +++++++++ 2019/Cargo.toml | 2 + 2019/input/day12.txt | 4 + 2019/src/bin/day12.rs | 172 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 2019/input/day12.txt create mode 100644 2019/src/bin/day12.rs diff --git a/2019/Cargo.lock b/2019/Cargo.lock index 4389501..c68f1a3 100644 --- a/2019/Cargo.lock +++ b/2019/Cargo.lock @@ -3,4 +3,39 @@ [[package]] name = "advent-of-code" version = "0.1.0" +dependencies = [ + "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" +"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" diff --git a/2019/Cargo.toml b/2019/Cargo.toml index 847bb75..85ec74d 100644 --- a/2019/Cargo.toml +++ b/2019/Cargo.toml @@ -7,3 +7,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +itertools = "0.8" +regex = { version = "1.0", default-features = false, features = ["std", "unicode-perl"] } \ No newline at end of file diff --git a/2019/input/day12.txt b/2019/input/day12.txt new file mode 100644 index 0000000..90aedaa --- /dev/null +++ b/2019/input/day12.txt @@ -0,0 +1,4 @@ + + + + diff --git a/2019/src/bin/day12.rs b/2019/src/bin/day12.rs new file mode 100644 index 0000000..5d2fe1d --- /dev/null +++ b/2019/src/bin/day12.rs @@ -0,0 +1,172 @@ +use itertools::Itertools; +use regex::Regex; +use std::str::FromStr; +use std::{fs, io, ops}; + +#[derive(Debug, Copy, Clone)] +struct Point3D { + x: i64, + y: i64, + z: i64, +} + +#[derive(Debug, Clone)] +struct Moon { + position: Point3D, + velocity: Point3D, +} + +fn main() -> io::Result<()> { + let mut moons = parse_positions(&fs::read_to_string("input/day12.txt")?)?; + + for _i in 0..1000 { + step(&mut moons); + } + let system: Vec<_> = moons.into_iter().map(|moon| moon.unwrap()).collect(); + println!("Part 1: {}", total_energy(&system)); + + Ok(()) +} + +fn parse_positions(s: &str) -> io::Result>> { + let positions: Vec = s + .lines() + .map(|line| line.parse()) + .collect::>() + .map_err(|_| io::Error::new(io::ErrorKind::Other, "parse error"))?; + + Ok(positions + .into_iter() + .map(|position| { + Some(Moon { + position, + velocity: Point3D { x: 0, y: 0, z: 0 }, + }) + }) + .collect()) +} + +fn step(system: &mut Vec>) { + // Within each time step, first update the velocity of every moon by applying gravity. + // Then, once all moons' velocities have been updated, update the position of every moon + // by applying velocity. + + // To apply gravity, consider every pair of moons. On each axis (x, y, and z), the velocity of + // each moon changes by exactly +1 or -1 to pull the moons together. For example, if Ganymede + // has an x position of 3, and Callisto has a x position of 5, then Ganymede's x velocity + // changes by +1 (because 5 > 3) and Callisto's x velocity changes by -1 (because 3 < 5). + // However, if the positions on a given axis are the same, the velocity on that axis does not + // change for that pair of moons. + + // Hackery.... + for pair in (0..system.len()).combinations(2) { + let mut moon1 = system[pair[0]].take().unwrap(); + let mut moon2 = system[pair[1]].take().unwrap(); + moon1.apply_gravity(&mut moon2); + system[pair[0]].replace(moon1); + system[pair[1]].replace(moon2); + } + + // Once all gravity has been applied, apply velocity: simply add the velocity of each moon to + // its own position. + for opt_moon in system { + if let Some(moon) = opt_moon { + moon.apply_velocity() + } + } +} + +fn total_energy(system: &[Moon]) -> i64 { + system.iter().map(|moon| moon.total_energy()).sum() +} + +impl Moon { + fn apply_gravity(&mut self, other: &mut Moon) { + if self.position.x == other.position.x { + } else if self.position.x < other.position.x { + self.velocity.x += 1; + other.velocity.x -= 1; + } else { + self.velocity.x -= 1; + other.velocity.x += 1; + } + + if self.position.y == other.position.y { + } else if self.position.y < other.position.y { + self.velocity.y += 1; + other.velocity.y -= 1; + } else { + self.velocity.y -= 1; + other.velocity.y += 1; + } + + if self.position.z == other.position.z { + } else if self.position.z < other.position.z { + self.velocity.z += 1; + other.velocity.z -= 1; + } else { + self.velocity.z -= 1; + other.velocity.z += 1; + } + } + + fn apply_velocity(&mut self) { + self.position += self.velocity; + } + + // The total energy for a single moon is its potential energy multiplied by its kinetic energy. + // A moon's potential energy is the sum of the absolute values of its x, y, and z position + // coordinates. A moon's kinetic energy is the sum of the absolute values of its velocity + // coordinates. + fn potential_energy(&self) -> i64 { + self.position.x.abs() + self.position.y.abs() + self.position.z.abs() + } + + fn kinetic_energy(&self) -> i64 { + self.velocity.x.abs() + self.velocity.y.abs() + self.velocity.z.abs() + } + + fn total_energy(&self) -> i64 { + self.potential_energy() * self.kinetic_energy() + } +} + +impl FromStr for Point3D { + type Err = std::num::ParseIntError; + + fn from_str(s: &str) -> Result { + let re = Regex::new(r"^$").unwrap(); + let caps = re.captures(s.trim()).unwrap(); + + Ok(Point3D { + x: caps[1].parse()?, + y: caps[2].parse()?, + z: caps[3].parse()?, + }) + } +} + +impl ops::AddAssign for Point3D { + fn add_assign(&mut self, rhs: Self) { + self.x += rhs.x; + self.y += rhs.y; + self.z += rhs.z; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_example() { + let input = " + + + +"; + let mut moons = parse_positions(input).unwrap(); + step(&mut moons); + dbg!(moons); + } +}