aoc2022/d22/part2.rs

198 lines
6.4 KiB
Rust

use std::iter::repeat;
type Pos = (isize, isize);
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
struct Instruction {
dist: Option<isize>,
rotation: Option<isize>,
}
fn rotate(facing: Pos, by: isize) -> Pos {
match by.rem_euclid(4) {
0 => facing,
1 => (-facing.1, facing.0),
2 => (-facing.0, -facing.1),
3 => (facing.1, -facing.0),
_ => unreachable!(),
}
}
fn facing_angle_to_axes(facing: isize) -> Pos {
match facing.rem_euclid(4) {
0 => (1, 0),
1 => (0, 1),
2 => (-1, 0),
3 => (0, -1),
_ => unreachable!(),
}
}
fn next_pos_facing(
pos: Pos,
facing: Pos,
map: &Vec<Vec<u8>>,
edges: &Vec<(Vec<Pos>, Vec<Pos>, isize, isize)>,
) -> (Pos, Pos) {
let mut next_pos = (pos.0 + facing.0, pos.1 + facing.1);
let mut next_facing = facing;
for edge in edges.iter() {
if let Some(i) = edge.0.iter().position(|p| *p == pos) {
if facing_angle_to_axes(edge.2) == facing {
(next_pos, next_facing) = (edge.1[i], rotate(facing, (edge.3 + 2) - edge.2));
#[cfg(debug_assertions)]
println!(
"cross edge: {} {} ({} {}) -> {} {} ({} {})",
pos.0,
pos.1,
facing.0,
facing.1,
next_pos.0,
next_pos.1,
next_facing.0,
next_facing.1
);
}
} else if let Some(i) = edge.1.iter().position(|p| *p == pos) {
if facing_angle_to_axes(edge.3) == facing {
(next_pos, next_facing) = (edge.0[i], rotate(facing, (edge.2 + 2) - edge.3));
#[cfg(debug_assertions)]
println!(
"cross edge: {} {} ({} {}) -> {} {} ({} {})",
pos.0,
pos.1,
facing.0,
facing.1,
next_pos.0,
next_pos.1,
next_facing.0,
next_facing.1
);
}
}
}
(next_pos, next_facing)
}
pub fn main() {
let s = include_bytes!("./input");
let mut pos = (s.iter().position(|b| *b == b'.').unwrap() as isize, 0);
let mut facing: (isize, isize) = (1, 0);
let w = s
.split(|b| *b == b'\n')
.take_while(|l| !l.is_empty())
.map(|l| l.len())
.max()
.unwrap();
let map = s
.split(|b| *b == b'\n')
.take_while(|l| !l.is_empty())
.map(|l| {
l.iter()
.take(l.len())
.chain(repeat(&b' ').take(w - l.len()))
.copied()
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let l = 50isize;
#[rustfmt::skip]
let edges: Vec<(Vec<Pos>, Vec<Pos>, isize, isize)> = vec![
((0..l).zip(repeat(l*2).take(l as usize)).collect(), repeat(l).take(l as usize).zip(l..l*2).collect(), 3, 2),
(repeat(0).take(l as usize).zip(l*2..l*3).collect(), repeat(l).take(l as usize).zip((0..l).rev()).collect(), 2, 2),
(repeat(0).take(l as usize).zip(l*3..l*4).collect(), (l..l*2).zip(repeat(0).take(l as usize)).collect(), 2, 3),
((0..l).zip(repeat(l*4-1).take(l as usize)).collect(), (l*2..l*3).zip(repeat(0).take(l as usize)).collect(), 1, 3),
(repeat(l-1).take(l as usize).zip(l*3..l*4).collect(), (l..l*2).zip(repeat(l*3-1).take(l as usize)).collect(), 0, 1),
(repeat(l*2-1).take(l as usize).zip(l*2..l*3).collect(), repeat(l*3-1).take(l as usize).zip((0..l).rev()).collect(), 0, 0),
(repeat(l*2-1).take(l as usize).zip(l..l*2).collect(), (l*2..l*3).zip(repeat(l-1).take(l as usize)).collect(), 0, 1),
];
let mut path: Vec<Instruction> = Vec::new();
let mut acc = 0;
s.split(|b| *b == b'\n')
.rev()
.nth(1)
.unwrap()
.iter()
.for_each(|b| match b {
b'0'..=b'9' => {
acc = acc * 10 + (b - b'0') as isize;
}
b'R' => {
path.push(Instruction {
dist: Some(acc),
rotation: None,
});
path.push(Instruction {
dist: None,
rotation: Some(1),
});
acc = 0;
}
b'L' => {
path.push(Instruction {
dist: Some(acc),
rotation: None,
});
path.push(Instruction {
dist: None,
rotation: Some(-1),
});
acc = 0;
}
_ => unreachable!(),
});
path.push(Instruction {
dist: Some(acc),
rotation: None,
});
#[cfg(debug_assertions)]
println!("{} {}", map.len(), map[0].len());
#[cfg(debug_assertions)]
println!(
"pos: ({}, {}), facing ({} {})",
pos.0, pos.1, facing.0, facing.1
);
for ins in path.iter() {
#[cfg(debug_assertions)]
println!("{:?}", ins);
match (ins.dist, ins.rotation) {
(Some(dist), None) => {
for _step in 0..dist {
let (next_pos, next_facing) = next_pos_facing(pos, facing, &map, &edges);
match map[next_pos.1 as usize][next_pos.0 as usize] {
b'#' => {
break;
}
b'.' => {
(pos, facing) = (next_pos, next_facing);
}
_ => {
panic!("unexpected wall as next character");
}
}
}
}
(None, Some(rot)) => {
(facing.0, facing.1) = rotate((facing.0, facing.1), rot);
}
_ => unreachable!(),
}
#[cfg(debug_assertions)]
println!(
"pos: ({}, {}), facing ({} {})",
pos.0, pos.1, facing.0, facing.1
);
}
let facing_dir = facing.0.abs() * (1 - facing.0) + facing.1.abs() * (2 - facing.1);
#[cfg(debug_assertions)]
println!(
"pos: ({}, {}), facing ({} {}) = {}",
pos.0 + 1,
pos.1 + 1,
facing.0,
facing.1,
facing_dir
);
print!("{} ", 1000 * (pos.1 + 1) + 4 * (pos.0 + 1) + facing_dir);
}