Compare commits
5 commits
dbd5139991
...
5eb76e73ca
Author | SHA1 | Date | |
---|---|---|---|
5eb76e73ca | |||
0416116a65 | |||
e02dbc3577 | |||
52929d9a95 | |||
e20149a5c4 |
92
src/day_08/mod.rs
Normal file
92
src/day_08/mod.rs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
mod model;
|
||||||
|
mod parsing;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
pub use model::{Input, Ssd};
|
||||||
|
pub use parsing::parse_input as parse_input_tokens;
|
||||||
|
use yap::IntoTokens;
|
||||||
|
|
||||||
|
#[aoc_generator(day8)]
|
||||||
|
pub fn parse_input(input: &str) -> Vec<Input> {
|
||||||
|
parse_input_tokens(&mut input.into_tokens())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day8, part1)]
|
||||||
|
pub fn part1(input: &[Input]) -> usize {
|
||||||
|
let mut trivials = 0;
|
||||||
|
for input in input {
|
||||||
|
let mut ssd = Ssd::default();
|
||||||
|
for number in &input.output_values {
|
||||||
|
match ssd.try_decode(number) {
|
||||||
|
Some('1') | Some('4') | Some('7') | Some('8') => trivials += 1,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trivials
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day8, part2)]
|
||||||
|
pub fn part2(input: &[Input]) -> usize {
|
||||||
|
input
|
||||||
|
.iter()
|
||||||
|
.map(|input| {
|
||||||
|
let mut ssd = Ssd::default();
|
||||||
|
let unresolved = input.unique_combinations.clone();
|
||||||
|
|
||||||
|
for num in [2, 3, 4, 7, 6, 5].iter() {
|
||||||
|
unresolved
|
||||||
|
.iter()
|
||||||
|
.filter(|pattern| pattern.len() == *num)
|
||||||
|
.for_each(|pattern| {
|
||||||
|
ssd.try_decode(pattern).unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg!(&ssd);
|
||||||
|
|
||||||
|
let value: String = input
|
||||||
|
.output_values
|
||||||
|
.iter()
|
||||||
|
.map(|val| ssd.try_decode(val).unwrap())
|
||||||
|
.collect();
|
||||||
|
value.parse::<usize>().unwrap()
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
const EXAMPLE_INPUT: &str =
|
||||||
|
"be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe
|
||||||
|
edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc
|
||||||
|
fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg
|
||||||
|
fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb
|
||||||
|
aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea
|
||||||
|
fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb
|
||||||
|
dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe
|
||||||
|
bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef
|
||||||
|
egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb
|
||||||
|
gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce";
|
||||||
|
const RESULT_PART_1: usize = 26;
|
||||||
|
const RESULT_PART_2: usize = 61229;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
let result = super::part1(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
let result = super::part2(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_example() {
|
||||||
|
let example = super::parse_input(EXAMPLE_INPUT);
|
||||||
|
assert_eq!(example.len(), 10);
|
||||||
|
}
|
||||||
|
}
|
223
src/day_08/model.rs
Normal file
223
src/day_08/model.rs
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
pub struct Input {
|
||||||
|
pub unique_combinations: Vec<String>,
|
||||||
|
pub output_values: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Ssd {
|
||||||
|
pub a: HashSet<char>,
|
||||||
|
pub b: HashSet<char>,
|
||||||
|
pub c: HashSet<char>,
|
||||||
|
pub d: HashSet<char>,
|
||||||
|
pub e: HashSet<char>,
|
||||||
|
pub f: HashSet<char>,
|
||||||
|
pub g: HashSet<char>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Ssd {
|
||||||
|
fn default() -> Self {
|
||||||
|
let possible_pins: HashSet<char> = "abcdefg".chars().collect();
|
||||||
|
Self {
|
||||||
|
a: possible_pins.clone(),
|
||||||
|
b: possible_pins.clone(),
|
||||||
|
c: possible_pins.clone(),
|
||||||
|
d: possible_pins.clone(),
|
||||||
|
e: possible_pins.clone(),
|
||||||
|
f: possible_pins.clone(),
|
||||||
|
g: possible_pins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ssd {
|
||||||
|
pub fn try_decode(&mut self, number: &str) -> Option<char> {
|
||||||
|
let number: HashSet<char> = number.chars().collect();
|
||||||
|
match number.len() {
|
||||||
|
2 => {
|
||||||
|
self.a.retain(|c| !number.contains(c));
|
||||||
|
self.b.retain(|c| !number.contains(c));
|
||||||
|
self.c.retain(|c| number.contains(c));
|
||||||
|
self.d.retain(|c| !number.contains(c));
|
||||||
|
self.e.retain(|c| !number.contains(c));
|
||||||
|
self.f.retain(|c| number.contains(c));
|
||||||
|
self.g.retain(|c| !number.contains(c));
|
||||||
|
Some('1')
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
self.a.retain(|c| number.contains(c));
|
||||||
|
self.b.retain(|c| !number.contains(c));
|
||||||
|
self.c.retain(|c| number.contains(c));
|
||||||
|
self.d.retain(|c| !number.contains(c));
|
||||||
|
self.e.retain(|c| !number.contains(c));
|
||||||
|
self.f.retain(|c| number.contains(c));
|
||||||
|
self.g.retain(|c| !number.contains(c));
|
||||||
|
Some('7')
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
self.a.retain(|c| !number.contains(c));
|
||||||
|
self.b.retain(|c| number.contains(c));
|
||||||
|
self.c.retain(|c| number.contains(c));
|
||||||
|
self.d.retain(|c| number.contains(c));
|
||||||
|
self.e.retain(|c| !number.contains(c));
|
||||||
|
self.f.retain(|c| number.contains(c));
|
||||||
|
self.g.retain(|c| !number.contains(c));
|
||||||
|
Some('4')
|
||||||
|
}
|
||||||
|
5 => {
|
||||||
|
let full: HashSet<char> = "abcdefg".chars().collect();
|
||||||
|
let missing = full.difference(&number).copied().collect::<HashSet<char>>();
|
||||||
|
let four = self.get_number_set(4);
|
||||||
|
let one = self.get_number_set(1);
|
||||||
|
let missing_six = full
|
||||||
|
.difference(&self.get_number_set(6))
|
||||||
|
.copied()
|
||||||
|
.collect::<HashSet<char>>();
|
||||||
|
let missing_nince = full
|
||||||
|
.difference(&self.get_number_set(9))
|
||||||
|
.copied()
|
||||||
|
.collect::<HashSet<char>>();
|
||||||
|
let missing_five = missing_six
|
||||||
|
.union(&missing_nince)
|
||||||
|
.copied()
|
||||||
|
.collect::<HashSet<char>>();
|
||||||
|
if missing.is_disjoint(&one) {
|
||||||
|
self.a.retain(|c| number.contains(c));
|
||||||
|
self.b.retain(|c| !number.contains(c));
|
||||||
|
self.c.retain(|c| number.contains(c));
|
||||||
|
self.d.retain(|c| number.contains(c));
|
||||||
|
self.e.retain(|c| !number.contains(c));
|
||||||
|
self.f.retain(|c| number.contains(c));
|
||||||
|
self.g.retain(|c| number.contains(c));
|
||||||
|
Some('3')
|
||||||
|
} else if missing.is_subset(&four) {
|
||||||
|
self.a.retain(|c| number.contains(c));
|
||||||
|
self.b.retain(|c| !number.contains(c));
|
||||||
|
self.c.retain(|c| number.contains(c));
|
||||||
|
self.d.retain(|c| number.contains(c));
|
||||||
|
self.e.retain(|c| number.contains(c));
|
||||||
|
self.f.retain(|c| !number.contains(c));
|
||||||
|
self.g.retain(|c| number.contains(c));
|
||||||
|
Some('2')
|
||||||
|
} else if missing == missing_five {
|
||||||
|
self.a.retain(|c| number.contains(c));
|
||||||
|
self.b.retain(|c| number.contains(c));
|
||||||
|
self.c.retain(|c| !number.contains(c));
|
||||||
|
self.d.retain(|c| number.contains(c));
|
||||||
|
self.e.retain(|c| !number.contains(c));
|
||||||
|
self.f.retain(|c| number.contains(c));
|
||||||
|
self.g.retain(|c| number.contains(c));
|
||||||
|
Some('5')
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
let full: HashSet<char> = "abcdefg".chars().collect();
|
||||||
|
let missing = full.difference(&number).copied().collect::<HashSet<char>>();
|
||||||
|
let four = self.get_number_set(4);
|
||||||
|
let one = self.get_number_set(1);
|
||||||
|
if missing.is_disjoint(&four) {
|
||||||
|
self.a.retain(|c| number.contains(c));
|
||||||
|
self.b.retain(|c| number.contains(c));
|
||||||
|
self.c.retain(|c| number.contains(c));
|
||||||
|
self.d.retain(|c| number.contains(c));
|
||||||
|
self.e.retain(|c| !number.contains(c));
|
||||||
|
self.f.retain(|c| number.contains(c));
|
||||||
|
self.g.retain(|c| number.contains(c));
|
||||||
|
Some('9')
|
||||||
|
} else if missing.is_disjoint(&one) {
|
||||||
|
self.a.retain(|c| number.contains(c));
|
||||||
|
self.b.retain(|c| number.contains(c));
|
||||||
|
self.c.retain(|c| number.contains(c));
|
||||||
|
self.d.retain(|c| !number.contains(c));
|
||||||
|
self.e.retain(|c| number.contains(c));
|
||||||
|
self.f.retain(|c| number.contains(c));
|
||||||
|
self.g.retain(|c| number.contains(c));
|
||||||
|
Some('0')
|
||||||
|
} else {
|
||||||
|
self.a.retain(|c| number.contains(c));
|
||||||
|
self.b.retain(|c| number.contains(c));
|
||||||
|
self.c.retain(|c| !number.contains(c));
|
||||||
|
self.d.retain(|c| number.contains(c));
|
||||||
|
self.e.retain(|c| number.contains(c));
|
||||||
|
self.f.retain(|c| number.contains(c));
|
||||||
|
self.g.retain(|c| number.contains(c));
|
||||||
|
Some('6')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
7 => Some('8'),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_number_set(&self, num: usize) -> HashSet<char> {
|
||||||
|
Self::merge_sets(self.get_number_sets(num))
|
||||||
|
}
|
||||||
|
fn get_number_sets(&self, num: usize) -> Vec<HashSet<char>> {
|
||||||
|
match num {
|
||||||
|
1 => vec![&self.c, &self.f],
|
||||||
|
2 => vec![&self.a, &self.c, &self.d, &self.e, &self.g],
|
||||||
|
3 => vec![&self.a, &self.c, &self.d, &self.f, &self.g],
|
||||||
|
4 => vec![&self.b, &self.c, &self.d, &self.f],
|
||||||
|
5 => vec![&self.a, &self.b, &self.d, &self.f, &self.g],
|
||||||
|
6 => vec![&self.a, &self.b, &self.d, &self.e, &self.f, &self.g],
|
||||||
|
7 => vec![&self.a, &self.c, &self.f],
|
||||||
|
8 => vec![
|
||||||
|
&self.a, &self.b, &self.c, &self.d, &self.e, &self.f, &self.g,
|
||||||
|
],
|
||||||
|
9 => vec![&self.a, &self.b, &self.c, &self.d, &self.f, &self.g],
|
||||||
|
0 => vec![&self.a, &self.b, &self.c, &self.e, &self.f, &self.g],
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
.iter()
|
||||||
|
.map(|set| set.iter().copied().collect())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
fn merge_sets(sets: Vec<HashSet<char>>) -> HashSet<char> {
|
||||||
|
sets.iter().flat_map(HashSet::iter).copied().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::Ssd;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn try_decode_trivials() {
|
||||||
|
let mut ssd = Ssd::default();
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("cf"), Some('1'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("acf"), Some('7'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("bcdf"), Some('4'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("abcdefg"), Some('8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn try_decode() {
|
||||||
|
let mut ssd = Ssd::default();
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("gf"), Some('1'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("fgd"), Some('7'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("fgec"), Some('4'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("bdegcaf"), Some('8'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("aegbdf"), Some('0'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("ecdfab"), Some('6'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("gdcebf"), Some('9'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("dbcfg"), Some('3'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("dacgb"), Some('2'));
|
||||||
|
dbg!(&ssd);
|
||||||
|
assert_eq!(ssd.try_decode("fbedc"), Some('5'));
|
||||||
|
}
|
||||||
|
}
|
37
src/day_08/parsing.rs
Normal file
37
src/day_08/parsing.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use crate::parsing::{newline, space};
|
||||||
|
use yap::Tokens;
|
||||||
|
|
||||||
|
use super::Input;
|
||||||
|
|
||||||
|
pub fn parse_input_line(tokens: &mut impl Tokens<Item = char>) -> Option<Input> {
|
||||||
|
let unique_combinations: Vec<String> = tokens
|
||||||
|
.sep_by(|t| parse_ssd_combo(t), |t| space(t))
|
||||||
|
.collect();
|
||||||
|
tokens.tokens(" | ".chars());
|
||||||
|
let output_values: Vec<String> = tokens
|
||||||
|
.sep_by(|t| parse_ssd_combo(t), |t| space(t))
|
||||||
|
.collect();
|
||||||
|
if unique_combinations.len() == 10 && output_values.len() == 4 {
|
||||||
|
Some(super::Input {
|
||||||
|
output_values,
|
||||||
|
unique_combinations,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_ssd_combo(tokens: &mut impl Tokens<Item = char>) -> Option<String> {
|
||||||
|
let val: String = tokens.tokens_while(|t| "abcdefg".contains(*t)).collect();
|
||||||
|
if val.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_input(tokens: &mut impl Tokens<Item = char>) -> Vec<Input> {
|
||||||
|
tokens
|
||||||
|
.sep_by(|t| parse_input_line(t), |t| newline(t))
|
||||||
|
.collect()
|
||||||
|
}
|
63
src/day_09/mod.rs
Normal file
63
src/day_09/mod.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
mod model;
|
||||||
|
mod parsing;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use itertools::Itertools;
|
||||||
|
pub use model::HeightMap;
|
||||||
|
pub use parsing::parse_map;
|
||||||
|
use yap::IntoTokens;
|
||||||
|
|
||||||
|
#[aoc_generator(day9)]
|
||||||
|
pub fn parse_input(input: &str) -> HeightMap {
|
||||||
|
parse_map(&mut input.into_tokens())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day9, part1)]
|
||||||
|
pub fn part1(input: &HeightMap) -> usize {
|
||||||
|
input
|
||||||
|
.get_low_points()
|
||||||
|
.iter()
|
||||||
|
.map(|point| input.get_risk_level(*point))
|
||||||
|
.flatten()
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day9, part2)]
|
||||||
|
pub fn part2(input: &HeightMap) -> usize {
|
||||||
|
input
|
||||||
|
.get_basin_sizes()
|
||||||
|
.iter()
|
||||||
|
.sorted()
|
||||||
|
.rev()
|
||||||
|
.take(3)
|
||||||
|
.product()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
const EXAMPLE_INPUT: &str = "2199943210
|
||||||
|
3987894921
|
||||||
|
9856789892
|
||||||
|
8767896789
|
||||||
|
9899965678";
|
||||||
|
const RESULT_PART_1: usize = 15;
|
||||||
|
const RESULT_PART_2: usize = 1134;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
let result = super::part1(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
let result = super::part2(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_example() {
|
||||||
|
let _example = super::parse_input(EXAMPLE_INPUT);
|
||||||
|
//unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
100
src/day_09/model.rs
Normal file
100
src/day_09/model.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HeightMap {
|
||||||
|
pub map: Vec<Vec<usize>>,
|
||||||
|
pub basin: Vec<Vec<Option<usize>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeightMap {
|
||||||
|
pub fn new(map: Vec<Vec<usize>>) -> Self {
|
||||||
|
let mut basin = Vec::new();
|
||||||
|
for line in &map {
|
||||||
|
let mut basin_line = Vec::new();
|
||||||
|
for _point in line {
|
||||||
|
basin_line.push(None)
|
||||||
|
}
|
||||||
|
basin.push(basin_line);
|
||||||
|
}
|
||||||
|
let mut new_self = Self { map, basin };
|
||||||
|
let mut basin_counter = 0;
|
||||||
|
for y in 0..new_self.map.len() {
|
||||||
|
for x in 0..new_self.map[y].len() {
|
||||||
|
if new_self.map[y][x] < 9 && new_self.basin[y][x].is_none() {
|
||||||
|
new_self.flood_basin((x, y), basin_counter);
|
||||||
|
basin_counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_low_points(&self) -> Vec<(isize, isize)> {
|
||||||
|
let mut low_points = Vec::new();
|
||||||
|
for y in 0..self.map.len() {
|
||||||
|
for x in 0..self.map[y].len() {
|
||||||
|
if self.is_low_point((x as isize, y as isize)) {
|
||||||
|
low_points.push((x as isize, y as isize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
low_points
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_low_point(&self, position: (isize, isize)) -> bool {
|
||||||
|
let val = match self.get(position) {
|
||||||
|
Some(pos) => pos,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
let (x, y) = position;
|
||||||
|
for (x_off, y_off) in [(1isize, 0isize), (0, 1), (-1, 0), (0, -1)].iter() {
|
||||||
|
if let Some(neighbour) = self.get((x + x_off, y + y_off)) {
|
||||||
|
if neighbour <= val {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flood_basin(&mut self, (x, y): (usize, usize), number: usize) {
|
||||||
|
if let Some(basin) = self.basin[y][x] {
|
||||||
|
assert_eq!(basin, number);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
self.basin[y][x] = Some(number)
|
||||||
|
}
|
||||||
|
for (x_off, y_off) in [(1isize, 0isize), (0, 1), (-1, 0), (0, -1)].iter() {
|
||||||
|
if let Some(neighbour) = self.get((x as isize + x_off, y as isize + y_off)) {
|
||||||
|
if neighbour < 9 {
|
||||||
|
self.flood_basin(
|
||||||
|
((x as isize + x_off) as usize, (y as isize + y_off) as usize),
|
||||||
|
number,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, (x, y): (isize, isize)) -> Option<usize> {
|
||||||
|
let (x, y): (usize, usize) = match (x.try_into(), y.try_into()) {
|
||||||
|
(Ok(x), Ok(y)) => (x, y),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
self.map.get(y).map(|val| val.get(x)).flatten().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_risk_level(&self, position: (isize, isize)) -> Option<usize> {
|
||||||
|
self.get(position).map(|x| x + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_basin_sizes(&self) -> Vec<usize> {
|
||||||
|
self.basin
|
||||||
|
.iter()
|
||||||
|
.flat_map(|line| line.iter())
|
||||||
|
.filter_map(|val| *val)
|
||||||
|
.counts()
|
||||||
|
.into_values()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
37
src/day_09/parsing.rs
Normal file
37
src/day_09/parsing.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use yap::Tokens;
|
||||||
|
|
||||||
|
use crate::parsing::{newline, parse_digit};
|
||||||
|
|
||||||
|
use super::HeightMap;
|
||||||
|
|
||||||
|
pub fn parse_map(tokens: &mut impl Tokens<Item = char>) -> HeightMap {
|
||||||
|
let map: Vec<Vec<usize>> = tokens.sep_by(|t| parse_line(t), |t| newline(t)).collect();
|
||||||
|
HeightMap::new(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_line(tokens: &mut impl Tokens<Item = char>) -> Option<Vec<usize>> {
|
||||||
|
let digits: Vec<usize> = tokens.many(|t| parse_digit(t)).collect();
|
||||||
|
if digits.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(digits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use yap::IntoTokens;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_line() {
|
||||||
|
assert_eq!(
|
||||||
|
dbg!(super::parse_line(&mut "2199943210".into_tokens()).unwrap()).len(),
|
||||||
|
10
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_map() {
|
||||||
|
super::parse_map(&mut "01\n11".into_tokens());
|
||||||
|
}
|
||||||
|
}
|
62
src/day_10/mod.rs
Normal file
62
src/day_10/mod.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
mod model;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use self::model::Mistake;
|
||||||
|
|
||||||
|
#[aoc_generator(day10)]
|
||||||
|
pub fn parse_input(input: &str) -> Vec<Mistake> {
|
||||||
|
input.lines().filter_map(model::find_mistake).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day10, part1)]
|
||||||
|
pub fn part1(input: &[Mistake]) -> usize {
|
||||||
|
input
|
||||||
|
.iter()
|
||||||
|
.filter_map(|mistake| mistake.unexpected_char_score())
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day10, part2)]
|
||||||
|
pub fn part2(input: &[Mistake]) -> usize {
|
||||||
|
let scores: Vec<usize> = input
|
||||||
|
.iter()
|
||||||
|
.filter_map(|mistake| mistake.autocomplete_score())
|
||||||
|
.sorted()
|
||||||
|
.collect();
|
||||||
|
scores[scores.len() / 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
const EXAMPLE_INPUT: &str = "[({(<(())[]>[[{[]{<()<>>
|
||||||
|
[(()[<>])]({[<{<<[]>>(
|
||||||
|
{([(<{}[<>[]}>{[]{[(<()>
|
||||||
|
(((({<>}<{<{<>}{[]{[]{}
|
||||||
|
[[<[([]))<([[{}[[()]]]
|
||||||
|
[{[{({}]{}}([{[{{{}}([]
|
||||||
|
{<[[]]>}<{[{[{[]{()[[[]
|
||||||
|
[<(<(<(<{}))><([]([]()
|
||||||
|
<{([([[(<>()){}]>(<<{{
|
||||||
|
<{([{{}}[<[[[<>{}]]]>[]]";
|
||||||
|
const RESULT_PART_1: usize = 26397;
|
||||||
|
const RESULT_PART_2: usize = 288957;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
let result = super::part1(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
let result = super::part2(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_example() {
|
||||||
|
super::parse_input(EXAMPLE_INPUT);
|
||||||
|
}
|
||||||
|
}
|
217
src/day_10/model.rs
Normal file
217
src/day_10/model.rs
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||||
|
pub enum Kind {
|
||||||
|
Round,
|
||||||
|
Square,
|
||||||
|
Curly,
|
||||||
|
Pointy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Kind {
|
||||||
|
pub fn unexpected_char_score(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Kind::Round => 3,
|
||||||
|
Kind::Square => 57,
|
||||||
|
Kind::Curly => 1197,
|
||||||
|
Kind::Pointy => 25137,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Mistake {
|
||||||
|
Incomplete(Vec<Kind>),
|
||||||
|
Unexpected(Kind),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mistake {
|
||||||
|
pub fn autocomplete_score(&self) -> Option<usize> {
|
||||||
|
if let Self::Incomplete(missing) = self {
|
||||||
|
let mut score = 0;
|
||||||
|
for kind in missing {
|
||||||
|
score *= 5;
|
||||||
|
score += match kind {
|
||||||
|
Kind::Round => 1,
|
||||||
|
Kind::Square => 2,
|
||||||
|
Kind::Curly => 3,
|
||||||
|
Kind::Pointy => 4,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(score)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unexpected_char_score(&self) -> Option<usize> {
|
||||||
|
if let Self::Unexpected(kind) = self {
|
||||||
|
Some(kind.unexpected_char_score())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_mistake(s: &str) -> Option<Mistake> {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for kind in s.chars() {
|
||||||
|
match kind {
|
||||||
|
'(' => vec.push(Kind::Round),
|
||||||
|
'[' => vec.push(Kind::Square),
|
||||||
|
'{' => vec.push(Kind::Curly),
|
||||||
|
'<' => vec.push(Kind::Pointy),
|
||||||
|
')' => {
|
||||||
|
if vec.pop().unwrap() != Kind::Round {
|
||||||
|
return Some(Mistake::Unexpected(Kind::Round));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
']' => {
|
||||||
|
if vec.pop().unwrap() != Kind::Square {
|
||||||
|
return Some(Mistake::Unexpected(Kind::Square));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'}' => {
|
||||||
|
if vec.pop().unwrap() != Kind::Curly {
|
||||||
|
return Some(Mistake::Unexpected(Kind::Curly));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'>' => {
|
||||||
|
if vec.pop().unwrap() != Kind::Pointy {
|
||||||
|
return Some(Mistake::Unexpected(Kind::Pointy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("illegal character"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vec.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
vec.reverse();
|
||||||
|
Some(Mistake::Incomplete(vec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//use std::{rc::Rc, sync::RwLock};
|
||||||
|
//
|
||||||
|
//#[derive(Debug)]
|
||||||
|
//pub struct Chunk {
|
||||||
|
// kind: Kind,
|
||||||
|
// children: Vec<Rc<RwLock<Chunk>>>,
|
||||||
|
// parent: Option<Rc<RwLock<Chunk>>>,
|
||||||
|
// closing: Option<Kind>,
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//impl Chunk {
|
||||||
|
// pub fn find_first_illegal_char(&self) -> Option<Kind> {
|
||||||
|
// for child in &self.children {
|
||||||
|
// if let Some(kind) = child.try_read().unwrap().find_first_illegal_char() {
|
||||||
|
// return Some(kind);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if Some(self.kind) != self.closing {
|
||||||
|
// self.closing.clone()
|
||||||
|
// } else {
|
||||||
|
// None
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn new(kind: Kind, parent: Option<Rc<RwLock<Chunk>>>) -> Rc<RwLock<Self>> {
|
||||||
|
// let new = Rc::new(RwLock::new(Self {
|
||||||
|
// kind,
|
||||||
|
// children: Vec::new(),
|
||||||
|
// parent: parent.clone(),
|
||||||
|
// closing: None
|
||||||
|
// }));
|
||||||
|
// if let Some(mut parent) = parent {
|
||||||
|
// if let Ok(mut parent_locked) = RwLock::try_write(&mut parent) {
|
||||||
|
// parent_locked.children.push(new.clone());
|
||||||
|
// } else {
|
||||||
|
// panic!("can't access parent to insert child")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// new
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn close(&mut self, kind: Kind) {
|
||||||
|
// self.closing = Some(kind);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn parse(s: &str) -> Result<Rc<RwLock<Self>>, &'static str> {
|
||||||
|
// let mut current: Option<Rc<RwLock<Self>>> = None;
|
||||||
|
//
|
||||||
|
// let open = |kind, current| {
|
||||||
|
// Some(Chunk::new(kind, current))
|
||||||
|
// };
|
||||||
|
// let close = |kind, current: Option<Rc<RwLock<Self>>>| {
|
||||||
|
// if let Some(mut current_some) = current {
|
||||||
|
// if let Ok(mut current_locked) = RwLock::try_write(&mut current_some) {
|
||||||
|
// current_locked.close(kind);
|
||||||
|
// Ok(current_locked.parent.clone())
|
||||||
|
// } else {
|
||||||
|
// Err("can't get a lock to the current chunk")
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// Ok(None)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// for kind in s.chars() {
|
||||||
|
// match kind {
|
||||||
|
// '(' => current = open(Kind::Round, current),
|
||||||
|
// '[' => current = open(Kind::Square, current),
|
||||||
|
// '{' => current = open(Kind::Curly, current),
|
||||||
|
// '<' => current = open(Kind::Pointy, current),
|
||||||
|
// ')' => current = close(Kind::Round, current)?,
|
||||||
|
// ']' => current = close(Kind::Square, current)?,
|
||||||
|
// '}' => current = close(Kind::Curly, current)?,
|
||||||
|
// '>' => current = close(Kind::Pointy, current)?,
|
||||||
|
// _ => return Err("Illegal character!"),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// current.ok_or("String seems to have been empty")
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//impl ToString for Chunk {
|
||||||
|
// fn to_string(&self) -> String {
|
||||||
|
// let mut ret_val = String::new();
|
||||||
|
// match self.kind {
|
||||||
|
// Kind::Round => ret_val.push('('),
|
||||||
|
// Kind::Square => ret_val.push('['),
|
||||||
|
// Kind::Curly => ret_val.push('{'),
|
||||||
|
// Kind::Pointy => ret_val.push('<'),
|
||||||
|
// }
|
||||||
|
// for child in &self.children {
|
||||||
|
// ret_val.push_str(&child.try_read().unwrap().to_string())
|
||||||
|
// }
|
||||||
|
// match self.closing {
|
||||||
|
// Some(Kind::Round) => ret_val.push(')'),
|
||||||
|
// Some(Kind::Square) => ret_val.push(']'),
|
||||||
|
// Some(Kind::Curly) => ret_val.push('}'),
|
||||||
|
// Some(Kind::Pointy) => ret_val.push('>'),
|
||||||
|
// None => (),
|
||||||
|
// }
|
||||||
|
// ret_val
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
fn unexpected_char() {
|
||||||
|
let res = super::find_mistake("{([(<{}[<>[]}>{[]{[(<()>");
|
||||||
|
assert!(res.is_some());
|
||||||
|
let res = res.unwrap();
|
||||||
|
assert_eq!(res.unexpected_char_score(), Some(1197));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incomplete_chunk() {
|
||||||
|
let res = super::find_mistake("<{([{{}}[<[[[<>{}]]]>[]]");
|
||||||
|
assert!(res.is_some());
|
||||||
|
let res = res.unwrap();
|
||||||
|
assert_eq!(res.autocomplete_score(), Some(294));
|
||||||
|
}
|
||||||
|
}
|
48
src/day_xx/mod.rs
Normal file
48
src/day_xx/mod.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
mod model;
|
||||||
|
mod parsing;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
//pub use model::{};
|
||||||
|
//pub use parsing::{};
|
||||||
|
use yap::IntoTokens;
|
||||||
|
|
||||||
|
#[aoc_generator(dayx)]
|
||||||
|
pub fn parse_input(input: &str) -> Vec<Input> {
|
||||||
|
parse_input_tokens(&mut input.into_tokens())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(dayx, part1)]
|
||||||
|
pub fn part1(input: &[Input]) -> usize {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(dayx, part2)]
|
||||||
|
pub fn part2(input: &[Input]) -> usize {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
const EXAMPLE_INPUT: &str = "";
|
||||||
|
const RESULT_PART_1: usize = 0;
|
||||||
|
const RESULT_PART_2: usize = 0;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
let result = super::part1(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
let result = super::part2(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_example() {
|
||||||
|
let example = super::parse_input(EXAMPLE_INPUT);
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
0
src/day_xx/model.rs
Normal file
0
src/day_xx/model.rs
Normal file
0
src/day_xx/parsing.rs
Normal file
0
src/day_xx/parsing.rs
Normal file
|
@ -9,5 +9,8 @@ pub mod day_04;
|
||||||
pub mod day_05;
|
pub mod day_05;
|
||||||
pub mod day_06;
|
pub mod day_06;
|
||||||
pub mod day_07;
|
pub mod day_07;
|
||||||
|
pub mod day_08;
|
||||||
|
pub mod day_09;
|
||||||
|
pub mod day_10;
|
||||||
|
|
||||||
aoc_lib! { year = 2021 }
|
aoc_lib! { year = 2021 }
|
||||||
|
|
|
@ -9,18 +9,43 @@ pub fn newline(tokens: &mut impl Tokens<Item = char>) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_number<T: Num>(tokens: &mut impl Tokens<Item = char>) -> Option<T> {
|
pub fn parse_number<T: Num>(tokens: &mut impl Tokens<Item = char>) -> Option<T> {
|
||||||
parse_number_with_radix(tokens, 10)
|
parse_number_with_radix(tokens, 10, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_digit<T: Num>(tokens: &mut impl Tokens<Item = char>) -> Option<T> {
|
||||||
|
parse_number_with_radix(tokens, 10, Some(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_number_with_radix<T: Num>(
|
pub fn parse_number_with_radix<T: Num>(
|
||||||
tokens: &mut impl Tokens<Item = char>,
|
tokens: &mut impl Tokens<Item = char>,
|
||||||
base: u32,
|
base: u32,
|
||||||
|
limit: Option<usize>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
tokens.skip_tokens_while(|t| *t == ' ');
|
tokens.skip_tokens_while(|t| *t == ' ');
|
||||||
let digits: String = tokens.tokens_while(|c| c.is_digit(base)).collect();
|
let mut remaining = limit.map(|x| x as isize).unwrap_or(isize::MAX);
|
||||||
|
let digits: String = tokens
|
||||||
|
.tokens_while(|c| {
|
||||||
|
remaining -= 1;
|
||||||
|
c.is_digit(base) && remaining >= 0
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
if !digits.is_empty() {
|
if !digits.is_empty() {
|
||||||
T::from_str_radix(&digits, base).ok()
|
T::from_str_radix(&digits, base).ok()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use yap::IntoTokens;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_digit() {
|
||||||
|
let mut tokens = "1234".into_tokens();
|
||||||
|
assert_eq!(Some(1), super::parse_digit(&mut tokens));
|
||||||
|
assert_eq!(Some(2), super::parse_digit(&mut tokens));
|
||||||
|
assert_eq!(Some(3), super::parse_digit(&mut tokens));
|
||||||
|
assert_eq!(Some(4), super::parse_digit(&mut tokens));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue