diff options
author | Jesse Luehrs <doy@tozt.net> | 2022-12-11 21:59:21 -0500 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2022-12-11 22:16:30 -0500 |
commit | e2d219b331a878bbb3c9dcef9ea4e218b2e3ee06 (patch) | |
tree | 93e418011c45cab8d4070d3d33b377a9364f4a27 /src/2020 | |
parent | 179467096141b7e8f67d63b89fd21e779a564fe6 (diff) | |
download | advent-of-code-e2d219b331a878bbb3c9dcef9ea4e218b2e3ee06.tar.gz advent-of-code-e2d219b331a878bbb3c9dcef9ea4e218b2e3ee06.zip |
refactor
Diffstat (limited to 'src/2020')
-rw-r--r-- | src/2020/1/mod.rs | 41 | ||||
-rw-r--r-- | src/2020/2/mod.rs | 77 | ||||
-rw-r--r-- | src/2020/3/mod.rs | 64 | ||||
-rw-r--r-- | src/2020/4/mod.rs | 137 | ||||
-rw-r--r-- | src/2020/5/mod.rs | 96 | ||||
-rw-r--r-- | src/2020/6/mod.rs | 59 | ||||
-rw-r--r-- | src/2020/7/mod.rs | 102 | ||||
-rw-r--r-- | src/2020/8/mod.rs | 133 | ||||
-rw-r--r-- | src/2020/9/mod.rs | 74 | ||||
-rw-r--r-- | src/2020/mod.rs | 47 |
10 files changed, 0 insertions, 830 deletions
diff --git a/src/2020/1/mod.rs b/src/2020/1/mod.rs deleted file mode 100644 index 1ed3824..0000000 --- a/src/2020/1/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::prelude::*; - -pub fn parse(fh: File) -> Result<Vec<i64>> { - Ok(parse::ints(parse::lines(fh)).collect()) -} - -pub fn part1(ints: Vec<i64>) -> Result<i64> { - for i in &ints { - for j in &ints { - if i + j == 2020 { - return Ok(i * j); - } - } - } - Err(anyhow!("no numbers summing to 2020 found")) -} - -pub fn part2(ints: Vec<i64>) -> Result<i64> { - for i in &ints { - for j in &ints { - for k in &ints { - if i + j + k == 2020 { - return Ok(i * j * k); - } - } - } - } - Err(anyhow!("no numbers summing to 2020 found")) -} - -#[test] -fn test() { - assert_eq!( - part1(parse(parse::data(2020, 1).unwrap()).unwrap()).unwrap(), - 445536 - ); - assert_eq!( - part2(parse(parse::data(2020, 1).unwrap()).unwrap()).unwrap(), - 138688160 - ); -} diff --git a/src/2020/2/mod.rs b/src/2020/2/mod.rs deleted file mode 100644 index a2ff9df..0000000 --- a/src/2020/2/mod.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::prelude::*; - -pub struct Line { - c: char, - n1: usize, - n2: usize, - password: String, -} - -impl Line { - fn parse(line: &str) -> Result<Self> { - let captures = - regex_captures!(r"^([0-9]+)-([0-9]+) (.): (.*)$", line) - .context("line failed to match regex")?; - let c = captures - .get(3) - .unwrap() - .as_str() - .parse() - .context("invalid policy char")?; - let n1 = captures - .get(1) - .unwrap() - .as_str() - .parse() - .context("invalid policy lower bound")?; - let n2 = captures - .get(2) - .unwrap() - .as_str() - .parse() - .context("invalid policy upper bound")?; - let password = captures.get(4).unwrap().as_str().to_string(); - Ok(Self { - c, - n1, - n2, - password, - }) - } - - fn valid_part_1(&self) -> bool { - let count = self.password.chars().filter(|c| *c == self.c).count(); - count >= self.n1 && count <= self.n2 - } - - fn valid_part_2(&self) -> bool { - (self.password.chars().nth(self.n1 - 1) == Some(self.c)) - ^ (self.password.chars().nth(self.n2 - 1) == Some(self.c)) - } -} - -pub fn parse(fh: File) -> Result<impl Iterator<Item = Line>> { - Ok(parse::lines(fh).map(|line| Line::parse(&line).unwrap())) -} - -pub fn part1(lines: impl Iterator<Item = Line>) -> Result<i64> { - let count = lines.filter(|l| l.valid_part_1()).count(); - Ok(count.try_into()?) -} - -pub fn part2(lines: impl Iterator<Item = Line>) -> Result<i64> { - let count = lines.filter(|l| l.valid_part_2()).count(); - Ok(count.try_into()?) -} - -#[test] -fn test() { - assert_eq!( - part1(parse(parse::data(2020, 2).unwrap()).unwrap()).unwrap(), - 638 - ); - assert_eq!( - part2(parse(parse::data(2020, 2).unwrap()).unwrap()).unwrap(), - 699 - ); -} diff --git a/src/2020/3/mod.rs b/src/2020/3/mod.rs deleted file mode 100644 index 29e20d2..0000000 --- a/src/2020/3/mod.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::prelude::*; - -pub struct Map { - grid: Grid<bool>, -} - -impl Map { - fn new(grid: Grid<bool>) -> Self { - Self { grid } - } - - fn rows(&self) -> usize { - self.grid.rows().0 - } - - fn tree_at(&self, row: Row, col: Col) -> Result<bool> { - // unwrap safe because cycle().nth() can never fail - Ok(*self.grid[row].iter().cycle().nth(col.0).unwrap()) - } - - fn trees_for_slope( - &self, - row_incr: usize, - col_incr: usize, - ) -> Result<i64> { - let mut trees = 0; - for r in 0..self.rows() / row_incr { - let row = r * row_incr; - let col = r * col_incr; - if self.tree_at(Row(row), Col(col))? { - trees += 1; - } - } - Ok(trees) - } -} - -pub fn parse(fh: File) -> Result<Map> { - Ok(Map::new(parse::bool_grid(parse::lines(fh), b'#', b'.'))) -} - -pub fn part1(map: Map) -> Result<i64> { - map.trees_for_slope(1, 3) -} - -pub fn part2(map: Map) -> Result<i64> { - Ok(map.trees_for_slope(1, 1)? - * map.trees_for_slope(1, 3)? - * map.trees_for_slope(1, 5)? - * map.trees_for_slope(1, 7)? - * map.trees_for_slope(2, 1)?) -} - -#[test] -fn test() { - assert_eq!( - part1(parse(parse::data(2020, 3).unwrap()).unwrap()).unwrap(), - 292 - ); - assert_eq!( - part2(parse(parse::data(2020, 3).unwrap()).unwrap()).unwrap(), - 9354744432 - ); -} diff --git a/src/2020/4/mod.rs b/src/2020/4/mod.rs deleted file mode 100644 index b198c31..0000000 --- a/src/2020/4/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::prelude::*; - -const REQUIRED_KEYS: &[&str] = - &["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]; - -pub fn parse(fh: File) -> Result<Vec<HashMap<String, String>>> { - let mut res = vec![]; - let mut cur = HashMap::new(); - let mut lines = parse::lines(fh).peekable(); - while lines.peek().is_some() { - for line in parse::chunk(&mut lines) { - for field in line.split(' ') { - let mut parts = field.split(':'); - let key = parts.next().with_context(|| { - format!("failed to parse field '{}'", field) - })?; - let value = parts.next().with_context(|| { - format!("failed to parse field '{}'", field) - })?; - cur.insert(key.to_string(), value.to_string()); - } - } - res.push(cur); - cur = HashMap::new(); - } - if !cur.is_empty() { - res.push(cur); - } - Ok(res) -} - -pub fn part1(passports: Vec<HashMap<String, String>>) -> Result<i64> { - let mut valid = 0; - for passport in passports { - let mut cur_valid = true; - for key in REQUIRED_KEYS { - if !passport.contains_key(&key.to_string()) { - cur_valid = false; - break; - } - } - if cur_valid { - valid += 1; - } - } - Ok(valid) -} - -pub fn part2(passports: Vec<HashMap<String, String>>) -> Result<i64> { - let mut valid = 0; - for passport in passports { - let mut cur_valid = true; - for key in REQUIRED_KEYS { - match passport.get(&key.to_string()) { - Some(val) => { - if !validate(key, val)? { - cur_valid = false; - break; - } - } - None => { - cur_valid = false; - break; - } - } - } - if cur_valid { - valid += 1; - } - } - Ok(valid) -} - -fn validate(key: &str, val: &str) -> Result<bool> { - match key { - "byr" => match val.parse::<i32>() { - Ok(year) => Ok((1920..=2002).contains(&year)), - Err(_) => Ok(false), - }, - "iyr" => match val.parse::<i32>() { - Ok(year) => Ok((2010..=2020).contains(&year)), - Err(_) => Ok(false), - }, - "eyr" => match val.parse::<i32>() { - Ok(year) => Ok((2020..=2030).contains(&year)), - Err(_) => Ok(false), - }, - "hgt" => { - if val.len() < 3 { - Ok(false) - } else if val.ends_with("in") { - match val[0..val.len() - 2].parse::<i32>() { - Ok(inches) => Ok((59..=76).contains(&inches)), - Err(_) => Ok(false), - } - } else if val.ends_with("cm") { - match val[0..val.len() - 2].parse::<i32>() { - Ok(inches) => Ok((150..=193).contains(&inches)), - Err(_) => Ok(false), - } - } else { - Ok(false) - } - } - "hcl" => Ok(val.len() == 7 - && val.starts_with('#') - && val[1..] - == val[1..] - .matches(|c: char| c.is_ascii_hexdigit()) - .collect::<String>()), - "ecl" => Ok(val == "amb" - || val == "blu" - || val == "brn" - || val == "gry" - || val == "grn" - || val == "hzl" - || val == "oth"), - "pid" => Ok(val.len() == 9 - && val - == val - .matches(|c: char| c.is_ascii_digit()) - .collect::<String>()), - _ => Err(anyhow!("invalid key found: {}", key)), - } -} - -#[test] -fn test() { - assert_eq!( - part1(parse(parse::data(2020, 4).unwrap()).unwrap()).unwrap(), - 247 - ); - assert_eq!( - part2(parse(parse::data(2020, 4).unwrap()).unwrap()).unwrap(), - 145 - ); -} diff --git a/src/2020/5/mod.rs b/src/2020/5/mod.rs deleted file mode 100644 index 1f57f4f..0000000 --- a/src/2020/5/mod.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::prelude::*; - -pub fn parse(fh: File) -> Result<impl Iterator<Item = i64>> { - Ok(parse::lines(fh).map(|line| seat_id(&line).unwrap())) -} - -pub fn part1(ids: impl Iterator<Item = i64>) -> Result<i64> { - let mut max = 0; - for id in ids { - if id > max { - max = id; - } - } - Ok(max) -} - -pub fn part2(ids: impl Iterator<Item = i64>) -> Result<i64> { - let mut seats = vec![false; 1024]; - for id in ids { - seats[id as usize] = true; - } - let first = seats - .iter() - .position(|b| *b) - .context("failed to find taken seat")?; - let seat = seats - .iter() - .skip(first) - .position(|b| !*b) - .context("failed to find free seat")? - + first; - if !seats[seat - 1] || seats[seat] || !seats[seat + 1] { - return Err(anyhow!("invalid seat found")); - } - Ok(seat.try_into()?) -} - -fn seat_id(desc: &str) -> Result<i64> { - if desc.len() != 10 { - return Err(anyhow!("invalid desc {}", desc)); - } - let row_desc = &desc[0..7]; - let col_desc = &desc[7..10]; - - let mut min_row = 0; - let mut max_row = 127; - for c in row_desc.chars() { - let mid = (max_row + min_row) / 2; - match c { - 'F' => { - max_row = mid; - } - 'B' => { - min_row = mid + 1; - } - _ => return Err(anyhow!("invalid desc {}", desc)), - } - } - if min_row != max_row { - return Err(anyhow!("bug")); - } - let row = min_row; - - let mut min_col = 0; - let mut max_col = 7; - for c in col_desc.chars() { - let mid = (max_col + min_col) / 2; - match c { - 'L' => { - max_col = mid; - } - 'R' => { - min_col = mid + 1; - } - _ => return Err(anyhow!("invalid desc {}", desc)), - } - } - if min_col != max_col { - return Err(anyhow!("bug")); - } - let col = min_col; - - Ok(row * 8 + col) -} - -#[test] -fn test() { - assert_eq!( - part1(parse(parse::data(2020, 5).unwrap()).unwrap()).unwrap(), - 978 - ); - assert_eq!( - part2(parse(parse::data(2020, 5).unwrap()).unwrap()).unwrap(), - 727 - ); -} diff --git a/src/2020/6/mod.rs b/src/2020/6/mod.rs deleted file mode 100644 index 0f61621..0000000 --- a/src/2020/6/mod.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::prelude::*; - -pub fn parse(fh: File) -> Result<impl Iterator<Item = String>> { - Ok(parse::lines(fh)) -} - -pub fn part1(lines: impl Iterator<Item = String>) -> Result<i64> { - let mut yes = HashSet::new(); - let mut total = 0; - for line in lines { - if line.is_empty() { - total += yes.len() as i64; - yes = HashSet::new(); - } else { - for c in line.chars() { - yes.insert(c); - } - } - } - total += yes.len() as i64; - Ok(total) -} - -pub fn part2(lines: impl Iterator<Item = String>) -> Result<i64> { - let mut yes = HashSet::new(); - for c in 'a'..='z' { - yes.insert(c); - } - let mut total = 0; - for line in lines { - if line.is_empty() { - total += yes.len() as i64; - yes = HashSet::new(); - for c in 'a'..='z' { - yes.insert(c); - } - } else { - for c in 'a'..='z' { - if !line.contains(c) { - yes.remove(&c); - } - } - } - } - total += yes.len() as i64; - Ok(total) -} - -#[test] -fn test() { - assert_eq!( - part1(parse(parse::data(2020, 6).unwrap()).unwrap()).unwrap(), - 6930 - ); - assert_eq!( - part2(parse(parse::data(2020, 6).unwrap()).unwrap()).unwrap(), - 3585 - ); -} diff --git a/src/2020/7/mod.rs b/src/2020/7/mod.rs deleted file mode 100644 index 2c8a1a7..0000000 --- a/src/2020/7/mod.rs +++ /dev/null @@ -1,102 +0,0 @@ -use crate::prelude::*; - -type Graph = HashMap<String, Vec<(i64, String)>>; - -pub fn parse(fh: File) -> Result<Graph> { - let input = parse::string(fh); - let mut graph = Graph::new(); - for line in input.lines() { - let (k, v) = parse_line(line)?; - graph.insert(k, v); - } - Ok(graph) -} - -pub fn part1(graph: Graph) -> Result<i64> { - let mut colors = 0; - for color in graph.keys() { - if bag_contains(&graph, color, "shiny gold")? { - colors += 1; - } - } - Ok(colors) -} - -pub fn part2(graph: Graph) -> Result<i64> { - // subtract 1 to not count the shiny gold bag itself - count_bags(&graph, "shiny gold").map(|i| i - 1) -} - -fn parse_line(line: &str) -> Result<(String, Vec<(i64, String)>)> { - let captures = regex_captures!(r"^(.*) bags contain (.*)\.$", line) - .context("line failed to match regex")?; - let color = captures.get(1).unwrap().as_str(); - let contents = captures.get(2).unwrap().as_str(); - if contents == "no other bags" { - Ok((color.to_string(), vec![])) - } else { - Ok(( - color.to_string(), - contents - .split(", ") - .map(|s| { - let captures = - regex_captures!(r"^([0-9]+) (.*) bags?", s) - .context("line failed to match regex")?; - Ok(( - captures - .get(1) - .unwrap() - .as_str() - .parse() - .context("invalid number of bags")?, - captures.get(2).unwrap().as_str().to_string(), - )) - }) - .collect::<Result<_>>()?, - )) - } -} - -fn bag_contains(graph: &Graph, start: &str, target: &str) -> Result<bool> { - let mut to_check = graph - .get(&start.to_string()) - .context("failed to find starting color in graph")? - .clone(); - while let Some((_, next)) = to_check.pop() { - if next == target { - return Ok(true); - } - to_check.extend( - graph - .get(&next) - .context("failed to find next color in graph")? - .iter() - .cloned(), - ); - } - Ok(false) -} - -fn count_bags(graph: &Graph, color: &str) -> Result<i64> { - Ok(1 + graph - .get(&color.to_string()) - .context("failed to find starting color in graph")? - .iter() - .map(|(count, child)| Ok(count * count_bags(graph, child)?)) - .collect::<Result<Vec<_>>>()? - .iter() - .sum::<i64>()) -} - -#[test] -fn test() { - assert_eq!( - part1(parse(parse::data(2020, 7).unwrap()).unwrap()).unwrap(), - 169 - ); - assert_eq!( - part2(parse(parse::data(2020, 7).unwrap()).unwrap()).unwrap(), - 82372 - ); -} diff --git a/src/2020/8/mod.rs b/src/2020/8/mod.rs deleted file mode 100644 index af08f5c..0000000 --- a/src/2020/8/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::prelude::*; - -#[derive(Clone, Copy)] -enum OpType { - Nop, - Acc, - Jmp, -} - -impl std::str::FromStr for OpType { - type Err = Error; - - fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { - Ok(match s { - "nop" => Self::Nop, - "acc" => Self::Acc, - "jmp" => Self::Jmp, - _ => return Err(anyhow!("invalid optype {}", s)), - }) - } -} - -#[derive(Clone, Copy)] -pub struct Op { - ty: OpType, - arg: i64, -} - -impl std::str::FromStr for Op { - type Err = Error; - - fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { - let captures = regex_captures!(r"^([^ ]*) ((?:-|\+)[0-9]+)$", s) - .context("failed to parse line")?; - let ty = captures.get(1).unwrap().as_str().parse()?; - let arg = captures - .get(2) - .unwrap() - .as_str() - .parse() - .context("invalid arg")?; - Ok(Self { ty, arg }) - } -} - -pub fn parse(fh: File) -> Result<Vec<Op>> { - parse::lines(fh).map(|line| line.parse()).collect() -} - -pub fn part1(opcodes: Vec<Op>) -> Result<i64> { - let (acc, success) = run(&opcodes)?; - if success { - return Err(anyhow!("unexpectedly succeeded")); - } - Ok(acc) -} - -pub fn part2(opcodes: Vec<Op>) -> Result<i64> { - for i in 0..opcodes.len() { - match opcodes[i].ty { - OpType::Nop => { - let mut attempt = opcodes.clone(); - attempt[i].ty = OpType::Jmp; - let (acc, success) = run(&attempt)?; - if success { - return Ok(acc); - } - } - OpType::Acc => {} - OpType::Jmp => { - let mut attempt = opcodes.clone(); - attempt[i].ty = OpType::Nop; - let (acc, success) = run(&attempt)?; - if success { - return Ok(acc); - } - } - } - } - Err(anyhow!("failed to find corrupted opcode")) -} - -fn run(opcodes: &[Op]) -> Result<(i64, bool)> { - let mut seen = vec![false; opcodes.len()]; - let mut pc = 0; - let mut acc = 0; - loop { - if pc >= opcodes.len() { - return Ok((acc, true)); - } else if seen[pc] { - return Ok((acc, false)); - } - seen[pc] = true; - - match opcodes[pc].ty { - OpType::Nop => { - pc += 1; - } - OpType::Acc => { - acc += opcodes[pc].arg; - pc += 1; - } - OpType::Jmp => { - let arg = opcodes[pc].arg; - if arg >= 0 { - if arg as usize > opcodes.len() - || pc > opcodes.len() - arg as usize - { - return Err(anyhow!("invalid jmp")); - } - pc += arg as usize; - } else { - if pc < (-arg as usize) { - return Err(anyhow!("invalid jmp")); - } - pc -= -arg as usize; - } - } - } - } -} - -#[test] -fn test() { - assert_eq!( - part1(parse(parse::data(2020, 8).unwrap()).unwrap()).unwrap(), - 1928 - ); - assert_eq!( - part2(parse(parse::data(2020, 8).unwrap()).unwrap()).unwrap(), - 1319 - ); -} diff --git a/src/2020/9/mod.rs b/src/2020/9/mod.rs deleted file mode 100644 index 0d2dd6f..0000000 --- a/src/2020/9/mod.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::prelude::*; - -const WINDOW: usize = 25; - -pub fn parse(fh: File) -> Result<Vec<i64>> { - Ok(parse::ints(parse::lines(fh)).collect()) -} - -pub fn part1(list: Vec<i64>) -> Result<i64> { - for i in 0..(list.len() - WINDOW) { - let set = &list[i..i + WINDOW]; - let n = list[i + WINDOW]; - if !valid(set, n) { - return Ok(n); - } - } - - Err(anyhow!("failed to find invalid number")) -} - -pub fn part2(list: Vec<i64>) -> Result<i64> { - let mut invalid = None; - for i in 0..(list.len() - WINDOW) { - let set = &list[i..i + WINDOW]; - let n = list[i + WINDOW]; - if !valid(set, n) { - invalid = Some(n); - } - } - if invalid.is_none() { - return Err(anyhow!("failed to find invalid number")); - } - let invalid = invalid.unwrap(); - - for i in 0..list.len() { - for j in i..list.len() { - let seq = &list[i..=j]; - if invalid == seq.iter().sum() { - return Ok(seq.iter().copied().min().unwrap() - + seq.iter().copied().max().unwrap()); - } - } - } - - Err(anyhow!("failed to find sequence summing to invalid number")) -} - -fn valid(set: &[i64], n: i64) -> bool { - for i in 0..set.len() { - for j in 0..set.len() { - if i == j { - continue; - } - let i = set[i]; - let j = set[j]; - if i + j == n { - return true; - } - } - } - false -} - -#[test] -fn test() { - assert_eq!( - part1(parse(parse::data(2020, 9).unwrap()).unwrap()).unwrap(), - 373803594 - ); - assert_eq!( - part2(parse(parse::data(2020, 9).unwrap()).unwrap()).unwrap(), - 51152360 - ); -} diff --git a/src/2020/mod.rs b/src/2020/mod.rs deleted file mode 100644 index 83e6a6e..0000000 --- a/src/2020/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::prelude::*; - -#[path = "1/mod.rs"] -mod day1; -#[path = "2/mod.rs"] -mod day2; -#[path = "3/mod.rs"] -mod day3; -#[path = "4/mod.rs"] -mod day4; -#[path = "5/mod.rs"] -mod day5; -#[path = "6/mod.rs"] -mod day6; -#[path = "7/mod.rs"] -mod day7; -#[path = "8/mod.rs"] -mod day8; -#[path = "9/mod.rs"] -mod day9; -// NEXT MOD - -pub fn run(day: u8, puzzle: u8) -> Result<i64> { - #[allow(clippy::match_single_binding)] - match (day, puzzle) { - (1, 1) => day1::part1(day1::parse(parse::data(2020, 1)?)?), - (1, 2) => day1::part2(day1::parse(parse::data(2020, 1)?)?), - (2, 1) => day2::part1(day2::parse(parse::data(2020, 2)?)?), - (2, 2) => day2::part2(day2::parse(parse::data(2020, 2)?)?), - (3, 1) => day3::part1(day3::parse(parse::data(2020, 3)?)?), - (3, 2) => day3::part2(day3::parse(parse::data(2020, 3)?)?), - (4, 1) => day4::part1(day4::parse(parse::data(2020, 4)?)?), - (4, 2) => day4::part2(day4::parse(parse::data(2020, 4)?)?), - (5, 1) => day5::part1(day5::parse(parse::data(2020, 5)?)?), - (5, 2) => day5::part2(day5::parse(parse::data(2020, 5)?)?), - (6, 1) => day6::part1(day6::parse(parse::data(2020, 6)?)?), - (6, 2) => day6::part2(day6::parse(parse::data(2020, 6)?)?), - (7, 1) => day7::part1(day7::parse(parse::data(2020, 7)?)?), - (7, 2) => day7::part2(day7::parse(parse::data(2020, 7)?)?), - (8, 1) => day8::part1(day8::parse(parse::data(2020, 8)?)?), - (8, 2) => day8::part2(day8::parse(parse::data(2020, 8)?)?), - (9, 1) => day9::part1(day9::parse(parse::data(2020, 9)?)?), - (9, 2) => day9::part2(day9::parse(parse::data(2020, 9)?)?), - // NEXT PART - _ => Err(anyhow!("unknown puzzle {}-{}", day, puzzle)), - } -} |