advent/2022/src/day5.rs

114 lines
2.8 KiB
Rust

use core::num::ParseIntError;
const DATA: &'static str = include_str!("../data/day5.txt");
// const DATA: &'static 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
// ";
enum BadLine {
NotEnoughWords,
ParseError(ParseIntError),
}
#[derive(Debug)]
struct State {
stacks: [Vec<char>; 9],
}
impl State {
fn process(&mut self, op: &Instruction) {
for _ in 0..(op.count) {
let item = self.stacks[op.src as usize - 1].pop().unwrap();
self.stacks[op.dst as usize - 1].push(item);
}
}
fn process_inorder(&mut self, op: &Instruction) {
let src_stack = &mut self.stacks[op.src as usize - 1];
let split_idx = src_stack.len() - op.count as usize;
let mut substack = src_stack.split_off(split_idx);
self.stacks[op.dst as usize - 1].append(&mut substack);
}
fn get_tops(&self) -> String {
let mut res = String::with_capacity(9);
for stack in &self.stacks {
res.push(*stack.last().unwrap());
}
res
}
}
#[derive(Debug)]
struct Instruction {
count: u32,
src: u32,
dst: u32,
}
fn parse_map(map: &str) -> State {
let rows: Vec<&str> = map.lines().collect();
let mut stacks: [Vec<char>; 9] = Default::default();
// skip the first row, because it's just the stack numbers
for row in rows.iter().rev().skip(1) {
for (col_num, c) in row.chars().enumerate() {
if c.is_ascii_uppercase() {
let stack_idx = (col_num - 1) / 4;
stacks[stack_idx].push(c);
}
}
}
State { stacks }
}
fn parse_instruction(data: &str) -> Result<Instruction, BadLine> {
let mut words = data.split(' ');
Ok(Instruction {
count: words.nth(1).ok_or(BadLine::NotEnoughWords)?.parse().map_err(|e| BadLine::ParseError(e))?,
src: words.nth(1).ok_or(BadLine::NotEnoughWords)?.parse().map_err(|e| BadLine::ParseError(e))?,
dst: words.nth(1).ok_or(BadLine::NotEnoughWords)?.parse().map_err(|e| BadLine::ParseError(e))?,
})
}
fn main() -> Result<(), String> {
let mut splat = DATA.trim_end().split("\n\n");
let map = splat.next().unwrap();
let instruction_lines = splat.next().unwrap();
let mut state = parse_map(map);
let instructions: Vec<Instruction> = instruction_lines.lines()
.map(|line| parse_instruction(line).map_err(|_e| format!("Bad line: {line}")))
.collect::<Result<Vec<Instruction>, String>>()?;
for i in &instructions {
state.process(i);
}
println!("Part 1: {}", state.get_tops());
let mut state2 = parse_map(map);
for i in &instructions {
state2.process_inorder(i);
}
println!("Part 2: {}", state2.get_tops());
Ok(())
}