From d16795e44aeac17bee08363bd08c1a9672edf3d4 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 18 Dec 2021 13:21:42 -0500 Subject: factor out parsing --- src/2020/1/mod.rs | 20 +++++++++---- src/2020/2/mod.rs | 35 ++++++++++++----------- src/2020/3/mod.rs | 26 ++++++++++++----- src/2020/4/mod.rs | 82 ++++++++++++++++++++++++++++++------------------------ src/2020/5/mod.rs | 26 ++++++++++------- src/2020/6/mod.rs | 26 +++++++++++------ src/2020/7/mod.rs | 37 +++++++++++++----------- src/2020/8/mod.rs | 30 +++++++++++--------- src/2020/9/mod.rs | 24 ++++++++++------ src/2020/mod.rs | 36 ++++++++++++------------ src/2021/1/mod.rs | 24 ++++++++++------ src/2021/10/mod.rs | 24 ++++++++++++---- src/2021/11/mod.rs | 32 ++++++++++++--------- src/2021/12/mod.rs | 47 +++++++++++++++++++------------ src/2021/13/mod.rs | 46 ++++++++++++++---------------- src/2021/14/mod.rs | 51 ++++++++++++++++++--------------- src/2021/15/mod.rs | 23 +++++++++++---- src/2021/16/mod.rs | 27 ++++++++++-------- src/2021/17/mod.rs | 45 +++++++++++++++++++----------- src/2021/18/mod.rs | 27 ++++++++++++------ src/2021/2/mod.rs | 73 +++++++++++++++++++++++++++++++++++------------- src/2021/3/mod.rs | 24 ++++++++++++---- src/2021/4/mod.rs | 22 ++++++++++----- src/2021/5/mod.rs | 47 ++++++++++++++++++------------- src/2021/6/mod.rs | 23 +++++++++++---- src/2021/7/mod.rs | 23 +++++++++++---- src/2021/8/mod.rs | 45 ++++++++++++++++++++---------- src/2021/9/mod.rs | 22 +++++++++++---- src/2021/mod.rs | 72 +++++++++++++++++++++++------------------------ src/util.rs | 5 ++++ src/util/parse.rs | 31 ++++++++------------- 31 files changed, 661 insertions(+), 414 deletions(-) diff --git a/src/2020/1/mod.rs b/src/2020/1/mod.rs index 8ac5e14..31c4c5d 100644 --- a/src/2020/1/mod.rs +++ b/src/2020/1/mod.rs @@ -1,5 +1,8 @@ -pub fn part1() -> anyhow::Result { - let ints: Vec<_> = data_ints!()?.collect(); +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + Ok(crate::util::parse::ints(crate::util::parse::lines(fh)).collect()) +} + +pub fn part1(ints: Vec) -> anyhow::Result { for i in &ints { for j in &ints { if i + j == 2020 { @@ -10,8 +13,7 @@ pub fn part1() -> anyhow::Result { Err(anyhow::anyhow!("no numbers summing to 2020 found")) } -pub fn part2() -> anyhow::Result { - let ints: Vec<_> = data_ints!()?.collect(); +pub fn part2(ints: Vec) -> anyhow::Result { for i in &ints { for j in &ints { for k in &ints { @@ -26,6 +28,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 445536); - assert_eq!(part2().unwrap(), 138688160); + assert_eq!( + part1(parse(crate::util::data(2020, 1).unwrap()).unwrap()).unwrap(), + 445536 + ); + assert_eq!( + part2(parse(crate::util::data(2020, 1).unwrap()).unwrap()).unwrap(), + 138688160 + ); } diff --git a/src/2020/2/mod.rs b/src/2020/2/mod.rs index af82ff4..31dfeb5 100644 --- a/src/2020/2/mod.rs +++ b/src/2020/2/mod.rs @@ -1,8 +1,7 @@ use anyhow::Context as _; use std::convert::TryInto as _; -use std::io::BufRead as _; -struct Line { +pub struct Line { c: char, n1: usize, n2: usize, @@ -52,28 +51,30 @@ impl Line { } } -pub fn part1() -> anyhow::Result { - let lines = read_lines()?; - let count = lines.iter().filter(|l| l.valid_part_1()).count(); - Ok(count.try_into()?) +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result> { + Ok(crate::util::parse::lines(fh).map(|line| Line::parse(&line).unwrap())) } -pub fn part2() -> anyhow::Result { - let lines = read_lines()?; - let count = lines.iter().filter(|l| l.valid_part_2()).count(); +pub fn part1(lines: impl Iterator) -> anyhow::Result { + let count = lines.filter(|l| l.valid_part_1()).count(); Ok(count.try_into()?) } -fn read_lines() -> anyhow::Result> { - let f = data!()?; - let f = std::io::BufReader::new(f); - f.lines() - .map(|l| Line::parse(&l.context("failed to read a line")?)) - .collect() +pub fn part2(lines: impl Iterator) -> anyhow::Result { + let count = lines.filter(|l| l.valid_part_2()).count(); + Ok(count.try_into()?) } #[test] fn test() { - assert_eq!(part1().unwrap(), 638); - assert_eq!(part2().unwrap(), 699); + assert_eq!( + part1(parse(crate::util::data(2020, 2).unwrap()).unwrap()).unwrap(), + 638 + ); + assert_eq!( + part2(parse(crate::util::data(2020, 2).unwrap()).unwrap()).unwrap(), + 699 + ); } diff --git a/src/2020/3/mod.rs b/src/2020/3/mod.rs index fb4e93d..03ddfce 100644 --- a/src/2020/3/mod.rs +++ b/src/2020/3/mod.rs @@ -1,6 +1,6 @@ use crate::util::grid::*; -struct Map { +pub struct Map { grid: Grid, } @@ -35,13 +35,19 @@ impl Map { } } -pub fn part1() -> anyhow::Result { - let map = Map::new(data_bool_grid!(b'#', b'.')); +pub fn parse(fh: std::fs::File) -> anyhow::Result { + Ok(Map::new(crate::util::parse::bool_grid( + crate::util::parse::lines(fh), + b'#', + b'.', + ))) +} + +pub fn part1(map: Map) -> anyhow::Result { map.trees_for_slope(1, 3) } -pub fn part2() -> anyhow::Result { - let map = Map::new(data_bool_grid!(b'#', b'.')); +pub fn part2(map: Map) -> anyhow::Result { Ok(map.trees_for_slope(1, 1)? * map.trees_for_slope(1, 3)? * map.trees_for_slope(1, 5)? @@ -51,6 +57,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 292); - assert_eq!(part2().unwrap(), 9354744432); + assert_eq!( + part1(parse(crate::util::data(2020, 3).unwrap()).unwrap()).unwrap(), + 292 + ); + assert_eq!( + part2(parse(crate::util::data(2020, 3).unwrap()).unwrap()).unwrap(), + 9354744432 + ); } diff --git a/src/2020/4/mod.rs b/src/2020/4/mod.rs index 5eace5a..f89993e 100644 --- a/src/2020/4/mod.rs +++ b/src/2020/4/mod.rs @@ -3,10 +3,40 @@ use anyhow::Context as _; const REQUIRED_KEYS: &[&str] = &["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]; -pub fn part1() -> anyhow::Result { - let batch = data_str!()?; +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result>> { + let mut res = vec![]; + let mut cur = std::collections::HashMap::new(); + for line in crate::util::parse::lines(fh) { + if line.is_empty() { + res.push(cur); + cur = std::collections::HashMap::new(); + continue; + } + + 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()); + } + } + if !cur.is_empty() { + res.push(cur); + } + Ok(res) +} + +pub fn part1( + passports: Vec>, +) -> anyhow::Result { let mut valid = 0; - for passport in parse(&batch)? { + for passport in passports { let mut cur_valid = true; for key in REQUIRED_KEYS { if !passport.contains_key(&key.to_string()) { @@ -21,10 +51,11 @@ pub fn part1() -> anyhow::Result { Ok(valid) } -pub fn part2() -> anyhow::Result { - let batch = data_str!()?; +pub fn part2( + passports: Vec>, +) -> anyhow::Result { let mut valid = 0; - for passport in parse(&batch)? { + for passport in passports { let mut cur_valid = true; for key in REQUIRED_KEYS { match passport.get(&key.to_string()) { @@ -47,35 +78,6 @@ pub fn part2() -> anyhow::Result { Ok(valid) } -fn parse( - batch: &str, -) -> anyhow::Result>> { - let mut res = vec![]; - let mut cur = std::collections::HashMap::new(); - for line in batch.lines() { - if line.is_empty() { - res.push(cur); - cur = std::collections::HashMap::new(); - continue; - } - - 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()); - } - } - if !cur.is_empty() { - res.push(cur); - } - Ok(res) -} - fn validate(key: &str, val: &str) -> anyhow::Result { match key { "byr" => match val.parse::() { @@ -131,6 +133,12 @@ fn validate(key: &str, val: &str) -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 247); - assert_eq!(part2().unwrap(), 145); + assert_eq!( + part1(parse(crate::util::data(2020, 4).unwrap()).unwrap()).unwrap(), + 247 + ); + assert_eq!( + part2(parse(crate::util::data(2020, 4).unwrap()).unwrap()).unwrap(), + 145 + ); } diff --git a/src/2020/5/mod.rs b/src/2020/5/mod.rs index e7c0f6e..b824e60 100644 --- a/src/2020/5/mod.rs +++ b/src/2020/5/mod.rs @@ -1,11 +1,13 @@ use anyhow::Context as _; use std::convert::TryInto as _; -pub fn part1() -> anyhow::Result { - let input = data_str!()?; +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + Ok(crate::util::parse::lines(fh).map(|line| seat_id(&line).unwrap())) +} + +pub fn part1(ids: impl Iterator) -> anyhow::Result { let mut max = 0; - for line in input.lines() { - let id = seat_id(line)?; + for id in ids { if id > max { max = id; } @@ -13,11 +15,9 @@ pub fn part1() -> anyhow::Result { Ok(max) } -pub fn part2() -> anyhow::Result { +pub fn part2(ids: impl Iterator) -> anyhow::Result { let mut seats = vec![false; 1024]; - let input = data_str!()?; - for line in input.lines() { - let id = seat_id(line)?; + for id in ids { seats[id as usize] = true; } let first = seats @@ -86,6 +86,12 @@ fn seat_id(desc: &str) -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 978); - assert_eq!(part2().unwrap(), 727); + assert_eq!( + part1(parse(crate::util::data(2020, 5).unwrap()).unwrap()).unwrap(), + 978 + ); + assert_eq!( + part2(parse(crate::util::data(2020, 5).unwrap()).unwrap()).unwrap(), + 727 + ); } diff --git a/src/2020/6/mod.rs b/src/2020/6/mod.rs index 5e918d7..21279af 100644 --- a/src/2020/6/mod.rs +++ b/src/2020/6/mod.rs @@ -1,8 +1,13 @@ -pub fn part1() -> anyhow::Result { - let input = data_str!()?; +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result> { + Ok(crate::util::parse::lines(fh)) +} + +pub fn part1(lines: impl Iterator) -> anyhow::Result { let mut yes = std::collections::HashSet::new(); let mut total = 0; - for line in input.lines() { + for line in lines { if line.is_empty() { total += yes.len() as i64; yes = std::collections::HashSet::new(); @@ -16,14 +21,13 @@ pub fn part1() -> anyhow::Result { Ok(total) } -pub fn part2() -> anyhow::Result { - let input = data_str!()?; +pub fn part2(lines: impl Iterator) -> anyhow::Result { let mut yes = std::collections::HashSet::new(); for c in 'a'..='z' { yes.insert(c); } let mut total = 0; - for line in input.lines() { + for line in lines { if line.is_empty() { total += yes.len() as i64; yes = std::collections::HashSet::new(); @@ -44,6 +48,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 6930); - assert_eq!(part2().unwrap(), 3585); + assert_eq!( + part1(parse(crate::util::data(2020, 6).unwrap()).unwrap()).unwrap(), + 6930 + ); + assert_eq!( + part2(parse(crate::util::data(2020, 6).unwrap()).unwrap()).unwrap(), + 3585 + ); } diff --git a/src/2020/7/mod.rs b/src/2020/7/mod.rs index b293f28..fbf2c43 100644 --- a/src/2020/7/mod.rs +++ b/src/2020/7/mod.rs @@ -2,9 +2,17 @@ use anyhow::Context as _; type Graph = std::collections::HashMap>; -pub fn part1() -> anyhow::Result { - let input = data_str!()?; - let graph = parse(&input)?; +pub fn parse(fh: std::fs::File) -> anyhow::Result { + let input = crate::util::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) -> anyhow::Result { let mut colors = 0; for color in graph.keys() { if bag_contains(&graph, color, "shiny gold")? { @@ -14,22 +22,11 @@ pub fn part1() -> anyhow::Result { Ok(colors) } -pub fn part2() -> anyhow::Result { - let input = data_str!()?; - let graph = parse(&input)?; +pub fn part2(graph: Graph) -> anyhow::Result { // subtract 1 to not count the shiny gold bag itself count_bags(&graph, "shiny gold").map(|i| i - 1) } -fn parse(input: &str) -> anyhow::Result { - let mut graph = Graph::new(); - for line in input.lines() { - let (k, v) = parse_line(line)?; - graph.insert(k, v); - } - Ok(graph) -} - fn parse_line(line: &str) -> anyhow::Result<(String, Vec<(i64, String)>)> { let main_rx = regex::Regex::new(r"^(.*) bags contain (.*)\.$").unwrap(); let contents_rx = regex::Regex::new(r"^([0-9]+) (.*) bags?").unwrap(); @@ -102,6 +99,12 @@ fn count_bags(graph: &Graph, color: &str) -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 169); - assert_eq!(part2().unwrap(), 82372); + assert_eq!( + part1(parse(crate::util::data(2020, 7).unwrap()).unwrap()).unwrap(), + 169 + ); + assert_eq!( + part2(parse(crate::util::data(2020, 7).unwrap()).unwrap()).unwrap(), + 82372 + ); } diff --git a/src/2020/8/mod.rs b/src/2020/8/mod.rs index 0756fe5..052538e 100644 --- a/src/2020/8/mod.rs +++ b/src/2020/8/mod.rs @@ -21,7 +21,7 @@ impl std::str::FromStr for OpType { } #[derive(Clone, Copy)] -struct Op { +pub struct Op { ty: OpType, arg: i64, } @@ -43,9 +43,13 @@ impl std::str::FromStr for Op { } } -pub fn part1() -> anyhow::Result { - let input = data_str!()?; - let opcodes = parse(&input)?; +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + crate::util::parse::lines(fh) + .map(|line| line.parse()) + .collect() +} + +pub fn part1(opcodes: Vec) -> anyhow::Result { let (acc, success) = run(&opcodes)?; if success { return Err(anyhow::anyhow!("unexpectedly succeeded")); @@ -53,9 +57,7 @@ pub fn part1() -> anyhow::Result { Ok(acc) } -pub fn part2() -> anyhow::Result { - let input = data_str!()?; - let opcodes = parse(&input)?; +pub fn part2(opcodes: Vec) -> anyhow::Result { for i in 0..opcodes.len() { match opcodes[i].ty { OpType::Nop => { @@ -80,10 +82,6 @@ pub fn part2() -> anyhow::Result { Err(anyhow::anyhow!("failed to find corrupted opcode")) } -fn parse(input: &str) -> anyhow::Result> { - input.lines().map(|line| line.parse()).collect() -} - fn run(opcodes: &[Op]) -> anyhow::Result<(i64, bool)> { let mut seen = vec![false; opcodes.len()]; let mut pc = 0; @@ -126,6 +124,12 @@ fn run(opcodes: &[Op]) -> anyhow::Result<(i64, bool)> { #[test] fn test() { - assert_eq!(part1().unwrap(), 1928); - assert_eq!(part2().unwrap(), 1319); + assert_eq!( + part1(parse(crate::util::data(2020, 8).unwrap()).unwrap()).unwrap(), + 1928 + ); + assert_eq!( + part2(parse(crate::util::data(2020, 8).unwrap()).unwrap()).unwrap(), + 1319 + ); } diff --git a/src/2020/9/mod.rs b/src/2020/9/mod.rs index 9d1bc9f..c067ca0 100644 --- a/src/2020/9/mod.rs +++ b/src/2020/9/mod.rs @@ -1,7 +1,10 @@ -pub fn part1() -> anyhow::Result { - const WINDOW: usize = 25; +const WINDOW: usize = 25; - let list: Vec<_> = data_ints!()?.collect(); +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + Ok(crate::util::parse::ints(crate::util::parse::lines(fh)).collect()) +} + +pub fn part1(list: Vec) -> anyhow::Result { for i in 0..(list.len() - WINDOW) { let set = &list[i..i + WINDOW]; let n = list[i + WINDOW]; @@ -13,10 +16,7 @@ pub fn part1() -> anyhow::Result { Err(anyhow::anyhow!("failed to find invalid number")) } -pub fn part2() -> anyhow::Result { - const WINDOW: usize = 25; - - let list: Vec<_> = data_ints!()?.collect(); +pub fn part2(list: Vec) -> anyhow::Result { let mut invalid = None; for i in 0..(list.len() - WINDOW) { let set = &list[i..i + WINDOW]; @@ -63,6 +63,12 @@ fn valid(set: &[i64], n: i64) -> bool { #[test] fn test() { - assert_eq!(part1().unwrap(), 373803594); - assert_eq!(part2().unwrap(), 51152360); + assert_eq!( + part1(parse(crate::util::data(2020, 9).unwrap()).unwrap()).unwrap(), + 373803594 + ); + assert_eq!( + part2(parse(crate::util::data(2020, 9).unwrap()).unwrap()).unwrap(), + 51152360 + ); } diff --git a/src/2020/mod.rs b/src/2020/mod.rs index 7a41d7c..b4ea179 100644 --- a/src/2020/mod.rs +++ b/src/2020/mod.rs @@ -20,24 +20,24 @@ mod day9; pub fn run(day: u8, puzzle: u8) -> anyhow::Result { match (day, puzzle) { - (1, 1) => day1::part1(), - (1, 2) => day1::part2(), - (2, 1) => day2::part1(), - (2, 2) => day2::part2(), - (3, 1) => day3::part1(), - (3, 2) => day3::part2(), - (4, 1) => day4::part1(), - (4, 2) => day4::part2(), - (5, 1) => day5::part1(), - (5, 2) => day5::part2(), - (6, 1) => day6::part1(), - (6, 2) => day6::part2(), - (7, 1) => day7::part1(), - (7, 2) => day7::part2(), - (8, 1) => day8::part1(), - (8, 2) => day8::part2(), - (9, 1) => day9::part1(), - (9, 2) => day9::part2(), + (1, 1) => day1::part1(day1::parse(crate::util::data(2020, 1)?)?), + (1, 2) => day1::part2(day1::parse(crate::util::data(2020, 1)?)?), + (2, 1) => day2::part1(day2::parse(crate::util::data(2020, 2)?)?), + (2, 2) => day2::part2(day2::parse(crate::util::data(2020, 2)?)?), + (3, 1) => day3::part1(day3::parse(crate::util::data(2020, 3)?)?), + (3, 2) => day3::part2(day3::parse(crate::util::data(2020, 3)?)?), + (4, 1) => day4::part1(day4::parse(crate::util::data(2020, 4)?)?), + (4, 2) => day4::part2(day4::parse(crate::util::data(2020, 4)?)?), + (5, 1) => day5::part1(day5::parse(crate::util::data(2020, 5)?)?), + (5, 2) => day5::part2(day5::parse(crate::util::data(2020, 5)?)?), + (6, 1) => day6::part1(day6::parse(crate::util::data(2020, 6)?)?), + (6, 2) => day6::part2(day6::parse(crate::util::data(2020, 6)?)?), + (7, 1) => day7::part1(day7::parse(crate::util::data(2020, 7)?)?), + (7, 2) => day7::part2(day7::parse(crate::util::data(2020, 7)?)?), + (8, 1) => day8::part1(day8::parse(crate::util::data(2020, 8)?)?), + (8, 2) => day8::part2(day8::parse(crate::util::data(2020, 8)?)?), + (9, 1) => day9::part1(day9::parse(crate::util::data(2020, 9)?)?), + (9, 2) => day9::part2(day9::parse(crate::util::data(2020, 9)?)?), // NEXT PART _ => Err(anyhow::anyhow!("unknown puzzle {}-{}", day, puzzle)), } diff --git a/src/2021/1/mod.rs b/src/2021/1/mod.rs index be03275..3ba61e2 100644 --- a/src/2021/1/mod.rs +++ b/src/2021/1/mod.rs @@ -1,6 +1,9 @@ -pub fn part1() -> anyhow::Result { - Ok(data_ints!()? - .collect::>() +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + Ok(crate::util::parse::ints(crate::util::parse::lines(fh)).collect()) +} + +pub fn part1(ints: Vec) -> anyhow::Result { + Ok(ints .windows(2) .map(|a| a[1] - a[0]) .filter(|x| *x > 0) @@ -8,9 +11,8 @@ pub fn part1() -> anyhow::Result { .try_into()?) } -pub fn part2() -> anyhow::Result { - Ok(data_ints!()? - .collect::>() +pub fn part2(ints: Vec) -> anyhow::Result { + Ok(ints .windows(3) .map(|a| a[0] + a[1] + a[2]) .collect::>() @@ -23,6 +25,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 1602); - assert_eq!(part2().unwrap(), 1633); + assert_eq!( + part1(parse(crate::util::data(2021, 1).unwrap()).unwrap()).unwrap(), + 1602 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 1).unwrap()).unwrap()).unwrap(), + 1633 + ); } diff --git a/src/2021/10/mod.rs b/src/2021/10/mod.rs index 29cced2..ae311e8 100644 --- a/src/2021/10/mod.rs +++ b/src/2021/10/mod.rs @@ -1,6 +1,12 @@ -pub fn part1() -> anyhow::Result { +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result> { + Ok(crate::util::parse::lines(fh)) +} + +pub fn part1(lines: impl Iterator) -> anyhow::Result { let mut total = 0; - for line in data_lines!()? { + for line in lines { let mut open = vec![]; for c in line.chars() { match c { @@ -38,9 +44,9 @@ pub fn part1() -> anyhow::Result { Ok(total) } -pub fn part2() -> anyhow::Result { +pub fn part2(lines: impl Iterator) -> anyhow::Result { let mut scores = vec![]; - for line in data_lines!()? { + for line in lines { let mut open = vec![]; let mut skip = false; for c in line.chars() { @@ -92,6 +98,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 166191); - assert_eq!(part2().unwrap(), 1152088313); + assert_eq!( + part1(parse(crate::util::data(2021, 10).unwrap()).unwrap()).unwrap(), + 166191 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 10).unwrap()).unwrap()).unwrap(), + 1152088313 + ); } diff --git a/src/2021/11/mod.rs b/src/2021/11/mod.rs index f7f3b99..38a1836 100644 --- a/src/2021/11/mod.rs +++ b/src/2021/11/mod.rs @@ -42,11 +42,16 @@ fn iterate(grid: &mut Grid<(u8, bool)>) -> i64 { flashes } -pub fn part1() -> anyhow::Result { - let mut map = data_digit_grid!() - .indexed_cells() - .map(|((row, col), cell)| ((row, col), (*cell, false))) - .collect(); +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + Ok( + crate::util::parse::digit_grid(crate::util::parse::lines(fh)) + .indexed_cells() + .map(|((row, col), cell)| ((row, col), (*cell, false))) + .collect(), + ) +} + +pub fn part1(mut map: Grid<(u8, bool)>) -> anyhow::Result { let mut flashes = 0; for _ in 0..100 { flashes += iterate(&mut map); @@ -54,12 +59,7 @@ pub fn part1() -> anyhow::Result { Ok(flashes) } -pub fn part2() -> anyhow::Result { - let mut map = data_digit_grid!() - .indexed_cells() - .map(|((row, col), cell)| ((row, col), (*cell, false))) - .collect(); - +pub fn part2(mut map: Grid<(u8, bool)>) -> anyhow::Result { let mut step = 1; loop { let flashes = iterate(&mut map); @@ -73,6 +73,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 1673); - assert_eq!(part2().unwrap(), 279); + assert_eq!( + part1(parse(crate::util::data(2021, 11).unwrap()).unwrap()).unwrap(), + 1673 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 11).unwrap()).unwrap()).unwrap(), + 279 + ); } diff --git a/src/2021/12/mod.rs b/src/2021/12/mod.rs index 022a8b8..5907e67 100644 --- a/src/2021/12/mod.rs +++ b/src/2021/12/mod.rs @@ -68,9 +68,13 @@ fn paths_from2<'a>( total } -pub fn part1() -> anyhow::Result { +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result< + std::collections::HashMap>, +> { let mut graph = std::collections::HashMap::new(); - for line in data_lines!()? { + for line in crate::util::parse::lines(fh) { let nodes: Vec = line.split('-').map(|s| s.to_string()).collect(); let edges = graph @@ -82,28 +86,35 @@ pub fn part1() -> anyhow::Result { .or_insert_with(std::collections::HashSet::new); edges.insert(nodes[0].clone()); } + Ok(graph) +} + +pub fn part1( + graph: std::collections::HashMap< + String, + std::collections::HashSet, + >, +) -> anyhow::Result { Ok(paths_from1(&graph, &mut vec!["start"])) } -pub fn part2() -> anyhow::Result { - let mut graph = std::collections::HashMap::new(); - for line in data_lines!()? { - let nodes: Vec = - line.split('-').map(|s| s.to_string()).collect(); - let edges = graph - .entry(nodes[0].clone()) - .or_insert_with(std::collections::HashSet::new); - edges.insert(nodes[1].clone()); - let edges = graph - .entry(nodes[1].clone()) - .or_insert_with(std::collections::HashSet::new); - edges.insert(nodes[0].clone()); - } +pub fn part2( + graph: std::collections::HashMap< + String, + std::collections::HashSet, + >, +) -> anyhow::Result { Ok(paths_from2(&graph, &mut vec!["start"])) } #[test] fn test() { - assert_eq!(part1().unwrap(), 3230); - assert_eq!(part2().unwrap(), 83475); + assert_eq!( + part1(parse(crate::util::data(2021, 12).unwrap()).unwrap()).unwrap(), + 3230 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 12).unwrap()).unwrap()).unwrap(), + 83475 + ); } diff --git a/src/2021/13/mod.rs b/src/2021/13/mod.rs index 0be74d1..717e032 100644 --- a/src/2021/13/mod.rs +++ b/src/2021/13/mod.rs @@ -4,7 +4,7 @@ use crate::util::grid::*; #[derive(Default)] -struct Paper { +pub struct Paper { grid: Grid, } @@ -94,10 +94,12 @@ impl std::fmt::Display for Paper { } } -pub fn part1() -> anyhow::Result { +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result<(Paper, Vec<(bool, usize)>)> { let mut paper = Paper::default(); let mut folds = vec![]; - for line in data_lines!()? { + for line in crate::util::parse::lines(fh) { if line.is_empty() { continue; } @@ -113,31 +115,19 @@ pub fn part1() -> anyhow::Result { paper.set(Row(y), Col(x)); } } + Ok((paper, folds)) +} +pub fn part1( + (mut paper, folds): (Paper, Vec<(bool, usize)>), +) -> anyhow::Result { paper.fold(folds[0].0, folds[0].1); Ok(paper.total()) } -pub fn part2() -> anyhow::Result { - let mut paper = Paper::default(); - let mut folds = vec![]; - for line in data_lines!()? { - if line.is_empty() { - continue; - } - if let Some(fold) = line.strip_prefix("fold along ") { - let mut fold = fold.split('='); - let horizontal = fold.next().unwrap() == "x"; - let coord: usize = fold.next().unwrap().parse()?; - folds.push((horizontal, coord)); - } else { - let mut coords = line.split(','); - let x: usize = coords.next().unwrap().parse()?; - let y: usize = coords.next().unwrap().parse()?; - paper.set(Row(y), Col(x)); - } - } - +pub fn part2( + (mut paper, folds): (Paper, Vec<(bool, usize)>), +) -> anyhow::Result { for fold in folds { paper.fold(fold.0, fold.1); } @@ -148,6 +138,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 678); - assert_eq!(part2().unwrap(), 95); + assert_eq!( + part1(parse(crate::util::data(2021, 13).unwrap()).unwrap()).unwrap(), + 678 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 13).unwrap()).unwrap()).unwrap(), + 95 + ); } diff --git a/src/2021/14/mod.rs b/src/2021/14/mod.rs index fb36468..97c2e9c 100644 --- a/src/2021/14/mod.rs +++ b/src/2021/14/mod.rs @@ -1,18 +1,3 @@ -#[allow(clippy::type_complexity)] -fn parse() -> anyhow::Result<(Vec, std::collections::HashMap, u8>)> -{ - let mut lines = data_lines!()?; - let polymer = lines.next().unwrap(); - lines.next(); - - let mut rules = std::collections::HashMap::new(); - for line in lines { - let rule: Vec<_> = line.split(" -> ").collect(); - rules.insert(rule[0].as_bytes().to_vec(), rule[1].as_bytes()[0]); - } - Ok((polymer.as_bytes().to_vec(), rules)) -} - fn process( polymer: &[u8], rules: &std::collections::HashMap, u8>, @@ -32,9 +17,25 @@ fn process( polymer } -pub fn part1() -> anyhow::Result { - let (mut polymer, rules) = parse()?; +#[allow(clippy::type_complexity)] +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result<(Vec, std::collections::HashMap, u8>)> { + let mut lines = crate::util::parse::lines(fh); + let polymer = lines.next().unwrap(); + lines.next(); + + let mut rules = std::collections::HashMap::new(); + for line in lines { + let rule: Vec<_> = line.split(" -> ").collect(); + rules.insert(rule[0].as_bytes().to_vec(), rule[1].as_bytes()[0]); + } + Ok((polymer.as_bytes().to_vec(), rules)) +} +pub fn part1( + (mut polymer, rules): (Vec, std::collections::HashMap, u8>), +) -> anyhow::Result { for _ in 0..10 { polymer = process(&polymer, &rules); } @@ -46,9 +47,9 @@ pub fn part1() -> anyhow::Result { Ok(elements.values().max().unwrap() - elements.values().min().unwrap()) } -pub fn part2() -> anyhow::Result { - let (polymer, rules) = parse()?; - +pub fn part2( + (polymer, rules): (Vec, std::collections::HashMap, u8>), +) -> anyhow::Result { let mut pairs = std::collections::HashMap::new(); for pair in polymer.windows(2) { let count = pairs.entry([pair[0], pair[1]]).or_insert(0); @@ -86,6 +87,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 2874); - assert_eq!(part2().unwrap(), 5208377027195); + assert_eq!( + part1(parse(crate::util::data(2021, 14).unwrap()).unwrap()).unwrap(), + 2874 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 14).unwrap()).unwrap()).unwrap(), + 5208377027195 + ); } diff --git a/src/2021/15/mod.rs b/src/2021/15/mod.rs index fa1d82a..e2d0656 100644 --- a/src/2021/15/mod.rs +++ b/src/2021/15/mod.rs @@ -40,12 +40,17 @@ fn dijkstra(map: &Grid) -> i64 { unreachable!() } -pub fn part1() -> anyhow::Result { - Ok(dijkstra(&data_digit_grid!())) +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + Ok(crate::util::parse::digit_grid(crate::util::parse::lines( + fh, + ))) } -pub fn part2() -> anyhow::Result { - let grid = data_digit_grid!(); +pub fn part1(grid: Grid) -> anyhow::Result { + Ok(dijkstra(&grid)) +} + +pub fn part2(grid: Grid) -> anyhow::Result { let mut large_grid = Grid::default(); large_grid.grow(Row(grid.rows().0 * 5), Col(grid.cols().0 * 5)); for lrow in 0..5 { @@ -65,6 +70,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 441); - assert_eq!(part2().unwrap(), 2849); + assert_eq!( + part1(parse(crate::util::data(2021, 15).unwrap()).unwrap()).unwrap(), + 441 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 15).unwrap()).unwrap()).unwrap(), + 2849 + ); } diff --git a/src/2021/16/mod.rs b/src/2021/16/mod.rs index fd57965..4a66fa8 100644 --- a/src/2021/16/mod.rs +++ b/src/2021/16/mod.rs @@ -58,7 +58,7 @@ impl FromIterator for LiteralU16 { } } -struct Packet { +pub struct Packet { version: u8, id: u8, contents: PacketContents, @@ -193,29 +193,34 @@ impl<'a> Iterator for Subpackets<'a> { } } -pub fn part1() -> anyhow::Result { - let line = data_lines!()?.next().unwrap(); +pub fn parse(fh: std::fs::File) -> anyhow::Result { + let line = crate::util::parse::lines(fh).next().unwrap(); let mut bits = bits(line.as_bytes().chunks(2).map(|bs| { u8::from_str_radix(std::str::from_utf8(bs).unwrap(), 16).unwrap() })); let (packet, _) = Packet::parse(bits.by_ref()); + Ok(packet) +} + +pub fn part1(packet: Packet) -> anyhow::Result { Ok(packet .subpackets() .map(|packet| i64::from(packet.version)) .sum()) } -pub fn part2() -> anyhow::Result { - let line = data_lines!()?.next().unwrap(); - let mut bits = bits(line.as_bytes().chunks(2).map(|bs| { - u8::from_str_radix(std::str::from_utf8(bs).unwrap(), 16).unwrap() - })); - let (packet, _) = Packet::parse(bits.by_ref()); +pub fn part2(packet: Packet) -> anyhow::Result { Ok(packet.eval()) } #[test] fn test() { - assert_eq!(part1().unwrap(), 979); - assert_eq!(part2().unwrap(), 277110354175); + assert_eq!( + part1(parse(crate::util::data(2021, 16).unwrap()).unwrap()).unwrap(), + 979 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 16).unwrap()).unwrap()).unwrap(), + 277110354175 + ); } diff --git a/src/2021/17/mod.rs b/src/2021/17/mod.rs index 5bf6651..806d9c1 100644 --- a/src/2021/17/mod.rs +++ b/src/2021/17/mod.rs @@ -35,18 +35,31 @@ fn fire( } } -pub fn part1() -> anyhow::Result { +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result<( + std::ops::RangeInclusive, + std::ops::RangeInclusive, +)> { let rx = regex::Regex::new( r"target area: x=(-?\d+)..(-?\d+), y=(-?\d+)..(-?\d+)", ) .unwrap(); - let line = data_lines!()?.next().unwrap(); + let line = crate::util::parse::lines(fh).next().unwrap(); let captures = rx.captures(&line).unwrap(); let xrange: std::ops::RangeInclusive = captures[1].parse().unwrap()..=captures[2].parse().unwrap(); let yrange: std::ops::RangeInclusive = captures[3].parse().unwrap()..=captures[4].parse().unwrap(); + Ok((xrange, yrange)) +} +pub fn part1( + (xrange, yrange): ( + std::ops::RangeInclusive, + std::ops::RangeInclusive, + ), +) -> anyhow::Result { let mut max_height = 0; for xv in *xrange.start().min(&0)..=*xrange.end().max(&0) { for yv in *yrange.start().min(&0) @@ -62,18 +75,12 @@ pub fn part1() -> anyhow::Result { Ok(max_height) } -pub fn part2() -> anyhow::Result { - let rx = regex::Regex::new( - r"target area: x=(-?\d+)..(-?\d+), y=(-?\d+)..(-?\d+)", - ) - .unwrap(); - let line = data_lines!()?.next().unwrap(); - let captures = rx.captures(&line).unwrap(); - let xrange: std::ops::RangeInclusive = - captures[1].parse().unwrap()..=captures[2].parse().unwrap(); - let yrange: std::ops::RangeInclusive = - captures[3].parse().unwrap()..=captures[4].parse().unwrap(); - +pub fn part2( + (xrange, yrange): ( + std::ops::RangeInclusive, + std::ops::RangeInclusive, + ), +) -> anyhow::Result { let mut count = 0; for xv in *xrange.start().min(&0)..=*xrange.end().max(&0) { for yv in *yrange.start().min(&0) @@ -89,6 +96,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 5886); - assert_eq!(part2().unwrap(), 1806); + assert_eq!( + part1(parse(crate::util::data(2021, 17).unwrap()).unwrap()).unwrap(), + 5886 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 17).unwrap()).unwrap()).unwrap(), + 1806 + ); } diff --git a/src/2021/18/mod.rs b/src/2021/18/mod.rs index e0616db..bd55f4d 100644 --- a/src/2021/18/mod.rs +++ b/src/2021/18/mod.rs @@ -1,7 +1,7 @@ #![allow(clippy::collapsible_else_if)] #[derive(Clone)] -enum Number { +pub enum Number { Value(u8), Pair(Box, Box), } @@ -210,14 +210,19 @@ impl std::fmt::Display for Number { } } -pub fn part1() -> anyhow::Result { - let sum: Number = data_lines!()?.map(|line| Number::parse(&line)).sum(); +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result> { + Ok(crate::util::parse::lines(fh).map(|line| Number::parse(&line))) +} + +pub fn part1(numbers: impl Iterator) -> anyhow::Result { + let sum: Number = numbers.sum(); Ok(sum.magnitude()) } -pub fn part2() -> anyhow::Result { - let nums: Vec<_> = - data_lines!()?.map(|line| Number::parse(&line)).collect(); +pub fn part2(numbers: impl Iterator) -> anyhow::Result { + let nums: Vec<_> = numbers.collect(); let mut max = 0; for (i, n1) in nums.iter().enumerate() { for (j, n2) in nums.iter().enumerate() { @@ -235,6 +240,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 3806); - assert_eq!(part2().unwrap(), 4727); + assert_eq!( + part1(parse(crate::util::data(2021, 18).unwrap()).unwrap()).unwrap(), + 3806 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 18).unwrap()).unwrap()).unwrap(), + 4727 + ); } diff --git a/src/2021/2/mod.rs b/src/2021/2/mod.rs index e175ab9..6cac620 100644 --- a/src/2021/2/mod.rs +++ b/src/2021/2/mod.rs @@ -1,31 +1,60 @@ -pub fn part1() -> anyhow::Result { - let mut horizontal = 0; - let mut vertical = 0; - for line in data_lines!()? { +pub enum Command { + Forward(i64), + Down(i64), + Up(i64), +} + +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result> { + Ok(crate::util::parse::lines(fh).map(|line| { if let Some(n) = line.strip_prefix("forward ") { - horizontal += n.parse::()?; + Command::Forward(n.parse().unwrap()) } else if let Some(n) = line.strip_prefix("down ") { - vertical += n.parse::()?; + Command::Down(n.parse().unwrap()) } else if let Some(n) = line.strip_prefix("up ") { - vertical -= n.parse::()?; + Command::Up(n.parse().unwrap()) + } else { + panic!("couldn't parse line: {}", line); + } + })) +} + +pub fn part1(commands: impl Iterator) -> anyhow::Result { + let mut horizontal = 0; + let mut vertical = 0; + for command in commands { + match command { + Command::Forward(n) => { + horizontal += n; + } + Command::Down(n) => { + vertical += n; + } + Command::Up(n) => { + vertical -= n; + } } } Ok(horizontal * vertical) } -pub fn part2() -> anyhow::Result { +pub fn part2(commands: impl Iterator) -> anyhow::Result { let mut aim = 0; let mut horizontal = 0; let mut vertical = 0; - for line in data_lines!()? { - if let Some(n) = line.strip_prefix("forward ") { - let x = n.parse::()?; - horizontal += x; - vertical += aim * x; - } else if let Some(n) = line.strip_prefix("down ") { - aim += n.parse::()?; - } else if let Some(n) = line.strip_prefix("up ") { - aim -= n.parse::()?; + for command in commands { + match command { + Command::Forward(n) => { + horizontal += n; + vertical += aim * n; + } + Command::Down(n) => { + aim += n; + } + Command::Up(n) => { + aim -= n; + } } } Ok(horizontal * vertical) @@ -33,6 +62,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 1694130); - assert_eq!(part2().unwrap(), 1698850445); + assert_eq!( + part1(parse(crate::util::data(2021, 2).unwrap()).unwrap()).unwrap(), + 1694130 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 2).unwrap()).unwrap()).unwrap(), + 1698850445 + ); } diff --git a/src/2021/3/mod.rs b/src/2021/3/mod.rs index a0fc64e..220ce33 100644 --- a/src/2021/3/mod.rs +++ b/src/2021/3/mod.rs @@ -1,5 +1,11 @@ -pub fn part1() -> anyhow::Result { - let (total_lines, by_pos) = pos_counts(data_lines!()?)?; +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result> { + Ok(crate::util::parse::lines(fh)) +} + +pub fn part1(lines: impl Iterator) -> anyhow::Result { + let (total_lines, by_pos) = pos_counts(lines)?; let gamma: String = by_pos .iter() .map(|pos| if pos * 2 >= total_lines { '1' } else { '0' }) @@ -11,8 +17,8 @@ pub fn part1() -> anyhow::Result { Ok(bin_str_to_int(&gamma) * bin_str_to_int(&epsilon)) } -pub fn part2() -> anyhow::Result { - let mut oxygen: Vec<_> = data_lines!()?.collect(); +pub fn part2(lines: impl Iterator) -> anyhow::Result { + let mut oxygen: Vec<_> = lines.collect(); for i in 0..oxygen[0].len() { if oxygen.len() == 1 { break; @@ -84,6 +90,12 @@ fn bin_str_to_int(s: &str) -> i64 { #[test] fn test() { - assert_eq!(part1().unwrap(), 3009600); - assert_eq!(part2().unwrap(), 6940518); + assert_eq!( + part1(parse(crate::util::data(2021, 3).unwrap()).unwrap()).unwrap(), + 3009600 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 3).unwrap()).unwrap()).unwrap(), + 6940518 + ); } diff --git a/src/2021/4/mod.rs b/src/2021/4/mod.rs index 421ad67..c3bd7d8 100644 --- a/src/2021/4/mod.rs +++ b/src/2021/4/mod.rs @@ -51,7 +51,7 @@ impl Board { } #[derive(Debug)] -struct Game { +pub struct Game { inputs: Vec, boards: Vec, } @@ -133,8 +133,11 @@ impl Game { } } -pub fn part1() -> anyhow::Result { - let game = Game::parse(data!()?)?; +pub fn parse(fh: std::fs::File) -> anyhow::Result { + Game::parse(fh) +} + +pub fn part1(game: Game) -> anyhow::Result { if let Some((n, board)) = game.find_first_winner() { Ok((n as i64) * board.value()) } else { @@ -142,8 +145,7 @@ pub fn part1() -> anyhow::Result { } } -pub fn part2() -> anyhow::Result { - let game = Game::parse(data!()?)?; +pub fn part2(game: Game) -> anyhow::Result { if let Some((n, board)) = game.find_last_winner() { Ok((n as i64) * board.value()) } else { @@ -153,6 +155,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 2745); - assert_eq!(part2().unwrap(), 6594); + assert_eq!( + part1(parse(crate::util::data(2021, 4).unwrap()).unwrap()).unwrap(), + 2745 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 4).unwrap()).unwrap()).unwrap(), + 6594 + ); } diff --git a/src/2021/5/mod.rs b/src/2021/5/mod.rs index 229ba20..2e81e34 100644 --- a/src/2021/5/mod.rs +++ b/src/2021/5/mod.rs @@ -72,34 +72,37 @@ impl Map { } } -pub fn part1() -> anyhow::Result { - let rx = regex::Regex::new("^(\\d+),(\\d+) -> (\\d+),(\\d+)$").unwrap(); - let mut map = Map::default(); - for line in data_lines!()? { - let nums: Vec = rx - .captures(&line) +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result>> { + let rx = regex::Regex::new("^(\\d+),(\\d+) -> (\\d+),(\\d+)$")?; + Ok(crate::util::parse::lines(fh).map(move |line| { + rx.captures(&line) .unwrap() .iter() .skip(1) .map(|s| s.unwrap().as_str().parse()) - .collect::>()?; + .collect::>() + .unwrap() + })) +} + +pub fn part1( + coords: impl Iterator>, +) -> anyhow::Result { + let mut map = Map::default(); + for nums in coords { let _ = map.mark_horizontal(nums[0], nums[1], nums[2], nums[3]) || map.mark_vertical(nums[0], nums[1], nums[2], nums[3]); } Ok(map.count_overlapping().try_into()?) } -pub fn part2() -> anyhow::Result { - let rx = regex::Regex::new("^(\\d+),(\\d+) -> (\\d+),(\\d+)$").unwrap(); +pub fn part2( + coords: impl Iterator>, +) -> anyhow::Result { let mut map = Map::default(); - for line in data_lines!()? { - let nums: Vec = rx - .captures(&line) - .unwrap() - .iter() - .skip(1) - .map(|s| s.unwrap().as_str().parse()) - .collect::>()?; + for nums in coords { let _ = map.mark_horizontal(nums[0], nums[1], nums[2], nums[3]) || map.mark_vertical(nums[0], nums[1], nums[2], nums[3]) || map.mark_diagonal(nums[0], nums[1], nums[2], nums[3]) @@ -110,6 +113,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 6311); - assert_eq!(part2().unwrap(), 19929); + assert_eq!( + part1(parse(crate::util::data(2021, 5).unwrap()).unwrap()).unwrap(), + 6311 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 5).unwrap()).unwrap()).unwrap(), + 19929 + ); } diff --git a/src/2021/6/mod.rs b/src/2021/6/mod.rs index 821e777..353aa8f 100644 --- a/src/2021/6/mod.rs +++ b/src/2021/6/mod.rs @@ -1,5 +1,11 @@ -pub fn part1() -> anyhow::Result { - let mut fishes: Vec<_> = data_ints!(b',')?.collect(); +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + Ok( + crate::util::parse::ints(crate::util::parse::split(fh, b',')) + .collect(), + ) +} + +pub fn part1(mut fishes: Vec) -> anyhow::Result { for _ in 0..80 { let mut new = 0; for fish in fishes.iter_mut() { @@ -15,8 +21,7 @@ pub fn part1() -> anyhow::Result { Ok(fishes.len().try_into()?) } -pub fn part2() -> anyhow::Result { - let fishes: Vec<_> = data_ints!(b',')?.collect(); +pub fn part2(fishes: Vec) -> anyhow::Result { let mut by_age = std::collections::VecDeque::new(); by_age.resize(9, 0); for fish in fishes { @@ -32,6 +37,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 379114); - assert_eq!(part2().unwrap(), 1702631502303); + assert_eq!( + part1(parse(crate::util::data(2021, 6).unwrap()).unwrap()).unwrap(), + 379114 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 6).unwrap()).unwrap()).unwrap(), + 1702631502303 + ); } diff --git a/src/2021/7/mod.rs b/src/2021/7/mod.rs index 1d8320a..ce942dc 100644 --- a/src/2021/7/mod.rs +++ b/src/2021/7/mod.rs @@ -1,5 +1,11 @@ -pub fn part1() -> anyhow::Result { - let crabs: Vec<_> = data_ints!(b',')?.collect(); +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + Ok( + crate::util::parse::ints(crate::util::parse::split(fh, b',')) + .collect(), + ) +} + +pub fn part1(crabs: Vec) -> anyhow::Result { Ok((0..=crabs.iter().copied().max().unwrap()) .map(|start| { crabs.iter().copied().map(|crab| (crab - start).abs()).sum() @@ -8,8 +14,7 @@ pub fn part1() -> anyhow::Result { .unwrap()) } -pub fn part2() -> anyhow::Result { - let crabs: Vec<_> = data_ints!(b',')?.collect(); +pub fn part2(crabs: Vec) -> anyhow::Result { Ok((0..=crabs.iter().copied().max().unwrap()) .map(|start| { crabs @@ -27,6 +32,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 333755); - assert_eq!(part2().unwrap(), 94017638); + assert_eq!( + part1(parse(crate::util::data(2021, 7).unwrap()).unwrap()).unwrap(), + 333755 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 7).unwrap()).unwrap()).unwrap(), + 94017638 + ); } diff --git a/src/2021/8/mod.rs b/src/2021/8/mod.rs index 7244524..8544247 100644 --- a/src/2021/8/mod.rs +++ b/src/2021/8/mod.rs @@ -1,10 +1,22 @@ -pub fn part1() -> anyhow::Result { +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result, Vec)>> { + Ok(crate::util::parse::lines(fh).map(|line| { + let parts: Vec<_> = line.split(" | ").collect(); + ( + parts[0].split(' ').map(str::to_string).collect(), + parts[1].split(' ').map(str::to_string).collect(), + ) + })) +} + +pub fn part1( + lines: impl Iterator, Vec)>, +) -> anyhow::Result { let mut count = 0i64; - for line in data_lines!()? { - let mut parts = line.split(" | "); - let output = parts.nth(1).unwrap(); + for (_, output) in lines { let line_count: i64 = output - .split(' ') + .iter() .filter(|s| [2, 3, 4, 7].contains(&s.len())) .count() .try_into() @@ -21,13 +33,11 @@ pub fn part1() -> anyhow::Result { // 4 5 // 4 5 // 66 -pub fn part2() -> anyhow::Result { +pub fn part2( + lines: impl Iterator, Vec)>, +) -> anyhow::Result { let mut total = 0; - for line in data_lines!()? { - let mut parts = line.split(" | "); - let numbers = parts.next().unwrap(); - let numbers: Vec<_> = numbers.split(' ').collect(); - + for (numbers, output) in lines { let mut segments = ['x'; 7]; // zero: 6 @@ -150,9 +160,8 @@ pub fn part2() -> anyhow::Result { let numbers = [zero, one, two, three, four, five, six, seven, eight, nine]; - let output = parts.next().unwrap(); let value: Vec<_> = output - .split(' ') + .iter() .map(|s| s.chars().collect::>()) .map(|set| numbers.iter().position(|num| &set == num).unwrap()) .collect(); @@ -165,6 +174,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 355); - assert_eq!(part2().unwrap(), 983030); + assert_eq!( + part1(parse(crate::util::data(2021, 8).unwrap()).unwrap()).unwrap(), + 355 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 8).unwrap()).unwrap()).unwrap(), + 983030 + ); } diff --git a/src/2021/9/mod.rs b/src/2021/9/mod.rs index 7c937a5..184c245 100644 --- a/src/2021/9/mod.rs +++ b/src/2021/9/mod.rs @@ -1,7 +1,12 @@ use crate::util::grid::*; -pub fn part1() -> anyhow::Result { - let map = data_digit_grid!(); +pub fn parse(fh: std::fs::File) -> anyhow::Result> { + Ok(crate::util::parse::digit_grid(crate::util::parse::lines( + fh, + ))) +} + +pub fn part1(map: Grid) -> anyhow::Result { let mut risk = 0; for ((row, col), pos) in map.indexed_cells() { if map @@ -15,8 +20,7 @@ pub fn part1() -> anyhow::Result { Ok(risk) } -pub fn part2() -> anyhow::Result { - let map = data_digit_grid!(); +pub fn part2(map: Grid) -> anyhow::Result { let mut low = vec![]; for ((row, col), pos) in map.indexed_cells() { if map @@ -58,6 +62,12 @@ pub fn part2() -> anyhow::Result { #[test] fn test() { - assert_eq!(part1().unwrap(), 570); - assert_eq!(part2().unwrap(), 899392); + assert_eq!( + part1(parse(crate::util::data(2021, 9).unwrap()).unwrap()).unwrap(), + 570 + ); + assert_eq!( + part2(parse(crate::util::data(2021, 9).unwrap()).unwrap()).unwrap(), + 899392 + ); } diff --git a/src/2021/mod.rs b/src/2021/mod.rs index b66f03e..507251e 100644 --- a/src/2021/mod.rs +++ b/src/2021/mod.rs @@ -38,42 +38,42 @@ mod day18; pub fn run(day: u8, puzzle: u8) -> anyhow::Result { match (day, puzzle) { - (1, 1) => day1::part1(), - (1, 2) => day1::part2(), - (2, 1) => day2::part1(), - (2, 2) => day2::part2(), - (3, 1) => day3::part1(), - (3, 2) => day3::part2(), - (4, 1) => day4::part1(), - (4, 2) => day4::part2(), - (5, 1) => day5::part1(), - (5, 2) => day5::part2(), - (6, 1) => day6::part1(), - (6, 2) => day6::part2(), - (7, 1) => day7::part1(), - (7, 2) => day7::part2(), - (8, 1) => day8::part1(), - (8, 2) => day8::part2(), - (9, 1) => day9::part1(), - (9, 2) => day9::part2(), - (10, 1) => day10::part1(), - (10, 2) => day10::part2(), - (11, 1) => day11::part1(), - (11, 2) => day11::part2(), - (12, 1) => day12::part1(), - (12, 2) => day12::part2(), - (13, 1) => day13::part1(), - (13, 2) => day13::part2(), - (14, 1) => day14::part1(), - (14, 2) => day14::part2(), - (15, 1) => day15::part1(), - (15, 2) => day15::part2(), - (16, 1) => day16::part1(), - (16, 2) => day16::part2(), - (17, 1) => day17::part1(), - (17, 2) => day17::part2(), - (18, 1) => day18::part1(), - (18, 2) => day18::part2(), + (1, 1) => day1::part1(day1::parse(crate::util::data(2021, 1)?)?), + (1, 2) => day1::part2(day1::parse(crate::util::data(2021, 1)?)?), + (2, 1) => day2::part1(day2::parse(crate::util::data(2021, 2)?)?), + (2, 2) => day2::part2(day2::parse(crate::util::data(2021, 2)?)?), + (3, 1) => day3::part1(day3::parse(crate::util::data(2021, 3)?)?), + (3, 2) => day3::part2(day3::parse(crate::util::data(2021, 3)?)?), + (4, 1) => day4::part1(day4::parse(crate::util::data(2021, 4)?)?), + (4, 2) => day4::part2(day4::parse(crate::util::data(2021, 4)?)?), + (5, 1) => day5::part1(day5::parse(crate::util::data(2021, 5)?)?), + (5, 2) => day5::part2(day5::parse(crate::util::data(2021, 5)?)?), + (6, 1) => day6::part1(day6::parse(crate::util::data(2021, 6)?)?), + (6, 2) => day6::part2(day6::parse(crate::util::data(2021, 6)?)?), + (7, 1) => day7::part1(day7::parse(crate::util::data(2021, 7)?)?), + (7, 2) => day7::part2(day7::parse(crate::util::data(2021, 7)?)?), + (8, 1) => day8::part1(day8::parse(crate::util::data(2021, 8)?)?), + (8, 2) => day8::part2(day8::parse(crate::util::data(2021, 8)?)?), + (9, 1) => day9::part1(day9::parse(crate::util::data(2021, 9)?)?), + (9, 2) => day9::part2(day9::parse(crate::util::data(2021, 9)?)?), + (10, 1) => day10::part1(day10::parse(crate::util::data(2021, 10)?)?), + (10, 2) => day10::part2(day10::parse(crate::util::data(2021, 10)?)?), + (11, 1) => day11::part1(day11::parse(crate::util::data(2021, 11)?)?), + (11, 2) => day11::part2(day11::parse(crate::util::data(2021, 11)?)?), + (12, 1) => day12::part1(day12::parse(crate::util::data(2021, 12)?)?), + (12, 2) => day12::part2(day12::parse(crate::util::data(2021, 12)?)?), + (13, 1) => day13::part1(day13::parse(crate::util::data(2021, 13)?)?), + (13, 2) => day13::part2(day13::parse(crate::util::data(2021, 13)?)?), + (14, 1) => day14::part1(day14::parse(crate::util::data(2021, 14)?)?), + (14, 2) => day14::part2(day14::parse(crate::util::data(2021, 14)?)?), + (15, 1) => day15::part1(day15::parse(crate::util::data(2021, 15)?)?), + (15, 2) => day15::part2(day15::parse(crate::util::data(2021, 15)?)?), + (16, 1) => day16::part1(day16::parse(crate::util::data(2021, 16)?)?), + (16, 2) => day16::part2(day16::parse(crate::util::data(2021, 16)?)?), + (17, 1) => day17::part1(day17::parse(crate::util::data(2021, 17)?)?), + (17, 2) => day17::part2(day17::parse(crate::util::data(2021, 17)?)?), + (18, 1) => day18::part1(day18::parse(crate::util::data(2021, 18)?)?), + (18, 2) => day18::part2(day18::parse(crate::util::data(2021, 18)?)?), // NEXT PART _ => Err(anyhow::anyhow!("unknown puzzle {}-{}", day, puzzle)), } diff --git a/src/util.rs b/src/util.rs index f60e78d..0bd31e5 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,3 +5,8 @@ pub mod parse; pub mod grid; + +pub fn data(year: u16, day: u16) -> anyhow::Result { + std::fs::File::open(format!("data/{}/{}.txt", year, day)) + .map_err(|e| anyhow::anyhow!(e)) +} diff --git a/src/util/parse.rs b/src/util/parse.rs index 08d35cc..5f42565 100644 --- a/src/util/parse.rs +++ b/src/util/parse.rs @@ -17,10 +17,13 @@ macro_rules! data_lines { macro_rules! data_ints { () => { - data!().map(|fh| crate::util::parse::ints_by_line(fh)) + data!() + .map(|fh| crate::util::parse::ints(crate::util::parse::lines(fh))) }; ($sep:expr) => { - data!().map(|fh| crate::util::parse::ints_by_split(fh, $sep)) + data!().map(|fh| { + crate::util::parse::ints(crate::util::parse::split(fh, $sep)) + }) }; } @@ -62,24 +65,14 @@ pub fn lines(fh: std::fs::File) -> impl Iterator { fh.lines().map(|res| res.unwrap()) } -pub fn ints_by_line(fh: std::fs::File) -> impl Iterator { - lines(fh).map(|l| l.parse().unwrap()) +pub fn split(fh: std::fs::File, sep: u8) -> impl Iterator { + let fh = std::io::BufReader::new(fh); + fh.split(sep) + .map(|res| String::from_utf8(res.unwrap()).unwrap()) } -pub fn ints_by_split( - fh: std::fs::File, - sep: u8, -) -> impl Iterator { - let fh = std::io::BufReader::new(fh); - fh.split(sep).filter_map(|res| { - let res = res.unwrap(); - let s = std::str::from_utf8(&res).unwrap().trim(); - if s.is_empty() { - None - } else { - Some(s.parse().unwrap()) - } - }) +pub fn ints(iter: impl Iterator) -> impl Iterator { + iter.map(|s| s.trim().parse().unwrap()) } pub fn bytes(fh: std::fs::File) -> impl Iterator { @@ -88,7 +81,7 @@ pub fn bytes(fh: std::fs::File) -> impl Iterator { pub fn string(fh: std::fs::File) -> String { let bytes: Vec<_> = bytes(fh).collect(); - std::string::String::from_utf8(bytes).unwrap() + String::from_utf8(bytes).unwrap() } pub fn bool_grid( -- cgit v1.2.3-54-g00ecf