151 lines
3.8 KiB
Rust
151 lines
3.8 KiB
Rust
|
use nom::{self, bytes::complete::tag, character::complete, sequence::separated_pair, IResult};
|
||
|
use std::collections::HashSet;
|
||
|
|
||
|
const INPUT: &str = include_str!("../data/day4.txt");
|
||
|
|
||
|
pub fn part1() -> String {
|
||
|
format!("{}", count_full_containments(input(INPUT)))
|
||
|
}
|
||
|
|
||
|
pub fn part2() -> String {
|
||
|
format!("{}", count_overlaps(input(INPUT)))
|
||
|
}
|
||
|
|
||
|
fn count_full_containments(pairings: Vec<ElfPairing>) -> u32 {
|
||
|
pairings.into_iter().fold(0, |cur, pairing| {
|
||
|
if pairing.has_full_containment() {
|
||
|
cur + 1
|
||
|
} else {
|
||
|
cur
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
fn count_overlaps(pairings: Vec<ElfPairing>) -> u32 {
|
||
|
pairings.into_iter().fold(
|
||
|
0,
|
||
|
|cur, pairing| {
|
||
|
if pairing.has_overlap() {
|
||
|
cur + 1
|
||
|
} else {
|
||
|
cur
|
||
|
}
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
|
||
|
struct ElfPairing {
|
||
|
elf1: Vec<u32>,
|
||
|
elf2: Vec<u32>,
|
||
|
}
|
||
|
|
||
|
impl ElfPairing {
|
||
|
fn has_full_containment(&self) -> bool {
|
||
|
let elf1 = self.elf1.iter().cloned().collect::<HashSet<u32>>();
|
||
|
let elf2 = self.elf2.iter().cloned().collect::<HashSet<u32>>();
|
||
|
elf1.is_subset(&elf2) || elf2.is_subset(&elf1)
|
||
|
}
|
||
|
|
||
|
fn has_overlap(&self) -> bool {
|
||
|
let element_count = self.elf1.len() + self.elf2.len();
|
||
|
let combined_set = self
|
||
|
.elf1
|
||
|
.iter()
|
||
|
.chain(self.elf2.iter())
|
||
|
.cloned()
|
||
|
.collect::<HashSet<u32>>();
|
||
|
combined_set.len() < element_count
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn input(data: &str) -> Vec<ElfPairing> {
|
||
|
data.lines()
|
||
|
.map(|line| {
|
||
|
let (_, pairing) = elf_pair(line).unwrap();
|
||
|
pairing
|
||
|
})
|
||
|
.collect::<Vec<ElfPairing>>()
|
||
|
}
|
||
|
|
||
|
fn elf_pair(input: &str) -> IResult<&str, ElfPairing> {
|
||
|
let (input, elf1) = elf_range(input)?;
|
||
|
let (input, _) = tag(",")(input)?;
|
||
|
let (input, elf2) = elf_range(input)?;
|
||
|
|
||
|
Ok((input, ElfPairing { elf1, elf2 }))
|
||
|
}
|
||
|
|
||
|
fn elf_range(input: &str) -> IResult<&str, Vec<u32>> {
|
||
|
let (input, start) = complete::u32(input)?;
|
||
|
let (input, _) = tag("-")(input)?;
|
||
|
let (input, end) = complete::u32(input)?;
|
||
|
|
||
|
Ok((input, (start..end + 1).collect::<Vec<u32>>()))
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod test {
|
||
|
use super::*;
|
||
|
|
||
|
const TEST_DATA: &str = "2-4,6-8
|
||
|
2-3,4-5
|
||
|
5-7,7-9
|
||
|
2-8,3-7
|
||
|
6-6,4-6
|
||
|
2-6,4-8";
|
||
|
|
||
|
#[test]
|
||
|
fn it_gets_a_hashset_from_a_range() {
|
||
|
assert_eq!(vec![2, 3, 4], (2..5).collect::<Vec<u32>>());
|
||
|
}
|
||
|
|
||
|
fn with_input<F>(test: F)
|
||
|
where
|
||
|
F: Fn(Vec<ElfPairing>) -> () + std::panic::UnwindSafe,
|
||
|
{
|
||
|
test(input(TEST_DATA));
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn it_parses_input_correctly() {
|
||
|
with_input(|pairings| {
|
||
|
assert_eq!(vec![2, 3, 4], pairings[0].elf1);
|
||
|
assert_eq!(vec![6, 7, 8], pairings[0].elf2);
|
||
|
|
||
|
assert_eq!(vec![6], pairings[4].elf1);
|
||
|
assert_eq!(vec![4, 5, 6], pairings[4].elf2);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn it_checks_full_containment() {
|
||
|
with_input(|pairings| {
|
||
|
assert!(!pairings[0].has_full_containment());
|
||
|
assert!(!pairings[1].has_full_containment());
|
||
|
assert!(!pairings[2].has_full_containment());
|
||
|
assert!(pairings[3].has_full_containment());
|
||
|
assert!(pairings[4].has_full_containment());
|
||
|
assert!(!pairings[5].has_full_containment());
|
||
|
});
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn it_counts_full_containments() {
|
||
|
with_input(|pairings| {
|
||
|
assert_eq!(2, count_full_containments(pairings));
|
||
|
});
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn it_checks_overlap() {
|
||
|
with_input(|pairings| {
|
||
|
assert!(!pairings[0].has_overlap());
|
||
|
assert!(!pairings[1].has_overlap());
|
||
|
assert!(pairings[2].has_overlap());
|
||
|
assert!(pairings[3].has_overlap());
|
||
|
assert!(pairings[4].has_overlap());
|
||
|
assert!(pairings[5].has_overlap());
|
||
|
});
|
||
|
}
|
||
|
}
|