Update day05 solution
This commit is contained in:
parent
f18d194ea8
commit
84c023b1dc
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue