diff --git a/2023/.gitignore b/2023/.gitignore new file mode 100644 index 0000000..4bc139e --- /dev/null +++ b/2023/.gitignore @@ -0,0 +1,21 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust +# Edit at https://www.toptal.com/developers/gitignore?templates=rust + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# End of https://www.toptal.com/developers/gitignore/api/rust + diff --git a/2023/Cargo.toml b/2023/Cargo.toml new file mode 100644 index 0000000..7450193 --- /dev/null +++ b/2023/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "aoc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.75" + +[features] diff --git a/2023/new.sh b/2023/new.sh new file mode 100755 index 0000000..fe0d3c3 --- /dev/null +++ b/2023/new.sh @@ -0,0 +1,15 @@ +#!/bin/sh +day=$1 +type=$2 +default=$3 +test=$4 + +echo "Creating file from template..." +sed -e "s/DAY/$day/g" -e "s/TYPE/$type/" -e "s/DEFAULT/$default/" -e "s/TEST/$test/" ./template.rs > ./src/bin/day${day}.rs + +echo "Downloading input..." +source ../.env +mkdir -p input/$1 +curl -s "https://adventofcode.com/2023/day/$1/input" -H "Cookie: session=${SESSION}" > input/$1/input + +echo "Done!" diff --git a/2023/src/lib.rs b/2023/src/lib.rs new file mode 100644 index 0000000..5e4b7db --- /dev/null +++ b/2023/src/lib.rs @@ -0,0 +1,46 @@ +#![feature(test)] +extern crate test; + +use core::fmt; +use std::{fmt::Debug, fs}; + +use anyhow::{Context, Result}; + +pub trait Solver { + type Output1: fmt::Display + Debug + PartialEq; + type Output2: fmt::Display + Debug + PartialEq; + + fn day() -> u8; + fn part1(input: &str) -> Self::Output1; + fn part2(input: &str) -> Self::Output2; + + fn test U, U: Debug + PartialEq>(f: T, name: &str, result: U) -> Result<()> { + // Select the right function + + // Read the test input + let input = fs::read_to_string(format!("input/{:02}/{name}", Self::day())) + .with_context(|| format!("Failed to read '{}' for day {:02}", name, Self::day()))?; + + // Assert that the result matches the expected value + assert_eq!(f(&input), result); + + Ok(()) + } + + fn solve() -> Result<()> { + let input = fs::read_to_string(format!("input/{:02}/input", Self::day())) + .with_context(|| format!("Failed to read 'input' for day {:02}", Self::day()))?; + println!("Part 1:\n{}", Self::part1(&input)); + println!("Part 2:\n{}", Self::part2(&input)); + + Ok(()) + } + + fn benchmark U, U: Debug + PartialEq>(f: T, b: &mut test::Bencher) { + let input = fs::read_to_string(format!("input/{:02}/input", Self::day())) + .with_context(|| format!("Failed to read 'input' for day {:02}", Self::day())) + .unwrap(); + + b.iter(|| f(&input)); + } +} diff --git a/2023/template.rs b/2023/template.rs new file mode 100644 index 0000000..d67c70d --- /dev/null +++ b/2023/template.rs @@ -0,0 +1,50 @@ +#![feature(test)] +use anyhow::Result; +use aoc::Solver; + +// -- Runners -- +fn main() -> Result<()> { + Day::solve() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1_test1() -> Result<()> { + Day::test(Day::part1, "test-1", TEST) + } + + // Benchmarks + extern crate test; + #[bench] + #[ignore] + fn part1_bench(b: &mut test::Bencher) { + Day::benchmark(Day::part1, b) + } + #[bench] + #[ignore] + fn part2_bench(b: &mut test::Bencher) { + Day::benchmark(Day::part2, b) + } +} + +// -- Solution -- +pub struct Day; +impl aoc::Solver for Day { + type Output1 = TYPE; + type Output2 = TYPE; + + fn day() -> u8 { + DAY + } + + fn part1(input: &str) -> Self::Output1 { + DEFAULT + } + + fn part2(input: &str) -> Self::Output2 { + DEFAULT + } +}