87 lines
1.9 KiB
Rust
87 lines
1.9 KiB
Rust
//! Day 4: Scratchcards
|
|
|
|
use std::collections::HashSet;
|
|
|
|
use itertools::Itertools;
|
|
|
|
pub const DAY: u8 = 4;
|
|
pub const TITLE: &str = "Scratchcards";
|
|
|
|
fn parse(line: &str) -> (HashSet<u32>, HashSet<u32>) {
|
|
let (_, tmp) = line.split_once(": ").unwrap();
|
|
let (win, got) = tmp.split_once(" | ").unwrap();
|
|
(
|
|
win.split_ascii_whitespace()
|
|
.map(|p| p.parse::<u32>().unwrap())
|
|
.collect(),
|
|
got.split_ascii_whitespace()
|
|
.map(|p| p.parse::<u32>().unwrap())
|
|
.collect(),
|
|
)
|
|
}
|
|
|
|
pub fn part1(input: Vec<String>) -> String {
|
|
input
|
|
.iter()
|
|
.map(|ln| {
|
|
let (win, got) = parse(ln);
|
|
let nm = win.intersection(&got).count();
|
|
if nm > 0 {
|
|
2_u32.pow(nm as u32 - 1)
|
|
} else {
|
|
0
|
|
}
|
|
})
|
|
.sum::<u32>()
|
|
.to_string()
|
|
}
|
|
|
|
pub fn part2(input: Vec<String>) -> String {
|
|
let cards = input
|
|
.iter()
|
|
.map(|ln| {
|
|
let (win, got) = parse(ln);
|
|
win.intersection(&got).count()
|
|
})
|
|
.collect_vec();
|
|
|
|
let mut n_cards = 0;
|
|
|
|
fn process_card(i: usize, cards: &[usize], n_cards: &mut u32) {
|
|
*n_cards += 1;
|
|
let nw = cards[i];
|
|
for ni in i + 1..(i + 1 + nw).min(cards.len()) {
|
|
process_card(ni, cards, n_cards);
|
|
}
|
|
}
|
|
|
|
for i in 0..cards.len() {
|
|
process_card(i, &cards, &mut n_cards);
|
|
}
|
|
n_cards.to_string()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn t_example1() {
|
|
assert_eq!(part1(crate::read_example(DAY, 1)), "13");
|
|
}
|
|
|
|
#[test]
|
|
fn t_part1() {
|
|
assert_eq!(part1(crate::read_input(DAY)), "20855");
|
|
}
|
|
|
|
#[test]
|
|
fn t_example2() {
|
|
assert_eq!(part2(crate::read_example(DAY, 2)), "30");
|
|
}
|
|
|
|
#[test]
|
|
fn t_part2() {
|
|
assert_eq!(part2(crate::read_input(DAY)), "5489600");
|
|
}
|
|
}
|