Compare commits

...

3 Commits

Author SHA1 Message Date
bd91fcb60c
day14: replace RE parser with nom consuming parser
All checks were successful
test / AoC 2024 (push) Successful in 3m6s
Almost 100x speedup on parsing phase avoiding RE compile, saves ~60ms
2024-12-16 14:45:23 -08:00
33615b015f
cargo: update and add nom, misc 2024-12-16 14:44:30 -08:00
b7a1f05b1e
utils: add misc and implement CustomBounded 2024-12-16 14:41:32 -08:00
6 changed files with 272 additions and 104 deletions

82
Cargo.lock generated
View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "ahash" name = "ahash"
@ -70,6 +70,8 @@ dependencies = [
"colored", "colored",
"grid", "grid",
"itertools", "itertools",
"misc",
"nom",
"rayon", "rayon",
"regex", "regex",
"rustc-hash", "rustc-hash",
@ -144,9 +146,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "colored" name = "colored"
version = "2.1.0" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"windows-sys", "windows-sys",
@ -154,9 +156,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.5" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [ dependencies = [
"crossbeam-epoch", "crossbeam-epoch",
"crossbeam-utils", "crossbeam-utils",
@ -173,9 +175,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.20" version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]] [[package]]
name = "darling" name = "darling"
@ -287,6 +289,29 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "misc"
version = "0.1.0"
dependencies = [
"num-traits",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@ -549,22 +574,23 @@ dependencies = [
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.48.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm",
"windows_aarch64_msvc", "windows_aarch64_msvc",
"windows_i686_gnu", "windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc", "windows_i686_msvc",
"windows_x86_64_gnu", "windows_x86_64_gnu",
"windows_x86_64_gnullvm", "windows_x86_64_gnullvm",
@ -573,45 +599,51 @@ dependencies = [
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.48.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.48.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.48.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.48.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.48.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.48.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.48.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"

View File

@ -1,7 +1,7 @@
[package] [package]
edition = "2021"
name = "aoc2024" name = "aoc2024"
version = "0.1.0" version = "0.1.0"
edition = "2021"
[dependencies] [dependencies]
aoc-runner = "0.3.0" aoc-runner = "0.3.0"
@ -10,8 +10,10 @@ atoi = "2.0.0"
bitflags = "2.6.0" bitflags = "2.6.0"
cached = "0.54.0" cached = "0.54.0"
colored = "2.1.0" colored = "2.1.0"
grid = { version = "0.1.0", path = "utils/grid" } grid = {version = "0.1.0", path = "utils/grid"}
itertools = "0.13.0" itertools = "0.13.0"
misc = {path = "utils/misc"}
nom = "7.1.3"
rayon = "1.10.0" rayon = "1.10.0"
regex = "1.11.1" regex = "1.11.1"
rustc-hash = "2.1.0" rustc-hash = "2.1.0"

View File

@ -1,12 +1,27 @@
use aoc_runner_derive::aoc; use aoc_runner_derive::aoc;
use colored::Colorize; use colored::Colorize;
use grid::{AsCoord2d, Coord2d, Grid}; use grid::{AsCoord2d, Coord2d, Grid};
use misc::CustomWrapped;
use nom::{
bytes::complete::tag,
character::complete::digit1,
combinator::{map_res, opt, recognize},
sequence::{preceded, separated_pair},
IResult,
};
use regex::Regex; use regex::Regex;
use std::str::FromStr; 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 +32,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)), |s| i64::from_str(s))(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 +70,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 +134,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)

25
utils/misc/Cargo.lock generated Normal file
View File

@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "misc"
version = "0.1.0"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]

7
utils/misc/Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "misc"
version = "0.1.0"
edition = "2021"
[dependencies]
num-traits = "0.2.19"

95
utils/misc/src/lib.rs Normal file
View File

@ -0,0 +1,95 @@
use num_traits::Signed;
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
#[derive(Eq, Clone, Copy)]
pub struct CustomWrapped<T: Signed + Copy> {
pub val: T,
pub bound: T,
}
impl<T: Signed + Copy> Add<T> for CustomWrapped<T> {
type Output = CustomWrapped<T>;
fn add(self, rhs: T) -> Self::Output {
Self {
val: ((self.val + rhs % self.bound) + self.bound) % self.bound,
bound: self.bound,
}
}
}
impl<T: Signed + Copy> Add<T> for &CustomWrapped<T> {
type Output = CustomWrapped<T>;
fn add(self, rhs: T) -> Self::Output {
CustomWrapped {
val: ((self.val + rhs % self.bound) + self.bound) % self.bound,
bound: self.bound,
}
}
}
impl<T: Signed + Copy> AddAssign<T> for CustomWrapped<T> {
fn add_assign(&mut self, rhs: T) {
self.val = ((self.val + rhs % self.bound) + self.bound) % self.bound
}
}
impl<T: Signed + Copy> CustomWrapped<T> {
pub fn new(val: T, bound: T) -> Self {
Self { val, bound }
}
}
impl<T: Signed + Copy + PartialEq> PartialEq for CustomWrapped<T> {
fn eq(&self, other: &Self) -> bool {
self.val.eq(&other.val)
}
}
impl<T: Signed + PartialOrd + Copy> PartialOrd for CustomWrapped<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.val.partial_cmp(&other.val)
}
}
impl<T: Signed + Ord + Copy> Ord for CustomWrapped<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.val.cmp(&other.val)
}
}
impl<T: Signed + PartialEq + Copy> PartialEq<T> for CustomWrapped<T> {
fn eq(&self, other: &T) -> bool {
self.val == *other
}
}
impl<T: Signed + PartialOrd + Copy> PartialOrd<T> for CustomWrapped<T> {
fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
self.val.partial_cmp(other)
}
}
impl<T: Display + Signed + Copy> Display for CustomWrapped<T> where T: Display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.val.fmt(f)
}
}
// impl<T> Into<T> for CustomWrapped<T> {
// fn into(self) -> T {
// self.val
// }
// }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}