summaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
authorJustin Wernick <justin@worthe-it.co.za>2019-12-19 22:38:15 +0200
committerJustin Wernick <justin@worthe-it.co.za>2019-12-19 22:38:15 +0200
commit8f88266bd0890ae8e238ab108d16dba9fea86bfd (patch)
treeaa6825adfc06b39677da8f7b06318e4221c9952b /src/bin
parent50c67c8cbe1cdccea28359eeea0280759931469b (diff)
Day 12 part 1
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/day_12.rs158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/bin/day_12.rs b/src/bin/day_12.rs
new file mode 100644
index 0000000..678653b
--- /dev/null
+++ b/src/bin/day_12.rs
@@ -0,0 +1,158 @@
+use rpds::vector::Vector;
+use std::fmt;
+use std::io;
+use std::io::prelude::*;
+use std::iter;
+use std::num::ParseIntError;
+use std::ops;
+use std::process;
+use std::str::FromStr;
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "Day 12: The N-Body Problem")]
+/// Simulates N bodies, physically interacting
+///
+/// See https://adventofcode.com/2019/day/12 for details.
+struct Opt {
+ #[structopt(short = "n")]
+ n: usize,
+}
+
+fn main() {
+ let stdin = io::stdin();
+ let opt = Opt::from_args();
+
+ let planets: Vec<Planet> = stdin
+ .lock()
+ .lines()
+ .map(|x| exit_on_failed_assertion(x, "Error reading input"))
+ .map(|x| exit_on_failed_assertion(x.parse::<Planet>(), "Input was not a valid planet"))
+ .collect();
+
+ println!("{}", energy(simulate_planets_n_iterations(planets, opt.n)));
+}
+
+fn exit_on_failed_assertion<A, E: std::error::Error>(data: Result<A, E>, message: &str) -> A {
+ match data {
+ Ok(data) => data,
+ Err(e) => {
+ eprintln!("{}: {}", message, e);
+ process::exit(1);
+ }
+ }
+}
+
+fn energy(planets: Vec<Planet>) -> i32 {
+ planets.into_iter().map(|p| p.energy()).sum()
+}
+
+fn simulate_planets_n_iterations(planets: Vec<Planet>, n: usize) -> Vec<Planet> {
+ iter::successors(Some((0, planets)), |(i, planets)| {
+ Some((i + 1, simulate_planets(planets.clone())))
+ })
+ .find(|(i, _)| *i == n)
+ .unwrap()
+ .1
+}
+
+fn simulate_planets(planets: Vec<Planet>) -> Vec<Planet> {
+ simulate_velocity(simulate_gravity(planets))
+}
+
+fn simulate_gravity(planets: Vec<Planet>) -> Vec<Planet> {
+ planets
+ .iter()
+ .map(|p| Planet {
+ pos: p.pos.clone(),
+ vel: planets
+ .iter()
+ .filter(|o| p != *o)
+ .map(|o| p.gravity_pull(o))
+ .fold(p.vel, |acc, next| acc + next),
+ })
+ .collect()
+}
+
+fn simulate_velocity(planets: Vec<Planet>) -> Vec<Planet> {
+ planets
+ .into_iter()
+ .map(|p| Planet {
+ pos: p.pos + p.vel,
+ vel: p.vel,
+ })
+ .collect()
+}
+
+#[derive(Debug, Default, Clone, PartialEq)]
+struct Planet {
+ pos: Vec3d,
+ vel: Vec3d,
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+struct Vec3d {
+ x: i32,
+ y: i32,
+ z: i32,
+}
+
+impl FromStr for Planet {
+ type Err = ParseIntError;
+
+ fn from_str(s: &str) -> Result<Self, ParseIntError> {
+ s.replace(|c| "<>xyz= ".contains(c), "")
+ .split(',')
+ .map(|i| i.parse::<i32>())
+ .collect::<Result<Vec<_>, _>>()
+ .and_then(|v| match &v[..] {
+ [x, y, z] => Ok(Planet {
+ pos: Vec3d {
+ x: *x,
+ y: *y,
+ z: *z,
+ },
+ vel: Vec3d::default(),
+ }),
+ _ => "wrong number of fields"
+ .parse::<i32>()
+ .map(|x| Planet::default()),
+ })
+ }
+}
+
+impl Planet {
+ fn gravity_pull(&self, other: &Planet) -> Vec3d {
+ Vec3d {
+ x: gravity(self.pos.x, other.pos.x),
+ y: gravity(self.pos.y, other.pos.y),
+ z: gravity(self.pos.z, other.pos.z),
+ }
+ }
+
+ fn energy(&self) -> i32 {
+ (self.pos.x.abs() + self.pos.y.abs() + self.pos.z.abs())
+ * (self.vel.x.abs() + self.vel.y.abs() + self.vel.z.abs())
+ }
+}
+
+fn gravity(this: i32, that: i32) -> i32 {
+ if that > this {
+ 1
+ } else if this > that {
+ -1
+ } else {
+ 0
+ }
+}
+
+impl ops::Add<Vec3d> for Vec3d {
+ type Output = Vec3d;
+ fn add(self, rhs: Vec3d) -> Self::Output {
+ Vec3d {
+ x: self.x + rhs.x,
+ y: self.y + rhs.y,
+ z: self.z + rhs.z,
+ }
+ }
+}