mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 12:50:32 +01:00
Implement 2023 day 19 part 1
This commit is contained in:
@@ -1,7 +1,201 @@
|
||||
pub fn part1(_input: &[u8]) -> anyhow::Result<String> {
|
||||
anyhow::bail!("Not implemented")
|
||||
use std::ops::Index;
|
||||
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::take;
|
||||
use nom::bytes::complete::take_until1;
|
||||
use nom::bytes::complete::take_while1;
|
||||
use nom::combinator::map;
|
||||
use nom::multi::fold_many1;
|
||||
use nom::multi::many1;
|
||||
use nom::sequence::delimited;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::separated_pair;
|
||||
use nom::sequence::terminated;
|
||||
use nom::sequence::tuple;
|
||||
use nom::IResult;
|
||||
|
||||
use crate::common::parse_input;
|
||||
|
||||
const RULES_LEN: usize = 26 * 26 * 26;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Item([u16; 4]);
|
||||
|
||||
impl Item {
|
||||
fn rating_sum(self) -> u32 {
|
||||
self.0.into_iter().map(u32::from).sum()
|
||||
}
|
||||
|
||||
fn passes(&self, rules: &[Rule]) -> bool {
|
||||
let mut pos = convert_name(b"in");
|
||||
|
||||
'outer: loop {
|
||||
let rule = &rules[pos as usize];
|
||||
|
||||
for &(condition, end) in &rule.checks {
|
||||
let is_valid = match condition {
|
||||
Condition::Greater(c, val) => self[c] > val,
|
||||
Condition::Less(c, val) => self[c] < val,
|
||||
};
|
||||
|
||||
if is_valid {
|
||||
match end {
|
||||
RuleEnd::Reject => return false,
|
||||
RuleEnd::Accept => return true,
|
||||
RuleEnd::Next(new_pos) => {
|
||||
pos = new_pos;
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match rule.end {
|
||||
RuleEnd::Reject => return false,
|
||||
RuleEnd::Accept => return true,
|
||||
RuleEnd::Next(new_pos) => {
|
||||
pos = new_pos;
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<u8> for Item {
|
||||
type Output = u16;
|
||||
|
||||
fn index(&self, index: u8) -> &Self::Output {
|
||||
static FALLBACK: u16 = 0;
|
||||
|
||||
match index {
|
||||
b'x' => &self.0[0],
|
||||
b'm' => &self.0[1],
|
||||
b'a' => &self.0[2],
|
||||
b's' => &self.0[3],
|
||||
other => {
|
||||
debug_assert!(false, "Invalid index: {}", other as char);
|
||||
&FALLBACK
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_item(i: &[u8]) -> IResult<&[u8], Item> {
|
||||
use nom::character::complete::u16;
|
||||
map(
|
||||
tuple((
|
||||
preceded(tag("{x="), u16),
|
||||
preceded(tag(",m="), u16),
|
||||
preceded(tag(",a="), u16),
|
||||
delimited(tag(",s="), u16, tag("}\n")),
|
||||
)),
|
||||
|(x, m, a, s)| Item([x, m, a, s]),
|
||||
)(i)
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone)]
|
||||
enum RuleEnd {
|
||||
#[default]
|
||||
Reject,
|
||||
Accept,
|
||||
Next(u16),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Condition {
|
||||
Greater(u8, u16),
|
||||
Less(u8, u16),
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct Rule {
|
||||
checks: Vec<(Condition, RuleEnd)>,
|
||||
end: RuleEnd,
|
||||
}
|
||||
|
||||
// Base 26 at it again, but now in lowercase
|
||||
fn convert_name(name: &[u8]) -> u16 {
|
||||
name.iter()
|
||||
.fold(0, |cur, &c| cur * 26 + u16::from(c - b'a'))
|
||||
}
|
||||
|
||||
fn parse_rule(i: &[u8]) -> IResult<&[u8], (u16, Rule)> {
|
||||
fn convert_end(i: &[u8]) -> RuleEnd {
|
||||
match i {
|
||||
b"A" => RuleEnd::Accept,
|
||||
b"R" => RuleEnd::Reject,
|
||||
other => RuleEnd::Next(convert_name(other)),
|
||||
}
|
||||
}
|
||||
|
||||
let (i, name) = map(take_while1(|c: u8| c.is_ascii_alphabetic()), convert_name)(i)?;
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, checks) = many1(terminated(
|
||||
map(
|
||||
tuple((
|
||||
take::<_, &[u8], _>(1usize),
|
||||
alt((tag("<"), tag(">"))),
|
||||
nom::character::complete::u16,
|
||||
preceded(tag(":"), take_until1(",")),
|
||||
)),
|
||||
|(index, cmp, val, dest)| {
|
||||
let condition = if cmp[0] == b'<' {
|
||||
Condition::Less(index[0], val)
|
||||
} else {
|
||||
debug_assert_eq!(cmp[0], b'>');
|
||||
Condition::Greater(index[0], val)
|
||||
};
|
||||
|
||||
(condition, convert_end(dest))
|
||||
},
|
||||
),
|
||||
tag(","),
|
||||
))(i)?;
|
||||
|
||||
let (i, end) = map(take_until1("}\n"), convert_end)(i)?;
|
||||
Ok((&i[2..], (name, Rule { checks, end })))
|
||||
}
|
||||
|
||||
fn parse_text(i: &[u8]) -> IResult<&[u8], (Box<[Rule]>, Vec<Item>)> {
|
||||
separated_pair(
|
||||
fold_many1(
|
||||
parse_rule,
|
||||
|| vec![Rule::default(); RULES_LEN].into_boxed_slice(),
|
||||
|mut rules, (index, rule)| {
|
||||
rules[index as usize] = rule;
|
||||
rules
|
||||
},
|
||||
),
|
||||
tag("\n"),
|
||||
many1(parse_item),
|
||||
)(i)
|
||||
}
|
||||
|
||||
pub fn part1(input: &[u8]) -> anyhow::Result<String> {
|
||||
let (rules, items) = parse_input(input, parse_text)?;
|
||||
|
||||
let passing = items
|
||||
.iter()
|
||||
.filter_map(|c| c.passes(&rules).then(|| c.rating_sum()))
|
||||
.sum::<u32>();
|
||||
|
||||
Ok(passing.to_string())
|
||||
}
|
||||
|
||||
pub fn part2(_input: &[u8]) -> anyhow::Result<String> {
|
||||
anyhow::bail!("Not implemented")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SAMPLE: &[u8] = include_bytes!("samples/19.txt");
|
||||
|
||||
#[test]
|
||||
fn sample_part1() {
|
||||
assert_eq!("19114", part1(SAMPLE).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
17
2023/src/samples/19.txt
Normal file
17
2023/src/samples/19.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
px{a<2006:qkq,m>2090:A,rfg}
|
||||
pv{a>1716:R,A}
|
||||
lnx{m>1548:A,A}
|
||||
rfg{s<537:gd,x>2440:R,A}
|
||||
qs{s>3448:A,lnx}
|
||||
qkq{x<1416:A,crn}
|
||||
crn{x>2662:A,R}
|
||||
in{s<1351:px,qqz}
|
||||
qqz{s>2770:qs,m<1801:hdj,R}
|
||||
gd{a>3333:R,R}
|
||||
hdj{m>838:A,pv}
|
||||
|
||||
{x=787,m=2655,a=1222,s=2876}
|
||||
{x=1679,m=44,a=2067,s=496}
|
||||
{x=2036,m=264,a=79,s=2244}
|
||||
{x=2461,m=1339,a=466,s=291}
|
||||
{x=2127,m=1623,a=2188,s=1013}
|
||||
Reference in New Issue
Block a user