Update day05 solution

This commit is contained in:
Malte Tammena 2022-12-05 22:31:03 +01:00
parent f18d194ea8
commit 84c023b1dc

View file

@ -1,20 +1,9 @@
use std::{
fs::File,
io::{BufRead, BufReader},
str::FromStr,
};
#[derive(Debug)]
struct Port {
containers: Vec<Vec<u8>>,
}
#[derive(Debug, Clone, Copy)]
struct Move {
count: usize,
from: usize,
to: usize,
}
#[derive(Debug, Default, PartialEq, Eq)]
enum SolvePuzzle {
First,
@ -22,42 +11,82 @@ enum SolvePuzzle {
Second,
}
/// The port with all containers
#[derive(Debug)]
struct Port {
columns: Vec<Vec<u8>>,
}
/// A number of moves that will eventually be executed by the CrateMover ~~9000~~ 9001.
#[derive(Debug, Clone, Copy)]
struct Move {
/// Number of crates to move
count: usize,
/// Container column to pick from
from: usize,
/// Container column to move to
to: usize,
}
impl Port {
/// Parse the port from a list of lines, like:
/// ```text
/// [D]
/// [N] [C]
/// [Z] [M] [P]
/// 1 2 3
/// ```
fn parse_from_lines<I, S>(lines: &mut I) -> Result<Self, ()>
where
I: Iterator<Item = S>,
S: AsRef<str>,
{
// Lines relevant to us
let mut relevant: Vec<_> = lines.take_while(|line| !line.as_ref().is_empty()).collect();
// The line containing the container column numbering, irrelevant
let _number_line = relevant.pop().unwrap();
let mut containers = vec![];
// Columns of containers for our port
let mut columns = vec![];
relevant.iter().rev().for_each(|line| {
// The current line as bytes
let bytes = line.as_ref().as_bytes();
let mut container_idx = 0;
while 4 * container_idx + 1 < bytes.len() {
if containers.len() <= container_idx {
containers.push(vec![])
// We will abuse the accurate formatting, picking letters from their correct position
// This is the index for the current column
let mut column_idx = 0;
// Iterate over the line as long as possible
while 4 * column_idx + 1 < bytes.len() {
// If the column does not exist yet, push it
// This abuses our counting method, but works!
if columns.len() <= column_idx {
columns.push(vec![])
}
let container = bytes[4 * container_idx + 1];
// Grab the container!
let container = bytes[4 * column_idx + 1];
// If the container is empty, skip it, otherwise add it to the correct column
if container != b' ' {
containers[container_idx].push(container)
columns[column_idx].push(container)
}
container_idx += 1;
// Move to the next column
column_idx += 1;
}
});
Ok(Self { containers })
Ok(Self { columns })
}
/// Apply the given move to the port.
/// `reverse_on_move` will cause the picked stack of containers to be turned upside down before placement.
/// See list of differences between *CrateMove 9000* and *CrateMove 9001*.
fn apply(&mut self, mv: Move, reverse_on_move: bool) {
let column = &mut self.containers[mv.from];
let column = &mut self.columns[mv.from];
let mut moved = column.split_off(column.len() - mv.count);
if reverse_on_move {
moved.reverse();
}
self.containers[mv.to].extend(moved);
self.columns[mv.to].extend(moved);
}
/// Read the letters on the top row of the container stacks.
fn to_top_row_string(self) -> String {
let bytes: Vec<u8> = self
.containers
.columns
.into_iter()
.map(|mut column| column.pop())
.map(Option::unwrap)
@ -66,17 +95,6 @@ impl Port {
}
}
fn parse_instruction_line<S: AsRef<str>>(line: S) -> Move {
let mut words = line.as_ref().split_ascii_whitespace();
let _move = words.next().unwrap();
let count = words.next().unwrap().parse().unwrap();
let _from = words.next().unwrap();
let from = words.next().unwrap().parse::<usize>().unwrap() - 1;
let _to = words.next().unwrap();
let to = words.next().unwrap().parse::<usize>().unwrap() - 1;
Move { from, to, count }
}
fn solve_puzzle<R>(reader: R, solve: SolvePuzzle) -> String
where
R: BufRead,
@ -84,7 +102,8 @@ where
let mut lines = reader.lines().map(Result::unwrap);
let mut port = Port::parse_from_lines(&mut lines).unwrap();
lines
.map(parse_instruction_line)
.map(|line| line.parse())
.map(Result::unwrap)
.for_each(|mv| port.apply(mv, solve == SolvePuzzle::First));
port.to_top_row_string()
}
@ -104,6 +123,28 @@ fn main() {
println!("{}", solve_puzzle(file, solve));
}
impl FromStr for Move {
type Err = ::std::convert::Infallible;
/// # Example
/// ```text
/// move 1 from 2 to 1
/// move 3 from 1 to 3
/// move 2 from 2 to 1
/// move 1 from 1 to 2
/// ```
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut words = s.split_ascii_whitespace();
let _move = words.next().unwrap();
let count = words.next().unwrap().parse().unwrap();
let _from = words.next().unwrap();
let from = words.next().unwrap().parse::<usize>().unwrap() - 1;
let _to = words.next().unwrap();
let to = words.next().unwrap().parse::<usize>().unwrap() - 1;
Ok(Self { from, to, count })
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;