advent-of-code/aoc2020/src/day10.rs

134 lines
3.2 KiB
Rust
Raw Normal View History

2020-12-10 14:51:38 +01:00
use std::collections::HashMap;
2020-12-10 14:09:47 +01:00
use std::fmt::Write;
2020-12-14 18:08:16 +01:00
use anyhow::{bail, Result};
2020-12-10 14:09:47 +01:00
const INPUT: &str = include_str!("../input/day10.txt");
2020-12-14 18:08:16 +01:00
pub fn run() -> Result<String> {
2020-12-10 14:09:47 +01:00
let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?;
2020-12-10 14:51:38 +01:00
writeln!(res, "part 2: {}", part2(INPUT)?)?;
2020-12-10 14:09:47 +01:00
Ok(res)
}
2020-12-14 18:08:16 +01:00
fn part1(input: &str) -> Result<usize> {
2020-12-10 14:09:47 +01:00
let mut jolts = input
.lines()
2020-12-14 18:08:16 +01:00
.map(|line| line.parse().map_err(anyhow::Error::new))
.collect::<Result<Vec<u64>>>()?;
2020-12-10 14:09:47 +01:00
if jolts.is_empty() {
2020-12-14 18:08:16 +01:00
bail!("input was empty!");
2020-12-10 14:09:47 +01:00
}
// charging outlet can be added
jolts.insert(0, 0);
jolts.sort_unstable();
// device is rated for max adapter + 3 jolts
let max_adapter = jolts[jolts.len() - 1];
let device_rating = max_adapter + 3;
jolts.push(device_rating);
let mut differences: [usize; 4] = [0; 4];
2020-12-10 15:16:41 +01:00
for window in jolts.windows(2) {
let prev = window[0];
let next = window[1];
2020-12-10 14:09:47 +01:00
let difference = (next - prev) as usize;
differences[difference] += 1;
}
Ok(differences[1] * differences[3])
}
2020-12-10 14:51:38 +01:00
fn find_possibilities(jolts: &[u64], possibilities: &mut HashMap<u64, usize>) -> usize {
if let Some(&count) = possibilities.get(&jolts[0]) {
return count;
}
if jolts.len() == 1 {
return 1;
}
let curr = jolts[0];
let possibilities_from_here = jolts
.iter()
.copied()
// we need the index to step in the jolt slice
.enumerate()
2020-12-10 15:20:19 +01:00
// skip the current adapter
2020-12-10 14:51:38 +01:00
.skip(1)
// its 3 successors can possibly be removed
.take(3)
// remove adapter if the jolt difference is too high
.filter(|(_, jolt)| jolt - curr <= 3)
// count each possible next adapter and all its possibilities
.map(|(idx, _)| find_possibilities(&jolts[idx..], possibilities))
.sum();
possibilities.insert(curr, possibilities_from_here);
possibilities_from_here
}
2020-12-14 18:08:16 +01:00
fn part2(input: &str) -> Result<usize> {
2020-12-10 14:51:38 +01:00
let mut jolts = input
.lines()
2020-12-14 18:08:16 +01:00
.map(|line| line.parse().map_err(anyhow::Error::new))
.collect::<Result<Vec<u64>>>()?;
2020-12-10 14:51:38 +01:00
if jolts.is_empty() {
2020-12-14 18:08:16 +01:00
bail!("input was empty!");
2020-12-10 14:51:38 +01:00
}
// charging outlet can be added
jolts.insert(0, 0);
jolts.sort_unstable();
// device is rated for max adapter + 3 jolts
let max_adapter = jolts[jolts.len() - 1];
let device_rating = max_adapter + 3;
jolts.push(device_rating);
let mut possibilities = HashMap::new();
Ok(find_possibilities(&jolts, &mut possibilities))
}
2020-12-10 14:09:47 +01:00
#[cfg(test)]
mod tests {
use super::*;
const PROVIDED1: &str = include_str!("../input/day10_provided1.txt");
const PROVIDED2: &str = include_str!("../input/day10_provided2.txt");
#[test]
fn part1_provided() {
assert_eq!(part1(PROVIDED1).unwrap(), 7 * 5);
assert_eq!(part1(PROVIDED2).unwrap(), 22 * 10);
}
#[test]
fn part1_real() {
assert_eq!(part1(INPUT).unwrap(), 2112);
}
2020-12-10 14:51:38 +01:00
#[test]
fn part2_provided() {
assert_eq!(part2(PROVIDED1).unwrap(), 8);
assert_eq!(part2(PROVIDED2).unwrap(), 19208);
}
#[test]
fn part2_real() {
assert_eq!(part2(INPUT).unwrap(), 3022415986688);
}
2020-12-10 14:09:47 +01:00
}