Minimal, flexible framework for implementing solutions to Advent of Code in Rust

Overview

Build Status Latest Version

impl Solution for AdventOfCode2021

What is this?

This is advent_of_code_traits, a minimal, flexible framework for implementing solutions to Advent of Code in Rust.

It takes a trait-based approach using const-generics.

Experimental

This is already serviceable, but there will be frequent breaking changes as the traits are improved and refined. The plan is to release a stable version in time for December 2021.

See the Changelog for a current view of progress.

Usage

Please see also the examples.

Implement traits with your solutions to each Day of Advent of Code.

Import the traits:

use advent_of_code_traits::{ParseInput, Solution};

and optionally, consts:

use advent_of_code_traits::{days::*, Part1, Part2};

Implement Solution for your struct.

pub struct AdventOfCode2020;

impl Solution for AdventOfCode2020 {
    type Part1Output = u32;
    type Part2Output = u32;

    fn part1(input: &Vec<u32>) -> u32 {
        // your solution to part1 here...
        todo!()
    }
    
    fn part2(input: &Vec<u32>) -> u32 {
        // your solution to part2 here...
        todo!()
    }
}

"But where does Vec come from?", you ask.

Well spotted, eagle-eyed reader!

That comes from your implementation of ParseInput.

Implement ParseInput for your struct

// ..continued from above

impl ParseInput for AdventOfCode2020 {
    type Parsed = Vec<u32>; // <-- this will be the input to both part1 and part2 for Solution

    fn parse_input(input: &str) -> Self::Parsed {
        input
            .lines()
            .map(|s| s.parse().expect("invalid integer"))
            .collect()
    }
}

Please refer to the examples for more possibilities, including parsing a different type for each Part and opting out of parsing entirely to work directly with the &str.

Run from main.rs

Here comes the ugly part.

>::run(input); ">
let input = std::fs::read_to_string("./input/2020/day1.txt").expect("failed to read input");
<AdventOfCode2020 as Solution<Day1>>::run(input);

This reads input from a file and passes it to your struct. Fully Qualified Syntax is required in order to disambiguate which day's Solution we are running.

How does this use const generics?

Because the Solution and ParseInput traits are generic over const Day: u32 you are free to implement them many times for the same struct. The compiler will only yell at you if you implement them for the same Day twice (as it should!).

Day1 is used in the examples (because it looks awesome in my humble opinion). It is simply 1_u32.

advent_of_code_traits::days looks like this:

mod days {
    pub const Day1: u32 = 1;
    pub const Day2: u32 = 2;
    // ...
    pub const Day25: u32 = 25;
}

Prior Art

I am very grateful for @gobanos' cargo-aoc which was a huge inspiration while creating this.

This crate is no match for the convenience or ease of use of cargo-aoc.

Having said that, I hope it brings something new to the table (faster compile times perhaps?) and that others enjoy using this half as much as I enjoyed using cargo-aoc.

I have used cargo-aoc for all of my Advent of Codes in Rust before 2021, and it is a brilliant, crazy use of procedural macros.

Thank you Gobanos! :)

Contributing

Contributions are welcome, please see CONTRIBUTING

Please also see ARCHITECTURE for a guided tour of sorts of the code base.

Thank you so much to everyone who has helped this project so far:


License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in advent_of_code_traits by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Comments
  • Rewrite Solution trait

    Rewrite Solution trait

    This will be a big breaking change, but I'm actually struggling more than I thought I would to make improvements as things stand.

    I think the following changes will help:

    • Make Solution generic over Part as well as Day, i.e. Solution<Day, Part>
    • Move input parsing into the Solution trait
      • this means each Part knows how to parse its own input, which could simplify things
      • I'm not sure yet how this would affect sharing the parsed input between Parts when possible
    opened by drmason13 2
  • Improve the efficiency of the default `run` method

    Improve the efficiency of the default `run` method

    The default run method will parse the input twice.

    This is ~twice as slow in the common case of using the same parsed input for both parts of a day. This feels like a trap and I consider it a bug.

    There is an example in the docs of overriding it to share the parsed input like this:

    let shared_parsed_input = <Self as ParseInput<Day1>>::parse_input(input);
    let part1_output = Self::part1(&shared_parsed_input);
    let part2_output = Self::part2(&shared_parsed_input);
    

    Could the default implementation be changed to do this where possible, and fall back to parsing input twice when needed? i.e. when ParseEachInput is implemented with different types of Parsed.

    This might be impossible, in which case I'd appreciate suggestions of other ideas.

    bug help wanted good first issue 
    opened by drmason13 1
  • Create a macro for conveniently running individual days

    Create a macro for conveniently running individual days

    Currently, running a day using a struct that implements more than one day requires Fully Qualified Syntax which is a bit on the verbose side and can be confusing:

    <AdventOfCode2020 as Solution<Day1>>::run(&input);
    

    I am considering creating a function style proc_macro that would turn the below into the the above when compiled:

    run!(&input, Day1, AdventOfCode2020);
    

    I'm open to suggestions on the exact syntax.

    I think this should be included under a default feature (if possible)

    enhancement good first issue 
    opened by drmason13 1
  • Rewrite the traits

    Rewrite the traits

    Massive breaking change, will release as 0.2.

    This fixes 2 of the biggest gripes I had with the original version:

    • running your solution needed ugly fully qualified syntax
    • by default you would parse input twice when running the solution, even if both parts used the same input type

    This fixes both of them using autoderef specialization in stable rust.

    I've also removed a couple of examples as sharing/splitting parsing between parts is so much more intuitive now.

    I also realised that the cli and boilerplate examples overlapped significantly, it just makes sense in hindsight to have one good example for now.

    opened by drmason13 0
  • More examples and feedback

    More examples and feedback

    If you use this library to do Advent of Code for any year, I'd love to hear your thoughts on the experience.

    I'd also like to link to external repositories with solutions using these traits or any other creative uses of them.

    documentation help wanted question 
    opened by drmason13 0
  • Allow using `self` in Solution implementation

    Allow using `self` in Solution implementation

    All the methods on the traits so far are associated functions.

    So essentially the implementing struct is just a marker or container for organising the solutions. It has no use for any data or fields.

    I'd like people to experiment with this idea, I have in mind fun implementations of Solution like this:

    // from Advent of Code 2019
    pub struct IntcodeComputer {
        program: Vec<Instruction>,
        stack: Vec<i32>,
        register: HashMap<Register, i32>,
    }
    
    impl Solution<Day2> for IntcodeComputer {
        fn part1(&self) -> u32 {
            // use the intcode computer and access its fields, methods!
            self.program.iter() // ...
        }
    }
    

    We could even have mutability.

    impl Solution<Day2> for IntcodeComputer {
        fn part1(&mut self) -> u32 {
            // use the intcode computer and mutate it while solving the solution
           self.stack.push(3)
        }
    }
    
    enhancement help wanted 
    opened by drmason13 1
  • Extract printing behaviour into a report or summary method with a default implementation

    Extract printing behaviour into a report or summary method with a default implementation

    Currently this is done inline in the run method.

    // TODO: extract printing behaviour into a report or summary method with a default implementation
    println!(
        "Day {0}, Part 1\n\
        {1}\n\n\
        Day {0}, Part 1\n\
        {2}",
        Day, part1_output, part2_output
    );
    

    I can foresee plenty of reasons to want to customise this and I'd like to enable that more easily.

    Separating into two methods means the user can do that without having to re-implement the running of part1 and part2 which is a bit more involved..

    enhancement good first issue 
    opened by drmason13 1
  • Support User defined Error Handling

    Support User defined Error Handling

    Currently none of the trait methods contain Result in their type signature, so ? won't work in their implementations.

    This encourages a lot of unwrap/expect where I would rather discourage it.

    I initially avoided using Result in the trait methods because I want to display the output, so I required Display. However Result doesn't implement Display. I think this might be solvable with a more complicated trait bound combining both Result and Display.

    Also, that then forces everyone to return a Result in their implementations, that might be a good thing though since this library aims to be used by beginners to Rust.

    This would be a breaking change so the sooner the better!

    enhancement good first issue 
    opened by drmason13 0
Releases(v0.2.0)
Owner
David
David
My solutions to the advent of code 2021 problems.

Advent of Code 2021 My solutions to the AOC 2021 problems in Rust. Solutions Task Status Day 1 ✔️ , ✔️ Day 2 ✔️ , ✔️ Day 3 ✔️ , ✔️ Day 4 ✔️ , ✔️ Day 5

null 1 Dec 25, 2021
A framework for iterating over collections of types implementing a trait without virtual dispatch

zero_v Zero_V is an experiment in defining behavior over collections of objects implementing some trait without dynamic polymorphism.

null 13 Jul 28, 2022
Code to brute-force Wordle solutions

Wordle solver The idea of this is to find the optimal solution to Wordle puzzles. The current implementation, for each guess, works out the worst case

Simon Frankau 1 Jan 9, 2022
An unofficial and incomplete no_std Rust library for implementing the ElectricUI Binary Protocol

An unofficial and incomplete no_std Rust library for implementing the ElectricUI Binary Protocol

Jon 2 Mar 29, 2022
Derive macro implementing 'From' for structs

derive-from-ext A derive macro that auto implements 'std::convert::From' for structs. The default behaviour is to create an instance of the structure

Andrew Lowndes 4 Sep 18, 2022
Demonstration of flexible function calls in Rust with function overloading and optional arguments

Table of Contents Table of Contents flexible-fn-rs What is this trying to demo? How is the code structured? Named/Unnamed and Optional arguments Mecha

Tien Duc (TiDu) Nguyen 81 Nov 3, 2022
A flexible, simple to use, immutable, clone-efficient String replacement for Rust

A flexible, simple to use, immutable, clone-efficient String replacement for Rust. It unifies literals, inlined, and heap allocated strings into a single type.

Scott Meeuwsen 118 Nov 29, 2022
Concatenate Amazon S3 files remotely using flexible patterns

S3 Concat This tool has been migrated into s3-utils, please use that crate for future updates. A small utility to concatenate files in AWS S3. Designe

Isaac Whitfield 32 May 1, 2022
Flexible snowflake generator, reference snoyflake and leaf.

Flexible snowflake generator, reference snoyflake and leaf.

Egccri 2 May 6, 2022
A lean, minimal, and stable set of types for color interoperation between crates in Rust.

This library provides a lean, minimal, and stable set of types for color interoperation between crates in Rust. Its goal is to serve the same function that mint provides for (linear algebra) math types.

Gray Olson 16 Sep 21, 2022
diff successive buffers with embedded ansi codes in rust, outputting a minimal change

ansi-diff diff successive buffers with embedded ansi codes in rust, outputting a minimal change You can use this crate to build command-line interface

James Halliday 7 Aug 11, 2022
Minimal viable ZFS autosnapshot tool

zfs-autosnap Minimal viable ZFS snapshot utility. Add zfs-autosnap snap to your cron.hourly, and zfs-autosnap gc to cron.daily; then set at.rollc.at:s

Kamil 16 May 30, 2022
untyped-arena provides an Arena allocator implementation that is safe and untyped with minimal complexity

untyped-arena untyped-arena provides an Arena allocator implementation that is safe and untyped with minimal complexity Usage let arena = Arena::new()

Max Bruce 1 Jan 9, 2022
Minimal virus genome coverage assessment for metagenomic diagnostics

vircov Minimal virus genome coverage assessment for metagenomic diagnostics Overview v0.5.0 Purpose Implementation Installation Usage Tests Concept Cl

Eike Steinig 14 Oct 17, 2022
Doku is a framework for building documentation with code-as-data methodology in mind.

Doku is a framework for building documentation with code-as-data methodology in mind. Say goodbye to stale, hand-written documentation - with D

ANIXE 72 Nov 8, 2022
Rust implementation of the Edge IoT framework

A Rust-based implementation of Edge-rs for the Raspberry Pi Pico Getting started For more details see the following article on getting started for get

Jim Hodapp 4 Apr 14, 2022
tracing - a framework for instrumenting Rust programs to collect structured, event-based diagnostic information

tracing-appender Writers for logging events and spans Documentation | Chat Overview tracing is a framework for instrumenting Rust programs to collect

Cris Liao 1 Mar 9, 2022
Examples of how to use Rust with Serverless Framework, Lambda, API Gateway v1 and v2, SQS, GraphQL, etc

Rust Serverless Examples All examples live in their own directories: project: there is nothing here, just a simple cargo new project_name with a custo

Fernando Daciuk 7 Aug 31, 2022
A rust `tracing` compatible framework inspired by log4rs.

trace4rs This crate allows users to configure output from tracing in the same way as you would configure the output of log4rs. Overview For a usage ex

Imperva 5 Oct 24, 2022