Last active
January 1, 2019 13:03
-
-
Save qubyte/f5a68779d4c7f14f2d722a5d13815bb4 to your computer and use it in GitHub Desktop.
My solution to Advent of code 2018 day 10 (both parts) in Rust.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "task-1" | |
version = "0.1.0" | |
authors = ["Mark S. Everitt <mark.s.everitt@gmail.com>"] | |
edition = "2018" | |
[dependencies] | |
regex = "1" | |
lazy_static = "1.2.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{ | |
io::{stdin, BufRead}, | |
cmp::{max, min}, | |
str::FromStr, | |
fmt::{self, Display, Formatter}, | |
error::Error | |
}; | |
use regex::Regex; | |
use lazy_static::lazy_static; | |
const IMAX: isize = isize::max_value(); | |
const IMIN: isize = isize::min_value(); | |
const PARTICLE_REGEX: &str = r"<\s*(-?\d+),\s*(-?\d+)>.*<\s*(-?\d+),\s*(-?\d)>"; | |
#[derive(Debug, Clone)] | |
struct ParseParticleError; | |
impl Display for ParseParticleError { | |
fn fmt(&self, f: &mut Formatter) -> fmt::Result { | |
write!(f, "Unable to parse to a Particle.") | |
} | |
} | |
impl Error for ParseParticleError { | |
fn description(&self) -> &str { | |
"Unable to parse to a Particle." | |
} | |
fn cause(&self) -> Option<&Error> { | |
None | |
} | |
} | |
impl From<std::num::ParseIntError> for ParseParticleError { | |
fn from(_error: std::num::ParseIntError) -> Self { | |
ParseParticleError | |
} | |
} | |
#[derive(Debug, Clone)] | |
struct Particle { | |
position_x: isize, | |
position_y: isize, | |
velocity_x: isize, | |
velocity_y: isize | |
} | |
impl FromStr for Particle { | |
type Err = ParseParticleError; | |
fn from_str(particle_str: &str) -> Result<Self, Self::Err> { | |
lazy_static! { | |
static ref regex: Regex = Regex::new(PARTICLE_REGEX).unwrap(); | |
} | |
regex.captures(particle_str) | |
.ok_or(ParseParticleError) | |
.and_then(|cap| Ok(Particle { | |
position_x: cap[1].parse()?, | |
position_y: cap[2].parse()?, | |
velocity_x: cap[3].parse()?, | |
velocity_y: cap[4].parse()? | |
})) | |
} | |
} | |
fn tick(particles: &Vec<Particle>) -> Vec<Particle> { | |
particles.iter().map(|particle| Particle { | |
position_x: particle.position_x + particle.velocity_x, | |
position_y: particle.position_y + particle.velocity_y, | |
velocity_x: particle.velocity_x, | |
velocity_y: particle.velocity_y | |
}).collect() | |
} | |
fn get_bounds(particles: &Vec<Particle>) -> (isize, isize, isize, isize) { | |
particles.iter().fold((IMAX, IMAX, IMIN, IMIN), |(least_x, least_y, most_x, most_y), particle| { | |
let px = particle.position_x; | |
let py = particle.position_y; | |
(min(least_x, px), min(least_y, py), max(most_x, px), max(most_y, py)) | |
}) | |
} | |
fn render(particles: &Vec<Particle>) -> String { | |
let (least_x, least_y, most_x, most_y) = get_bounds(particles); | |
let mut message = String::from(""); | |
for j in least_y..=most_y { | |
for i in least_x..=most_x { | |
if particles.iter().any(|p| p.position_x == i && p.position_y == j) { | |
message.push_str("#"); | |
} else { | |
message.push_str("-"); | |
} | |
} | |
message.push_str("\n"); | |
} | |
message | |
} | |
fn main() { | |
let mut particles: Vec<Particle> = stdin() | |
.lock() | |
.lines() | |
.filter_map(|line| line.ok()) | |
.filter_map(|line| line.parse().ok()) | |
.collect(); | |
let mut seconds = 0; | |
// I assume the message is when the particles occupy the tightest bounding | |
// box (they have converged). This is not true for some known inputs. A | |
// better approach might be to look for lines in the grid of particles. | |
let message = loop { | |
let (least_x, least_y, most_x, most_y) = get_bounds(&particles); | |
let last_area = (most_y - least_y) * (most_x - least_x); | |
let last_particles = particles; | |
seconds += 1; | |
particles = tick(&last_particles); | |
let (least_x, least_y, most_x, most_y) = get_bounds(&particles); | |
let area = (most_y - least_y) * (most_x - least_x); | |
if last_area > 0 && last_area < area { | |
break render(&last_particles); | |
} | |
}; | |
println!("Second {}", seconds - 1); | |
println!("{}", message); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment