summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-12-18 13:21:42 -0500
committerJesse Luehrs <doy@tozt.net>2021-12-18 13:21:42 -0500
commitd16795e44aeac17bee08363bd08c1a9672edf3d4 (patch)
tree3f70ddaf74b10db3f0e38c8a81838607f4e26a2c
parentd1cacab50a8cab269da867ae900e903648b42cff (diff)
downloadadvent-of-code-d16795e44aeac17bee08363bd08c1a9672edf3d4.tar.gz
advent-of-code-d16795e44aeac17bee08363bd08c1a9672edf3d4.zip
factor out parsing
-rw-r--r--src/2020/1/mod.rs20
-rw-r--r--src/2020/2/mod.rs35
-rw-r--r--src/2020/3/mod.rs26
-rw-r--r--src/2020/4/mod.rs82
-rw-r--r--src/2020/5/mod.rs26
-rw-r--r--src/2020/6/mod.rs26
-rw-r--r--src/2020/7/mod.rs37
-rw-r--r--src/2020/8/mod.rs30
-rw-r--r--src/2020/9/mod.rs24
-rw-r--r--src/2020/mod.rs36
-rw-r--r--src/2021/1/mod.rs24
-rw-r--r--src/2021/10/mod.rs24
-rw-r--r--src/2021/11/mod.rs32
-rw-r--r--src/2021/12/mod.rs47
-rw-r--r--src/2021/13/mod.rs46
-rw-r--r--src/2021/14/mod.rs51
-rw-r--r--src/2021/15/mod.rs23
-rw-r--r--src/2021/16/mod.rs27
-rw-r--r--src/2021/17/mod.rs45
-rw-r--r--src/2021/18/mod.rs27
-rw-r--r--src/2021/2/mod.rs73
-rw-r--r--src/2021/3/mod.rs24
-rw-r--r--src/2021/4/mod.rs22
-rw-r--r--src/2021/5/mod.rs47
-rw-r--r--src/2021/6/mod.rs23
-rw-r--r--src/2021/7/mod.rs23
-rw-r--r--src/2021/8/mod.rs45
-rw-r--r--src/2021/9/mod.rs22
-rw-r--r--src/2021/mod.rs72
-rw-r--r--src/util.rs5
-rw-r--r--src/util/parse.rs31
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<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)),
}
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<i64> {
- Ok(data_ints!()?
- .collect::<Vec<_>>()
+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> {
+ Ok(ints
.windows(2)
.map(|a| a[1] - a[0])
.filter(|x| *x > 0)
@@ -8,9 +11,8 @@ pub fn part1() -> anyhow::Result<i64> {
.try_into()?)
}
-pub fn part2() -> anyhow::Result<i64> {
- Ok(data_ints!()?
- .collect::<Vec<_>>()
+pub fn part2(ints: Vec<i64>) -> anyhow::Result<i64> {
+ Ok(ints
.windows(3)
.map(|a| a[0] + a[1] + a[2])
.collect::<Vec<_>>()
@@ -23,6 +25,12 @@ pub fn part2() -> anyhow::Result<i64> {
#[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<i64> {
+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 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<i64> {
Ok(total)
}
-pub fn part2() -> anyhow::Result<i64> {
+pub fn part2(lines: impl Iterator<Item = String>) -> anyhow::Result<i64> {
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<i64> {
#[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<i64> {
- 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<Grid<(u8, bool)>> {
+ 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<i64> {
let mut flashes = 0;
for _ in 0..100 {
flashes += iterate(&mut map);
@@ -54,12 +59,7 @@ pub fn part1() -> anyhow::Result<i64> {
Ok(flashes)
}
-pub fn part2() -> anyhow::Result<i64> {
- 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<i64> {
let mut step = 1;
loop {
let flashes = iterate(&mut map);
@@ -73,6 +73,12 @@ pub fn part2() -> anyhow::Result<i64> {
#[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<i64> {
+pub fn parse(
+ fh: std::fs::File,
+) -> anyhow::Result<
+ std::collections::HashMap<String, std::collections::HashSet<String>>,
+> {
let mut graph = std::collections::HashMap::new();
- for line in data_lines!()? {
+ for line in crate::util::parse::lines(fh) {
let nodes: Vec<String> =
line.split('-').map(|s| s.to_string()).collect();
let edges = graph
@@ -82,28 +86,35 @@ pub fn part1() -> anyhow::Result<i64> {
.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<String>,
+ >,
+) -> anyhow::Result<i64> {
Ok(paths_from1(&graph, &mut vec!["start"]))
}
-pub fn part2() -> anyhow::Result<i64> {
- let mut graph = std::collections::HashMap::new();
- for line in data_lines!()? {
- let nodes: Vec<String> =
- 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<String>,
+ >,
+) -> anyhow::Result<i64> {
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<bool>,
}
@@ -94,10 +94,12 @@ impl std::fmt::Display for Paper {
}
}
-pub fn part1() -> anyhow::Result<i64> {
+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<i64> {
paper.set(Row(y), Col(x));
}
}
+ Ok((paper, folds))
+}
+pub fn part1(
+ (mut paper, folds): (Paper, Vec<(bool, usize)>),
+) -> anyhow::Result<i64> {
paper.fold(folds[0].0, folds[0].1);
Ok(paper.total())
}
-pub fn part2() -> anyhow::Result<i64> {
- 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<i64> {
for fold in folds {
paper.fold(fold.0, fold.1);
}
@@ -148,6 +138,12 @@ pub fn part2() -> anyhow::Result<i64> {
#[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<u8>, std::collections::HashMap<Vec<u8>, 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<Vec<u8>, u8>,
@@ -32,9 +17,25 @@ fn process(
polymer
}
-pub fn part1() -> anyhow::Result<i64> {
- let (mut polymer, rules) = parse()?;
+#[allow(clippy::type_complexity)]
+pub fn parse(
+ fh: std::fs::File,
+) -> anyhow::Result<(Vec<u8>, std::collections::HashMap<Vec<u8>, 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<u8>, std::collections::HashMap<Vec<u8>, u8>),
+) -> anyhow::Result<i64> {
for _ in 0..10 {
polymer = process(&polymer, &rules);
}
@@ -46,9 +47,9 @@ pub fn part1() -> anyhow::Result<i64> {
Ok(elements.values().max().unwrap() - elements.values().min().unwrap())
}
-pub fn part2() -> anyhow::Result<i64> {
- let (polymer, rules) = parse()?;
-
+pub fn part2(
+ (polymer, rules): (Vec<u8>, std::collections::HashMap<Vec<u8>, u8>),
+) -> anyhow::Result<i64> {
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<i64> {
#[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<u8>) -> i64 {
unreachable!()
}
-pub fn part1() -> anyhow::Result<i64> {
- Ok(dijkstra(&data_digit_grid!()))
+pub fn parse(fh: std::fs::File) -> anyhow::Result<Grid<u8>> {
+ Ok(crate::util::parse::digit_grid(crate::util::parse::lines(
+ fh,
+ )))
}
-pub fn part2() -> anyhow::Result<i64> {
- let grid = data_digit_grid!();
+pub fn part1(grid: Grid<u8>) -> anyhow::Result<i64> {
+ Ok(dijkstra(&grid))
+}
+
+pub fn part2(grid: Grid<u8>) -> anyhow::Result<i64> {
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<i64> {
#[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<bool> 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<i64> {
- let line = data_lines!()?.next().unwrap();
+pub fn parse(fh: std::fs::File) -> anyhow::Result<Packet> {
+ 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<i64> {
Ok(packet
.subpackets()
.map(|packet| i64::from(packet.version))
.sum())
}
-pub fn part2() -> anyhow::Result<i64> {
- 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<i64> {
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<i64> {
+pub fn parse(
+ fh: std::fs::File,
+) -> anyhow::Result<(
+ std::ops::RangeInclusive<i64>,
+ std::ops::RangeInclusive<i64>,
+)> {
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<i64> =
captures[1].parse().unwrap()..=captures[2].parse().unwrap();
let yrange: std::ops::RangeInclusive<i64> =
captures[3].parse().unwrap()..=captures[4].parse().unwrap();
+ Ok((xrange, yrange))
+}
+pub fn part1(
+ (xrange, yrange): (
+ std::ops::RangeInclusive<i64>,
+ std::ops::RangeInclusive<i64>,
+ ),
+) -> anyhow::Result<i64> {
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<i64> {
Ok(max_height)
}
-pub fn part2() -> anyhow::Result<i64> {
- 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<i64> =
- captures[1].parse().unwrap()..=captures[2].parse().unwrap();
- let yrange: std::ops::RangeInclusive<i64> =
- captures[3].parse().unwrap()..=captures[4].parse().unwrap();
-
+pub fn part2(
+ (xrange, yrange): (
+ std::ops::RangeInclusive<i64>,
+ std::ops::RangeInclusive<i64>,
+ ),
+) -> anyhow::Result<i64> {
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<i64> {
#[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<Number>, Box<Number>),
}
@@ -210,14 +210,19 @@ impl std::fmt::Display for Number {
}
}
-pub fn part1() -> anyhow::Result<i64> {
- let sum: Number = data_lines!()?.map(|line| Number::parse(&line)).sum();
+pub fn parse(
+ fh: std::fs::File,
+) -> anyhow::Result<impl Iterator<Item = Number>> {
+ Ok(crate::util::parse::lines(fh).map(|line| Number::parse(&line)))
+}
+
+pub fn part1(numbers: impl Iterator<Item = Number>) -> anyhow::Result<i64> {
+ let sum: Number = numbers.sum();
Ok(sum.magnitude())
}
-pub fn part2() -> anyhow::Result<i64> {
- let nums: Vec<_> =
- data_lines!()?.map(|line| Number::parse(&line)).collect();
+pub fn part2(numbers: impl Iterator<Item = Number>) -> anyhow::Result<i64> {
+ 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<i64> {
#[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<i64> {
- 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<impl Iterator<Item = Command>> {
+ Ok(crate::util::parse::lines(fh).map(|line| {
if let Some(n) = line.strip_prefix("forward ") {
- horizontal += n.parse::<i64>()?;
+ Command::Forward(n.parse().unwrap())
} else if let Some(n) = line.strip_prefix("down ") {
- vertical += n.parse::<i64>()?;
+ Command::Down(n.parse().unwrap())
} else if let Some(n) = line.strip_prefix("up ") {
- vertical -= n.parse::<i64>()?;
+ Command::Up(n.parse().unwrap())
+ } else {
+ panic!("couldn't parse line: {}", line);
+ }
+ }))
+}
+
+pub fn part1(commands: impl Iterator<Item = Command>) -> anyhow::Result<i64> {
+ 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<i64> {
+pub fn part2(commands: impl Iterator<Item = Command>) -> anyhow::Result<i64> {
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::<i64>()?;
- horizontal += x;
- vertical += aim * x;
- } else if let Some(n) = line.strip_prefix("down ") {
- aim += n.parse::<i64>()?;
- } else if let Some(n) = line.strip_prefix("up ") {
- aim -= n.parse::<i64>()?;
+ 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<i64> {
#[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<i64> {
- let (total_lines, by_pos) = pos_counts(data_lines!()?)?;
+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 (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<i64> {
Ok(bin_str_to_int(&gamma) * bin_str_to_int(&epsilon))
}
-pub fn part2() -> anyhow::Result<i64> {
- let mut oxygen: Vec<_> = data_lines!()?.collect();
+pub fn part2(lines: impl Iterator<Item = String>) -> anyhow::Result<i64> {
+ 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<u8>,
boards: Vec<Board>,
}
@@ -133,8 +133,11 @@ impl Game {
}
}
-pub fn part1() -> anyhow::Result<i64> {
- let game = Game::parse(data!()?)?;
+pub fn parse(fh: std::fs::File) -> anyhow::Result<Game> {
+ Game::parse(fh)
+}
+
+pub fn part1(game: Game) -> anyhow::Result<i64> {
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<i64> {
}
}
-pub fn part2() -> anyhow::Result<i64> {
- let game = Game::parse(data!()?)?;
+pub fn part2(game: Game) -> anyhow::Result<i64> {
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<i64> {
#[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<i64> {
- let rx = regex::Regex::new("^(\\d+),(\\d+) -> (\\d+),(\\d+)$").unwrap();
- let mut map = Map::default();
- for line in data_lines!()? {
- let nums: Vec<usize> = rx
- .captures(&line)
+pub fn parse(
+ fh: std::fs::File,
+) -> anyhow::Result<impl Iterator<Item = Vec<usize>>> {
+ 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::<Result<_, _>>()?;
+ .collect::<Result<_, _>>()
+ .unwrap()
+ }))
+}
+
+pub fn part1(
+ coords: impl Iterator<Item = Vec<usize>>,
+) -> anyhow::Result<i64> {
+ 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<i64> {
- let rx = regex::Regex::new("^(\\d+),(\\d+) -> (\\d+),(\\d+)$").unwrap();
+pub fn part2(
+ coords: impl Iterator<Item = Vec<usize>>,
+) -> anyhow::Result<i64> {
let mut map = Map::default();
- for line in data_lines!()? {
- let nums: Vec<usize> = rx
- .captures(&line)
- .unwrap()
- .iter()
- .skip(1)
- .map(|s| s.unwrap().as_str().parse())
- .collect::<Result<_, _>>()?;
+ 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<i64> {
#[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<i64> {
- let mut fishes: Vec<_> = data_ints!(b',')?.collect();
+pub fn parse(fh: std::fs::File) -> anyhow::Result<Vec<i64>> {
+ Ok(
+ crate::util::parse::ints(crate::util::parse::split(fh, b','))
+ .collect(),
+ )
+}
+
+pub fn part1(mut fishes: Vec<i64>) -> anyhow::Result<i64> {
for _ in 0..80 {
let mut new = 0;
for fish in fishes.iter_mut() {
@@ -15,8 +21,7 @@ pub fn part1() -> anyhow::Result<i64> {
Ok(fishes.len().try_into()?)
}
-pub fn part2() -> anyhow::Result<i64> {
- let fishes: Vec<_> = data_ints!(b',')?.collect();
+pub fn part2(fishes: Vec<i64>) -> anyhow::Result<i64> {
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<i64> {
#[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<i64> {
- let crabs: Vec<_> = data_ints!(b',')?.collect();
+pub fn parse(fh: std::fs::File) -> anyhow::Result<Vec<i64>> {
+ Ok(
+ crate::util::parse::ints(crate::util::parse::split(fh, b','))
+ .collect(),
+ )
+}
+
+pub fn part1(crabs: Vec<i64>) -> anyhow::Result<i64> {
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<i64> {
.unwrap())
}
-pub fn part2() -> anyhow::Result<i64> {
- let crabs: Vec<_> = data_ints!(b',')?.collect();
+pub fn part2(crabs: Vec<i64>) -> anyhow::Result<i64> {
Ok((0..=crabs.iter().copied().max().unwrap())
.map(|start| {
crabs
@@ -27,6 +32,12 @@ pub fn part2() -> anyhow::Result<i64> {
#[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<i64> {
+pub fn parse(
+ fh: std::fs::File,
+) -> anyhow::Result<impl Iterator<Item = (Vec<String>, Vec<String>)>> {
+ 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<Item = (Vec<String>, Vec<String>)>,
+) -> anyhow::Result<i64> {
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<i64> {
// 4 5
// 4 5
// 66
-pub fn part2() -> anyhow::Result<i64> {
+pub fn part2(
+ lines: impl Iterator<Item = (Vec<String>, Vec<String>)>,
+) -> anyhow::Result<i64> {
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<i64> {
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::<std::collections::HashSet<_>>())
.map(|set| numbers.iter().position(|num| &set == num).unwrap())
.collect();
@@ -165,6 +174,12 @@ pub fn part2() -> anyhow::Result<i64> {
#[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<i64> {
- let map = data_digit_grid!();
+pub fn parse(fh: std::fs::File) -> anyhow::Result<Grid<u8>> {
+ Ok(crate::util::parse::digit_grid(crate::util::parse::lines(
+ fh,
+ )))
+}
+
+pub fn part1(map: Grid<u8>) -> anyhow::Result<i64> {
let mut risk = 0;
for ((row, col), pos) in map.indexed_cells() {
if map
@@ -15,8 +20,7 @@ pub fn part1() -> anyhow::Result<i64> {
Ok(risk)
}
-pub fn part2() -> anyhow::Result<i64> {
- let map = data_digit_grid!();
+pub fn part2(map: Grid<u8>) -> anyhow::Result<i64> {
let mut low = vec![];
for ((row, col), pos) in map.indexed_cells() {
if map
@@ -58,6 +62,12 @@ pub fn part2() -> anyhow::Result<i64> {
#[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<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(),
- (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> {
+ 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<Item = String> {
fh.lines().map(|res| res.unwrap())
}
-pub fn ints_by_line(fh: std::fs::File) -> impl Iterator<Item = i64> {
- lines(fh).map(|l| l.parse().unwrap())
+pub fn split(fh: std::fs::File, sep: u8) -> impl Iterator<Item = String> {
+ 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<Item = i64> {
- 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<Item = String>) -> impl Iterator<Item = i64> {
+ iter.map(|s| s.trim().parse().unwrap())
}
pub fn bytes(fh: std::fs::File) -> impl Iterator<Item = u8> {
@@ -88,7 +81,7 @@ pub fn bytes(fh: std::fs::File) -> impl Iterator<Item = u8> {
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(