initial commit

This commit is contained in:
2023-11-15 18:38:04 +01:00
commit 0684289592
13 changed files with 543 additions and 0 deletions

9
src/lib.rs Normal file
View File

@@ -0,0 +1,9 @@
pub mod util {
pub mod grid;
pub mod index;
pub mod parse;
}
pub mod year2016 {
pub mod day01;
}

69
src/main.rs Normal file
View File

@@ -0,0 +1,69 @@
use std::time::Instant;
use aoc::util::parse::ParseExt;
struct Solution {
year: u32,
day: u32,
input: &'static str,
run: fn(&str) -> (String, String),
}
macro_rules! solution {
($year:tt, $day:tt) => {
Solution {
year: (&stringify!($year)).parse_u32(),
day: (&stringify!($day)).parse_u32(),
input: include_str!(concat![
"../input/",
stringify!($year),
"/",
stringify!($day),
".txt"
]),
run: |input: &str| {
use aoc::$year::$day::*;
let part1 = part1(&input).to_string();
let part2 = part2(&input).to_string();
(part1, part2)
},
}
};
}
fn main() {
let (year, day) = match std::env::args().nth(1) {
Some(arg) => {
let mut split = arg.split("::").map(|s| s.parse_u32());
(split.next(), split.next())
}
None => (None, None),
};
let solutions: Vec<_> = solutions()
.filter(|s| year == Some(s.year) || year.is_none())
.filter(|s| day == Some(s.day) || day.is_none())
.collect();
for Solution {
year,
day,
input,
run,
} in solutions
{
let start = Instant::now();
let (aw1, aw2) = (run)(input);
let elapsed = start.elapsed();
println!("{year} Day {day:02} ({})", elapsed.as_micros());
println!(" Part 1: {aw1}");
println!(" Part 2: {aw2}");
}
}
fn solutions() -> impl Iterator<Item = Solution> {
std::iter::empty().chain(year2016())
}
fn year2016() -> Vec<Solution> {
vec![solution!(year2016, day01)]
}

29
src/util/grid.rs Normal file
View File

@@ -0,0 +1,29 @@
use std::{collections::HashMap, hash::Hash};
pub struct Grid<Dim, T> {
data: HashMap<Dim, T>,
}
impl<Dim, T> Grid<Dim, T>
where
Dim: Eq + PartialEq + Hash,
{
pub fn new() -> Self {
Self {
data: HashMap::new(),
}
}
pub fn get(&self, idx: impl Into<Dim>) -> Option<&T> {
self.data.get(&idx.into())
}
}
impl<Dim, T> Default for Grid<Dim, T>
where
Dim: Eq + PartialEq + Hash,
{
fn default() -> Self {
Self::new()
}
}

144
src/util/index.rs Normal file
View File

@@ -0,0 +1,144 @@
#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Hash, Clone, Copy)]
pub struct Dim<I: ?Sized>(pub I);
pub type Ix = i64;
macro_rules! impl_ix {
($($t:ident: $n:literal),+) => {
$(
pub type $t = Dim<[Ix; $n]>;
impl From<[Ix; $n]> for $t {
fn from(value: [Ix; $n]) -> Self {
Dim(value)
}
}
#[allow(non_snake_case)]
pub fn $t() -> Dim<[Ix; $n]> { Dim([0; $n]) }
)+
};
}
impl_ix!(
Ix2: 2,
Ix3: 3
);
impl Ix2 {
pub const ORIGIN: Ix2 = Dim([0, 0]);
pub const UP: Ix2 = Dim([0, 1]);
pub const DOWN: Ix2 = Dim([0, -1]);
pub const LEFT: Ix2 = Dim([-1, 0]);
pub const RIGHT: Ix2 = Dim([1, 0]);
#[inline]
pub fn clockwise(self) -> Self {
Dim([-self.0[1], self.0[0]])
}
#[inline]
pub fn counter_clockwise(self) -> Self {
Dim([self.0[1], -self.0[0]])
}
pub fn manhatten(&self, other: Ix2) -> i64 {
(self.0[0] - other.0[0]).abs() + (self.0[1] - other.0[1]).abs()
}
}
impl std::ops::Add for Ix2 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Dim([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1]])
}
}
impl std::ops::AddAssign for Ix2 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0[0] += rhs.0[0];
self.0[1] += rhs.0[1];
}
}
impl std::ops::Sub for Ix2 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Dim([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1]])
}
}
impl std::ops::SubAssign for Ix2 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.0[0] -= rhs.0[0];
self.0[1] -= rhs.0[1];
}
}
impl std::ops::Mul for Ix2 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Dim([self.0[0] * rhs.0[0], self.0[1] * rhs.0[1]])
}
}
impl std::ops::Mul<i64> for Ix2 {
type Output = Self;
#[inline]
fn mul(self, rhs: i64) -> Self::Output {
Dim([self.0[0] * rhs, self.0[1] * rhs])
}
}
impl std::ops::Add for Ix3 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Dim([
self.0[0] + rhs.0[0],
self.0[1] + rhs.0[1],
self.0[2] + rhs.0[2],
])
}
}
impl std::ops::AddAssign for Ix3 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0[0] += rhs.0[0];
self.0[1] += rhs.0[1];
self.0[2] += rhs.0[2];
}
}
impl std::ops::Sub for Ix3 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Dim([
self.0[0] - rhs.0[0],
self.0[1] - rhs.0[1],
self.0[2] - rhs.0[2],
])
}
}
impl std::ops::SubAssign for Ix3 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.0[0] -= rhs.0[0];
self.0[1] -= rhs.0[1];
self.0[2] -= rhs.0[2];
}
}

58
src/util/parse.rs Normal file
View File

@@ -0,0 +1,58 @@
use std::str::Bytes;
pub struct U32s<'a> {
bytes: Bytes<'a>,
}
impl<'a> Iterator for U32s<'a> {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
try_parse_u32(&mut self.bytes)
}
}
pub trait ParseExt {
fn parse_u32(&self) -> u32;
fn u32s(&self) -> U32s<'_>;
}
impl ParseExt for &str {
fn parse_u32(&self) -> u32 {
match try_parse_u32(&mut self.bytes()) {
Some(num) => num,
None => panic!("unable to parse u32: {self}"),
}
}
fn u32s(&self) -> U32s<'_> {
U32s {
bytes: self.bytes(),
}
}
}
fn try_parse_u32(bytes: &mut Bytes<'_>) -> Option<u32> {
let mut n = loop {
let byte = bytes.next()?;
let digit = byte.wrapping_sub(b'0');
if digit < 10 {
break digit as u32;
}
};
loop {
let Some(byte) = bytes.next() else {
break Some(n);
};
let digit = byte.wrapping_sub(b'0');
if digit < 10 {
n = 10 * n + digit as u32;
} else {
break Some(n);
}
}
}

59
src/year2016/day01.rs Normal file
View File

@@ -0,0 +1,59 @@
use std::collections::HashSet;
use crate::util::{index::*, parse::ParseExt};
fn parse(input: &str) -> Vec<(u8, u32)> {
let dirs = input.bytes().filter(u8::is_ascii_uppercase);
let steps = input.u32s();
dirs.zip(steps).collect()
}
pub fn part1(input: &str) -> impl std::fmt::Display {
let mut pos = Ix2::ORIGIN;
let mut dir = Ix2::UP;
for (d, s) in parse(input) {
dir = if d == b'R' {
dir.clockwise()
} else {
dir.counter_clockwise()
};
pos += dir * (s as i64);
}
pos.manhatten(Ix2::ORIGIN)
}
pub fn part2(input: &str) -> impl std::fmt::Display {
let mut pos = Ix2::ORIGIN;
let mut dir = Ix2::UP;
let mut set = HashSet::new();
for (d, s) in parse(input) {
dir = if d == b'R' {
dir.clockwise()
} else {
dir.counter_clockwise()
};
for _ in 0..s {
pos += dir;
if !set.insert(pos) {
return pos.manhatten(Ix2::ORIGIN);
}
}
}
unreachable!()
}
#[test]
fn test_part1() {
assert_eq!("12", part1("R5, L5, R5, R3").to_string())
}
#[test]
fn test_part2() {
assert_eq!("4", part2("R8, R4, R4, R8").to_string())
}