diff options
author | Jesse Luehrs <doy@tozt.net> | 2021-12-18 13:21:42 -0500 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2021-12-18 13:21:42 -0500 |
commit | d16795e44aeac17bee08363bd08c1a9672edf3d4 (patch) | |
tree | 3f70ddaf74b10db3f0e38c8a81838607f4e26a2c /src/2020 | |
parent | d1cacab50a8cab269da867ae900e903648b42cff (diff) | |
download | advent-of-code-d16795e44aeac17bee08363bd08c1a9672edf3d4.tar.gz advent-of-code-d16795e44aeac17bee08363bd08c1a9672edf3d4.zip |
factor out parsing
Diffstat (limited to 'src/2020')
-rw-r--r-- | src/2020/1/mod.rs | 20 | ||||
-rw-r--r-- | src/2020/2/mod.rs | 35 | ||||
-rw-r--r-- | src/2020/3/mod.rs | 26 | ||||
-rw-r--r-- | src/2020/4/mod.rs | 82 | ||||
-rw-r--r-- | src/2020/5/mod.rs | 26 | ||||
-rw-r--r-- | src/2020/6/mod.rs | 26 | ||||
-rw-r--r-- | src/2020/7/mod.rs | 37 | ||||
-rw-r--r-- | src/2020/8/mod.rs | 30 | ||||
-rw-r--r-- | src/2020/9/mod.rs | 24 | ||||
-rw-r--r-- | src/2020/mod.rs | 36 |
10 files changed, 200 insertions, 142 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<i64> { - let ints: Vec<_> = data_ints!()?.collect(); +pub fn parse(fh: std::fs::File) -> anyhow::Result<Vec<i64>> { + Ok(crate::util::parse::ints(crate::util::parse::lines(fh)).collect()) +} + +pub fn part1(ints: Vec<i64>) -> anyhow::Result<i64> { for i in &ints { for j in &ints { if i + j == 2020 { @@ -10,8 +13,7 @@ pub fn part1() -> anyhow::Result<i64> { Err(anyhow::anyhow!("no numbers summing to 2020 found")) } -pub fn part2() -> anyhow::Result<i64> { - let ints: Vec<_> = data_ints!()?.collect(); +pub fn part2(ints: Vec<i64>) -> anyhow::Result<i64> { for i in &ints { for j in &ints { for k in &ints { @@ -26,6 +28,12 @@ pub fn part2() -> anyhow::Result<i64> { #[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<i64> { - 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<impl Iterator<Item = Line>> { + Ok(crate::util::parse::lines(fh).map(|line| Line::parse(&line).unwrap())) } -pub fn part2() -> anyhow::Result<i64> { - let lines = read_lines()?; - let count = lines.iter().filter(|l| l.valid_part_2()).count(); +pub fn part1(lines: impl Iterator<Item = Line>) -> anyhow::Result<i64> { + let count = lines.filter(|l| l.valid_part_1()).count(); Ok(count.try_into()?) } -fn read_lines() -> anyhow::Result<Vec<Line>> { - 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<Item = Line>) -> anyhow::Result<i64> { + 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<bool>, } @@ -35,13 +35,19 @@ impl Map { } } -pub fn part1() -> anyhow::Result<i64> { - let map = Map::new(data_bool_grid!(b'#', b'.')); +pub fn parse(fh: std::fs::File) -> anyhow::Result<Map> { + Ok(Map::new(crate::util::parse::bool_grid( + crate::util::parse::lines(fh), + b'#', + b'.', + ))) +} + +pub fn part1(map: Map) -> anyhow::Result<i64> { map.trees_for_slope(1, 3) } -pub fn part2() -> anyhow::Result<i64> { - let map = Map::new(data_bool_grid!(b'#', b'.')); +pub fn part2(map: Map) -> anyhow::Result<i64> { 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<i64> { #[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<i64> { - let batch = data_str!()?; +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result<Vec<std::collections::HashMap<String, String>>> { + 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<std::collections::HashMap<String, String>>, +) -> anyhow::Result<i64> { 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<i64> { Ok(valid) } -pub fn part2() -> anyhow::Result<i64> { - let batch = data_str!()?; +pub fn part2( + passports: Vec<std::collections::HashMap<String, String>>, +) -> anyhow::Result<i64> { 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<i64> { Ok(valid) } -fn parse( - batch: &str, -) -> anyhow::Result<Vec<std::collections::HashMap<String, String>>> { - 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<bool> { match key { "byr" => match val.parse::<i32>() { @@ -131,6 +133,12 @@ fn validate(key: &str, val: &str) -> anyhow::Result<bool> { #[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<i64> { - let input = data_str!()?; +pub fn parse(fh: std::fs::File) -> anyhow::Result<impl Iterator<Item = i64>> { + Ok(crate::util::parse::lines(fh).map(|line| seat_id(&line).unwrap())) +} + +pub fn part1(ids: impl Iterator<Item = i64>) -> anyhow::Result<i64> { 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<i64> { Ok(max) } -pub fn part2() -> anyhow::Result<i64> { +pub fn part2(ids: impl Iterator<Item = i64>) -> anyhow::Result<i64> { 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<i64> { #[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<i64> { - let input = data_str!()?; +pub fn parse( + fh: std::fs::File, +) -> anyhow::Result<impl Iterator<Item = String>> { + Ok(crate::util::parse::lines(fh)) +} + +pub fn part1(lines: impl Iterator<Item = String>) -> anyhow::Result<i64> { 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<i64> { Ok(total) } -pub fn part2() -> anyhow::Result<i64> { - let input = data_str!()?; +pub fn part2(lines: impl Iterator<Item = String>) -> anyhow::Result<i64> { 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<i64> { #[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<String, Vec<(i64, String)>>; -pub fn part1() -> anyhow::Result<i64> { - let input = data_str!()?; - let graph = parse(&input)?; +pub fn parse(fh: std::fs::File) -> anyhow::Result<Graph> { + 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<i64> { 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<i64> { Ok(colors) } -pub fn part2() -> anyhow::Result<i64> { - let input = data_str!()?; - let graph = parse(&input)?; +pub fn part2(graph: Graph) -> anyhow::Result<i64> { // 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<Graph> { - 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<i64> { #[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<i64> { - let input = data_str!()?; - let opcodes = parse(&input)?; +pub fn parse(fh: std::fs::File) -> anyhow::Result<Vec<Op>> { + crate::util::parse::lines(fh) + .map(|line| line.parse()) + .collect() +} + +pub fn part1(opcodes: Vec<Op>) -> anyhow::Result<i64> { let (acc, success) = run(&opcodes)?; if success { return Err(anyhow::anyhow!("unexpectedly succeeded")); @@ -53,9 +57,7 @@ pub fn part1() -> anyhow::Result<i64> { Ok(acc) } -pub fn part2() -> anyhow::Result<i64> { - let input = data_str!()?; - let opcodes = parse(&input)?; +pub fn part2(opcodes: Vec<Op>) -> anyhow::Result<i64> { for i in 0..opcodes.len() { match opcodes[i].ty { OpType::Nop => { @@ -80,10 +82,6 @@ pub fn part2() -> anyhow::Result<i64> { Err(anyhow::anyhow!("failed to find corrupted opcode")) } -fn parse(input: &str) -> anyhow::Result<Vec<Op>> { - 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<i64> { - const WINDOW: usize = 25; +const WINDOW: usize = 25; - let list: Vec<_> = data_ints!()?.collect(); +pub fn parse(fh: std::fs::File) -> anyhow::Result<Vec<i64>> { + Ok(crate::util::parse::ints(crate::util::parse::lines(fh)).collect()) +} + +pub fn part1(list: Vec<i64>) -> anyhow::Result<i64> { 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<i64> { Err(anyhow::anyhow!("failed to find invalid number")) } -pub fn part2() -> anyhow::Result<i64> { - const WINDOW: usize = 25; - - let list: Vec<_> = data_ints!()?.collect(); +pub fn part2(list: Vec<i64>) -> anyhow::Result<i64> { 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<i64> { 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)), } |