From 8f88266bd0890ae8e238ab108d16dba9fea86bfd Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Thu, 19 Dec 2019 22:38:15 +0200 Subject: Day 12 part 1 --- inputs/day_12.txt | 4 ++ src/bin/day_12.rs | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 inputs/day_12.txt create mode 100644 src/bin/day_12.rs diff --git a/inputs/day_12.txt b/inputs/day_12.txt new file mode 100644 index 0000000..7e72a58 --- /dev/null +++ b/inputs/day_12.txt @@ -0,0 +1,4 @@ + + + + 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 = stdin + .lock() + .lines() + .map(|x| exit_on_failed_assertion(x, "Error reading input")) + .map(|x| exit_on_failed_assertion(x.parse::(), "Input was not a valid planet")) + .collect(); + + println!("{}", energy(simulate_planets_n_iterations(planets, opt.n))); +} + +fn exit_on_failed_assertion(data: Result, message: &str) -> A { + match data { + Ok(data) => data, + Err(e) => { + eprintln!("{}: {}", message, e); + process::exit(1); + } + } +} + +fn energy(planets: Vec) -> i32 { + planets.into_iter().map(|p| p.energy()).sum() +} + +fn simulate_planets_n_iterations(planets: Vec, n: usize) -> Vec { + 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) -> Vec { + simulate_velocity(simulate_gravity(planets)) +} + +fn simulate_gravity(planets: Vec) -> Vec { + 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) -> Vec { + 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 { + s.replace(|c| "<>xyz= ".contains(c), "") + .split(',') + .map(|i| i.parse::()) + .collect::, _>>() + .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::() + .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 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, + } + } +} -- cgit v1.2.3