Compare commits

...

2 Commits

Author SHA1 Message Date
5e8b974137
clippies!
All checks were successful
test / AoC 2024 (push) Successful in 1m44s
2024-12-16 14:54:48 -08:00
28a88e1aa7
day14: replace RE parser with nom consuming parser
Almost 100x speedup on parsing phase avoiding RE compile, saves ~60ms
2024-12-16 14:47:48 -08:00
4 changed files with 108 additions and 95 deletions

View File

@ -1,12 +1,26 @@
use aoc_runner_derive::aoc; use aoc_runner_derive::aoc;
use colored::Colorize; use colored::Colorize;
use grid::{AsCoord2d, Coord2d, Grid}; use grid::{AsCoord2d, Grid};
use regex::Regex; use misc::CustomWrapped;
use std::str::FromStr; use nom::{
bytes::complete::tag,
character::complete::digit1,
combinator::{map_res, opt, recognize},
sequence::{preceded, separated_pair},
IResult,
};
use std::{fmt::Display, str::FromStr};
type Coord = (CustomWrapped<i64>, CustomWrapped<i64>);
struct Robot { struct Robot {
pos: Coord2d, pos: Coord,
vel: Coord2d, vel: (i64, i64),
}
struct Robots {
robots: Vec<Robot>,
width: i64,
height: i64,
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
@ -17,52 +31,37 @@ enum Quadrant {
SE = 3, SE = 3,
} }
impl FromStr for Robot { fn nom_i64(input: &str) -> IResult<&str, i64> {
type Err = Box<dyn std::error::Error>; let (i, number) = map_res(recognize(preceded(opt(tag("-")), digit1)), i64::from_str)(input)?;
fn from_str(s: &str) -> Result<Self, Self::Err> { Ok((i, number))
let re = Regex::new(r"p=(\d+),(\d+) v=([+-]?\d+),([+-]?\d+)").unwrap(); }
match re.captures(s) { fn nom_i64_pair(input: &str) -> IResult<&str, (i64, i64)> {
Some(c) => Ok(Self { let (i, pair) = separated_pair(nom_i64, tag(","), nom_i64)(input)?;
pos: ( Ok((i, pair))
c.get(1).unwrap().as_str().parse::<i64>().unwrap(),
c.get(2).unwrap().as_str().parse().unwrap(),
)
.to_coord(),
vel: (
c.get(3).unwrap().as_str().parse::<i64>().unwrap(),
c.get(4).unwrap().as_str().parse().unwrap(),
)
.to_coord(),
}),
None => panic!(),
}
}
} }
impl Robot { impl Robot {
fn step(&mut self, bounds: (i64, i64)) { fn from_str(s: &str, bounds: (i64, i64)) -> Self {
let mut candidate_new_pos = ((self.pos.x() + self.vel.x()), (self.pos.y() + self.vel.y())); let (s, pos) = preceded(tag("p="), nom_i64_pair)(s).unwrap();
if candidate_new_pos.0 < 0 { let (_, vel) = preceded(tag(" v="), nom_i64_pair)(s).unwrap();
// if pos goes negative, add the upper bound Self {
candidate_new_pos.0 += bounds.0; pos: (CustomWrapped::new(pos.0, bounds.0), CustomWrapped::new(pos.1, bounds.1)),
vel,
} }
if candidate_new_pos.1 < 0 { }
candidate_new_pos.1 += bounds.1; fn step(&mut self, count: i64) {
} self.pos.0 += self.vel.x() * count;
candidate_new_pos.0 %= bounds.0; self.pos.1 += self.vel.y() * count;
candidate_new_pos.1 %= bounds.1;
self.pos = candidate_new_pos.to_coord();
} }
fn quad(&self, bounds: (i64, i64)) -> Option<Quadrant> { fn quad(&self, bounds: (i64, i64)) -> Option<Quadrant> {
let splits = (bounds.0 / 2, bounds.1 / 2); let splits = (bounds.0 / 2, bounds.1 / 2);
if self.pos.x() < splits.0 && self.pos.y() < splits.1 { if self.pos.0 < splits.0 && self.pos.1 < splits.1 {
Some(Quadrant::NW) Some(Quadrant::NW)
} else if self.pos.x() > splits.0 && self.pos.y() < splits.1 { } else if self.pos.0 > splits.0 && self.pos.1 < splits.1 {
Some(Quadrant::NE) Some(Quadrant::NE)
} else if self.pos.x() < splits.0 && self.pos.y() > splits.1 { } else if self.pos.0 < splits.0 && self.pos.1 > splits.1 {
Some(Quadrant::SW) Some(Quadrant::SW)
} else if self.pos.x() > splits.0 && self.pos.y() > splits.1 { } else if self.pos.0 > splits.0 && self.pos.1 > splits.1 {
Some(Quadrant::SE) Some(Quadrant::SE)
} else { } else {
None None
@ -70,49 +69,58 @@ impl Robot {
} }
} }
#[allow(dead_code)] impl Robots {
fn display(robots: &Vec<Robot>, bounds: (i64, i64)) { fn from_vec(robots: Vec<Robot>, width: i64, height: i64) -> Self {
let grid = as_grid(robots, bounds); Self { robots, width, height }
for row in 0..grid.height() { }
for col in 0..grid.width() { fn as_grid(&self) -> Grid<usize> {
print!( let mut grid = Grid::with_shape(self.width as usize, self.height as usize, 0usize);
"{}", for r in &self.robots {
if *grid.get(&(col, row)).unwrap() != 0 { grid.increment(&(r.pos.0.val, r.pos.1.val), 1usize);
"".green() }
} else { grid
" ".color(colored::Color::Black) }
} fn count_quads(&self) -> [u64; 4] {
); let mut counts = [0; 4];
for r in &self.robots {
if let Some(q) = r.quad((self.width, self.height)) {
counts[q as usize] += 1
}
}
counts
}
fn step(&mut self, count: i64) {
for robot in &mut self.robots {
robot.step(count)
} }
println!();
} }
} }
fn as_grid(robots: &Vec<Robot>, bounds: (i64, i64)) -> Grid<usize> { impl Display for Robots {
let mut grid = Grid::with_shape(bounds.0 as usize, bounds.1 as usize, 0); fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for r in robots { let grid = self.as_grid();
grid.increment(&r.pos, 1usize); for row in 0..grid.height() {
for col in 0..grid.width() {
if *grid.get(&(col, row)).unwrap() != 0 {
"".green().fmt(f)?;
} else {
" ".color(colored::Color::Black).fmt(f)?;
}
}
writeln!(f)?
}
Ok(())
} }
grid
} }
fn parse(input: &str) -> Vec<Robot> { fn parse(input: &str, width: i64, height: i64) -> Vec<Robot> {
input.lines().map(|l| l.parse().unwrap()).collect() input.lines().map(|l| Robot::from_str(l, (width, height))).collect()
} }
fn part1_impl(input: &str, width: i64, height: i64) -> u64 { fn part1_impl(input: &str, width: i64, height: i64) -> u64 {
let mut robots = parse(input); let mut robots = Robots::from_vec(parse(input, width, height), width, height);
for _ in 0..100 { robots.step(100);
for r in &mut robots { let counts = robots.count_quads();
r.step((width, height))
}
}
let mut counts = [0; 4];
for r in robots {
if let Some(q) = r.quad((width, height)) {
counts[q as usize] += 1
}
}
counts.iter().product() counts.iter().product()
} }
@ -125,13 +133,11 @@ pub fn part1(input: &str) -> u64 {
pub fn part2(input: &str) -> u64 { pub fn part2(input: &str) -> u64 {
let width = 101; let width = 101;
let height = 103; let height = 103;
let mut robots = parse(input); let mut robots = Robots::from_vec(parse(input, width, height), width, height);
for i in 1.. { for i in 1.. {
for r in &mut robots { robots.step(1);
r.step((width, height))
}
// collect into lines // collect into lines
let g = as_grid(&robots, (width, height)); let g = robots.as_grid();
if g.data if g.data
.chunk_by(|a, b| *a != 0 && *b != 0) .chunk_by(|a, b| *a != 0 && *b != 0)
.filter(|c| !c.is_empty() && c[0] != 0) .filter(|c| !c.is_empty() && c[0] != 0)

View File

@ -16,7 +16,7 @@ impl Display for Warehouse {
impl Warehouse { impl Warehouse {
fn step_robot(&mut self, dir: Move) { fn step_robot(&mut self, dir: Move) {
let start = self.robot_pos.clone(); let start = self.robot_pos;
if self.push(&start, &dir) { if self.push(&start, &dir) {
self.robot_pos = &self.robot_pos + dir.ofs(); self.robot_pos = &self.robot_pos + dir.ofs();
} }
@ -40,12 +40,12 @@ impl Warehouse {
// move both parts // move both parts
self.push(&target, dir); self.push(&target, dir);
self.push(&(&target + (-1, 0)), dir); self.push(&(&target + (-1, 0)), dir);
self.map.swap(&target, pos); self.map.swap(target, pos);
} }
b'[' => { b'[' => {
self.push(&target, dir); self.push(&target, dir);
self.push(&(&target + (1, 0)), dir); self.push(&(&target + (1, 0)), dir);
self.map.swap(&target, pos); self.map.swap(target, pos);
} }
c => panic!("unexpected char {}", c), c => panic!("unexpected char {}", c),
} }

View File

@ -1,9 +1,9 @@
use aoc_runner_derive::aoc; use aoc_runner_derive::aoc;
use grid::Grid; use grid::Grid;
use std::{ use std::{
cmp::Ordering,
collections::{BinaryHeap, HashMap}, collections::{BinaryHeap, HashMap},
str::FromStr, str::FromStr,
usize,
}; };
type CoordType = i16; type CoordType = i16;
@ -73,7 +73,7 @@ impl Ord for PathState {
} }
impl PartialOrd for PathState { impl PartialOrd for PathState {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.state.partial_cmp(&other.state) Some(self.cmp(other))
} }
} }
@ -177,14 +177,18 @@ impl Maze {
continue; continue;
} }
if state.position == finish { if state.position == finish {
if state.cost < best_cost { match state.cost.cmp(&best_cost) {
path.push(state.position); Ordering::Less => {
best_paths.clear(); path.push(state.position);
best_paths.push(path); best_paths.clear();
best_cost = state.cost best_paths.push(path);
} else if state.cost == best_cost { best_cost = state.cost
path.push(state.position); }
best_paths.push(path); Ordering::Equal => {
path.push(state.position);
best_paths.push(path);
}
_ => {}
} }
continue; continue;
} }
@ -204,7 +208,7 @@ impl Maze {
} }
} }
} }
return (best_cost, best_paths); (best_cost, best_paths)
} }
} }

View File

@ -1,6 +1,6 @@
use num_traits::Signed; use num_traits::Signed;
use std::fmt::Display;
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use std::fmt::{Debug, Display};
/// Wrapped signed integer with custom upper bound with wrapping of 0s to the upper bound /// Wrapped signed integer with custom upper bound with wrapping of 0s to the upper bound
#[derive(Eq, Clone, Copy)] #[derive(Eq, Clone, Copy)]
@ -71,7 +71,10 @@ impl<T: Signed + PartialOrd + Copy> PartialOrd<T> for CustomWrapped<T> {
} }
} }
impl<T: Display + Signed + Copy> Display for CustomWrapped<T> where T: Display { impl<T: Display + Signed + Copy> Display for CustomWrapped<T>
where
T: Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.val.fmt(f) self.val.fmt(f)
} }