From ae922f8292abe24203994a49cd9a651fb65ad27d Mon Sep 17 00:00:00 2001 From: Dylan Thies Date: Fri, 1 Sep 2023 20:09:54 -0400 Subject: [PATCH] Added testing to day 5 and rewrote to use nom --- Cargo.lock | 3 + day5/Cargo.toml | 1 + day5/src/main.rs | 229 +++++++++++++++++++++++++++++------------------ 3 files changed, 146 insertions(+), 87 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0e19a1..f5e9215 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,6 +72,9 @@ dependencies = [ [[package]] name = "day5" version = "2022.0.0" +dependencies = [ + "nom", +] [[package]] name = "day6" diff --git a/day5/Cargo.toml b/day5/Cargo.toml index ddbf17c..b93cf5e 100644 --- a/day5/Cargo.toml +++ b/day5/Cargo.toml @@ -8,3 +8,4 @@ repository.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +nom.workspace = true diff --git a/day5/src/main.rs b/day5/src/main.rs index 39abdda..ba01e50 100644 --- a/day5/src/main.rs +++ b/day5/src/main.rs @@ -1,40 +1,77 @@ #![warn(clippy::all, clippy::pedantic)] -use std::fs::File; -use std::io::{prelude::*, BufReader}; +use std::fs; -#[derive(Clone, Debug)] -struct Crate { - label: String, +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, } -#[derive(Debug)] -struct GameBoard { +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, - board: Vec>, + board: Vec>>, } -impl GameBoard { - fn _game1_move(&mut self, count: usize, from: usize, to: usize) { +impl<'a> GameBoard<'a> { + pub fn game1_move(&mut self, m: &GameMove) { let v = &mut Vec::new(); - let work = self.board.get_mut(from - 1).unwrap(); - for _ in 0..count { + 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(to - 1).unwrap(); - for _ in 0..count { + let work = self.board.get_mut(m.to - 1).unwrap(); + for _ in 0..m.quantity { work.append(v); } } - fn game2_move(&mut self, count: usize, from: usize, to: usize) { + pub fn game2_move(&mut self, m: &GameMove) { let v = &mut Vec::new(); - let work = self.board.get_mut(from - 1).unwrap(); - for _ in 0..count { + 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(to - 1).unwrap(); - for _ in 0..count { + let work = self.board.get_mut(m.to - 1).unwrap(); + for _ in 0..m.quantity { work.append(v); } } @@ -42,81 +79,99 @@ impl GameBoard { self.board .iter() .map(|x| x.last().unwrap().label.clone()) - .fold(String::new(), |acc, x| acc + &x) - } -} -impl From> for GameBoard { - fn from(v: Vec) -> Self { - let mut board_vec = v.clone(); - let label_vec = board_vec.pop().unwrap(); - // get labels - let labels = label_vec - .split_whitespace() - .map(std::string::ToString::to_string) - .collect::>(); - //TODO sscanf for crates - board_vec.reverse(); - let mut board = vec![Vec::new(); labels.len()]; - for line in &board_vec { - board.iter_mut().enumerate().for_each(|(i, col)| { - let (begin, end) = (i * 4, std::cmp::min(i * 4 + 4, line.len())); - let crate_str = line[begin..end] - .to_string() - .matches(char::is_alphabetic) - .collect::(); - if !crate_str.is_empty() { - col.push(Crate { label: crate_str }); - } - }); - } - GameBoard { - _labels: labels, - board, - } + .fold(String::new(), |acc, x| acc + x) } } -fn main() -> std::io::Result<()> { +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)> { + 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 = File::open("input")?; - let reader = BufReader::new(file); + let file = fs::read_to_string("input").unwrap(); //read in the parts of the file - let (board_lines, movement_lines, _) = reader.lines().fold( - (Vec::new(), Vec::new(), false), - |mut acc: (Vec, Vec, bool), line| { - let line = line.unwrap(); - if line.is_empty() { - acc.2 = true; - } else if acc.2 { - acc.1.push(line); - } else { - acc.0.push(line); - } - acc - }, - ); - println!("{board_lines:?}"); - let mut board = GameBoard::from(board_lines); - for line in &movement_lines { - match line - .split_whitespace() - .filter_map(|x| x.parse::().ok()) - .collect::>()[..] - { - [count, from, to] => board.game2_move(count, from, to), - _ => panic!( - "{:#?} {:?}", - board, - line.matches(char::is_numeric) - .map(|x| x.parse::().unwrap()) - .collect::>() - ), - }; + 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"); } - println!("{}", board.get_tops()); - - Ok(()) + #[test] + fn part2_works() { + assert_eq!(part2(INPUT), "MCD"); + } }