Added testing to day 5 and rewrote to use nom

This commit is contained in:
Dylan Thies
2023-09-01 20:09:54 -04:00
parent 4f3bbb986d
commit ae922f8292
3 changed files with 146 additions and 87 deletions

3
Cargo.lock generated
View File

@@ -72,6 +72,9 @@ dependencies = [
[[package]] [[package]]
name = "day5" name = "day5"
version = "2022.0.0" version = "2022.0.0"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "day6" name = "day6"

View File

@@ -8,3 +8,4 @@ repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
nom.workspace = true

View File

@@ -1,40 +1,77 @@
#![warn(clippy::all, clippy::pedantic)] #![warn(clippy::all, clippy::pedantic)]
use std::fs::File; use std::fs;
use std::io::{prelude::*, BufReader};
#[derive(Clone, Debug)] use nom::branch::alt;
struct Crate { use nom::bytes::complete::tag;
label: String, 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)] impl<'a> Parser<&'a str, Self, Error<&'a str>> for Crate<'a> {
struct GameBoard { 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>, _labels: Vec<String>,
board: Vec<Vec<Crate>>, board: Vec<Vec<Crate<'a>>>,
} }
impl GameBoard { impl<'a> GameBoard<'a> {
fn _game1_move(&mut self, count: usize, from: usize, to: usize) { pub fn game1_move(&mut self, m: &GameMove) {
let v = &mut Vec::new(); let v = &mut Vec::new();
let work = self.board.get_mut(from - 1).unwrap(); let work = self.board.get_mut(m.from - 1).unwrap();
for _ in 0..count { for _ in 0..m.quantity {
v.push(work.pop().unwrap()); v.push(work.pop().unwrap());
} }
let work = self.board.get_mut(to - 1).unwrap(); let work = self.board.get_mut(m.to - 1).unwrap();
for _ in 0..count { for _ in 0..m.quantity {
work.append(v); 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 v = &mut Vec::new();
let work = self.board.get_mut(from - 1).unwrap(); let work = self.board.get_mut(m.from - 1).unwrap();
for _ in 0..count { for _ in 0..m.quantity {
v.push(work.pop().unwrap()); v.push(work.pop().unwrap());
} }
v.reverse(); v.reverse();
let work = self.board.get_mut(to - 1).unwrap(); let work = self.board.get_mut(m.to - 1).unwrap();
for _ in 0..count { for _ in 0..m.quantity {
work.append(v); work.append(v);
} }
} }
@@ -42,81 +79,99 @@ impl GameBoard {
self.board self.board
.iter() .iter()
.map(|x| x.last().unwrap().label.clone()) .map(|x| x.last().unwrap().label.clone())
.fold(String::new(), |acc, x| acc + &x) .fold(String::new(), |acc, x| acc + x)
}
}
impl From<Vec<String>> for GameBoard {
fn from(v: Vec<String>) -> 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::<Vec<String>>();
//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::<String>();
if !crate_str.is_empty() {
col.push(Crate { label: crate_str });
}
});
}
GameBoard {
_labels: labels,
board,
}
} }
} }
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<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 //Read in file
let file = File::open("input")?; let file = fs::read_to_string("input").unwrap();
let reader = BufReader::new(file);
//read in the parts of the file //read in the parts of the file
let (board_lines, movement_lines, _) = reader.lines().fold(
(Vec::new(), Vec::new(), false),
|mut acc: (Vec<String>, Vec<String>, 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 { println!("Part 1: {}", part1(&file));
match line println!("Part 2: {}", part2(&file));
.split_whitespace()
.filter_map(|x| x.parse::<usize>().ok())
.collect::<Vec<usize>>()[..]
{
[count, from, to] => board.game2_move(count, from, to),
_ => panic!(
"{:#?} {:?}",
board,
line.matches(char::is_numeric)
.map(|x| x.parse::<usize>().unwrap())
.collect::<Vec<usize>>()
),
};
} }
println!("{}", board.get_tops()); #[cfg(test)]
mod test {
use super::*;
const INPUT: &str = " [D]
[N] [C]
[Z] [M] [P]
1 2 3
Ok(()) 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");
}
} }