1
Fork 0

Add missing documentation lints.

This commit is contained in:
Bauke 2024-01-14 22:04:10 +01:00
parent 1030c6f60f
commit 3c9bfef10a
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
43 changed files with 356 additions and 2 deletions

View File

@ -27,5 +27,9 @@ rand = "0.8.5"
regex = "1.10.2"
ureq = { version = "2.8.0", features = ["cookie", "cookie_store"] }
[lints.clippy]
missing_docs_in_private_items = "warn"
[lints.rust]
missing_docs = "warn"
unsafe_code = "forbid"

View File

@ -15,6 +15,7 @@ pub mod utilities;
pub mod year_2020;
pub mod year_2021;
/// CLI arguments.
#[derive(Debug, Parser)]
#[clap(about, author, version)]
pub struct Args {
@ -22,7 +23,7 @@ pub struct Args {
pub filter: Option<String>,
}
pub fn main() -> Result<()> {
fn main() -> Result<()> {
color_eyre::install()?;
let args = Args::parse();

View File

@ -1,53 +1,72 @@
//! Functionality for [`Day`]s and [`Solution`]s.
use {color_eyre::Result, derivative::Derivative};
/// The information for an advent day of a given year.
#[derive(Debug)]
pub struct Day {
/// The advent day.
pub day: i32,
/// The year for the advent.
pub year: i32,
}
impl Day {
/// Create a new [`Day`] from a given day and year.
pub fn new(day: i32, year: i32) -> Self {
Self { day, year }
}
/// Return the filter string as `YEAR::DAY` for this day.
pub fn filter_string(&self) -> String {
format!("{}::{:02}", self.year, self.day)
}
/// Return the link for this day on the Advent of Code website.
pub fn day_link(&self) -> String {
format!("{}/day/{}", self.year_link(), self.day)
}
/// Return the link for this year on the Advent of Code website.
pub fn year_link(&self) -> String {
format!("https://adventofcode.com/{}", self.year)
}
}
/// Function type alias for the expected input and outputs of a day.
pub type DayFunction = fn(input: &str) -> Result<String>;
/// The solution to one of the advent calendar days.
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Solution {
/// Which day the solution applies to.
pub day: Day,
/// The calculated result for part one.
pub part_1: String,
/// The calculated result for part two.
pub part_2: String,
/// The expected result for part one (for validation).
pub part_1_expected: String,
/// The expected result for part two (for validation).
pub part_2_expected: String,
/// The function for part one.
#[derivative(Debug = "ignore")]
pub part_1_fn: DayFunction,
/// The function for part two.
#[derivative(Debug = "ignore")]
pub part_2_fn: DayFunction,
}
impl Solution {
/// Create a new [`Solution`] with a given [`Day`] and two [`DayFunction`]s.
pub fn new(day: Day, part_1_fn: DayFunction, part_2_fn: DayFunction) -> Self {
Self {
day,
@ -60,6 +79,7 @@ impl Solution {
}
}
/// Helper function to add the expected results for both parts.
pub fn with_expected<A: std::fmt::Display, B: std::fmt::Display>(
self,
part_1_expected: A,
@ -72,6 +92,8 @@ impl Solution {
}
}
/// Run the [`DayFunction`]s for this [`Solution`] and put the results in
/// their respective fields.
pub fn solve(self, data: &str) -> Result<Self> {
let (part_1, part_2) = ((self.part_1_fn)(data)?, (self.part_2_fn)(data)?);
Ok(Self {

View File

@ -1,14 +1,20 @@
//! The templating code for [`askama`].
use askama::Template;
use crate::solution::Solution;
/// The HTML template for all solutions.
#[derive(Template)]
#[template(path = "solutions.html")]
pub struct SolutionsTemplate {
/// All solutions grouped by year.
pub years: Vec<Vec<Solution>>,
}
/// Custom [`askama`] filters.
pub mod filters {
/// See [`crate::utilities::random_emoji`].
pub fn random_emoji(s: &str) -> askama::Result<String> {
Ok(format!("{s} {}", crate::utilities::random_emoji()))
}

View File

@ -1,3 +1,5 @@
//! Miscellaneous utility functions.
use std::{
fs::{create_dir_all, read_to_string, write},
path::PathBuf,
@ -7,10 +9,12 @@ use {color_eyre::Result, dialoguer::Password, rand::seq::IteratorRandom};
use crate::{solution::Solution, year_2020, year_2021};
/// Shorthand to get the solutions for all years.
pub fn get_solutions() -> Vec<Vec<Solution>> {
vec![year_2020::get_solutions(), year_2021::get_solutions()]
}
/// Return a random emoji from the [`emojis::Group::AnimalsAndNature`] set.
pub fn random_emoji() -> String {
emojis::iter()
.filter(|emoji| emoji.group() == emojis::Group::AnimalsAndNature)
@ -19,6 +23,8 @@ pub fn random_emoji() -> String {
.to_string()
}
/// Get the Advent of Code session cookie from an existing file or start an
/// interactive prompt to ask the user for it.
pub fn session_cookie() -> Result<String> {
let session_cookie_path = PathBuf::from("aoc/session.txt");
@ -33,6 +39,8 @@ pub fn session_cookie() -> Result<String> {
}
}
/// Write the given contents to a path, ensuring the parent directory for the
/// location exists.
pub fn write_file(path: PathBuf, contents: String) -> Result<String> {
create_dir_all(path.parent().unwrap())?;
write(path, &contents)?;

View File

@ -1,12 +1,17 @@
//! Day 01 of 2020.
use crate::prelude::*;
/// Get the solution for day 01 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(1, 2020), part_1, part_2)
.with_expected(605364, 128397680)
}
/// The target sum that two entries need to add up to.
const TARGET: i32 = 2020;
/// Parse the input lines into integers.
fn parse_lines(input: &str) -> Vec<i32> {
input
.lines()
@ -14,6 +19,7 @@ fn parse_lines(input: &str) -> Vec<i32> {
.collect()
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(
parse_lines(input)
@ -26,6 +32,7 @@ fn part_1(input: &str) -> Result<String> {
)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
Ok(
parse_lines(input)

View File

@ -1,17 +1,26 @@
//! Day 02 of 2020.
use crate::prelude::*;
/// Get the solution for day 02 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(2, 2020), part_1, part_2).with_expected(638, 699)
}
/// The data for each line of the puzzle input.
#[derive(Debug)]
struct Item {
/// The minimum amount of times the letter has to be in the password.
min: usize,
/// The maximum amount of times the letter has to be in the password.
max: usize,
/// The letter that must be in the password.
letter: String,
/// The password to check.
password: String,
}
/// Parse all the [`Item`]s inside the puzzle input.
fn parse_items(input: &str) -> Vec<Item> {
let mut items = vec![];
@ -32,6 +41,7 @@ fn parse_items(input: &str) -> Vec<Item> {
items
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(
parse_items(input)
@ -43,6 +53,7 @@ fn part_1(input: &str) -> Result<String> {
)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
Ok(
parse_items(input)

View File

@ -1,10 +1,15 @@
//! Day 03 of 2020.
use crate::prelude::*;
/// Get the solution for day 03 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(3, 2020), part_1, part_2)
.with_expected(198, 5140884672_i64)
}
/// Generic solver that takes in a horizontal and vertical movement for the
/// slope.
fn solve(data: &str, (horizontal, vertical): (usize, usize)) -> usize {
let line_length = data.find('\n').unwrap();
let mut result = 0;
@ -28,10 +33,12 @@ fn solve(data: &str, (horizontal, vertical): (usize, usize)) -> usize {
result
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(solve(input, (3, 1)).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
Ok(
[(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]

View File

@ -1,21 +1,34 @@
//! Day 04 of 2020.
use crate::prelude::*;
/// Get the solution for day 04 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(4, 2020), part_1, part_2).with_expected(237, 172)
}
/// The passport puzzle input.
#[derive(Debug, Clone)]
struct Passport {
/// Birth year.
byr: Option<String>,
/// Issue year.
iyr: Option<String>,
/// Expiration year.
eyr: Option<String>,
/// Height.
hgt: Option<String>,
/// Hair color.
hcl: Option<String>,
/// Eye color.
ecl: Option<String>,
/// Passport ID.
pid: Option<String>,
/// Country ID.
cid: Option<String>,
}
/// Parse the puzzle input into a list of passports.
fn parse_passports(input: &str) -> Vec<Passport> {
let mut passports = vec![];
let blank = Passport {
@ -59,6 +72,7 @@ fn parse_passports(input: &str) -> Vec<Passport> {
passports
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(
parse_passports(input)
@ -77,6 +91,7 @@ fn part_1(input: &str) -> Result<String> {
)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let hcl_regex = Regex::new("^#[a-fA-F0-9]{6}$").unwrap();
let pid_regex = Regex::new("^[0-9]{9}$").unwrap();

View File

@ -1,12 +1,20 @@
//! Day 05 of 2020.
use crate::prelude::*;
/// Get the solution for day 05 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(5, 2020), part_1, part_2).with_expected(850, 599)
}
/// The maximum range of rows.
const MAX_ROW_RANGE: i32 = 128;
/// The maximum range of columns.
const MAX_COLUMN_RANGE: i32 = 8;
/// Calculate the row and column from the input according to the puzzle's
/// instructions.
fn calculate_row_and_column(input: &str) -> (i32, i32) {
let mut row_range = 0..MAX_ROW_RANGE;
let mut column_range = 0..MAX_COLUMN_RANGE;
@ -40,10 +48,12 @@ fn calculate_row_and_column(input: &str) -> (i32, i32) {
(row_range.start, column_range.start)
}
/// Calculate the sead ID by multiplying the row by 8 and adding the column.
fn calculate_seat_id((row, column): (i32, i32)) -> i32 {
(row * 8) + column
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(
input
@ -56,6 +66,7 @@ fn part_1(input: &str) -> Result<String> {
)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let seat_ids = input
.lines()

View File

@ -1,9 +1,13 @@
//! Day 06 of 2020.
use crate::prelude::*;
/// Get the solution for day 06 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(6, 2020), part_1, part_2).with_expected(6437, 3229)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(
input
@ -20,6 +24,7 @@ fn part_1(input: &str) -> Result<String> {
)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
Ok(
input

View File

@ -1,11 +1,16 @@
//! Day 07 of 2020.
use crate::prelude::*;
/// Get the solution for day 07 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(7, 2020), part_1, part_2).with_expected(169, 82372)
}
/// The target bag to look for.
const TARGET: &str = "shiny gold";
/// Parse the bags from the puzzle input.
fn parse_bags(input: &str) -> Vec<(&str, Vec<(usize, &str)>)> {
let mut bags = vec![];
@ -29,6 +34,7 @@ fn parse_bags(input: &str) -> Vec<(&str, Vec<(usize, &str)>)> {
bags
}
/// Find out which bags can hold the [`TARGET`] bag.
fn can_hold_target_bag<'a>(
target: &'a str,
bags: &[(&'a str, Vec<(usize, &'a str)>)],
@ -44,6 +50,7 @@ fn can_hold_target_bag<'a>(
can_hold
}
/// Find out how many bags are required to be inside the [`TARGET`] bag.
fn target_bag_holds_amount<'a>(
target: &'a str,
bags: &[(&'a str, Vec<(usize, &'a str)>)],
@ -64,6 +71,7 @@ fn target_bag_holds_amount<'a>(
count
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(
can_hold_target_bag(TARGET, &parse_bags(input))
@ -74,6 +82,7 @@ fn part_1(input: &str) -> Result<String> {
)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
Ok(target_bag_holds_amount("shiny gold", &parse_bags(input)).to_string())
}

View File

@ -1,13 +1,20 @@
//! Day 08 of 2020.
use crate::prelude::*;
/// Get the solution for day 08 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(8, 2020), part_1, part_2).with_expected(1814, 1056)
}
/// The possible operations an instruction can perform.
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
enum Operation {
/// Increase or decrease the accumulator.
Acc,
/// Jump to a new instruction.
Jmp,
/// Do nothing, no operation.
Nop,
}
@ -22,12 +29,17 @@ impl From<&str> for Operation {
}
}
/// Type alias for the arguments of operations.
type Argument = i32;
/// A single instruction.
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
struct Instruction {
/// The line the instruction was parsed from, used for debugging.
line: usize,
/// The operation to perform.
op: Operation,
/// The argument for the operation.
arg: Argument,
}
@ -42,6 +54,8 @@ impl From<(usize, &str)> for Instruction {
}
}
/// Execute the list of instructions and return the accumulator and whether or
/// not an infinite loop was encountered.
fn execute(instructions: &[Instruction]) -> (i32, bool) {
let mut seen_instructions = HashSet::new();
@ -69,14 +83,17 @@ fn execute(instructions: &[Instruction]) -> (i32, bool) {
(accumulator, encountered_infinite_loop)
}
/// Parse the list of instructions from the puzzle input.
fn parse_instructions(input: &str) -> Vec<Instruction> {
input.lines().enumerate().map(Into::into).collect()
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(execute(&parse_instructions(input)).0.to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let instructions = parse_instructions(input);
let mut part_2_result = None;

View File

@ -1,12 +1,17 @@
//! Day 09 of 2020.
use crate::prelude::*;
/// Get the solution for day 09 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(9, 2020), part_1, part_2)
.with_expected(22477624, 2980044)
}
/// The amount of numbers to consider as the preamble.
const PREAMBLE_LENGTH: usize = 25;
/// Parse the puzzle input into a list of numbers.
fn parse_numbers(input: &str) -> Vec<usize> {
input
.lines()
@ -14,6 +19,7 @@ fn parse_numbers(input: &str) -> Vec<usize> {
.collect()
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let numbers = parse_numbers(input);
let mut result_one = 0;
@ -47,6 +53,7 @@ fn part_1(input: &str) -> Result<String> {
Ok(result_one.to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let numbers = parse_numbers(input);
let result_one = part_1(input)?;

View File

@ -1,10 +1,14 @@
//! Day 10 of 2020.
use crate::prelude::*;
/// Get the solution for day 10 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(10, 2020), part_1, part_2)
.with_expected(2368, 1727094849536_i64)
}
/// Parse the puzzle input and return the intervals and streaks of the adapters.
fn parse_numbers(input: &str) -> (HashMap<usize, i32>, HashMap<i32, u32>) {
let mut numbers = input
.lines()
@ -35,11 +39,13 @@ fn parse_numbers(input: &str) -> (HashMap<usize, i32>, HashMap<i32, u32>) {
(intervals, streaks)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let (intervals, _) = parse_numbers(input);
Ok((intervals.get(&1).unwrap() * intervals.get(&3).unwrap()).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let (_, streaks) = parse_numbers(input);
Ok(

View File

@ -1,14 +1,24 @@
//! [`Grid`], [`Cell`] and [`Ruleset`] code.
/// The different types of cell available in the [`Grid`].
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Cell {
/// The cell is an edge of the grid.
Edge,
/// The cell is a floor tile.
Floor,
/// The cell is an empty seat.
Empty,
/// The cell is an occupied seat.
Occupied,
}
/// The different rulesets for part one and two.
#[derive(Debug, Eq, PartialEq)]
pub enum Ruleset {
/// The ruleset for part one.
PartOne,
/// The ruleset for part two.
PartTwo,
}
@ -24,16 +34,23 @@ impl From<char> for Cell {
}
}
/// The grid of seats.
#[derive(Debug)]
pub struct Grid {
/// The length of lines from the puzzle input.
pub line_length: isize,
/// The amount of [`Cell`]s that are occupied.
pub occupied_cell_count: usize,
/// How tolerant people are for seats to become empty.
pub occupied_cell_tolerance: usize,
/// The list of [`Cell`]s.
pub cells: Vec<Cell>,
/// Which [`Ruleset`] is being used.
pub ruleset: Ruleset,
}
impl Grid {
/// Create a new grid based on the puzzle input and which [`Ruleset`] to use.
pub fn new(input: &str, ruleset: Ruleset) -> Self {
let line_length = input.find('\n').unwrap() + 2;
@ -62,6 +79,7 @@ impl Grid {
}
}
/// Simulate a single step and return the new list of [`Cell`]s.
pub fn simulate_step(&self) -> Vec<Cell> {
let mut cells = self.cells.clone();
@ -137,11 +155,12 @@ impl Grid {
cells
}
/// Count the amount of [`Cell`]s matching a given target.
pub fn count_cells(&self, target: Cell) -> usize {
self.cells.iter().filter(|cell| cell == &&target).count()
}
// Useful for debugging.
/// Draw the grid, used for debugging.
pub fn _draw(&self) -> String {
let mut result = String::new();
for (index, cell) in self.cells.iter().enumerate() {

View File

@ -1,13 +1,17 @@
//! Day 11 of 2020.
use crate::prelude::*;
mod grid;
use grid::*;
/// Get the solution for day 11 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(11, 2020), part_1, part_2).with_expected(2243, 2027)
}
/// Calculate the result based on the ruleset.
fn calculate(input: &str, ruleset: Ruleset) -> usize {
let mut grid = Grid::new(input, ruleset);
@ -28,10 +32,12 @@ fn calculate(input: &str, ruleset: Ruleset) -> usize {
previous_occupied_cell_count.unwrap()
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(calculate(input, Ruleset::PartOne).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
Ok(calculate(input, Ruleset::PartTwo).to_string())
}

View File

@ -1,11 +1,21 @@
//! [`Action`] and [`Instruction`] code.
/// The list of actions the ship can take.
#[derive(Debug, Eq, PartialEq)]
pub enum Action {
/// Move North by a given value.
North,
/// Move East by a given value.
East,
/// Move South by a given value.
South,
/// Move West by a given value.
West,
/// Turn left by a given number of degrees.
Left,
/// Turn right by a given number of degrees.
Right,
/// Move forward by a given value in the direction the ship is facing.
Forward,
}
@ -24,9 +34,12 @@ impl From<char> for Action {
}
}
/// The combination of an [`Action`] and the value for it.
#[derive(Debug)]
pub struct Instruction {
/// Which action to take.
pub action: Action,
/// The amount to use for that action.
pub value: i32,
}

View File

@ -1,17 +1,22 @@
//! Day 12 of 2020.
use crate::prelude::*;
mod extra;
use extra::*;
/// Get the solution for day 12 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(12, 2020), part_1, part_2).with_expected(1496, 63843)
}
/// Parse the list of [`Instruction`]s based on the puzzle input.
fn parse_instructions(input: &str) -> Vec<Instruction> {
input.lines().map(Into::into).collect()
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let instructions = parse_instructions(input);
let mut horizontal = 0;
@ -48,6 +53,7 @@ fn part_1(input: &str) -> Result<String> {
Ok((horizontal.abs() + vertical.abs()).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let instructions = parse_instructions(input);

View File

@ -1,10 +1,14 @@
//! Day 13 of 2020.
use crate::prelude::*;
/// Get the solution for day 13 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(13, 2020), part_1, part_2)
.with_expected(1895, 840493039281088_i64)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let mut lines = input.lines();
@ -33,6 +37,7 @@ fn part_1(input: &str) -> Result<String> {
Ok((earliest_bus * difference).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
// Skip the first line, as it isn't used for the second part of the puzzle.
let input = input.lines().nth(1).unwrap();

View File

@ -1,14 +1,18 @@
//! Day 14 of 2020.
use crate::prelude::*;
mod operation;
use operation::*;
/// Get the solution for day 14 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(14, 2020), part_1, part_2)
.with_expected(9967721333886_i64, 4355897790573_i64)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let operations = input.lines().map(Into::into).collect::<Vec<Operation>>();
@ -35,6 +39,7 @@ fn part_1(input: &str) -> Result<String> {
Ok(memory.values().sum::<i64>().to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let operations = input.lines().map(Into::into).collect::<Vec<Operation>>();
@ -95,6 +100,7 @@ fn part_2(input: &str) -> Result<String> {
Ok(memory.values().sum::<i64>().to_string())
}
/// Loop over the input mask and replace any floating bits.
fn combine(input: String) -> Vec<String> {
let mut result = vec![];

View File

@ -1,6 +1,11 @@
//! [`Operation`] parsing code.
/// The list of operations to perform.
#[derive(Debug)]
pub enum Operation {
/// Set the mask using a given string.
SetMask(String),
/// Set the memory of an address to a given value.
SetMemory(i64, i64),
}

View File

@ -1,9 +1,13 @@
//! Day 15 of 2020.
use crate::prelude::*;
/// Get the solution for day 15 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(15, 2020), part_1, part_2).with_expected(441, 10613991)
}
/// Solve the puzzle for a given target.
fn solve(input: &str, target: usize) -> isize {
let mut numbers = HashMap::new();
let mut previous_number = (0, (0, 0));
@ -33,10 +37,12 @@ fn solve(input: &str, target: usize) -> isize {
*numbers.iter().max_by(|a, b| a.1 .1.cmp(&b.1 .1)).unwrap().0
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(solve(input, 2020).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
Ok(solve(input, 30000000).to_string())
}

View File

@ -1,10 +1,14 @@
//! Day 16 of 2020.
use crate::prelude::*;
/// Get the solution for day 16 of 2020.
pub fn solution() -> Solution {
Solution::new(Day::new(16, 2020), part_1, part_2)
.with_expected(26980, 3021381607403_i64)
}
/// Solve the puzzle.
fn solve(input: &str, return_part_1: bool) -> Result<String> {
let mut parts = input.trim().split("\n\n");
let mut ranges = vec![];
@ -133,10 +137,12 @@ fn solve(input: &str, return_part_1: bool) -> Result<String> {
Ok(result_two)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
solve(input, true)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
solve(input, false)
}

View File

@ -1,3 +1,5 @@
//! Solutions for Advent of Code 2020.
use crate::solution::Solution;
mod day_01;
@ -17,6 +19,7 @@ mod day_14;
mod day_15;
mod day_16;
/// Get all the solutions of 2020 as a [`Vec<Solution>`].
pub fn get_solutions() -> Vec<Solution> {
vec![
day_01::solution(),

View File

@ -1,9 +1,13 @@
//! Day 01 of 2021.
use crate::prelude::*;
/// Get the solution for day 01 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(1, 2021), part_1, part_2).with_expected(1451, 1395)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(
parse_measurements(input)?
@ -15,6 +19,7 @@ fn part_1(input: &str) -> Result<String> {
)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
Ok(
parse_measurements(input)?
@ -26,6 +31,7 @@ fn part_2(input: &str) -> Result<String> {
)
}
/// Parse the measurements from the puzzle input.
fn parse_measurements(input: &str) -> Result<Vec<i32>> {
input
.lines()

View File

@ -1,14 +1,21 @@
//! Day 02 of 2021.
use crate::prelude::*;
/// Get the solution for day 02 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(2, 2021), part_1, part_2)
.with_expected(1727835, 1544000595)
}
/// The possible commands to execute.
#[derive(Debug)]
enum Command {
/// Move forward a given amount.
Forward(i32),
/// Move down a given amount.
Down(i32),
/// Move up a given amount.
Up(i32),
}
@ -34,14 +41,19 @@ impl FromStr for Command {
}
}
/// The submarine data.
#[derive(Debug, Default)]
struct Submarine {
/// The direction the submarine is aiming at.
aim: i32,
/// The depth of the submarine is at.
depth: i32,
/// The horizontal position of the submarine.
horizontal_position: i32,
}
impl Submarine {
/// Execute the [`Command`] according to the logic for part one.
fn execute_command_1(&mut self, command: Command) {
match command {
Command::Forward(amount) => self.horizontal_position += amount,
@ -50,6 +62,7 @@ impl Submarine {
}
}
/// Execute the [`Command`] according to the logic for part two.
fn execute_command_2(&mut self, command: Command) {
match command {
Command::Forward(amount) => {
@ -61,15 +74,18 @@ impl Submarine {
}
}
/// Calculate the final result.
fn final_result(&self) -> i32 {
self.horizontal_position * self.depth
}
}
/// Parse the list of [`Command`]s from the puzzle input.
fn parse_commands(input: &str) -> Result<Vec<Command>> {
input.lines().map(Command::from_str).collect::<Result<_>>()
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let mut submarine = Submarine::default();
@ -80,6 +96,7 @@ fn part_1(input: &str) -> Result<String> {
Ok(submarine.final_result().to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let mut submarine = Submarine::default();

View File

@ -1,10 +1,14 @@
//! Day 03 of 2021.
use crate::prelude::*;
/// Get the solution for day 03 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(3, 2021), part_1, part_2)
.with_expected(1092896, 4672151)
}
/// Count the bits for each line of the puzzle input.
fn count_bits(input: &str) -> Result<Vec<(usize, i32)>> {
let mut bits = HashMap::<usize, i32>::new();
@ -28,6 +32,7 @@ fn count_bits(input: &str) -> Result<Vec<(usize, i32)>> {
)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let bits = count_bits(input)?;
@ -51,6 +56,7 @@ fn part_1(input: &str) -> Result<String> {
Ok((gamma_rate * epsilon_rate).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let mut most_common_lines = input.lines().collect::<Vec<_>>();
let mut least_common_lines = input.lines().collect::<Vec<_>>();

View File

@ -1,12 +1,17 @@
//! [`Bingo`] code and implementation.
use std::str::FromStr;
use color_eyre::eyre::{eyre, Error};
use super::board::Board;
/// The game of bingo to play with its boards and numbers.
#[derive(Debug)]
pub struct Bingo {
/// The list of [`Board`]s to play bingo with.
pub boards: Vec<Board>,
/// The list of numbers to play.
pub numbers: Vec<i32>,
}

View File

@ -1,3 +1,5 @@
//! [`Board`] code and implementation.
use std::str::FromStr;
use color_eyre::eyre::Error;
@ -18,9 +20,13 @@ const WIN_MATRIX: &[&[usize]] = &[
&[4, 9, 14, 19, 24],
];
/// A single bingo board.
#[derive(Debug, Clone)]
pub struct Board {
/// Whether this board has at least one line filled.
pub has_won: bool,
/// The state of the board, with indexes determining the position and whether
/// that position has been filled.
pub state: Vec<(i32, bool)>,
}

View File

@ -1,3 +1,5 @@
//! Day 04 of 2021.
mod bingo;
mod board;
@ -5,16 +7,19 @@ use bingo::Bingo;
use crate::prelude::*;
/// Get the solution for day 04 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(4, 2021), part_1, part_2).with_expected(8580, 9576)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let (winning_board, latest_number) =
Bingo::from_str(input)?.play_until_first_win();
Ok((winning_board.sum_unmarked() * latest_number).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let (winning_board, latest_number) =
Bingo::from_str(input)?.play_until_last_win();

View File

@ -1,16 +1,23 @@
//! Day 05 of 2021.
use crate::prelude::*;
/// Get the solution for day 05 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(5, 2021), part_1, part_2).with_expected(6687, 19851)
}
/// A vector of two sets of coordinates.
#[derive(Debug)]
struct Vector {
/// The starting coordinate.
a: (isize, isize),
/// The ending coordinate.
b: (isize, isize),
}
impl Vector {
/// Calculate the covered diagonal coordinates of the [`Vector`].
fn diagonal_coordinates(&self) -> Vec<(isize, isize)> {
let x_coordinates = {
let x_amount = (self.a.0 - self.b.0).abs();
@ -33,6 +40,7 @@ impl Vector {
x_coordinates.into_iter().zip(y_coordinates).collect()
}
/// Calculate the covered horizontal coordinates of the [`Vector`].
fn horizontal_coordinates(&self) -> Vec<(isize, isize)> {
let y = self.a.1;
@ -45,6 +53,7 @@ impl Vector {
(x1..=x2).map(|x| (x, y)).collect()
}
/// Calculate the covered vertical coordinates of the [`Vector`].
fn vertical_coordinates(&self) -> Vec<(isize, isize)> {
let x = self.a.0;
@ -79,6 +88,7 @@ impl FromStr for Vector {
}
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let vectors = input
.lines()
@ -110,6 +120,7 @@ fn part_1(input: &str) -> Result<String> {
Ok(count_result(travelled_coordinates).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let vectors = input
.lines()
@ -135,6 +146,7 @@ fn part_2(input: &str) -> Result<String> {
Ok(count_result(travelled_coordinates).to_string())
}
/// Count the results of the coordinates that overlap at least two lines.
fn count_result(coordinates: HashMap<(isize, isize), isize>) -> usize {
coordinates
.into_iter()

View File

@ -1,12 +1,17 @@
//! Day 06 of 2021.
use crate::prelude::*;
/// Shorthand for a [`HashMap`] using [`isize`]s.
type FishMap = HashMap<isize, isize>;
/// Get the solution for day 06 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(6, 2021), part_1, part_2)
.with_expected(343441, 1569108373832_i64)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let mut fishes = parse_fishes(input)?;
@ -17,6 +22,7 @@ fn part_1(input: &str) -> Result<String> {
Ok(count_fishes(fishes).to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let mut fishes = parse_fishes(input)?;
@ -27,6 +33,7 @@ fn part_2(input: &str) -> Result<String> {
Ok(count_fishes(fishes).to_string())
}
/// Parse the [`FishMap`]s from the puzzle input.
fn parse_fishes(input: &str) -> Result<FishMap> {
let mut fishes = FishMap::new();
let individual_fishes = input
@ -42,10 +49,12 @@ fn parse_fishes(input: &str) -> Result<FishMap> {
Ok(fishes)
}
/// Get the sum of the [`FishMap`] values.
fn count_fishes(fishes: FishMap) -> isize {
fishes.values().sum()
}
/// Simulate the fishes according to the puzzle's instructions.
fn simulate_fishes(fishes: FishMap) -> FishMap {
let mut new_fishes = HashMap::new();

View File

@ -1,10 +1,14 @@
//! Day 07 of 2021.
use crate::prelude::*;
/// Get the solution for day 07 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(7, 2021), part_1, part_2)
.with_expected(328318, 89791146)
}
/// Parse the list and highest number of crabs from the puzzle input.
fn parse_crabs(input: &str) -> Result<(Vec<isize>, isize)> {
let crabs = input
.split(',')
@ -19,6 +23,7 @@ fn parse_crabs(input: &str) -> Result<(Vec<isize>, isize)> {
Ok((crabs, highest_crab))
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let (crabs, highest_crab) = parse_crabs(input)?;
(0..=highest_crab)
@ -33,6 +38,7 @@ fn part_1(input: &str) -> Result<String> {
.ok_or_else(|| eyre!("Unable to find lowest fuel"))
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let (crabs, highest_crab) = parse_crabs(input)?;
(0..=highest_crab)

View File

@ -1,17 +1,23 @@
//! [`Display`] implementation.
use std::collections::{HashMap, HashSet};
use color_eyre::Result;
/// Type alias for [`HashSet`] with [`char`]s.
pub type CharSet = HashSet<char>;
/// The display with a given size and set of characters.
#[derive(Debug)]
pub struct Display(pub isize, pub CharSet);
impl Display {
/// Parse a [`Display`] from a string.
pub fn parse(s: &str) -> Self {
Self(s.len() as isize, CharSet::from_iter(s.chars()))
}
/// Calculate the remaining displays based on the existing other ones.
pub fn figure_out_from_others(
others: Vec<Self>,
) -> Result<HashMap<isize, CharSet>> {

View File

@ -1,13 +1,17 @@
//! Day 08 of 2021.
mod display;
use display::{CharSet, Display};
use crate::prelude::*;
/// Get the solution for day 08 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(8, 2021), part_1, part_2).with_expected(521, 1016804)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
Ok(
input
@ -27,6 +31,7 @@ fn part_1(input: &str) -> Result<String> {
)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let mut sum = 0;

View File

@ -1,13 +1,19 @@
//! Day 09 of 2021.
use crate::prelude::*;
/// Get the solution for day 09 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(9, 2021), part_1, part_2).with_expected(600, 987840)
}
/// Type alias for an [`isize`] tuple.
type Coordinate = (isize, isize);
/// Type alias for a [`HashMap`] with [`Coordinate`]s pointing to a value.
type HeightMap = HashMap<Coordinate, isize>;
/// Constants for coordinate offsets to adjacent positions.
const ADJACENT_OFFSETS: &[Coordinate] = &[
(-1, 0), // Left
(1, 0), // Right
@ -15,6 +21,7 @@ const ADJACENT_OFFSETS: &[Coordinate] = &[
(0, 1), // Down
];
/// Parse the [`HeightMap`] from the puzzle input.
fn parse_heightmap(input: &str) -> Result<HeightMap> {
let mut height_map = HeightMap::new();
@ -32,6 +39,7 @@ fn parse_heightmap(input: &str) -> Result<HeightMap> {
Ok(height_map)
}
/// Parse the map bounds uzing the puzzle input.
fn parse_map_bounds(input: &str) -> Result<Coordinate> {
Ok((
input
@ -44,6 +52,7 @@ fn parse_map_bounds(input: &str) -> Result<Coordinate> {
))
}
/// Discover all the basins according to the puzzle instructions.
fn discover_basins(
coordinate: Coordinate,
discovered_coordinates: &mut Vec<Coordinate>,
@ -74,6 +83,7 @@ fn discover_basins(
coordinates
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let height_map = parse_heightmap(input)?;
let map_bounds = parse_map_bounds(input)?;
@ -103,6 +113,7 @@ fn part_1(input: &str) -> Result<String> {
Ok(risk_level.to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let height_map = parse_heightmap(input)?;
let map_bounds = parse_map_bounds(input)?;

View File

@ -1,10 +1,14 @@
//! Day 10 of 2021.
use crate::prelude::*;
/// Get the solution for day 10 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(10, 2021), part_1, part_2)
.with_expected(387363, 4330777059_i64)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let mut syntax_error_score = 0;
@ -40,6 +44,7 @@ fn part_1(input: &str) -> Result<String> {
Ok(syntax_error_score.to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let mut scores = vec![];

View File

@ -1,22 +1,33 @@
//! [`Point`], [`Fold`] and [`Canvas`] implementation.
use std::{collections::HashSet, fmt::Display};
/// A point coordinate.
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Point(pub isize, pub isize);
/// The types of fold that can be made.
#[derive(Debug)]
pub enum Fold {
/// A horizontal fold going left.
Horizontal(isize),
/// A vertical fold going up.
Vertical(isize),
}
/// The canvas of the paper.
#[derive(Debug)]
pub struct Canvas {
/// The width of the canvas.
pub width: isize,
/// The height of the canvas.
pub height: isize,
/// The points on the canvas.
pub points: HashSet<Point>,
}
impl Canvas {
/// Perform a fold on the canvas and return the resulting version of it.
pub fn fold(self, fold: &Fold) -> Self {
let (width, height) = match fold {
Fold::Horizontal(amount) => (self.width, *amount),

View File

@ -1,9 +1,12 @@
//! Day 13 of 2021.
mod canvas;
use canvas::{Canvas, Fold, Point};
use crate::prelude::*;
/// Get the solution for day 13 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(13, 2021), part_1, part_2).with_expected(
"712",
@ -17,6 +20,7 @@ pub fn solution() -> Solution {
)
}
/// Parse the canvas and list of folds to perform from the puzzle input.
fn parse(input: &str) -> Result<(Canvas, Vec<Fold>)> {
let mut canvas = Canvas {
width: 0,
@ -74,12 +78,14 @@ fn parse(input: &str) -> Result<(Canvas, Vec<Fold>)> {
Ok((canvas, folds))
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let (mut canvas, folds) = parse(input)?;
canvas = canvas.fold(&folds[0]);
Ok(canvas.points.len().to_string())
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let (mut canvas, folds) = parse(input)?;

View File

@ -1,13 +1,19 @@
//! Day 14 of 2021.
use crate::prelude::*;
/// Type alias for a map of pairs.
type PairMap = HashMap<(char, char), char>;
/// Type alias for a map of pair counts.
type PairCounts = HashMap<(char, char), isize>;
/// Get the solution for day 14 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(14, 2021), part_1, part_2)
.with_expected(3411, 7477815755570_i64)
}
/// Parse the puzzle input into the template and map of pairs.
fn parse(input: &str) -> Result<(String, PairMap)> {
let mut lines = input.lines();
let template = lines
@ -30,6 +36,7 @@ fn parse(input: &str) -> Result<(String, PairMap)> {
Ok((template, pairs))
}
/// Apply the counts to the pairs and return the new counts to add.
fn apply(counts: &PairCounts, pairs: &PairMap) -> PairCounts {
let mut to_add = PairCounts::new();
@ -43,6 +50,7 @@ fn apply(counts: &PairCounts, pairs: &PairMap) -> PairCounts {
to_add
}
/// Count the totals of the pair counts.
fn count_totals(counts: &PairCounts) -> HashMap<char, isize> {
let mut totals = HashMap::new();
@ -55,6 +63,7 @@ fn count_totals(counts: &PairCounts) -> HashMap<char, isize> {
totals
}
/// Solve the puzzle for a given amount of steps.
fn run(input: &str, steps: isize) -> Result<String> {
let (template, pairs) = parse(input)?;
@ -80,10 +89,12 @@ fn run(input: &str, steps: isize) -> Result<String> {
Ok((max - min).to_string())
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
run(input, 10)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
run(input, 40)
}

View File

@ -1,19 +1,26 @@
//! Day 15 of 2021.
use crate::prelude::*;
/// Get the solution for day 15 of 2021.
pub fn solution() -> Solution {
Solution::new(Day::new(15, 2021), part_1, part_2).with_expected(386, 2806)
}
/// Type alias for a map of coordinates and risk levels.
type Grid = HashMap<Coordinate, isize>;
/// A coordinate in the grid.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
struct Coordinate(isize, isize);
impl Coordinate {
/// Get the distance between two [`Coordinate`]s.
fn distance(&self, target: &Coordinate) -> usize {
self.0.abs_diff(target.0) + self.1.abs_diff(target.1)
}
/// Calculate the successors of the grid based on this [`Coordinate`].
fn successors(&self, grid: &Grid) -> Vec<(Coordinate, isize)> {
let &Coordinate(x, y) = self;
let mut successors = vec![];
@ -33,6 +40,7 @@ impl Coordinate {
}
}
/// Parse the [`Grid`] and end [`Coordinate`] from the puzzle input.
fn parse(input: &str) -> Result<(Grid, Coordinate)> {
let mut grid = Grid::new();
let mut end = Coordinate(0, 0);
@ -53,6 +61,8 @@ fn parse(input: &str) -> Result<(Grid, Coordinate)> {
Ok((grid, end))
}
/// Enlarge the grid by five times its size, accounting for the additional rules
/// specified in the puzzle.
fn enlarge_grid(grid: Grid, end: Coordinate) -> (Grid, Coordinate) {
let Coordinate(width, height) = end;
let mut larger_grid = grid.clone();
@ -74,6 +84,7 @@ fn enlarge_grid(grid: Grid, end: Coordinate) -> (Grid, Coordinate) {
(larger_grid, Coordinate(width * 5, height * 5))
}
/// Solve the grid using [`astar`].
fn run(grid: Grid, end: Coordinate) -> Result<String> {
Ok(
astar(
@ -88,11 +99,13 @@ fn run(grid: Grid, end: Coordinate) -> Result<String> {
)
}
/// The logic to solve part one.
fn part_1(input: &str) -> Result<String> {
let (grid, end) = parse(input)?;
run(grid, end)
}
/// The logic to solve part two.
fn part_2(input: &str) -> Result<String> {
let (grid, end) = parse(input)?;
let (grid, end) = enlarge_grid(grid, end);

View File

@ -1,3 +1,5 @@
//! Solutions for Advent of Code 2021.
use crate::solution::Solution;
mod day_01;
@ -14,6 +16,7 @@ mod day_13;
mod day_14;
mod day_15;
/// Get all the solutions of 2021 as a [`Vec<Solution>`].
pub fn get_solutions() -> Vec<Solution> {
vec![
day_01::solution(),