Completed day 12

This commit is contained in:
Tyler Hallada 2020-01-11 23:09:39 -05:00
parent 79645abe5a
commit b189630416
6 changed files with 585 additions and 0 deletions

143
day12/Cargo.lock generated Normal file
View File

@ -0,0 +1,143 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "day12"
version = "0.1.0"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num-complex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-bigint"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-complex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-iter"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-rational"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "thread_local"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223"
"checksum num 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36"
"checksum num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f115de20ad793e857f76da2563ff4a09fbcfd6fe93cca0c5d996ab5f3ee38d"
"checksum num-complex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
"checksum num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00"
"checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3"
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
"checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87"
"checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90"
"checksum thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88ddf1ad580c7e3d1efff877d972bcc93f995556b9087a5a259630985c88ceab"

12
day12/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "day12"
version = "0.1.0"
authors = ["Tyler Hallada <tyler@hallada.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lazy_static = "1.4.0"
num = "0.2.1"
regex = "1.3.3"

4
day12/input/input.txt Normal file
View File

@ -0,0 +1,4 @@
<x=3, y=2, z=-6>
<x=-13, y=18, z=10>
<x=-8, y=-1, z=13>
<x=5, y=10, z=4>

4
day12/input/test1.txt Normal file
View File

@ -0,0 +1,4 @@
<x=-1, y=0, z=2>
<x=2, y=-10, z=-7>
<x=4, y=-8, z=8>
<x=3, y=5, z=-1>

4
day12/input/test2.txt Normal file
View File

@ -0,0 +1,4 @@
<x=-8, y=-10, z=0>
<x=5, y=5, z=10>
<x=2, y=-7, z=3>
<x=9, y=-8, z=-3>

418
day12/src/main.rs Normal file
View File

@ -0,0 +1,418 @@
#[macro_use]
extern crate lazy_static;
use std::collections::HashSet;
use std::error::Error;
use std::fs::File;
use std::io::{prelude::*, BufReader};
use std::ops::AddAssign;
use std::ops::Index;
use std::result;
use std::str::FromStr;
use num::integer::lcm;
use regex::Regex;
type Result<T> = result::Result<T, Box<dyn Error>>;
const INPUT: &str = "input/input.txt";
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
struct Vector {
x: i64,
y: i64,
z: i64,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
struct Body {
position: Vector,
velocity: Vector,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
struct NBody {
bodies: Vec<Body>,
}
impl FromStr for Body {
type Err = Box<dyn Error>;
fn from_str(s: &str) -> Result<Body> {
lazy_static! {
static ref RE: Regex =
Regex::new(r"<x=(?P<x>-?\d+), y=(?P<y>-?\d+), z=(?P<z>-?\d+)>").unwrap();
}
let captures = match RE.captures(s) {
None => {
return Err(From::from("Malformed scan, no positions could be found"));
}
Some(captures) => captures,
};
Ok(Body {
position: Vector {
x: captures["x"].parse()?,
y: captures["y"].parse()?,
z: captures["z"].parse()?,
},
velocity: Vector::new(),
})
}
}
impl Vector {
fn new() -> Vector {
Vector { x: 0, y: 0, z: 0 }
}
}
impl AddAssign for Vector {
fn add_assign(&mut self, other: Self) {
*self = Self {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl Index<&str> for Vector {
type Output = i64;
fn index(&self, index: &str) -> &i64 {
match index {
"x" => &self.x,
"y" => &self.y,
"z" => &self.z,
_ => panic!("unknown field: {}", index),
}
}
}
impl Body {
fn add_gravity(&self, gravity: &mut Vector, other: &Self) {
if self.position.x > other.position.x {
gravity.x -= 1;
} else if self.position.x < other.position.x {
gravity.x += 1;
}
if self.position.y > other.position.y {
gravity.y -= 1;
} else if self.position.y < other.position.y {
gravity.y += 1;
}
if self.position.z > other.position.z {
gravity.z -= 1;
} else if self.position.z < other.position.z {
gravity.z += 1;
}
}
}
impl NBody {
fn run_step(&mut self) {
let mut gravities = Vec::new();
for body in self.bodies.iter() {
let mut gravity = Vector::new();
for other_body in self.bodies.iter() {
body.add_gravity(&mut gravity, other_body);
}
gravities.push(gravity);
}
for (index, gravity) in gravities.into_iter().enumerate() {
self.bodies[index].velocity += gravity;
let velocity = self.bodies[index].velocity;
self.bodies[index].position += velocity;
}
}
fn total_energy(&self) -> i64 {
let mut total_energy = 0;
for body in self.bodies.iter() {
let potential_energy =
body.position.x.abs() + body.position.y.abs() + body.position.z.abs();
let kinetic_energy =
body.velocity.x.abs() + body.velocity.y.abs() + body.velocity.z.abs();
total_energy += potential_energy * kinetic_energy;
}
total_energy
}
fn state(&self, component: &str) -> [(i64, i64); 4] {
[
(
self.bodies[0].position[component],
self.bodies[0].velocity[component],
),
(
self.bodies[1].position[component],
self.bodies[1].velocity[component],
),
(
self.bodies[2].position[component],
self.bodies[2].velocity[component],
),
(
self.bodies[3].position[component],
self.bodies[3].velocity[component],
),
]
}
}
fn read_moon_scan(filename: &str) -> Result<NBody> {
let file = File::open(filename)?;
let reader = BufReader::new(file);
let mut moons = vec![];
for line in reader.lines() {
moons.push(line?.parse()?);
}
Ok(NBody { bodies: moons })
}
fn solve_part1(filename: &str) -> Result<i64> {
let mut nbody = read_moon_scan(filename)?;
for _ in 0..1000 {
nbody.run_step();
}
Ok(nbody.total_energy())
}
fn solve_part2(filename: &str) -> Result<u64> {
let mut step_count = 0;
let mut x_states: HashSet<[(i64, i64); 4]> = HashSet::new();
let mut y_states: HashSet<[(i64, i64); 4]> = HashSet::new();
let mut z_states: HashSet<[(i64, i64); 4]> = HashSet::new();
let mut x_repeated_step_count = None;
let mut y_repeated_step_count = None;
let mut z_repeated_step_count = None;
let mut nbody = read_moon_scan(filename)?;
while x_repeated_step_count == None
|| y_repeated_step_count == None
|| z_repeated_step_count == None
{
if x_repeated_step_count == None {
let x_state = nbody.state("x");
if x_states.contains(&x_state) {
x_repeated_step_count = Some(step_count);
} else {
x_states.insert(x_state);
}
}
if y_repeated_step_count == None {
let y_state = nbody.state("y");
if y_states.contains(&y_state) {
y_repeated_step_count = Some(step_count);
} else {
y_states.insert(y_state);
}
}
if z_repeated_step_count == None {
let z_state = nbody.state("z");
if z_states.contains(&z_state) {
z_repeated_step_count = Some(step_count);
} else {
z_states.insert(z_state);
}
}
nbody.run_step();
step_count += 1;
}
Ok(lcm(
x_repeated_step_count.unwrap(),
lcm(
y_repeated_step_count.unwrap(),
z_repeated_step_count.unwrap(),
),
))
}
fn main() -> Result<()> {
println!("Part 1: {}", solve_part1(INPUT)?);
println!("Part 2: {}", solve_part2(INPUT)?);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_INPUT1: &str = "input/test1.txt";
const TEST_INPUT2: &str = "input/test2.txt";
fn nbody_1() -> NBody {
NBody {
bodies: vec![
Body {
position: Vector { x: -1, y: 0, z: 2 },
velocity: Vector { x: 0, y: 0, z: 0 },
},
Body {
position: Vector {
x: 2,
y: -10,
z: -7,
},
velocity: Vector { x: 0, y: 0, z: 0 },
},
Body {
position: Vector { x: 4, y: -8, z: 8 },
velocity: Vector { x: 0, y: 0, z: 0 },
},
Body {
position: Vector { x: 3, y: 5, z: -1 },
velocity: Vector { x: 0, y: 0, z: 0 },
},
],
}
}
fn nbody_1_after_10_steps() -> NBody {
NBody {
bodies: vec![
Body {
position: Vector { x: 2, y: 1, z: -3 },
velocity: Vector { x: -3, y: -2, z: 1 },
},
Body {
position: Vector { x: 1, y: -8, z: 0 },
velocity: Vector { x: -1, y: 1, z: 3 },
},
Body {
position: Vector { x: 3, y: -6, z: 1 },
velocity: Vector { x: 3, y: 2, z: -3 },
},
Body {
position: Vector { x: 2, y: 0, z: 4 },
velocity: Vector { x: 1, y: -1, z: -1 },
},
],
}
}
fn nbody_2() -> NBody {
NBody {
bodies: vec![
Body {
position: Vector {
x: -8,
y: -10,
z: 0,
},
velocity: Vector { x: 0, y: 0, z: 0 },
},
Body {
position: Vector { x: 5, y: 5, z: 10 },
velocity: Vector { x: 0, y: 0, z: 0 },
},
Body {
position: Vector { x: 2, y: -7, z: 3 },
velocity: Vector { x: 0, y: 0, z: 0 },
},
Body {
position: Vector { x: 9, y: -8, z: -3 },
velocity: Vector { x: 0, y: 0, z: 0 },
},
],
}
}
fn nbody_2_after_100_steps() -> NBody {
NBody {
bodies: vec![
Body {
position: Vector {
x: 8,
y: -12,
z: -9,
},
velocity: Vector { x: -7, y: 3, z: 0 },
},
Body {
position: Vector {
x: 13,
y: 16,
z: -3,
},
velocity: Vector {
x: 3,
y: -11,
z: -5,
},
},
Body {
position: Vector {
x: -29,
y: -11,
z: -1,
},
velocity: Vector { x: -3, y: 7, z: 4 },
},
Body {
position: Vector {
x: 16,
y: -13,
z: 23,
},
velocity: Vector { x: 7, y: 1, z: 1 },
},
],
}
}
#[test]
fn reads_moon_scan_file() {
assert_eq!(read_moon_scan(TEST_INPUT1).unwrap(), nbody_1());
assert_eq!(read_moon_scan(TEST_INPUT2).unwrap(), nbody_2());
}
#[test]
fn runs_10_steps() {
let mut nbody = read_moon_scan(TEST_INPUT1).unwrap();
for _ in 0..10 {
nbody.run_step();
}
assert_eq!(nbody, nbody_1_after_10_steps());
}
#[test]
fn runs_100_steps() {
let mut nbody = read_moon_scan(TEST_INPUT2).unwrap();
for _ in 0..100 {
nbody.run_step();
}
assert_eq!(nbody, nbody_2_after_100_steps());
}
#[test]
fn calculates_total_energy_after_10_steps() {
let mut nbody = read_moon_scan(TEST_INPUT1).unwrap();
for _ in 0..10 {
nbody.run_step();
}
assert_eq!(nbody.total_energy(), 179);
}
#[test]
fn calculates_total_energy_after_100_steps() {
let mut nbody = read_moon_scan(TEST_INPUT2).unwrap();
for _ in 0..100 {
nbody.run_step();
}
assert_eq!(nbody.total_energy(), 1940);
}
#[test]
fn finds_repeated_states() {
assert_eq!(solve_part2(TEST_INPUT1).unwrap(), 2772);
assert_eq!(solve_part2(TEST_INPUT2).unwrap(), 4686774924);
}
}