138 lines
3.7 KiB
Rust
138 lines
3.7 KiB
Rust
use nom::branch::alt;
|
|
use nom::bytes::complete::tag;
|
|
use nom::character::complete;
|
|
use nom::character::complete::{alpha1, newline};
|
|
use nom::multi::{many1, separated_list1};
|
|
use nom::sequence::delimited;
|
|
use nom::IResult;
|
|
|
|
pub fn process_part_1(input: &str) -> String {
|
|
let (_, (mut stacks, _, moves)) = parse_crates(input).unwrap();
|
|
|
|
for m in moves {
|
|
for _ in 0..m.count {
|
|
let c = stacks[(m.from - 1) as usize]
|
|
.pop()
|
|
.expect("expected a crate");
|
|
stacks[(m.to - 1) as usize].push(c);
|
|
}
|
|
}
|
|
|
|
stacks
|
|
.iter_mut()
|
|
.map(|stack| stack.pop().unwrap_or(""))
|
|
.collect::<Vec<&str>>()
|
|
.join("")
|
|
}
|
|
|
|
pub fn process_part_2(input: &str) -> String {
|
|
let (_, (mut stacks, _, moves)) = parse_crates(input).unwrap();
|
|
|
|
for m in moves {
|
|
let from_stack = &mut stacks[(m.from - 1) as usize];
|
|
let mut boxes: Vec<&str> = from_stack
|
|
.drain((from_stack.len() - m.count as usize)..)
|
|
.collect();
|
|
stacks[(m.to - 1) as usize].append(&mut boxes);
|
|
}
|
|
|
|
stacks
|
|
.iter_mut()
|
|
.map(|stack| stack.pop().unwrap_or(""))
|
|
.collect::<Vec<&str>>()
|
|
.join("")
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Move {
|
|
count: u32,
|
|
from: u32,
|
|
to: u32,
|
|
}
|
|
|
|
fn parse_crates(input: &str) -> IResult<&str, (Vec<Vec<&str>>, Vec<u32>, Vec<Move>)> {
|
|
let (input, crates_transposed) = parse_crate_stacks(input)?;
|
|
let (input, _) = newline(input)?;
|
|
let (input, crate_numbers) = separated_list1(tag(" "), parse_crate_name)(input)?;
|
|
let (input, _) = many1(newline)(input)?; // newlines be gone
|
|
|
|
// [
|
|
// [ A, B, C ],
|
|
// [ D, E, _ ],
|
|
// ] => [ [D, A], [E, B], [C]
|
|
// (its transposed for pop() and push() magic :))
|
|
// it also unwraps all `None`'s from the lists
|
|
let crates: Vec<Vec<&str>> = (0..crates_transposed[0].len())
|
|
.map(|i| {
|
|
crates_transposed
|
|
.iter()
|
|
.map(|items| items[i])
|
|
.rev()
|
|
.filter_map(|item| item)
|
|
.collect()
|
|
})
|
|
.collect();
|
|
|
|
let (input, moves) = parse_moves(input)?;
|
|
Ok((input, (crates, crate_numbers, moves)))
|
|
}
|
|
|
|
fn parse_moves(input: &str) -> IResult<&str, Vec<Move>> {
|
|
separated_list1(newline, parse_move)(input)
|
|
}
|
|
|
|
fn parse_move(input: &str) -> IResult<&str, Move> {
|
|
let (input, _) = tag("move ")(input)?;
|
|
let (input, count) = complete::u32(input)?;
|
|
let (input, _) = tag(" from ")(input)?;
|
|
let (input, from) = complete::u32(input)?;
|
|
let (input, _) = tag(" to ")(input)?;
|
|
let (input, to) = complete::u32(input)?;
|
|
Ok((input, Move { count, from, to }))
|
|
}
|
|
|
|
fn parse_crate_name(input: &str) -> IResult<&str, u32> {
|
|
delimited(tag(" "), complete::u32, tag(" "))(input)
|
|
}
|
|
|
|
fn parse_crate_stacks(input: &str) -> IResult<&str, Vec<Vec<Option<&str>>>> {
|
|
separated_list1(newline, parse_crate_line)(input)
|
|
}
|
|
|
|
fn parse_crate_line(input: &str) -> IResult<&str, Vec<Option<&str>>> {
|
|
separated_list1(tag(" "), parse_crate_single)(input)
|
|
}
|
|
|
|
fn parse_crate_single(input: &str) -> IResult<&str, Option<&str>> {
|
|
let (input, maybe_crate) = alt((tag(" "), delimited(tag("["), alpha1, tag("]"))))(input)?;
|
|
match maybe_crate {
|
|
" " => Ok((input, None)),
|
|
_ => Ok((input, Some(maybe_crate))),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
const INPUT: &str = " [D]
|
|
[N] [C]
|
|
[Z] [M] [P]
|
|
1 2 3
|
|
|
|
move 1 from 2 to 1
|
|
move 3 from 1 to 3
|
|
move 2 from 2 to 1
|
|
move 1 from 1 to 2";
|
|
|
|
#[test]
|
|
fn day1() {
|
|
assert_eq!(process_part_1(INPUT), "CMZ");
|
|
}
|
|
|
|
#[test]
|
|
fn day2() {
|
|
assert_eq!(process_part_2(INPUT), "MCD");
|
|
}
|
|
}
|