day(22): finish it off…
Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
parent
f3b7ead65b
commit
13a0b3dbf7
1 changed files with 301 additions and 110 deletions
411
src/bin/day22.rs
411
src/bin/day22.rs
|
@ -5,11 +5,28 @@ use aoc_2022::*;
|
||||||
type Input = MonkeyMap;
|
type Input = MonkeyMap;
|
||||||
type Output = isize;
|
type Output = isize;
|
||||||
|
|
||||||
type Position = Vector2D<usize>;
|
type Position = Vector2D<isize>;
|
||||||
type Direction = Vector2D<isize>;
|
type Direction = Vector2D<isize>;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref DIRECTIONS: HashMap<Direction, usize> = HashMap::from([
|
||||||
|
(Direction::new(1, 0), 0),
|
||||||
|
(Direction::new(0, 1), 1),
|
||||||
|
(Direction::new(-1, 0), 2),
|
||||||
|
(Direction::new(0, -1), 3)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_left(d: Direction) -> Direction {
|
||||||
|
Direction::new(d.y(), -d.x())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_right(d: Direction) -> Direction {
|
||||||
|
Direction::new(-d.y(), d.x())
|
||||||
|
}
|
||||||
|
|
||||||
enum Instruction {
|
enum Instruction {
|
||||||
Move(usize),
|
Move(isize),
|
||||||
TurnLeft,
|
TurnLeft,
|
||||||
TurnRight,
|
TurnRight,
|
||||||
}
|
}
|
||||||
|
@ -19,143 +36,238 @@ enum Orientation {
|
||||||
Horizontal(usize),
|
Horizontal(usize),
|
||||||
Vertical(usize),
|
Vertical(usize),
|
||||||
}
|
}
|
||||||
|
impl Orientation {
|
||||||
|
fn horizontal(width: usize) -> Self {
|
||||||
|
Self::Horizontal(width)
|
||||||
|
}
|
||||||
|
fn vertical(height: usize) -> Self {
|
||||||
|
Self::Vertical(height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct MonkeyMap {
|
struct MonkeyMap {
|
||||||
map: Vec<Vec<char>>,
|
map: Vec<Vec<char>>,
|
||||||
boundaries: HashMap<Orientation, Range<usize>>,
|
boundaries: HashMap<Orientation, Range<isize>>,
|
||||||
instructions: Vec<Instruction>,
|
instructions: Vec<Instruction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrapping_step(position: usize, diff: isize, range: &Range<usize>) -> usize {
|
fn wrapping_step(position: isize, diff: isize, range: &Range<isize>) -> isize {
|
||||||
let (lower, upper) = (range.start, range.end);
|
let (lower, upper) = (range.start, range.end);
|
||||||
let range_size = (upper - lower) as isize;
|
let range_size = upper - lower;
|
||||||
|
|
||||||
(lower as isize + (position as isize + diff - lower as isize + range_size) % range_size)
|
lower + (position + diff - lower + range_size) % range_size
|
||||||
as usize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Wrap {
|
trait Wrap: Clone {
|
||||||
fn next(&self, state: &State<'_>) -> (Position, Direction);
|
type State;
|
||||||
|
|
||||||
|
// simulation
|
||||||
|
fn is_blocked(&self) -> bool;
|
||||||
|
fn step(&mut self, steps: isize);
|
||||||
|
fn turn_left(&mut self);
|
||||||
|
fn turn_right(&mut self);
|
||||||
|
|
||||||
|
// movement
|
||||||
|
fn next(&self) -> (Self::State, Direction);
|
||||||
|
|
||||||
|
// final answer
|
||||||
|
fn answer(&self) -> Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Wrap2D;
|
#[derive(Clone, Copy)]
|
||||||
impl Wrap for Wrap2D {
|
struct Wrap2D<'a> {
|
||||||
fn next(&self, state: &State<'_>) -> (Position, Direction) {
|
|
||||||
let h_bound = &state.input.boundaries[&Orientation::Horizontal(state.position.y())];
|
|
||||||
let v_bound = &state.input.boundaries[&Orientation::Vertical(state.position.x())];
|
|
||||||
|
|
||||||
(
|
|
||||||
Position::new(
|
|
||||||
wrapping_step(state.position.x(), state.direction.x(), h_bound),
|
|
||||||
wrapping_step(state.position.y(), state.direction.y(), v_bound),
|
|
||||||
),
|
|
||||||
state.direction,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Face {
|
|
||||||
horizontal_bound: Range<usize>,
|
|
||||||
vertical_bound: Range<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Wrap3D {
|
|
||||||
faces: Vec<Face>,
|
|
||||||
}
|
|
||||||
impl Wrap3D {
|
|
||||||
fn face(&self, position: Position) -> &Face {
|
|
||||||
let (x, y) = (position.x(), position.y());
|
|
||||||
|
|
||||||
for f in &self.faces {}
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Wrap for Wrap3D {
|
|
||||||
fn next(&self, state: &State<'_>) -> (Position, Direction) {
|
|
||||||
let face = self.face(state.position);
|
|
||||||
|
|
||||||
(
|
|
||||||
Position::new(
|
|
||||||
wrapping_step(
|
|
||||||
state.position.x(),
|
|
||||||
state.direction.x(),
|
|
||||||
&face.horizontal_bound,
|
|
||||||
),
|
|
||||||
wrapping_step(
|
|
||||||
state.position.y(),
|
|
||||||
state.direction.y(),
|
|
||||||
&face.vertical_bound,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
state.direction,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct State<'a> {
|
|
||||||
input: &'a Input,
|
input: &'a Input,
|
||||||
position: Position,
|
position: Position,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
}
|
}
|
||||||
|
impl<'a> Wrap2D<'a> {
|
||||||
impl<'a> State<'a> {
|
fn new(input: &'a Input) -> Wrap2D<'a> {
|
||||||
fn new(input: &'a Input) -> State<'a> {
|
|
||||||
Self {
|
Self {
|
||||||
input,
|
input,
|
||||||
position: Position::new(input.boundaries[&Orientation::Horizontal(0)].start, 0),
|
position: Position::new(input.boundaries[&Orientation::Horizontal(0)].start, 0),
|
||||||
direction: Direction::new(1, 0),
|
direction: Direction::new(1, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
impl Wrap for Wrap2D<'_> {
|
||||||
|
type State = Position;
|
||||||
|
|
||||||
fn is_blocked(&self, wrapper: &impl Wrap) -> bool {
|
// simulation
|
||||||
let (next_position, _) = wrapper.next(self);
|
fn is_blocked(&self) -> bool {
|
||||||
|
let (next_position, _) = self.next();
|
||||||
self.input.map[next_position] == '#'
|
self.input.map[next_position] == '#'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step(&mut self, wrapper: &impl Wrap, steps: usize) {
|
fn step(&mut self, steps: isize) {
|
||||||
for _ in 0..steps {
|
for _ in 0..steps {
|
||||||
if self.is_blocked(wrapper) {
|
if self.is_blocked() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(self.position, self.direction) = wrapper.next(self);
|
(self.position, self.direction) = self.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn turn_left(&mut self) {
|
fn turn_left(&mut self) {
|
||||||
let previous = self.direction;
|
self.direction = turn_left(self.direction);
|
||||||
self.direction = Direction::new(previous.y(), -previous.x());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn turn_right(&mut self) {
|
fn turn_right(&mut self) {
|
||||||
let previous = self.direction;
|
self.direction = turn_right(self.direction);
|
||||||
self.direction = Direction::new(-previous.y(), previous.x());
|
}
|
||||||
|
|
||||||
|
// movement
|
||||||
|
fn next(&self) -> (Self::State, Direction) {
|
||||||
|
let h_bound = &self.input.boundaries[&Orientation::Horizontal(self.position.y() as usize)];
|
||||||
|
let v_bound = &self.input.boundaries[&Orientation::Vertical(self.position.x() as usize)];
|
||||||
|
|
||||||
|
(
|
||||||
|
Position::new(
|
||||||
|
wrapping_step(self.position.x(), self.direction.x(), h_bound),
|
||||||
|
wrapping_step(self.position.y(), self.direction.y(), v_bound),
|
||||||
|
),
|
||||||
|
self.direction,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// final answer
|
||||||
|
fn answer(&self) -> Output {
|
||||||
|
1000 * (self.position.y() + 1)
|
||||||
|
+ 4 * (self.position.x() + 1)
|
||||||
|
+ DIRECTIONS[&self.direction] as isize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
#[derive(Clone)]
|
||||||
static ref DIRECTIONS: HashMap<(isize, isize), usize> =
|
struct Face {
|
||||||
HashMap::from([((1, 0), 0), ((0, 1), 1), ((-1, 0), 2), ((0, -1), 3)]);
|
horizontal_bound: Range<isize>,
|
||||||
|
vertical_bound: Range<isize>,
|
||||||
|
continuation: Vec<(usize, usize)>,
|
||||||
|
}
|
||||||
|
impl Face {
|
||||||
|
fn normalized(&self, p: Position) -> Position {
|
||||||
|
Position::new(
|
||||||
|
p.x() + self.horizontal_bound.start,
|
||||||
|
p.y() + self.vertical_bound.start,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solve(wrapper: &impl Wrap, input: &Input) -> Output {
|
#[derive(Debug, Clone)]
|
||||||
let final_state = input
|
struct State3D {
|
||||||
|
face: usize,
|
||||||
|
position: Position,
|
||||||
|
}
|
||||||
|
impl State3D {
|
||||||
|
fn global_position(&self, faces: &[Face]) -> Position {
|
||||||
|
faces[self.face].normalized(self.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Wrap3D<'a> {
|
||||||
|
input: &'a Input,
|
||||||
|
state: State3D,
|
||||||
|
direction: Direction,
|
||||||
|
faces: Vec<Face>,
|
||||||
|
}
|
||||||
|
impl<'a> Wrap3D<'a> {
|
||||||
|
fn new(configuration: Vec<Face>, input: &'a Input) -> Wrap3D<'a> {
|
||||||
|
Self {
|
||||||
|
input,
|
||||||
|
state: State3D {
|
||||||
|
face: 0,
|
||||||
|
position: Position::new(0, 0),
|
||||||
|
},
|
||||||
|
direction: Direction::new(1, 0),
|
||||||
|
faces: configuration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn face(&self) -> &Face {
|
||||||
|
&self.faces[self.state.face]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Wrap for Wrap3D<'_> {
|
||||||
|
type State = State3D;
|
||||||
|
|
||||||
|
// simulation
|
||||||
|
fn is_blocked(&self) -> bool {
|
||||||
|
let (next_state, _) = self.next();
|
||||||
|
self.input.map[next_state.global_position(&self.faces)] == '#'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&mut self, steps: isize) {
|
||||||
|
for _ in 0..steps {
|
||||||
|
if self.is_blocked() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(self.state, self.direction) = self.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_left(&mut self) {
|
||||||
|
self.direction = turn_left(self.direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_right(&mut self) {
|
||||||
|
self.direction = turn_right(self.direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// movement
|
||||||
|
fn next(&self) -> (Self::State, Direction) {
|
||||||
|
let side = self.faces[0].horizontal_bound.len() as isize;
|
||||||
|
let wraps =
|
||||||
|
|pos: Position| pos.x() < 0 || pos.x() >= side || pos.y() < 0 || pos.y() >= side;
|
||||||
|
|
||||||
|
let mut next_face = self.state.face;
|
||||||
|
let mut position = self.state.position + self.direction;
|
||||||
|
let mut direction = self.direction;
|
||||||
|
|
||||||
|
if wraps(position) {
|
||||||
|
let (face, rotations) = self.face().continuation[DIRECTIONS[&self.direction]];
|
||||||
|
|
||||||
|
next_face = face;
|
||||||
|
position = Position::new((position.x() + side) % side, (position.y() + side) % side);
|
||||||
|
|
||||||
|
for _ in 0..rotations {
|
||||||
|
position = Position::new(side - position.y() - 1, position.x());
|
||||||
|
direction = turn_right(direction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
State3D {
|
||||||
|
face: next_face,
|
||||||
|
position,
|
||||||
|
},
|
||||||
|
direction,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// final answer
|
||||||
|
fn answer(&self) -> Output {
|
||||||
|
1000 * (self.state.global_position(&self.faces).y() + 1)
|
||||||
|
+ 4 * (self.state.global_position(&self.faces).x() + 1)
|
||||||
|
+ DIRECTIONS[&self.direction] as isize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve(initial_state: impl Wrap, input: &Input) -> Output {
|
||||||
|
input
|
||||||
.instructions
|
.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.fold(State::new(input), |mut state, y| {
|
.fold(initial_state, |previous_state, y| {
|
||||||
|
let mut state = previous_state;
|
||||||
|
|
||||||
match &y {
|
match &y {
|
||||||
Instruction::Move(steps) => state.step(wrapper, *steps),
|
Instruction::Move(steps) => state.step(*steps),
|
||||||
Instruction::TurnLeft => state.turn_left(),
|
Instruction::TurnLeft => state.turn_left(),
|
||||||
Instruction::TurnRight => state.turn_right(),
|
Instruction::TurnRight => state.turn_right(),
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
state
|
||||||
});
|
})
|
||||||
|
.answer()
|
||||||
(1000 * (final_state.position.y() + 1)
|
|
||||||
+ 4 * (final_state.position.x() + 1)
|
|
||||||
+ DIRECTIONS[&(final_state.direction.x(), final_state.direction.y())])
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Day22;
|
struct Day22;
|
||||||
|
@ -169,27 +281,39 @@ impl Solution<Input, Output> for Day22 {
|
||||||
map.iter_mut().for_each(|l| l.resize(max_row_length, ' '));
|
map.iter_mut().for_each(|l| l.resize(max_row_length, ' '));
|
||||||
|
|
||||||
let mut boundaries = HashMap::new();
|
let mut boundaries = HashMap::new();
|
||||||
for row in 0..map.len() {
|
let mut find_boundaries = |constructor: fn(usize) -> Orientation,
|
||||||
let mut first_non_empty = map[row].iter().enumerate().skip_while(|&(_, &c)| c == ' ');
|
iterator: &mut dyn Iterator<Item = &char>,
|
||||||
let start = first_non_empty.next().unwrap().0;
|
upper_bound,
|
||||||
|
i| {
|
||||||
|
let mut first_non_empty = iterator.enumerate().skip_while(|&(_, &c)| c == ' ');
|
||||||
|
let start = first_non_empty.next().unwrap().0 as isize;
|
||||||
|
|
||||||
let mut last_non_empty = first_non_empty.skip_while(|&(_, &c)| c != ' ');
|
let mut last_non_empty = first_non_empty.skip_while(|&(_, &c)| c != ' ');
|
||||||
let end = last_non_empty.next().unwrap_or((map[row].len(), &'_')).0;
|
let end = last_non_empty.next().unwrap_or((upper_bound, &'_')).0 as isize;
|
||||||
|
|
||||||
boundaries.insert(Orientation::Horizontal(row), start..end);
|
boundaries.insert(constructor(i), start..end);
|
||||||
}
|
(constructor(i), start..end)
|
||||||
|
};
|
||||||
|
|
||||||
for col in 0..map[0].len() {
|
// construct all horizontal boundaries
|
||||||
let mut first_non_empty = ColumnIterator::new(&map, col)
|
(0..map.len()).for_each(|row| {
|
||||||
.enumerate()
|
find_boundaries(
|
||||||
.skip_while(|&(_, &c)| c == ' ');
|
Orientation::horizontal,
|
||||||
let start = first_non_empty.next().unwrap().0;
|
&mut map[row].iter(),
|
||||||
|
map[row].len(),
|
||||||
|
row,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
let mut last_non_empty = first_non_empty.skip_while(|&(_, &c)| c != ' ');
|
// construct all vertical boundaries
|
||||||
let end = last_non_empty.next().unwrap_or((map.len(), &'_')).0;
|
(0..map[0].len()).for_each(|col| {
|
||||||
|
find_boundaries(
|
||||||
boundaries.insert(Orientation::Vertical(col), start..end);
|
Orientation::vertical,
|
||||||
}
|
&mut ColumnIterator::new(&map, col),
|
||||||
|
map.len(),
|
||||||
|
col,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
let unparsed_instructions_str = instructions.trim_end();
|
let unparsed_instructions_str = instructions.trim_end();
|
||||||
|
|
||||||
|
@ -204,7 +328,7 @@ impl Solution<Input, Output> for Day22 {
|
||||||
.collect();
|
.collect();
|
||||||
if !steps.is_empty() {
|
if !steps.is_empty() {
|
||||||
// found a move instruction
|
// found a move instruction
|
||||||
instructions.push(Instruction::Move(steps.parse::<usize>().unwrap()));
|
instructions.push(Instruction::Move(steps.parse::<isize>().unwrap()));
|
||||||
skip += steps.len();
|
skip += steps.len();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -225,12 +349,79 @@ impl Solution<Input, Output> for Day22 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part_1(input: &Input) -> Output {
|
fn part_1(input: &Input) -> Output {
|
||||||
solve(&Wrap2D, input)
|
solve(Wrap2D::new(input), input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part_2(input: &Input) -> Output {
|
fn part_2(input: &Input) -> Output {
|
||||||
let wrapper = Wrap2D;
|
// sample
|
||||||
solve(&wrapper, input)
|
let config = vec![
|
||||||
|
Face {
|
||||||
|
horizontal_bound: 8..12,
|
||||||
|
vertical_bound: 0..4,
|
||||||
|
continuation: vec![(5, 2), (3, 0), (2, 1), (1, 2)],
|
||||||
|
},
|
||||||
|
Face {
|
||||||
|
horizontal_bound: 0..4,
|
||||||
|
vertical_bound: 4..8,
|
||||||
|
continuation: vec![(2, 0), (4, 2), (5, 3), (0, 2)],
|
||||||
|
},
|
||||||
|
Face {
|
||||||
|
horizontal_bound: 4..8,
|
||||||
|
vertical_bound: 4..8,
|
||||||
|
continuation: vec![(3, 0), (4, 3), (1, 0), (0, 1)],
|
||||||
|
},
|
||||||
|
Face {
|
||||||
|
horizontal_bound: 8..12,
|
||||||
|
vertical_bound: 4..8,
|
||||||
|
continuation: vec![(5, 1), (4, 0), (2, 0), (0, 0)],
|
||||||
|
},
|
||||||
|
Face {
|
||||||
|
horizontal_bound: 8..12,
|
||||||
|
vertical_bound: 8..12,
|
||||||
|
continuation: vec![(5, 0), (1, 2), (2, 1), (3, 0)],
|
||||||
|
},
|
||||||
|
Face {
|
||||||
|
horizontal_bound: 12..16,
|
||||||
|
vertical_bound: 8..12,
|
||||||
|
continuation: vec![(0, 2), (1, 2), (4, 0), (3, 3)],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// challenge input
|
||||||
|
// let config = vec![
|
||||||
|
// Face {
|
||||||
|
// horizontal_bound: 50..100,
|
||||||
|
// vertical_bound: 0..50,
|
||||||
|
// continuation: vec![(1, 0), (2, 0), (3, 2), (5, 1)],
|
||||||
|
// },
|
||||||
|
// Face {
|
||||||
|
// horizontal_bound: 100..150,
|
||||||
|
// vertical_bound: 0..50,
|
||||||
|
// continuation: vec![(4, 2), (2, 1), (0, 0), (5, 0)],
|
||||||
|
// },
|
||||||
|
// Face {
|
||||||
|
// horizontal_bound: 50..100,
|
||||||
|
// vertical_bound: 50..100,
|
||||||
|
// continuation: vec![(1, 3), (4, 0), (3, 3), (0, 0)],
|
||||||
|
// },
|
||||||
|
// Face {
|
||||||
|
// horizontal_bound: 0..50,
|
||||||
|
// vertical_bound: 100..150,
|
||||||
|
// continuation: vec![(4, 0), (5, 0), (0, 2), (2, 1)],
|
||||||
|
// },
|
||||||
|
// Face {
|
||||||
|
// horizontal_bound: 50..100,
|
||||||
|
// vertical_bound: 100..150,
|
||||||
|
// continuation: vec![(1, 2), (5, 1), (3, 0), (2, 0)],
|
||||||
|
// },
|
||||||
|
// Face {
|
||||||
|
// horizontal_bound: 0..50,
|
||||||
|
// vertical_bound: 150..200,
|
||||||
|
// continuation: vec![(4, 3), (1, 0), (0, 3), (3, 0)],
|
||||||
|
// },
|
||||||
|
// ];
|
||||||
|
|
||||||
|
solve(Wrap3D::new(config, input), input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue