Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solutions/2016 #9

Draft
wants to merge 37 commits into
base: main
Choose a base branch
from
Draft
Changes from 14 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6f61fb4
2016 Day 1 Part 1
alycda Jan 20, 2025
8faa724
2016 Day 1 Part 2 WIP
alycda Jan 20, 2025
6e6eb76
2016 Day 1 Part 2
alycda Jan 20, 2025
d02206f
2016 Day 2 Part 1
alycda Jan 21, 2025
8d53365
2016 Day 2 Part 2
alycda Jan 21, 2025
c1031f6
2016 Day 2
alycda Jan 21, 2025
a142d4d
2016 Day 3 Part 1
alycda Jan 21, 2025
7177645
2016 Day 3 Part 2
alycda Jan 21, 2025
d44a58a
Merge branch 'main' into solutions/2016
alycda Jan 22, 2025
9000280
bump patch
alycda Jan 22, 2025
8483a64
2016 Day 4 Part 1 WIP
alycda Jan 23, 2025
51293c7
2016 Day 4 Part 1
alycda Jan 23, 2025
b23756d
2016 Day 4 Part 2
alycda Jan 23, 2025
ec7e1ed
bacon
alycda Jan 23, 2025
9f17cac
2016 Day 6 WIP
alycda Jan 23, 2025
ceae170
2016 Day 6
alycda Jan 23, 2025
4e0c083
2016 Day 7 WIP
alycda Jan 24, 2025
1f1bc92
2016 Day 7 WIP
alycda Jan 24, 2025
dceffd1
2016 Day 5
alycda Jan 24, 2025
97c4769
2016 Day 7 Part 1
alycda Jan 25, 2025
d686ca6
2016 Day 8 WIP
alycda Jan 26, 2025
b430107
2016 Day 8 WIP
alycda Jan 26, 2025
f5988ac
2016 Day 8 WIP
alycda Jan 27, 2025
547086d
2016 Day 8 WIP
alycda Jan 27, 2025
49b2a40
2016 Day 8 Part 1
alycda Jan 27, 2025
0e11674
2016 Day 8 Part 2
alycda Jan 27, 2025
e68a3ef
2016 Day 8
alycda Jan 27, 2025
513e222
2016 Day 7 Part 2
alycda Jan 28, 2025
0cfeef8
2016 Day 10 WIP
alycda Jan 28, 2025
12bcfb3
2016 Day 10 WIP
alycda Jan 28, 2025
b5e1164
2016 Day 10 WIP
alycda Jan 28, 2025
13d6af1
2016 Day 10 WIP
alycda Jan 28, 2025
bfc6999
2016 Day 10 WIP
alycda Jan 28, 2025
88b733a
2016 Day 10 WIP
alycda Jan 29, 2025
34424dc
2016 Day 10 WIP
alycda Jan 29, 2025
2a12319
2016 Day 10 Part 1
alycda Jan 29, 2025
0450c85
2016 Day 10 Part 2 (needs finishing/cleanup)
alycda Jan 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,267 changes: 2,164 additions & 103 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "aoc-ornaments"
description = "Advent of Code tools"
version = "0.2.0"
version = "0.2.1"
edition = "2024"
rust-version = "1.85"
authors = ["Alyssa Evans <alyda@me.com>"]
@@ -25,4 +25,5 @@ rand = "0.8"
serde_json = "1.0"

[dev-dependencies]
bacon = "3.8"
rstest = "0.24"
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Advent of Code

see the [Examples](./examples/) and [Solutions](./src/bin/) for more.
## Tests

`cargo install --locked bacon`
`bacon test -- --bin BINARY_NAME`

---

see the [Examples](./examples/) and [Solutions](./src/bin/) for more.
25 changes: 5 additions & 20 deletions src/bin/2015-12-02.rs
Original file line number Diff line number Diff line change
@@ -2,11 +2,9 @@

use std::str::FromStr;

use aoc_ornaments::{Part, Solution};
use aoc_ornaments::{nom::parse_dimensions, Part, Solution};

use nom::{
character::complete::{char, u32}, multi::separated_list1, IResult
};
use nom::IResult;

type Width = u32;
type Length = u32;
@@ -26,29 +24,16 @@ impl FromStr for Day {
/// - `2x3x4` has dimensions `2`, `3`, and `4`.
///
fn from_str(input: &str) -> miette::Result<Self> {
let parsed = input.lines()
Ok(Self(input.lines()
.map(|line| {
let (_, (l, w, h)) = Self::parse_dimensions(line).unwrap();
let (_, (l, w, h)) = parse_dimensions(line).unwrap();

(l, w, h)
}).collect();

Ok(Self(parsed))
}).collect()))
}
}

impl Day {
fn parse_dimensions(input: &str) -> IResult<&str, (u32, u32, u32)> {
let (input, nums) = separated_list1(char('x'), u32)(input)?;
match nums.as_slice() {
[l, w, h] => Ok((input, (*l, *w, *h))),
_ => Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::LengthValue
)))
}
}

fn dimensions(sides: (u32, u32, u32)) -> u32 {
let (l, w, h) = sides;
let a = l * w;
121 changes: 121 additions & 0 deletions src/bin/2016-12-01.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! # Day 1: No Time for a Taxicab
//!
//! Manhattan Distance
//! Cycle Detection
//!

use std::str::FromStr;

use aoc_ornaments::{spatial::{manhattan_distance, Direction, Position}, Part, Solution};
use nom::{character::complete::{digit1, one_of}, sequence::tuple, IResult};

#[derive(Debug, derive_more::Deref)]
struct Day(Vec<Position>);

impl FromStr for Day {
type Err = miette::Error;

fn from_str(input: &str) -> miette::Result<Self> {
let mut direction = Direction::Up;

Ok(Day(input.split(", ")
.map(|turn| {
let (_, (turn, distance)) = Self::parse_direction(turn)
.expect("parse error");

match turn {
'L' => direction = direction.turn_left(),
'R' => direction = direction.turn_right(),
_ => unreachable!()
}

(direction, distance)
})
.map(|(direction, distance)| {
match direction {
Direction::Up | Direction::Down => {
Position::new(0, distance) * direction.to_offset()
},
Direction::Left | Direction::Right => {
Position::new(distance, 0) * direction.to_offset()
},
}
})
.collect::<Vec<_>>()))
}
}

impl Day {
fn parse_direction(input: &str) -> IResult<&str, (char, i32)> {
tuple((one_of("LR"), digit1))(input)
.map(|(input, (turn, distance))| (input, (turn, distance.parse().unwrap())))
}
}

impl Solution for Day {
type Output = i32;

fn part1(&mut self) -> miette::Result<Self::Output> {
Ok(manhattan_distance(&Position::ZERO, &self.iter().sum()))
}

fn part2(&mut self) -> miette::Result<Self::Output> {
let mut visited = std::collections::HashSet::new();
let current = Position::ZERO;
visited.insert(Position::ZERO);

Ok(self.iter()
.flat_map(|straight_line| {
// Break down movement into unit steps
let unit = match (straight_line.x, straight_line.y) {
(x, 0) => Position::new(x.signum(), 0),
(0, y) => Position::new(0, y.signum()),
_ => unreachable!(),
};
let steps = straight_line.x.abs().max(straight_line.y.abs());
(0..steps).map(move |_| unit)
})
.scan(current, |pos, step| {
*pos += step;
Some(*pos)
})
.find(|pos| !visited.insert(*pos))
.map(|pos| manhattan_distance(&Position::ZERO, &pos))
// can't find the Easter Bunny HQ
.expect("no position visited twice"))

}
}

fn main() -> miette::Result<()> {
let mut day = Day::from_str(include_str!("../inputs/2016-12-01.txt"))?;
let part1 = day.solve(Part::One)?;
let part2 = day.solve(Part::Two)?;

println!("Part 1: {}", part1);
println!("Part 2: {}", part2);

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

use rstest::rstest;

#[rstest]
#[case("R2, L3", 5)]
#[case("R2, R2, R2", 2)]
#[case("R5, L5, R5, R3", 12)]
fn part_1(#[case] input: &str, #[case] expected: i32) {
let mut day = Day::from_str(input).unwrap();
assert_eq!(day.solve(Part::One).unwrap(), expected.to_string());
}

#[test]
fn part_2() {
let mut day = Day::from_str("R8, R4, R4, R8").unwrap();
assert_eq!(day.solve(Part::Two).unwrap(), "4");
}
}
185 changes: 185 additions & 0 deletions src/bin/2016-12-02.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//! # Day 2: Bathroom Security
//!

use std::{collections::HashMap, str::FromStr};

use aoc_ornaments::{keypad::{ButtonPad, NumberPad3x3Telephone}, spatial::{Direction, Position}, Part, Solution};
use nom::{character::complete::one_of, multi::many0, IResult};

/// NewType impl for orphan rules
#[derive(Debug, derive_more::Deref)]
struct MyButtonPad(ButtonPad<DiamondPad>);

#[derive(Debug)]
struct DiamondPad;

impl Default for MyButtonPad {
fn default() -> Self {
let mut map = HashMap::new();
map.insert('1', Position::new(2, 0));
map.insert('2', Position::ONE);
map.insert('3', Position::new(2, 1));
map.insert('4', Position::new(3, 1));
map.insert('5', Position::new(0, 2));
map.insert('6', Position::new(1, 2));
map.insert('7', Position::splat(2));
map.insert('8', Position::new(3, 2));
map.insert('9', Position::new(4, 2));
map.insert('A', Position::new(1, 3));
map.insert('B', Position::new(2, 3));
map.insert('C', Position::splat(3));
map.insert('D', Position::new(2, 4));

Self(ButtonPad::new(map))
}
}

#[derive(Debug, derive_more::Deref)]
struct Day(Vec<Vec<Direction>>);

impl FromStr for Day {
type Err = miette::Error;

fn from_str(input: &str) -> miette::Result<Self> {
Ok(Self(input.lines()
.map(|line| {
Day::to_directions(line).unwrap().1
})
.collect()))
}
}

impl Day {
fn to_directions(input: &str) -> IResult<&str, Vec<Direction>> {
let (_, chars) = many0(one_of("ULDR"))(input)?;

Ok(("", chars.iter().map(| c | {
Direction::parse(c).unwrap()
}).collect::<Vec<_>>()))
}

// ButtonPad<> and MyButtonPad are still different types and Generics are overkill
// fn to_digits(&self, pad: ) -> Vec<usize> {
// let mut start = *pad.get(&'5').expect("Invalid start position (should be (1, 1)");

// self.iter().map(|directions| {
// for direction in directions {
// let next = start + direction.to_offset();

// if pad.values().any(|&pos| pos == next) {
// start = next;
// }
// }

// pad.iter().find(|(_, pos)| **pos == start).unwrap().0

// }).collect::<String>()
// }
}

impl Solution for Day {
type Output = String;

// `match` arms have incompatible types
// fn solve(&mut self, part: Part) -> miette::Result<String> {
// let pad = match part {
// Part::One => ButtonPad::<NumberPad3x3Telephone>::default(),
// Part::Two => MyButtonPad::default(),
// _ => unreachable!("Part 3 is not implemented"),
// };

// let mut start = *pad.get(&'5').expect("Invalid start position");

// Ok(self.iter().map(|directions| {
// for direction in directions {
// let next = start + direction.to_offset();

// if pad.values().any(|&pos| pos == next) {
// start = next;
// }
// }

// pad.iter().find(|(_, pos)| **pos == start).unwrap().0

// }).collect::<String>())
// }

fn part1(&mut self) -> miette::Result<Self::Output> {
let pad = ButtonPad::<NumberPad3x3Telephone>::default();

let mut start = *pad.get(&'5').expect("Invalid start position (should be (1, 1)");

Ok(self.iter().map(|directions| {
for direction in directions {
let next = start + direction.to_offset();

if pad.values().any(|&pos| pos == next) {
start = next;
}
}

pad.iter().find(|(_, pos)| **pos == start).unwrap().0

}).collect::<String>())
}

fn part2(&mut self) -> miette::Result<Self::Output> {
let pad = MyButtonPad::default();

let mut start = *pad.get(&'5').expect("Invalid start position (should be (0, 2)");

Ok(self.iter().map(|directions| {
for direction in directions {
let next = start + direction.to_offset();

if pad.values().any(|&pos| pos == next) {
start = next;
}
}

pad.iter().find(|(_, pos)| **pos == start).unwrap().0

}).collect::<String>())
}
}

fn main() -> miette::Result<()> {
let mut day = Day::from_str(include_str!("../inputs/2016-12-02.txt"))?;
let part1 = day.solve(Part::One)?;
let part2 = day.solve(Part::Two)?;

println!("Part 1: {}", part1);
println!("Part 2: {}", part2);

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

use rstest::rstest;

#[test]
fn part_1() {
let input = "ULL\nRRDDD\nLURDL\nUUUUD";
let mut day = Day::from_str(input).unwrap();

assert_eq!(day.solve(Part::One).unwrap(), "1985");
}

#[rstest]
#[case("ULL", "5")]
fn test_cases(#[case] input: &str, #[case] expected: &str) {
let mut day = Day::from_str(input).unwrap();
assert_eq!(day.solve(Part::Two).unwrap(), expected);
}

#[test]
fn part_2() {
let input = "ULL\nRRDDD\nLURDL\nUUUUD";
let mut day = Day::from_str(input).unwrap();

assert_eq!(day.solve(Part::Two).unwrap(), "5DB3");
}
}
Loading