3 Commits

Author SHA1 Message Date
9e37026f30 Remove unnecessary indexing 2021-12-14 19:54:45 +01:00
a926243f0d Improve "winning" logic
You don't need to check every possible win condition, just the one you
touched.
2021-12-14 19:51:57 +01:00
a35ae82548 Replace unnecessary HashMap and allocations 2021-12-14 19:39:00 +01:00
3 changed files with 70 additions and 57 deletions

View File

@@ -16,6 +16,10 @@ criterion = "0.3"
# Keep debug information in release for better flamegraphs # Keep debug information in release for better flamegraphs
debug = true debug = true
[profile.bench]
# And same for benchmarking
debug = true
[[bench]] [[bench]]
name = "days" name = "days"
harness = false harness = false

View File

@@ -7,7 +7,7 @@ use criterion::criterion_main;
use criterion::BenchmarkId; use criterion::BenchmarkId;
use criterion::Criterion; use criterion::Criterion;
const DAYS_IMPLEMENTED: usize = 13; const DAYS_IMPLEMENTED: usize = 14;
fn read_input(day: usize) -> Vec<u8> { fn read_input(day: usize) -> Vec<u8> {
let input_path = format!("inputs/{:02}.txt", day); let input_path = format!("inputs/{:02}.txt", day);

View File

@@ -1,86 +1,91 @@
use std::collections::HashMap;
use std::io::Read; use std::io::Read;
use crate::common::LineIter; use nom::bytes::complete::tag;
use nom::character::complete::multispace1;
use nom::multi::many1;
use nom::multi::separated_list1;
use nom::sequence::preceded;
use nom::sequence::tuple;
use nom::Finish;
use nom::IResult;
#[derive(Debug)] #[derive(Debug)]
struct BingoCard { struct BingoCard([(bool, u8); 25]);
ticks: [[bool; 5]; 5],
mapping: HashMap<u8, (u8, u8)>,
}
impl BingoCard { impl BingoCard {
pub fn cross(&mut self, num: u8) -> bool { pub fn cross(&mut self, num: u8) -> Option<usize> {
if let Some(&(x, y)) = self.mapping.get(&num) { self.0
self.ticks[y as usize][x as usize] = true; .iter_mut()
true .enumerate()
} else { .find_map(|(pos, (ticked, x))| {
false if *x == num {
} *ticked = true;
Some(pos)
} else {
None
}
})
} }
pub fn has_won(&self) -> bool { pub fn has_won(&self, crossed: usize) -> bool {
// Check horizontal lines // Check horizontal lines
if self.ticks.iter().any(|s| s.iter().all(|&b| b)) { if self
.0
.chunks_exact(5)
.nth(crossed / 5)
.unwrap()
.iter()
.all(|&b| b.0)
{
return true; return true;
} }
// Check vertical lines // Check vertical lines
(0..5).any(|x| self.ticks.iter().all(|row| row[x])) self.0.iter().skip(crossed % 5).step_by(5).all(|b| b.0)
// Diagonals do not count // Diagonals do not count
} }
pub fn remaining(&self) -> u32 { pub fn remaining(&self) -> u32 {
self.mapping self.0
.iter() .iter()
.filter_map(|(&num, &(x, y))| { .filter_map(|&(ticked, num)| if !ticked { Some(num as u32) } else { None })
if self.ticks[y as usize][x as usize] {
None
} else {
Some(num as u32)
}
})
.sum() .sum()
} }
} }
fn read_input(input: &mut dyn Read) -> (Vec<u8>, Vec<BingoCard>) { fn parse_numbers(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
let mut reader = LineIter::new(input); use nom::character::complete::u8;
separated_list1(tag(","), u8)(input)
}
let numbers: Vec<u8> = reader fn parse_bingo(mut input: &[u8]) -> IResult<&[u8], BingoCard> {
.next() use nom::character::complete::u8;
.unwrap()
.split(',')
.map(|num| num.parse().unwrap())
.collect();
let mut bingo_cards = Vec::new(); let mut card = [0; 25];
while reader.next().is_some() { let mut parse_num = preceded(multispace1, u8);
let mut mapping = HashMap::with_capacity(25);
for y in 0..5 { // fill doesn't work with preceded
reader for num in &mut card {
.next() let result = parse_num(input)?;
.unwrap() *num = result.1;
.split(' ') input = result.0;
.filter_map(|s| if s.is_empty() { None } else { s.parse().ok() })
.enumerate()
.for_each(|(x, num)| {
mapping.insert(num, (x as u8, y));
});
}
let card = BingoCard {
mapping,
ticks: Default::default(),
};
bingo_cards.push(card);
} }
(numbers, bingo_cards) Ok((input, BingoCard(card.map(|x| (false, x)))))
}
fn parse_input(input: &[u8]) -> IResult<&[u8], (Vec<u8>, Vec<BingoCard>)> {
tuple((parse_numbers, many1(parse_bingo)))(input)
}
fn read_input(input: &mut dyn Read) -> (Vec<u8>, Vec<BingoCard>) {
let mut buffer = Vec::new();
input.read_to_end(&mut buffer).unwrap();
parse_input(&buffer).finish().unwrap().1
} }
pub fn part1(input: &mut dyn Read) -> String { pub fn part1(input: &mut dyn Read) -> String {
@@ -88,7 +93,7 @@ pub fn part1(input: &mut dyn Read) -> String {
for number in numbers { for number in numbers {
for card in &mut bingo_cards { for card in &mut bingo_cards {
if card.cross(number) && card.has_won() { if matches!(card.cross(number), Some(pos) if card.has_won(pos)) {
return (number as u32 * card.remaining()).to_string(); return (number as u32 * card.remaining()).to_string();
} }
} }
@@ -105,7 +110,11 @@ pub fn part2(input: &mut dyn Read) -> String {
for num in numbers { for num in numbers {
for (won, card) in bingo_won.iter_mut().zip(bingo_cards.iter_mut()) { for (won, card) in bingo_won.iter_mut().zip(bingo_cards.iter_mut()) {
if !*won && card.cross(num) && card.has_won() { if *won {
continue;
}
if matches!(card.cross(num), Some(pos) if card.has_won(pos)) {
*won = true; *won = true;
num_won += 1; num_won += 1;