Skip to content

Instantly share code, notes, and snippets.

@qubyte
Last active January 1, 2019 13:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qubyte/f5a68779d4c7f14f2d722a5d13815bb4 to your computer and use it in GitHub Desktop.
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.
[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"
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