198 lines
6.4 KiB
Rust
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);
|
||
|
}
|