2020: switch to anyhow
This commit is contained in:
parent
f148347e5b
commit
d91165207f
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -9,6 +9,9 @@ checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aoc"
|
name = "aoc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aoc2015"
|
name = "aoc2015"
|
||||||
|
|
|
@ -7,3 +7,4 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
pub type Error = Box<dyn std::error::Error>;
|
use anyhow::{Context, Result};
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! err {
|
|
||||||
($($string:expr),+) => (Box::<dyn std::error::Error>::from(format!($($string),+)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type DayFunc = fn() -> Result<String>;
|
pub type DayFunc = fn() -> Result<String>;
|
||||||
|
|
||||||
|
@ -16,15 +10,15 @@ pub fn run(days: &[DayFunc]) -> Result<()> {
|
||||||
|
|
||||||
match args.next() {
|
match args.next() {
|
||||||
Some(arg) => {
|
Some(arg) => {
|
||||||
let day: usize = arg.parse().expect("Please provide a day number");
|
let day: usize = arg.parse().context("couldn't parse day number")?;
|
||||||
let res = days[day - 1]().map_err(|e| err!("error running day specified: {}", e))?;
|
let res = days[day - 1]().context("error running day specified")?;
|
||||||
println!("{}", res);
|
println!("{}", res);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
for (i, day) in days.iter().enumerate() {
|
for (i, day) in days.iter().enumerate() {
|
||||||
let i = i + 1;
|
let i = i + 1;
|
||||||
println!("day{}: ", i);
|
println!("day{}: ", i);
|
||||||
let res = day().map_err(|e| err!("error running day {}: {}", i, e))?;
|
let res = day().with_context(|| format!("error running day {}", i))?;
|
||||||
println!("{}", res);
|
println!("{}", res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use aoc::{err, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day01.txt");
|
const INPUT: &str = include_str!("../input/day01.txt");
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ pub fn run() -> Result<String> {
|
||||||
fn part1(input: &str) -> Result<i64> {
|
fn part1(input: &str) -> Result<i64> {
|
||||||
let entries = input
|
let entries = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse::<i64>().map_err(|e| err!("{}", e)))
|
.map(|line| line.parse::<i64>().map_err(anyhow::Error::new))
|
||||||
.collect::<Result<Vec<i64>>>()?;
|
.collect::<Result<Vec<i64>>>()?;
|
||||||
|
|
||||||
let (a, b) = find_2020_2_sum(&entries)?;
|
let (a, b) = find_2020_2_sum(&entries)?;
|
||||||
|
@ -28,8 +28,8 @@ fn part1(input: &str) -> Result<i64> {
|
||||||
fn part2(input: &str) -> Result<i64> {
|
fn part2(input: &str) -> Result<i64> {
|
||||||
let entries = input
|
let entries = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse::<i64>().map_err(|e| err!("{}", e)))
|
.map(|line| line.parse::<i64>().map_err(anyhow::Error::new))
|
||||||
.collect::<Result<Vec<i64>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
let (a, b, c) = find_2020_3_sum(&entries)?;
|
let (a, b, c) = find_2020_3_sum(&entries)?;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ fn find_2020_2_sum(entries: &[i64]) -> Result<(i64, i64)> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(err!("couldn't find 2 elements that sum to 2020"))
|
Err(anyhow!("couldn't find 2 elements that sum to 2020"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_2020_3_sum(entries: &[i64]) -> Result<(i64, i64, i64)> {
|
fn find_2020_3_sum(entries: &[i64]) -> Result<(i64, i64, i64)> {
|
||||||
|
@ -70,7 +70,7 @@ fn find_2020_3_sum(entries: &[i64]) -> Result<(i64, i64, i64)> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(err!("couldn't find 2 elements that sum to 2020"))
|
Err(anyhow!("couldn't find 2 elements that sum to 2020"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use aoc::{err, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day02.txt");
|
const INPUT: &str = include_str!("../input/day02.txt");
|
||||||
|
|
||||||
|
@ -44,28 +44,28 @@ impl PassPolicy {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for PassPolicy {
|
impl FromStr for PassPolicy {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let s = s.trim_end();
|
let s = s.trim_end();
|
||||||
|
|
||||||
let space = s
|
let space = s
|
||||||
.find(' ')
|
.find(' ')
|
||||||
.ok_or_else(|| err!("couldn't parse password policy: didn't find space"))?;
|
.context("couldn't parse password policy: didn't find space")?;
|
||||||
let dash = s
|
let dash = s
|
||||||
.find('-')
|
.find('-')
|
||||||
.ok_or_else(|| err!("couldn't parse password policy: didn't find dash"))?;
|
.context("couldn't parse password policy: didn't find dash")?;
|
||||||
|
|
||||||
let min_bound = s[..dash]
|
let min_bound = s[..dash]
|
||||||
.parse::<usize>()
|
.parse::<usize>()
|
||||||
.map_err(|e| err!("couldn't parse range: {}", e))?;
|
.context("couldn't parse range")?;
|
||||||
let max_bound = s[(dash + 1)..space]
|
let max_bound = s[(dash + 1)..space]
|
||||||
.parse::<usize>()
|
.parse::<usize>()
|
||||||
.map_err(|e| err!("couldn't parse range: {}", e))?;
|
.context("couldn't parse range")?;
|
||||||
|
|
||||||
let colon = s
|
let colon = s
|
||||||
.find(':')
|
.find(':')
|
||||||
.ok_or_else(|| err!("couldn't parse password policy: didn't find colon"))?;
|
.context("couldn't parse password policy: didn't find colon")?;
|
||||||
|
|
||||||
let letter = s.as_bytes()[colon - 1];
|
let letter = s.as_bytes()[colon - 1];
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ pub fn run() -> Result<String> {
|
||||||
fn part1(input: &str) -> Result<usize> {
|
fn part1(input: &str) -> Result<usize> {
|
||||||
let policies = input
|
let policies = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse::<PassPolicy>().map_err(|e| err!("{}", e)))
|
.map(|line| line.parse::<PassPolicy>())
|
||||||
.collect::<Result<Vec<PassPolicy>>>()?;
|
.collect::<Result<Vec<PassPolicy>>>()?;
|
||||||
|
|
||||||
Ok(policies
|
Ok(policies
|
||||||
|
@ -104,7 +104,7 @@ fn part1(input: &str) -> Result<usize> {
|
||||||
fn part2(input: &str) -> Result<usize> {
|
fn part2(input: &str) -> Result<usize> {
|
||||||
let policies = input
|
let policies = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse::<PassPolicy>().map_err(|e| err!("{}", e)))
|
.map(|line| line.parse::<PassPolicy>())
|
||||||
.collect::<Result<Vec<PassPolicy>>>()?;
|
.collect::<Result<Vec<PassPolicy>>>()?;
|
||||||
|
|
||||||
Ok(policies
|
Ok(policies
|
||||||
|
|
|
@ -3,9 +3,11 @@ use std::iter::FromIterator;
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day03.txt");
|
const INPUT: &str = include_str!("../input/day03.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -14,13 +16,13 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<usize> {
|
fn part1(input: &str) -> Result<usize> {
|
||||||
let forest = input.parse()?;
|
let forest = input.parse()?;
|
||||||
|
|
||||||
Ok(count_trees(&forest, (3, 1)))
|
Ok(count_trees(&forest, (3, 1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<usize> {
|
fn part2(input: &str) -> Result<usize> {
|
||||||
let forest = input.parse()?;
|
let forest = input.parse()?;
|
||||||
|
|
||||||
let slopes = &[(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)];
|
let slopes = &[(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)];
|
||||||
|
@ -89,9 +91,9 @@ impl Index<usize> for Forest {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Forest {
|
impl FromStr for Forest {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let trees = s
|
let trees = s
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.chars().map(|c| matches!(c, '#')).collect())
|
.map(|line| line.chars().map(|c| matches!(c, '#')).collect())
|
||||||
|
|
|
@ -3,9 +3,11 @@ use std::fmt::Write;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day04.txt");
|
const INPUT: &str = include_str!("../input/day04.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -14,7 +16,7 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_passports(input: &str) -> aoc::Result<Vec<Passport>> {
|
fn get_passports(input: &str) -> Result<Vec<Passport>> {
|
||||||
let mut passports: Vec<Passport> = Vec::new();
|
let mut passports: Vec<Passport> = Vec::new();
|
||||||
|
|
||||||
let mut passport = String::new();
|
let mut passport = String::new();
|
||||||
|
@ -35,13 +37,13 @@ fn get_passports(input: &str) -> aoc::Result<Vec<Passport>> {
|
||||||
Ok(passports)
|
Ok(passports)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<usize> {
|
fn part1(input: &str) -> Result<usize> {
|
||||||
let passports = get_passports(input)?;
|
let passports = get_passports(input)?;
|
||||||
|
|
||||||
Ok(passports.iter().filter(|p| p.is_complete()).count())
|
Ok(passports.iter().filter(|p| p.is_complete()).count())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<usize> {
|
fn part2(input: &str) -> Result<usize> {
|
||||||
let passports = get_passports(input)?;
|
let passports = get_passports(input)?;
|
||||||
|
|
||||||
Ok(passports
|
Ok(passports
|
||||||
|
@ -89,9 +91,9 @@ impl Passport {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Passport {
|
impl FromStr for Passport {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let mut fields: HashMap<&str, String> = s
|
let mut fields: HashMap<&str, String> = s
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use aoc::err;
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day05.txt");
|
const INPUT: &str = include_str!("../input/day05.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -14,26 +14,24 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<usize> {
|
fn part1(input: &str) -> Result<usize> {
|
||||||
let seats = input
|
let seats = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse())
|
.map(|line| line.parse())
|
||||||
.collect::<aoc::Result<Vec<Seat>>>()
|
.collect::<Result<Vec<Seat>>>()?;
|
||||||
.map_err(|e| err!("{}", e))?;
|
|
||||||
|
|
||||||
seats
|
seats
|
||||||
.iter()
|
.iter()
|
||||||
.map(|seat| seat.id())
|
.map(|seat| seat.id())
|
||||||
.max()
|
.max()
|
||||||
.ok_or_else(|| err!("0 seats processed"))
|
.context("0 seats processed")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<usize> {
|
fn part2(input: &str) -> Result<usize> {
|
||||||
let mut seats = input
|
let mut seats = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse())
|
.map(|line| line.parse())
|
||||||
.collect::<aoc::Result<Vec<Seat>>>()
|
.collect::<Result<Vec<Seat>>>()?;
|
||||||
.map_err(|e| err!("{}", e))?;
|
|
||||||
|
|
||||||
// Seats will be sorted by lexicographical order of fields thanks to `derive(PartialOrd, Ord)`,
|
// Seats will be sorted by lexicographical order of fields thanks to `derive(PartialOrd, Ord)`,
|
||||||
// which should produce the same result as a manual implementation of `Ord` and `PartialOrd`
|
// which should produce the same result as a manual implementation of `Ord` and `PartialOrd`
|
||||||
|
@ -49,7 +47,7 @@ fn part2(input: &str) -> aoc::Result<usize> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(err!("didn't find missing seat"))
|
Err(anyhow!("didn't find missing seat"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
@ -65,11 +63,11 @@ impl Seat {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Seat {
|
impl FromStr for Seat {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
if s.len() != 10 {
|
if s.len() != 10 {
|
||||||
return Err(err!("Seat encoding must be 10 chars long: {}", s));
|
bail!("Seat encoding must be 10 chars long: {}", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut row_begin, mut row_end) = (0, 128);
|
let (mut row_begin, mut row_end) = (0, 128);
|
||||||
|
@ -90,7 +88,7 @@ impl FromStr for Seat {
|
||||||
'R' => {
|
'R' => {
|
||||||
col_begin += col_range;
|
col_begin += col_range;
|
||||||
}
|
}
|
||||||
_ => return Err(err!("Wrong char while decoding seat: `{}`", c)),
|
_ => bail!("Wrong char while decoding seat: `{}`", c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day06.txt");
|
const INPUT: &str = include_str!("../input/day06.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -12,7 +14,7 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_groups(input: &str) -> aoc::Result<Vec<Group>> {
|
fn get_groups(input: &str) -> Result<Vec<Group>> {
|
||||||
let mut groups = Vec::new();
|
let mut groups = Vec::new();
|
||||||
|
|
||||||
let mut answers = Vec::new();
|
let mut answers = Vec::new();
|
||||||
|
@ -36,7 +38,7 @@ fn get_groups(input: &str) -> aoc::Result<Vec<Group>> {
|
||||||
Ok(groups)
|
Ok(groups)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<usize> {
|
fn part1(input: &str) -> Result<usize> {
|
||||||
let groups = get_groups(input)?;
|
let groups = get_groups(input)?;
|
||||||
|
|
||||||
Ok(groups
|
Ok(groups
|
||||||
|
@ -45,7 +47,7 @@ fn part1(input: &str) -> aoc::Result<usize> {
|
||||||
.sum())
|
.sum())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<usize> {
|
fn part2(input: &str) -> Result<usize> {
|
||||||
let groups = get_groups(input)?;
|
let groups = get_groups(input)?;
|
||||||
|
|
||||||
Ok(groups
|
Ok(groups
|
||||||
|
|
|
@ -2,11 +2,11 @@ use std::collections::HashMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use aoc::err;
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day07.txt");
|
const INPUT: &str = include_str!("../input/day07.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -15,11 +15,11 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<usize> {
|
fn part1(input: &str) -> Result<usize> {
|
||||||
let bag_rules = input
|
let bag_rules = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse())
|
.map(|line| line.parse())
|
||||||
.collect::<aoc::Result<Vec<BagRule>>>()
|
.collect::<Result<Vec<BagRule>>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// create map with Key = color, Value = BagRule
|
// create map with Key = color, Value = BagRule
|
||||||
|
@ -39,11 +39,11 @@ fn part1(input: &str) -> aoc::Result<usize> {
|
||||||
.count())
|
.count())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<usize> {
|
fn part2(input: &str) -> Result<usize> {
|
||||||
let bag_rules = input
|
let bag_rules = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse())
|
.map(|line| line.parse())
|
||||||
.collect::<aoc::Result<Vec<BagRule>>>()
|
.collect::<Result<Vec<BagRule>>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// create map with Key = color, Value = BagRule
|
// create map with Key = color, Value = BagRule
|
||||||
|
@ -102,9 +102,9 @@ impl BagRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for BagRule {
|
impl FromStr for BagRule {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let words: Vec<&str> = s.split(' ').collect();
|
let words: Vec<&str> = s.split(' ').collect();
|
||||||
|
|
||||||
// let's assume our input is always valid for now
|
// let's assume our input is always valid for now
|
||||||
|
@ -130,7 +130,7 @@ impl FromStr for BagRule {
|
||||||
number => {
|
number => {
|
||||||
let n = number
|
let n = number
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|e| err!("couldn't parse number `{}` in bag rule", e))?;
|
.context("couldn't parse number in bag rule")?;
|
||||||
|
|
||||||
let adjective = words[1];
|
let adjective = words[1];
|
||||||
let color = words[2];
|
let color = words[2];
|
||||||
|
@ -169,7 +169,7 @@ mod tests {
|
||||||
let bag_rules = PROVIDED1
|
let bag_rules = PROVIDED1
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse())
|
.map(|line| line.parse())
|
||||||
.collect::<aoc::Result<Vec<BagRule>>>()
|
.collect::<Result<Vec<BagRule>>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use aoc::err;
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day08.txt");
|
const INPUT: &str = include_str!("../input/day08.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -14,25 +14,25 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<i64> {
|
fn part1(input: &str) -> Result<i64> {
|
||||||
let instructions = input
|
let instructions = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse())
|
.map(|line| line.parse())
|
||||||
.collect::<aoc::Result<Vec<Instruction>>>()?;
|
.collect::<Result<Vec<Instruction>>>()?;
|
||||||
|
|
||||||
let mut interpreter = Interpreter::new(instructions);
|
let mut interpreter = Interpreter::new(instructions);
|
||||||
|
|
||||||
Ok(match interpreter.run() {
|
Ok(match interpreter.run() {
|
||||||
ExitStatus::InfiniteLoop(value) => value,
|
ExitStatus::InfiniteLoop(value) => value,
|
||||||
ExitStatus::End(_) => return Err(err!("interpreter doesn't have an infinite loop")),
|
ExitStatus::End(_) => bail!("interpreter doesn't have an infinite loop"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<i64> {
|
fn part2(input: &str) -> Result<i64> {
|
||||||
let instructions = input
|
let instructions = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse())
|
.map(|line| line.parse())
|
||||||
.collect::<aoc::Result<Vec<Instruction>>>()?;
|
.collect::<Result<Vec<Instruction>>>()?;
|
||||||
|
|
||||||
for idx in 0..instructions.len() {
|
for idx in 0..instructions.len() {
|
||||||
let mut instructions = instructions.clone();
|
let mut instructions = instructions.clone();
|
||||||
|
@ -51,7 +51,7 @@ fn part2(input: &str) -> aoc::Result<i64> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(err!(
|
Err(anyhow!(
|
||||||
"interpreter always had an infinite loop, no solution found"
|
"interpreter always had an infinite loop, no solution found"
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -110,21 +110,21 @@ enum Instruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for Instruction {
|
impl std::str::FromStr for Instruction {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let space = s.find(' ').ok_or_else(|| err!("couldn't split on space"))?;
|
let space = s.find(' ').context("couldn't split on space")?;
|
||||||
|
|
||||||
let inst = &s[..space];
|
let inst = &s[..space];
|
||||||
let arg = s[(space + 1)..]
|
let arg = s[(space + 1)..]
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|e| err!("couldn't parse argument for instruction: {}", e))?;
|
.context("couldn't parse argument for instruction")?;
|
||||||
|
|
||||||
Ok(match inst {
|
Ok(match inst {
|
||||||
"acc" => Self::Acc(arg),
|
"acc" => Self::Acc(arg),
|
||||||
"jmp" => Self::Jmp(arg),
|
"jmp" => Self::Jmp(arg),
|
||||||
"nop" => Self::Nop(arg),
|
"nop" => Self::Nop(arg),
|
||||||
_ => return Err(err!("unrecognized instruction `{}`", inst)),
|
_ => bail!("unrecognized instruction `{}`", inst),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use aoc::err;
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day09.txt");
|
const INPUT: &str = include_str!("../input/day09.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -27,7 +27,7 @@ fn find_pair_sum(data: &[u64], total: u64) -> Option<(u64, u64)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_outlier(numbers: &[u64], preamble_size: usize) -> aoc::Result<(u64, usize)> {
|
fn find_outlier(numbers: &[u64], preamble_size: usize) -> Result<(u64, usize)> {
|
||||||
// start checking numbers after the preamble only
|
// start checking numbers after the preamble only
|
||||||
for i in preamble_size..numbers.len() {
|
for i in preamble_size..numbers.len() {
|
||||||
let preamble = &numbers[(i - preamble_size)..i];
|
let preamble = &numbers[(i - preamble_size)..i];
|
||||||
|
@ -39,22 +39,21 @@ fn find_outlier(numbers: &[u64], preamble_size: usize) -> aoc::Result<(u64, usiz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(err!("couldn't find number with that property"))
|
Err(anyhow!("couldn't find number with that property"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<u64> {
|
fn part1(input: &str) -> Result<u64> {
|
||||||
let numbers = input
|
let numbers = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse::<u64>())
|
.map(|line| line.parse::<u64>().map_err(anyhow::Error::new))
|
||||||
.collect::<Result<Vec<u64>, _>>()
|
.collect::<Result<Vec<u64>, _>>()?;
|
||||||
.map_err(|e| err!("couldn't parse number: {}", e))?;
|
|
||||||
|
|
||||||
let (solution, _) = find_outlier(&numbers, 25)?;
|
let (solution, _) = find_outlier(&numbers, 25)?;
|
||||||
|
|
||||||
Ok(solution)
|
Ok(solution)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_contiguous_range(numbers: &[u64], total: u64) -> aoc::Result<(u64, u64)> {
|
fn find_contiguous_range(numbers: &[u64], total: u64) -> Result<(u64, u64)> {
|
||||||
// compute cumulated sums for the whole range
|
// compute cumulated sums for the whole range
|
||||||
let (sums, _) = numbers.iter().fold((vec![0], 0), |(mut vec, acc), n| {
|
let (sums, _) = numbers.iter().fold((vec![0], 0), |(mut vec, acc), n| {
|
||||||
let acc = acc + n;
|
let acc = acc + n;
|
||||||
|
@ -74,15 +73,14 @@ fn find_contiguous_range(numbers: &[u64], total: u64) -> aoc::Result<(u64, u64)>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(err!("couldn't find number with that property"))
|
Err(anyhow!("couldn't find number with that property"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<u64> {
|
fn part2(input: &str) -> Result<u64> {
|
||||||
let numbers = input
|
let numbers = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse::<u64>())
|
.map(|line| line.parse::<u64>().map_err(anyhow::Error::new))
|
||||||
.collect::<Result<Vec<u64>, _>>()
|
.collect::<Result<Vec<u64>, _>>()?;
|
||||||
.map_err(|e| err!("couldn't parse number: {}", e))?;
|
|
||||||
|
|
||||||
let (outlier, idx) = find_outlier(&numbers, 25)?;
|
let (outlier, idx) = find_outlier(&numbers, 25)?;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use aoc::err;
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day10.txt");
|
const INPUT: &str = include_str!("../input/day10.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -14,17 +14,14 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<usize> {
|
fn part1(input: &str) -> Result<usize> {
|
||||||
let mut jolts = input
|
let mut jolts = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| {
|
.map(|line| line.parse().map_err(anyhow::Error::new))
|
||||||
line.parse()
|
.collect::<Result<Vec<u64>>>()?;
|
||||||
.map_err(|e| err!("couldn't parse joltage: {}", e))
|
|
||||||
})
|
|
||||||
.collect::<aoc::Result<Vec<u64>>>()?;
|
|
||||||
|
|
||||||
if jolts.is_empty() {
|
if jolts.is_empty() {
|
||||||
return Err(err!("input was empty!"));
|
bail!("input was empty!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// charging outlet can be added
|
// charging outlet can be added
|
||||||
|
@ -80,17 +77,14 @@ fn find_possibilities(jolts: &[u64], possibilities: &mut HashMap<u64, usize>) ->
|
||||||
possibilities_from_here
|
possibilities_from_here
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<usize> {
|
fn part2(input: &str) -> Result<usize> {
|
||||||
let mut jolts = input
|
let mut jolts = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| {
|
.map(|line| line.parse().map_err(anyhow::Error::new))
|
||||||
line.parse()
|
.collect::<Result<Vec<u64>>>()?;
|
||||||
.map_err(|e| err!("couldn't parse joltage: {}", e))
|
|
||||||
})
|
|
||||||
.collect::<aoc::Result<Vec<u64>>>()?;
|
|
||||||
|
|
||||||
if jolts.is_empty() {
|
if jolts.is_empty() {
|
||||||
return Err(err!("input was empty!"));
|
bail!("input was empty!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// charging outlet can be added
|
// charging outlet can be added
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use aoc::err;
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day11.txt");
|
const INPUT: &str = include_str!("../input/day11.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -13,7 +13,7 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<usize> {
|
fn part1(input: &str) -> Result<usize> {
|
||||||
let mut layout: Layout = input.parse()?;
|
let mut layout: Layout = input.parse()?;
|
||||||
|
|
||||||
let occupied_threshold = 4;
|
let occupied_threshold = 4;
|
||||||
|
@ -22,7 +22,7 @@ fn part1(input: &str) -> aoc::Result<usize> {
|
||||||
Ok(layout.occupied_seats())
|
Ok(layout.occupied_seats())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<usize> {
|
fn part2(input: &str) -> Result<usize> {
|
||||||
let mut layout: Layout = input.parse()?;
|
let mut layout: Layout = input.parse()?;
|
||||||
|
|
||||||
let occupied_threshold = 5;
|
let occupied_threshold = 5;
|
||||||
|
@ -177,9 +177,9 @@ impl std::ops::IndexMut<usize> for Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for Layout {
|
impl std::str::FromStr for Layout {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let grid = s
|
let grid = s
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| {
|
.map(|line| {
|
||||||
|
@ -188,11 +188,11 @@ impl std::str::FromStr for Layout {
|
||||||
'.' => Ok(Cell::Floor),
|
'.' => Ok(Cell::Floor),
|
||||||
'L' => Ok(Cell::EmptySeat),
|
'L' => Ok(Cell::EmptySeat),
|
||||||
'#' => Ok(Cell::OccupiedSeat),
|
'#' => Ok(Cell::OccupiedSeat),
|
||||||
_ => Err(err!("unknown char `{}`", c)),
|
_ => Err(anyhow!("unknown char `{}`", c)),
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.collect::<aoc::Result<Grid>>()?;
|
.collect::<Result<Grid>>()?;
|
||||||
|
|
||||||
let height = grid.len();
|
let height = grid.len();
|
||||||
let width = grid[0].len();
|
let width = grid[0].len();
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use aoc::err;
|
use anyhow::{bail, Context, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day12.txt");
|
const INPUT: &str = include_str!("../input/day12.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -13,11 +13,11 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<i64> {
|
fn part1(input: &str) -> Result<i64> {
|
||||||
let actions: Vec<Action> = input
|
let actions: Vec<Action> = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse())
|
.map(|line| line.parse())
|
||||||
.collect::<aoc::Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
let mut ship = Ship::new();
|
let mut ship = Ship::new();
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ fn part1(input: &str) -> aoc::Result<i64> {
|
||||||
Ok(ship.manhattan_distance())
|
Ok(ship.manhattan_distance())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<i64> {
|
fn part2(input: &str) -> Result<i64> {
|
||||||
let actions: Vec<Action> = input
|
let actions: Vec<Action> = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse())
|
.map(|line| line.parse())
|
||||||
.collect::<aoc::Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
let mut ship = Ship::new();
|
let mut ship = Ship::new();
|
||||||
|
|
||||||
|
@ -118,9 +118,9 @@ struct Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for Action {
|
impl std::str::FromStr for Action {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
s.len() >= 2,
|
s.len() >= 2,
|
||||||
"tried to parse action but it is too short: `{}`",
|
"tried to parse action but it is too short: `{}`",
|
||||||
|
@ -129,7 +129,7 @@ impl std::str::FromStr for Action {
|
||||||
let letter = s
|
let letter = s
|
||||||
.chars()
|
.chars()
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| err!("couldn't parse action: empty string"))?;
|
.context("couldn't parse action: empty string")?;
|
||||||
|
|
||||||
let kind = match letter {
|
let kind = match letter {
|
||||||
'N' => ActionKind::Move(Direction::North),
|
'N' => ActionKind::Move(Direction::North),
|
||||||
|
@ -142,12 +142,10 @@ impl std::str::FromStr for Action {
|
||||||
|
|
||||||
'F' => ActionKind::Forward,
|
'F' => ActionKind::Forward,
|
||||||
|
|
||||||
_ => return Err(err!("couldn't parse action with letter `{}`", letter)),
|
_ => bail!("couldn't parse action with letter `{}`", letter),
|
||||||
};
|
};
|
||||||
|
|
||||||
let arg = s[1..]
|
let arg = s[1..].parse().context("couldn't parse action arg")?;
|
||||||
.parse()
|
|
||||||
.map_err(|e| err!("couldn't parse action arg: {}", e))?;
|
|
||||||
|
|
||||||
Ok(Self { kind, arg })
|
Ok(Self { kind, arg })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use aoc::err;
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day13.txt");
|
const INPUT: &str = include_str!("../input/day13.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -13,27 +13,27 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<u64> {
|
fn part1(input: &str) -> Result<u64> {
|
||||||
let mut lines = input.lines();
|
let mut lines = input.lines();
|
||||||
|
|
||||||
let earliest_timestamp = lines
|
let earliest_timestamp = lines
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| err!("input was empty"))?
|
.context("input was empty")?
|
||||||
.parse::<u64>()
|
.parse::<u64>()
|
||||||
.map_err(|e| err!("couldn't parse first line: {}", e))?;
|
.context("couldn't parse first line")?;
|
||||||
|
|
||||||
let bus_ids = lines
|
let bus_ids = lines
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| err!("no second line"))?
|
.context("no second line")?
|
||||||
.split(',')
|
.split(',')
|
||||||
.filter_map(|num| {
|
.filter_map(|num| {
|
||||||
if num == "x" {
|
if num == "x" {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(num.parse::<u64>().map_err(|e| err!("{}", e)))
|
Some(num.parse::<u64>().map_err(anyhow::Error::new))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<aoc::Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
let (bus_id, earliest_departure) = bus_ids
|
let (bus_id, earliest_departure) = bus_ids
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -47,16 +47,16 @@ fn part1(input: &str) -> aoc::Result<u64> {
|
||||||
Ok(bus_id * (earliest_departure - earliest_timestamp))
|
Ok(bus_id * (earliest_departure - earliest_timestamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<u64> {
|
fn part2(input: &str) -> Result<u64> {
|
||||||
let mut lines = input.lines();
|
let mut lines = input.lines();
|
||||||
|
|
||||||
// we don't need the first line anymore, skip it
|
// we don't need the first line anymore, skip it
|
||||||
lines.next().ok_or_else(|| err!("input was empty"))?;
|
lines.next().context("input was empty")?;
|
||||||
|
|
||||||
find_timestamp(lines.next().ok_or_else(|| err!("no second line"))?)
|
find_timestamp(lines.next().context("no second line")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_timestamp(input: &str) -> aoc::Result<u64> {
|
fn find_timestamp(input: &str) -> Result<u64> {
|
||||||
let bus_ids: Vec<(u64, u64)> = input
|
let bus_ids: Vec<(u64, u64)> = input
|
||||||
.split(',')
|
.split(',')
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
@ -64,14 +64,14 @@ fn find_timestamp(input: &str) -> aoc::Result<u64> {
|
||||||
if num == "x" {
|
if num == "x" {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some((idx as u64, num.parse::<u64>().map_err(|e| err!("{}", e))))
|
Some((idx as u64, num.parse::<u64>().map_err(anyhow::Error::new)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(|(idx, res)| match res {
|
.map(|(idx, res)| match res {
|
||||||
Ok(num) => Ok((idx, num)),
|
Ok(num) => Ok((idx, num)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
})
|
})
|
||||||
.collect::<aoc::Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
// previous constraints is empty for now
|
// previous constraints is empty for now
|
||||||
let mut current_solution = 0;
|
let mut current_solution = 0;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use aoc::err;
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../input/day14.txt");
|
const INPUT: &str = include_str!("../input/day14.txt");
|
||||||
|
|
||||||
pub fn run() -> aoc::Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
@ -14,7 +14,7 @@ pub fn run() -> aoc::Result<String> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> aoc::Result<u64> {
|
fn part1(input: &str) -> Result<u64> {
|
||||||
let mut program: Program = input.parse()?;
|
let mut program: Program = input.parse()?;
|
||||||
|
|
||||||
program.run_part1()?;
|
program.run_part1()?;
|
||||||
|
@ -22,7 +22,7 @@ fn part1(input: &str) -> aoc::Result<u64> {
|
||||||
Ok(program.memory_sum())
|
Ok(program.memory_sum())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> aoc::Result<u64> {
|
fn part2(input: &str) -> Result<u64> {
|
||||||
let mut program: Program = input.parse()?;
|
let mut program: Program = input.parse()?;
|
||||||
|
|
||||||
program.run_part2()?;
|
program.run_part2()?;
|
||||||
|
@ -175,9 +175,9 @@ impl BitMask {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for BitMask {
|
impl std::str::FromStr for BitMask {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let masks = s
|
let masks = s
|
||||||
.chars()
|
.chars()
|
||||||
.rev()
|
.rev()
|
||||||
|
@ -187,10 +187,10 @@ impl std::str::FromStr for BitMask {
|
||||||
'1' => Ok(Mask::One),
|
'1' => Ok(Mask::One),
|
||||||
'0' => Ok(Mask::Zero),
|
'0' => Ok(Mask::Zero),
|
||||||
'X' => Ok(Mask::Floating),
|
'X' => Ok(Mask::Floating),
|
||||||
_ => Err(err!("unknown character in mask: `{}`", c)),
|
_ => Err(anyhow!("unknown character in mask: `{}`", c)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<aoc::Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
Ok(BitMask { masks })
|
Ok(BitMask { masks })
|
||||||
}
|
}
|
||||||
|
@ -203,23 +203,17 @@ enum Instruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for Instruction {
|
impl std::str::FromStr for Instruction {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let mut words = s.split(' ');
|
let mut words = s.split(' ');
|
||||||
|
|
||||||
let first = words
|
let first = words.next().context("missing first word in instruction")?;
|
||||||
.next()
|
let second = words.next().context("missing second word in instruction")?;
|
||||||
.ok_or_else(|| err!("missing first word in instruction"))?;
|
let third = words.next().context("missing third word in instruction")?;
|
||||||
let second = words
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| err!("missing second word in instruction"))?;
|
|
||||||
let third = words
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| err!("missing third word in instruction"))?;
|
|
||||||
|
|
||||||
if second != "=" {
|
if second != "=" {
|
||||||
return Err(err!("expected `=` as second word in instruction: `{}`", s));
|
bail!("expected `=` as second word in instruction: `{}`", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if first == "mask" {
|
if first == "mask" {
|
||||||
|
@ -227,18 +221,16 @@ impl std::str::FromStr for Instruction {
|
||||||
} else {
|
} else {
|
||||||
let left_bracket = first
|
let left_bracket = first
|
||||||
.find('[')
|
.find('[')
|
||||||
.ok_or_else(|| err!("couldn't find bracket in memory instruction"))?;
|
.context("couldn't find bracket in memory instruction")?;
|
||||||
let right_bracket = first
|
let right_bracket = first
|
||||||
.find(']')
|
.find(']')
|
||||||
.ok_or_else(|| err!("couldn't find bracket in memory instruction"))?;
|
.context("couldn't find bracket in memory instruction")?;
|
||||||
|
|
||||||
let offset = first[(left_bracket + 1)..right_bracket]
|
let offset = first[(left_bracket + 1)..right_bracket]
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|e| err!("couldn't parse memory offset: `{}`", e))?;
|
.context("couldn't parse memory offset")?;
|
||||||
|
|
||||||
let value = third
|
let value = third.parse().context("couldn't parse memory offset")?;
|
||||||
.parse()
|
|
||||||
.map_err(|e| err!("couldn't parse memory offset: `{}`", e))?;
|
|
||||||
|
|
||||||
Ok(Self::MemWrite { offset, value })
|
Ok(Self::MemWrite { offset, value })
|
||||||
}
|
}
|
||||||
|
@ -252,7 +244,7 @@ struct Program {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
fn run_part1(&mut self) -> aoc::Result<()> {
|
fn run_part1(&mut self) -> Result<()> {
|
||||||
for inst in &self.instructions {
|
for inst in &self.instructions {
|
||||||
match inst {
|
match inst {
|
||||||
Instruction::ChangeMask(bitmask) => self.current_mask = Some(bitmask.clone()),
|
Instruction::ChangeMask(bitmask) => self.current_mask = Some(bitmask.clone()),
|
||||||
|
@ -263,7 +255,7 @@ impl Program {
|
||||||
.insert(*offset, bitmask.apply_no_floating(*value));
|
.insert(*offset, bitmask.apply_no_floating(*value));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(err!("tried to execute MemWrite but mask isn't initialized"))
|
bail!("tried to execute MemWrite but mask isn't initialized")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -272,7 +264,7 @@ impl Program {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_part2(&mut self) -> aoc::Result<()> {
|
fn run_part2(&mut self) -> Result<()> {
|
||||||
for inst in &self.instructions {
|
for inst in &self.instructions {
|
||||||
match inst {
|
match inst {
|
||||||
Instruction::ChangeMask(bitmask) => self.current_mask = Some(bitmask.clone()),
|
Instruction::ChangeMask(bitmask) => self.current_mask = Some(bitmask.clone()),
|
||||||
|
@ -284,7 +276,7 @@ impl Program {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(err!("tried to execute MemWrite but mask isn't initialized"))
|
bail!("tried to execute MemWrite but mask isn't initialized")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -299,13 +291,10 @@ impl Program {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for Program {
|
impl std::str::FromStr for Program {
|
||||||
type Err = aoc::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let instructions = s
|
let instructions = s.lines().map(|line| line.parse()).collect::<Result<_>>()?;
|
||||||
.lines()
|
|
||||||
.map(|line| line.parse())
|
|
||||||
.collect::<aoc::Result<_>>()?;
|
|
||||||
|
|
||||||
Ok(Program {
|
Ok(Program {
|
||||||
instructions,
|
instructions,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
use aoc::DayFunc;
|
use aoc::DayFunc;
|
||||||
use aoc::Result;
|
|
||||||
|
|
||||||
use aoc2020::day01;
|
use aoc2020::day01;
|
||||||
use aoc2020::day02;
|
use aoc2020::day02;
|
||||||
|
|
Loading…
Reference in a new issue