mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +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> {
|
use std::ops::Index;
|
||||||
anyhow::bail!("Not implemented")
|
|
||||||
|
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> {
|
pub fn part2(_input: &[u8]) -> anyhow::Result<String> {
|
||||||
anyhow::bail!("Not implemented")
|
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