You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
2.9 KiB
Rust
155 lines
2.9 KiB
Rust
static INPUT: &str = include_str!("inputs/day5.txt");
|
|
|
|
type CrateID = char;
|
|
#[derive(Debug)]
|
|
struct Stacks(Vec<Vec<CrateID>>);
|
|
impl Stacks {
|
|
fn apply_move_part_1(&mut self, mv: &Move) {
|
|
let from = mv.from - 1;
|
|
let to = mv.to - 1;
|
|
|
|
for _ in 0..mv.count {
|
|
let c = self.0[from].pop().unwrap();
|
|
|
|
self.0[to].push(c)
|
|
}
|
|
}
|
|
|
|
fn apply_move_part_2(&mut self, mv: &Move) {
|
|
let from = mv.from - 1;
|
|
let to = mv.to - 1;
|
|
|
|
// SAFETY: should be safe, borrowing from disjoint sub-vectors and
|
|
// not modifying the length of the primary vector. Technically it
|
|
// is possible that this could alias if to and from are the same,
|
|
// but even if that happened this should be safe (although the
|
|
// answer given would be incorrect)
|
|
unsafe {
|
|
let from: &'static mut Vec<_> =
|
|
core::mem::transmute(&mut self.0[from]);
|
|
|
|
let new_len = from.len() - mv.count;
|
|
let picked_up = &from[new_len..];
|
|
self.0[to].extend_from_slice(picked_up);
|
|
from.truncate(new_len);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parse_stacks(s: &str) -> Stacks {
|
|
let mut lines = s.split('\n').filter(|s| !s.is_empty()).map(|line| {
|
|
let mut chars = line.chars();
|
|
|
|
core::iter::from_fn(move || {
|
|
match chars.next() {
|
|
None => return None,
|
|
Some(_) => (),
|
|
};
|
|
|
|
let id = chars.next().unwrap();
|
|
chars.next();
|
|
chars.next();
|
|
|
|
if id == ' ' {
|
|
return Some(None);
|
|
}
|
|
|
|
Some(Some(id))
|
|
})
|
|
});
|
|
|
|
|
|
let mut crates = Vec::new();
|
|
let first_line = lines.next().unwrap();
|
|
for id in first_line {
|
|
let mut stack = Vec::new();
|
|
if let Some(id) = id {
|
|
stack.push(id);
|
|
}
|
|
|
|
crates.push(stack);
|
|
}
|
|
|
|
for ids in lines {
|
|
for (idx, id) in ids.enumerate() {
|
|
if let Some(id) = id {
|
|
crates[idx].push(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
for stack in &mut crates {
|
|
stack.pop();
|
|
stack.reverse();
|
|
}
|
|
|
|
Stacks(crates)
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Move {
|
|
count: usize,
|
|
from: usize,
|
|
to: usize,
|
|
}
|
|
fn parse_moves(s: &str) -> impl Iterator<Item = Move> + '_ {
|
|
s.split('\n').filter(|s| !s.is_empty()).map(|line| {
|
|
let mut words = line.split(' ');
|
|
let count = {
|
|
words.next();
|
|
words.next().unwrap()
|
|
};
|
|
let from = {
|
|
words.next();
|
|
words.next().unwrap()
|
|
};
|
|
let to = {
|
|
words.next();
|
|
words.next().unwrap()
|
|
};
|
|
|
|
Move {
|
|
count: count.parse().unwrap(),
|
|
from: from.parse().unwrap(),
|
|
to: to.parse().unwrap(),
|
|
}
|
|
})
|
|
}
|
|
|
|
fn parse(s: &str) -> (Stacks, impl Iterator<Item = Move> + '_) {
|
|
let mut parts = s.split("\n\n");
|
|
let crates_str = parts.next().unwrap();
|
|
let stacks = parse_stacks(crates_str);
|
|
|
|
let moves_str = parts.next().unwrap();
|
|
let moves = parse_moves(moves_str);
|
|
|
|
(stacks, moves)
|
|
}
|
|
|
|
pub fn part1() {
|
|
let (mut stacks, moves) = parse(INPUT);
|
|
|
|
for mv in moves {
|
|
stacks.apply_move_part_1(&mv);
|
|
}
|
|
|
|
for stack in &stacks.0 {
|
|
print!("{}", stack.last().unwrap());
|
|
}
|
|
println!();
|
|
}
|
|
|
|
pub fn part2() {
|
|
let (mut stacks, moves) = parse(INPUT);
|
|
|
|
for mv in moves {
|
|
stacks.apply_move_part_2(&mv);
|
|
}
|
|
|
|
for stack in &stacks.0 {
|
|
print!("{}", stack.last().unwrap());
|
|
}
|
|
println!();
|
|
}
|