diff options
author | cos <cos> | 2019-12-12 16:40:24 +0100 |
---|---|---|
committer | cos <cos> | 2019-12-12 16:40:24 +0100 |
commit | 043c1a447fe611c54cf414e743dd521a2e0088e7 (patch) | |
tree | 32a96f8b02efa47f598088241ece91262ca4f0c7 /2019/rust/day12 | |
parent | 1e38aeed2565578b98bbb60b6a4d46ad0739d915 (diff) | |
download | adventofcode-043c1a447fe611c54cf414e743dd521a2e0088e7.zip |
Add day12, 2019
Diffstat (limited to '2019/rust/day12')
-rw-r--r-- | 2019/rust/day12/Cargo.toml | 9 | ||||
-rwxr-xr-x | 2019/rust/day12/both_parts.sh | 2 | ||||
-rw-r--r-- | 2019/rust/day12/src/main.rs | 154 |
3 files changed, 165 insertions, 0 deletions
diff --git a/2019/rust/day12/Cargo.toml b/2019/rust/day12/Cargo.toml new file mode 100644 index 0000000..c2357ee --- /dev/null +++ b/2019/rust/day12/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "day12" +version = "0.1.0" +authors = ["cos <cos>"] +edition = "2018" + +[dependencies] +regex = "1" +num = "0" diff --git a/2019/rust/day12/both_parts.sh b/2019/rust/day12/both_parts.sh new file mode 100755 index 0000000..853e985 --- /dev/null +++ b/2019/rust/day12/both_parts.sh @@ -0,0 +1,2 @@ +../target/release/day12 < input + diff --git a/2019/rust/day12/src/main.rs b/2019/rust/day12/src/main.rs new file mode 100644 index 0000000..8653f36 --- /dev/null +++ b/2019/rust/day12/src/main.rs @@ -0,0 +1,154 @@ +use num::integer::Integer; +use regex::Regex; +use std::collections::HashSet; +use std::io::{self, BufRead}; + +type CoordType = i32; +type EnergyType = i32; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +struct Coords { + x: CoordType, + y: CoordType, + z: CoordType, +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +struct Moon { + pos: Coords, + vel: Coords, +} + +fn read_input() -> Vec<Coords> { + let stdin = io::stdin(); + let re = Regex::new(r"<x=(?P<x>[0-9-]*), *y=(?P<y>[0-9-]*), *z=(?P<z>[0-9-]*)"). + expect("Regex failed to compile"); + + stdin.lock().lines() + .map(|line| { + match line { + Ok(l) => { + let caps = match re.captures(&l) { + Some(c) => c, + _ => panic!("Regex issue for line: {:?}", l), + }; + Coords { + x: caps["x"].parse().unwrap(), + y: caps["y"].parse().unwrap(), + z: caps["z"].parse().unwrap(), + } + }, + _ => panic!("Failed to parse line: {:?}", line), + } + }).collect() +} + +// 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. However, if the positions on a +// given axis are the same, the velocity on that axis does not change for that pair of moons. +fn apply_gravity(this: &mut Moon, other: &Moon) { + fn vel_change(a: CoordType, b: CoordType) -> CoordType { + if a < b { 1 } else if a > b { -1 } else { 0 } + } + + this.vel.x += vel_change(this.pos.x, other.pos.x); + this.vel.y += vel_change(this.pos.y, other.pos.y); + this.vel.z += vel_change(this.pos.z, other.pos.z); +} + +// Once all gravity has been applied, apply velocity: simply add the velocity of each moon to its +// own position. +fn apply_velocity(moon: &mut Moon) { + moon.pos.x += moon.vel.x; + moon.pos.y += moon.vel.y; + moon.pos.z += moon.vel.z; +} + +// A moon's potential energy is the sum of the absolute values of its x, y, and z position +// coordinates. +fn potential_energy(moon: &Moon) -> EnergyType { + moon.pos.x.abs() + moon.pos.y.abs() + moon.pos.z.abs() +} + +// A moon's kinetic energy is the sum of the absolute values of its velocity coordinates. Below, +// each line shows the calculations for a moon's potential energy (pot), kinetic energy (kin), and +// total energy: +fn kinetic_energy(moon: &Moon) -> EnergyType { + moon.vel.x.abs() + moon.vel.y.abs() + moon.vel.z.abs() +} + +fn print_moon(moon: &Moon) { + println!("pos=<x={:2}, y={:2}, z={:2}>, vel=<x={:2}, y={:2}, z={:2}>", + moon.pos.x, moon.pos.y, moon.pos.z, + moon.vel.x, moon.vel.y, moon.vel.z,); +} + +fn iterations_until_repeat(mut moons: Vec<Moon>) -> usize{ + let mut seen = HashSet::new(); + seen.insert(moons.clone()); + + for n in 1.. { + let old_moons = moons.clone(); + for (i, moon) in moons.iter_mut().enumerate() { + for (j, old) in old_moons.iter().enumerate() { + if i != j { apply_gravity(moon, old) }; + } + } + moons.iter_mut().map(|moon| apply_velocity(moon)).last(); + if ! seen.insert(moons.clone()) { + return n; + } + } + 0 +} + +fn main() { + let coords = read_input(); + let mut moons: Vec<Moon> = coords.iter() + .map(|pos| Moon { pos: *pos, vel: Coords { x: 0, y: 0, z: 0 }}).collect(); + + // Simulate the motion of the moons in time steps. 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. Time progresses by one step + // once all of the positions are updated. + for _ in 0..std::env::args().nth(1).unwrap().parse::<i128>().unwrap() { + let old_moons = moons.clone(); + for (i, moon) in moons.iter_mut().enumerate() { + for (j, old) in old_moons.iter().enumerate() { + if i != j { apply_gravity(moon, old) }; + } + } + moons.iter_mut().map(|moon| apply_velocity(moon)).last(); + } + + // Then, it might help to calculate the total energy in the system. The total energy for a + // single moon is its potential energy multiplied by its kinetic energy. + let energy = moons.iter() + .map(|moon| potential_energy(moon) * kinetic_energy(moon)).sum::<EnergyType>(); + + // What is the total energy in the system after simulating the moons given in your scan for + // 1000 steps? + println!("Total energy: {}", energy); + + // Determine the number of steps that must occur before all of the moons' positions and + // velocities exactly match a previous point in time. + let moons_x: Vec<Moon> = moons.iter().map(|m| Moon { + pos: Coords { x: m.pos.x, y: 0, z: 0}, + vel: Coords { x: m.vel.x, y: 0, z: 0}, + }).collect(); + let moons_y: Vec<Moon> = moons.iter().map(|m| Moon { + pos: Coords { x: 0, y: m.pos.y, z: 0}, + vel: Coords { x: 0, y: m.vel.y, z: 0}, + }).collect(); + let moons_z: Vec<Moon> = moons.iter().map(|m| Moon { + pos: Coords { x: 0, y: 0, z: m.pos.z }, + vel: Coords { x: 0, y: 0, z: m.vel.z }, + }).collect(); + let repeat_x = iterations_until_repeat(moons_x); + let repeat_y = iterations_until_repeat(moons_y); + let repeat_z = iterations_until_repeat(moons_z); + // Clearly, you might need to find a more efficient way to simulate the universe. + let repeat = repeat_z.lcm(&repeat_x.lcm(&repeat_y)); + + println!("Iterations until repeat: {}", repeat); +} |