Files
AOC/2022/day5/src/main.rs
2023-12-12 08:58:38 -05:00

178 lines
4.7 KiB
Rust

#![warn(clippy::all, clippy::pedantic)]
use std::fs;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::{self, newline};
use nom::error::Error;
use nom::multi::separated_list1;
use nom::sequence::{delimited, preceded, separated_pair};
use nom::Parser;
#[derive(Clone, Debug, Default, Copy)]
struct Crate<'a> {
label: &'a str,
}
impl<'a> Parser<&'a str, Self, Error<&'a str>> for Crate<'a> {
fn parse(&mut self, input: &'a str) -> nom::IResult<&'a str, Self, Error<&'a str>> {
let (input, label) = delimited::<&str, _, &str, _, Error<&'a str>, _, _, _>(
tag("["),
complete::alpha1,
tag("]"),
)(input)?;
self.label = label;
Ok((input, *self))
}
}
#[derive(Clone, Debug, Default, Copy)]
struct GameMove {
quantity: usize,
from: usize,
to: usize,
}
impl<'a> Parser<&'a str, Self, Error<&'a str>> for GameMove {
fn parse(&mut self, input: &'a str) -> nom::IResult<&'a str, Self, Error<&'a str>> {
let (input, quantity) = preceded(tag("move "), complete::u8.map(|x| x as usize))(input)?;
let (input, from) = preceded(tag(" from "), complete::u8.map(|x| x as usize))(input)?;
let (input, to) = preceded(tag(" to "), complete::u8.map(|x| x as usize))(input)?;
self.quantity = quantity;
self.from = from;
self.to = to;
Ok((input, *self))
}
}
#[derive(Debug, Default)]
struct GameBoard<'a> {
_labels: Vec<String>,
board: Vec<Vec<Crate<'a>>>,
}
impl<'a> GameBoard<'a> {
pub fn game1_move(&mut self, m: &GameMove) {
let v = &mut Vec::new();
let work = self.board.get_mut(m.from - 1).unwrap();
for _ in 0..m.quantity {
v.push(work.pop().unwrap());
}
let work = self.board.get_mut(m.to - 1).unwrap();
for _ in 0..m.quantity {
work.append(v);
}
}
pub fn game2_move(&mut self, m: &GameMove) {
let v = &mut Vec::new();
let work = self.board.get_mut(m.from - 1).unwrap();
for _ in 0..m.quantity {
v.push(work.pop().unwrap());
}
v.reverse();
let work = self.board.get_mut(m.to - 1).unwrap();
for _ in 0..m.quantity {
work.append(v);
}
}
fn get_tops(&self) -> String {
self.board
.iter()
.map(|x| x.last().unwrap().label.clone())
.fold(String::new(), |acc, x| acc + x)
}
}
impl<'a> Parser<&'a str, Self, Error<&'a str>> for GameBoard<'a> {
fn parse(&mut self, input: &'a str) -> nom::IResult<&'a str, Self, Error<&'a str>> {
let (input, crates) = separated_list1(
newline,
separated_list1(
tag(" "),
alt((tag(" ").map(|_| None), Crate::default().map(Some))),
),
)(input)?;
let (input, _) = newline(input)?;
let (input, labels) = separated_list1(
tag(" "),
delimited(tag(" "), complete::u8.map(|x| x.to_string()), tag(" ")),
)(input)?;
let (input, _) = newline(input)?;
//self._labels = labels;
let mut board = vec![Vec::new(); crates[0].len()];
for cols in crates {
for (col, c) in cols.iter().enumerate() {
if c.is_none() {
continue;
}
board[col].push(c.unwrap());
}
}
board.iter_mut().for_each(|col| col.reverse());
self.board = board;
let b = GameBoard {
_labels: labels,
board: self.board.clone(),
};
Ok((input, b))
}
}
fn parse_input(input: &str) -> nom::IResult<&str, (GameBoard, Vec<GameMove>)> {
separated_pair(
GameBoard::default(),
newline,
separated_list1(newline, GameMove::default()),
)(input)
}
fn part1(input: &str) -> String {
let (_, (mut board, moves)) = parse_input(input).unwrap();
for m in moves {
board.game1_move(&m);
}
board.get_tops()
}
fn part2(input: &str) -> String {
let (_, (mut board, moves)) = parse_input(input).unwrap();
for m in moves {
board.game2_move(&m);
}
board.get_tops()
}
fn main() {
//Read in file
let file = fs::read_to_string("input").unwrap();
//read in the parts of the file
println!("Part 1: {}", part1(&file));
println!("Part 2: {}", part2(&file));
}
#[cfg(test)]
mod test {
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 part1_works() {
assert_eq!(part1(INPUT), "CMZ");
}
#[test]
fn part2_works() {
assert_eq!(part2(INPUT), "MCD");
}
}