From 068428959240b9c9b3e3456ef300a4bb8cc6517f Mon Sep 17 00:00:00 2001 From: fuckwit Date: Wed, 15 Nov 2023 18:38:04 +0100 Subject: [PATCH] initial commit --- .cargo/config | 2 + .gitignore | 1 + Cargo.lock | 7 ++ Cargo.toml | 4 ++ flake.lock | 130 +++++++++++++++++++++++++++++++++++ flake.nix | 30 ++++++++ input/year2016/day01.txt | 1 + src/lib.rs | 9 +++ src/main.rs | 69 +++++++++++++++++++ src/util/grid.rs | 29 ++++++++ src/util/index.rs | 144 +++++++++++++++++++++++++++++++++++++++ src/util/parse.rs | 58 ++++++++++++++++ src/year2016/day01.rs | 59 ++++++++++++++++ 13 files changed, 543 insertions(+) create mode 100644 .cargo/config create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 input/year2016/day01.txt create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/util/grid.rs create mode 100644 src/util/index.rs create mode 100644 src/util/parse.rs create mode 100644 src/year2016/day01.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..51a3a2f --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[target.x86_64-unknown-linux-gnu] +rustflags = ["-Ctarget-cpu=native"] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7c75bb2 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aoc" +version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9bdbb53 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "aoc" +version = "0.0.1" +edition = "2021" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..5df43f1 --- /dev/null +++ b/flake.lock @@ -0,0 +1,130 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1699099776, + "narHash": "sha256-X09iKJ27mGsGambGfkKzqvw5esP1L/Rf8H3u3fCqIiU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "85f1ba3e51676fa8cc604a3d863d729026a6b8eb", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1681358109, + "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1699236891, + "narHash": "sha256-J0uhoYlufJncIFbM/pAoggzHK/qERB9KfQRkmYD56yo=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "a7f9bf91dc5065d470cd57169a9f2ebdbdfe1f24", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..e7880ec --- /dev/null +++ b/flake.nix @@ -0,0 +1,30 @@ +{ + description = "req-limit"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + in + with pkgs; + { + devShells.default = mkShell { + buildInputs = [ + (rust-bin.stable.latest.default .override { + extensions = [ "rust-src" "rust-analyzer" ]; + }) + ]; + }; + } + ); +} + diff --git a/input/year2016/day01.txt b/input/year2016/day01.txt new file mode 100644 index 0000000..954bfc8 --- /dev/null +++ b/input/year2016/day01.txt @@ -0,0 +1 @@ +R3, L5, R2, L2, R1, L3, R1, R3, L4, R3, L1, L1, R1, L3, R2, L3, L2, R1, R1, L1, R4, L1, L4, R3, L2, L2, R1, L1, R5, R4, R2, L5, L2, R5, R5, L2, R3, R1, R1, L3, R1, L4, L4, L190, L5, L2, R4, L5, R4, R5, L4, R1, R2, L5, R50, L2, R1, R73, R1, L2, R191, R2, L4, R1, L5, L5, R5, L3, L5, L4, R4, R5, L4, R4, R4, R5, L2, L5, R3, L4, L4, L5, R2, R2, R2, R4, L3, R4, R5, L3, R5, L2, R3, L1, R2, R2, L3, L1, R5, L3, L5, R2, R4, R1, L1, L5, R3, R2, L3, L4, L5, L1, R3, L5, L2, R2, L3, L4, L1, R1, R4, R2, R2, R4, R2, R2, L3, L3, L4, R4, L4, L4, R1, L4, L4, R1, L2, R5, R2, R3, R3, L2, L5, R3, L3, R5, L2, R3, R2, L4, L3, L1, R2, L2, L3, L5, R3, L1, L3, L4, L3 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cf72e57 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ +pub mod util { + pub mod grid; + pub mod index; + pub mod parse; +} + +pub mod year2016 { + pub mod day01; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f549069 --- /dev/null +++ b/src/main.rs @@ -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 { + std::iter::empty().chain(year2016()) +} + +fn year2016() -> Vec { + vec![solution!(year2016, day01)] +} diff --git a/src/util/grid.rs b/src/util/grid.rs new file mode 100644 index 0000000..cacc247 --- /dev/null +++ b/src/util/grid.rs @@ -0,0 +1,29 @@ +use std::{collections::HashMap, hash::Hash}; + +pub struct Grid { + data: HashMap, +} + +impl Grid +where + Dim: Eq + PartialEq + Hash, +{ + pub fn new() -> Self { + Self { + data: HashMap::new(), + } + } + + pub fn get(&self, idx: impl Into) -> Option<&T> { + self.data.get(&idx.into()) + } +} + +impl Default for Grid +where + Dim: Eq + PartialEq + Hash, +{ + fn default() -> Self { + Self::new() + } +} diff --git a/src/util/index.rs b/src/util/index.rs new file mode 100644 index 0000000..be4c3bb --- /dev/null +++ b/src/util/index.rs @@ -0,0 +1,144 @@ +#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Hash, Clone, Copy)] +pub struct Dim(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 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]; + } +} diff --git a/src/util/parse.rs b/src/util/parse.rs new file mode 100644 index 0000000..8e93241 --- /dev/null +++ b/src/util/parse.rs @@ -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 { + 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 { + 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); + } + } +} diff --git a/src/year2016/day01.rs b/src/year2016/day01.rs new file mode 100644 index 0000000..807e54f --- /dev/null +++ b/src/year2016/day01.rs @@ -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()) +}