summaryrefslogtreecommitdiff
path: root/2021/src/bin/day_6.rs
blob: 9a40f9ea627b864e9782811a4d73338058e071a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use nom::{
    bytes::complete::tag,
    character::complete::u32 as nom_u32,
    combinator::{map, map_res},
    multi::separated_list1,
    IResult, ToUsize,
};
use std::{collections::VecDeque, fs};
use thiserror::Error;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let input = fs::read_to_string("inputs/day_6.txt")?;
    let mut swarm = parse_swarm(&input).unwrap().1;
    for _ in 0..80 {
        swarm.grow();
    }
    dbg!(swarm.fish_sum());

    for _ in 80..256 {
        swarm.grow();
    }
    dbg!(swarm.fish_sum());

    Ok(())
}

#[derive(
    Default, Debug, Clone, Copy, derive_more::Add, derive_more::AddAssign, derive_more::Sum,
)]
struct FishCount(u64);

const FISH_INITIAL_SPAWN_COUNTDOWN: usize = 9;
const FISH_REPEAT_SPAWN_COUNTDOWN: usize = 7;
#[derive(Debug)]
struct Swarm {
    fish: VecDeque<FishCount>,
}

#[derive(Debug, Error)]
enum SwarmParseError {
    #[error("input was out of range")]
    OutOfRange,
}

impl Swarm {
    fn new(fish_counters: Vec<usize>) -> Result<Swarm, SwarmParseError> {
        let mut fish = VecDeque::with_capacity(FISH_INITIAL_SPAWN_COUNTDOWN);
        for _ in 0..FISH_INITIAL_SPAWN_COUNTDOWN {
            fish.push_back(FishCount::default());
        }
        for fish_counter in fish_counters {
            if fish_counter > fish.len() {
                return Err(SwarmParseError::OutOfRange);
            }
            fish[fish_counter] += FishCount(1);
        }
        Ok(Swarm { fish })
    }

    fn grow(&mut self) {
        let spawning = self
            .fish
            .pop_front()
            .expect("Fish buffer should maintain exactly 9 entries");
        self.fish[FISH_REPEAT_SPAWN_COUNTDOWN - 1] += spawning;
        self.fish.push_back(spawning);
    }

    fn fish_sum(&self) -> FishCount {
        self.fish.iter().copied().sum()
    }
}

fn parse_swarm(input: &str) -> IResult<&str, Swarm> {
    map_res(
        separated_list1(tag(","), map(nom_u32, |n| n.to_usize())),
        Swarm::new,
    )(input)
}