From 1a45edd3ff90263e360c67712f197675c975db4c Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Mon, 4 Dec 2023 08:11:27 +0100 Subject: [PATCH] Implement 2023 day 4 --- 2023/benches/days.rs | 2 +- 2023/src/day04.rs | 105 ++++++++++++++++++++++++++++++++++++++-- 2023/src/samples/04.txt | 6 +++ 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 2023/src/samples/04.txt diff --git a/2023/benches/days.rs b/2023/benches/days.rs index 8662026..22d3836 100644 --- a/2023/benches/days.rs +++ b/2023/benches/days.rs @@ -9,7 +9,7 @@ use criterion::Criterion; use aoc_2023::get_implementation; /// Number of days we have an implementation to benchmark -const DAYS_IMPLEMENTED: u8 = 3; +const DAYS_IMPLEMENTED: u8 = 4; fn read_input(day: u8) -> std::io::Result> { let input_path = format!("inputs/{day:02}.txt"); diff --git a/2023/src/day04.rs b/2023/src/day04.rs index 7c1760f..c335378 100644 --- a/2023/src/day04.rs +++ b/2023/src/day04.rs @@ -1,7 +1,104 @@ -pub fn part1(_input: &[u8]) -> anyhow::Result { - anyhow::bail!("Not implemented") +use nom::bytes::complete::tag; +use nom::character::complete::newline; +use nom::character::complete::space1; +use nom::combinator::iterator; +use nom::combinator::map; +use nom::multi::fold_many1; +use nom::multi::many1; +use nom::sequence::delimited; +use nom::sequence::pair; +use nom::sequence::preceded; +use nom::sequence::tuple; +use nom::IResult; + +use crate::common::convert_nom_error; +use crate::common::parse_input; + +struct Card { + have: u128, + winning: u128, } -pub fn part2(_input: &[u8]) -> anyhow::Result { - anyhow::bail!("Not implemented") +fn parse_card(i: &[u8]) -> IResult<&[u8], Card> { + fn parse_set(i: &[u8]) -> IResult<&[u8], u128> { + fold_many1( + preceded(space1, nom::character::complete::u8), + || 0u128, + |cur, bit| cur | (1 << bit), + )(i) + } + + map( + pair( + preceded( + delimited( + pair(tag("Card"), space1), + nom::character::complete::u32, + tag(":"), + ), + parse_set, + ), + delimited(tag(" |"), parse_set, newline), + ), + |(have, winning)| Card { have, winning }, + )(i) +} + +pub fn part1(input: &[u8]) -> anyhow::Result { + let mut card_it = iterator(input, parse_card); + + let total: u32 = card_it + .into_iter() + .map(|card| { + let winners = (card.have & card.winning).count_ones(); + + if winners > 0 { + 1 << winners - 1 + } else { + 0 + } + }) + .sum(); + + card_it.finish().map_err(|e| match e { + nom::Err::Incomplete(_) => anyhow::anyhow!("unreachable"), + nom::Err::Failure(e) | nom::Err::Error(e) => convert_nom_error(e), + })?; + + Ok(total.to_string()) +} + +pub fn part2(input: &[u8]) -> anyhow::Result { + let cards = parse_input(input, many1(parse_card))?; + let mut counts = vec![1; cards.len()]; + + for (id, card) in cards.iter().enumerate() { + let winners = (card.have & card.winning).count_ones() as usize; + let count = counts[id]; + + for offset in 1..=winners { + counts[id + offset] += count; + } + } + + let total: u32 = counts.iter().sum(); + + Ok(total.to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE: &[u8] = include_bytes!("samples/04.txt"); + + #[test] + fn sample_part1() { + assert_eq!(part1(SAMPLE).unwrap(), "13"); + } + + #[test] + fn sample_part2() { + assert_eq!(part2(SAMPLE).unwrap(), "30"); + } } diff --git a/2023/src/samples/04.txt b/2023/src/samples/04.txt new file mode 100644 index 0000000..9bdb874 --- /dev/null +++ b/2023/src/samples/04.txt @@ -0,0 +1,6 @@ +Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 +Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 +Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 +Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 +Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 +Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11