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 Output = isize;
|
||||
|
||||
type Position = Vector2D<usize>;
|
||||
type Position = 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 {
|
||||
Move(usize),
|
||||
Move(isize),
|
||||
TurnLeft,
|
||||
TurnRight,
|
||||
}
|
||||
|
@ -19,143 +36,238 @@ enum Orientation {
|
|||
Horizontal(usize),
|
||||
Vertical(usize),
|
||||
}
|
||||
impl Orientation {
|
||||
fn horizontal(width: usize) -> Self {
|
||||
Self::Horizontal(width)
|
||||
}
|
||||
fn vertical(height: usize) -> Self {
|
||||
Self::Vertical(height)
|
||||
}
|
||||
}
|
||||
|
||||
struct MonkeyMap {
|
||||
map: Vec<Vec<char>>,
|
||||
boundaries: HashMap<Orientation, Range<usize>>,
|
||||
boundaries: HashMap<Orientation, Range<isize>>,
|
||||
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 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)
|
||||
as usize
|
||||
lower + (position + diff - lower + range_size) % range_size
|
||||
}
|
||||
|
||||
trait Wrap {
|
||||
fn next(&self, state: &State<'_>) -> (Position, Direction);
|
||||
trait Wrap: Clone {
|
||||
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;
|
||||
impl Wrap for Wrap2D {
|
||||
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> {
|
||||
#[derive(Clone, Copy)]
|
||||
struct Wrap2D<'a> {
|
||||
input: &'a Input,
|
||||
position: Position,
|
||||
direction: Direction,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
fn new(input: &'a Input) -> State<'a> {
|
||||
impl<'a> Wrap2D<'a> {
|
||||
fn new(input: &'a Input) -> Wrap2D<'a> {
|
||||
Self {
|
||||
input,
|
||||
position: Position::new(input.boundaries[&Orientation::Horizontal(0)].start, 0),
|
||||
direction: Direction::new(1, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Wrap for Wrap2D<'_> {
|
||||
type State = Position;
|
||||
|
||||
fn is_blocked(&self, wrapper: &impl Wrap) -> bool {
|
||||
let (next_position, _) = wrapper.next(self);
|
||||
// simulation
|
||||
fn is_blocked(&self) -> bool {
|
||||
let (next_position, _) = self.next();
|
||||
self.input.map[next_position] == '#'
|
||||
}
|
||||
|
||||
fn step(&mut self, wrapper: &impl Wrap, steps: usize) {
|
||||
fn step(&mut self, steps: isize) {
|
||||
for _ in 0..steps {
|
||||
if self.is_blocked(wrapper) {
|
||||
if self.is_blocked() {
|
||||
return;
|
||||
}
|
||||
(self.position, self.direction) = wrapper.next(self);
|
||||
(self.position, self.direction) = self.next();
|
||||
}
|
||||
}
|
||||
|
||||
fn turn_left(&mut self) {
|
||||
let previous = self.direction;
|
||||
self.direction = Direction::new(previous.y(), -previous.x());
|
||||
self.direction = turn_left(self.direction);
|
||||
}
|
||||
|
||||
fn turn_right(&mut self) {
|
||||
let previous = self.direction;
|
||||
self.direction = Direction::new(-previous.y(), previous.x());
|
||||
self.direction = turn_right(self.direction);
|
||||
}
|
||||
|
||||
// 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! {
|
||||
static ref DIRECTIONS: HashMap<(isize, isize), usize> =
|
||||
HashMap::from([((1, 0), 0), ((0, 1), 1), ((-1, 0), 2), ((0, -1), 3)]);
|
||||
#[derive(Clone)]
|
||||
struct Face {
|
||||
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 {
|
||||
let final_state = input
|
||||
#[derive(Debug, Clone)]
|
||||
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
|
||||
.iter()
|
||||
.fold(State::new(input), |mut state, y| {
|
||||
.fold(initial_state, |previous_state, y| {
|
||||
let mut state = previous_state;
|
||||
|
||||
match &y {
|
||||
Instruction::Move(steps) => state.step(wrapper, *steps),
|
||||
Instruction::Move(steps) => state.step(*steps),
|
||||
Instruction::TurnLeft => state.turn_left(),
|
||||
Instruction::TurnRight => state.turn_right(),
|
||||
}
|
||||
|
||||
state
|
||||
});
|
||||
|
||||
(1000 * (final_state.position.y() + 1)
|
||||
+ 4 * (final_state.position.x() + 1)
|
||||
+ DIRECTIONS[&(final_state.direction.x(), final_state.direction.y())])
|
||||
.try_into()
|
||||
.unwrap()
|
||||
})
|
||||
.answer()
|
||||
}
|
||||
|
||||
struct Day22;
|
||||
|
@ -169,27 +281,39 @@ impl Solution<Input, Output> for Day22 {
|
|||
map.iter_mut().for_each(|l| l.resize(max_row_length, ' '));
|
||||
|
||||
let mut boundaries = HashMap::new();
|
||||
for row in 0..map.len() {
|
||||
let mut first_non_empty = map[row].iter().enumerate().skip_while(|&(_, &c)| c == ' ');
|
||||
let start = first_non_empty.next().unwrap().0;
|
||||
let mut find_boundaries = |constructor: fn(usize) -> Orientation,
|
||||
iterator: &mut dyn Iterator<Item = &char>,
|
||||
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 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() {
|
||||
let mut first_non_empty = ColumnIterator::new(&map, col)
|
||||
.enumerate()
|
||||
.skip_while(|&(_, &c)| c == ' ');
|
||||
let start = first_non_empty.next().unwrap().0;
|
||||
// construct all horizontal boundaries
|
||||
(0..map.len()).for_each(|row| {
|
||||
find_boundaries(
|
||||
Orientation::horizontal,
|
||||
&mut map[row].iter(),
|
||||
map[row].len(),
|
||||
row,
|
||||
);
|
||||
});
|
||||
|
||||
let mut last_non_empty = first_non_empty.skip_while(|&(_, &c)| c != ' ');
|
||||
let end = last_non_empty.next().unwrap_or((map.len(), &'_')).0;
|
||||
|
||||
boundaries.insert(Orientation::Vertical(col), start..end);
|
||||
}
|
||||
// construct all vertical boundaries
|
||||
(0..map[0].len()).for_each(|col| {
|
||||
find_boundaries(
|
||||
Orientation::vertical,
|
||||
&mut ColumnIterator::new(&map, col),
|
||||
map.len(),
|
||||
col,
|
||||
);
|
||||
});
|
||||
|
||||
let unparsed_instructions_str = instructions.trim_end();
|
||||
|
||||
|
@ -204,7 +328,7 @@ impl Solution<Input, Output> for Day22 {
|
|||
.collect();
|
||||
if !steps.is_empty() {
|
||||
// found a move instruction
|
||||
instructions.push(Instruction::Move(steps.parse::<usize>().unwrap()));
|
||||
instructions.push(Instruction::Move(steps.parse::<isize>().unwrap()));
|
||||
skip += steps.len();
|
||||
continue;
|
||||
}
|
||||
|
@ -225,12 +349,79 @@ impl Solution<Input, Output> for Day22 {
|
|||
}
|
||||
|
||||
fn part_1(input: &Input) -> Output {
|
||||
solve(&Wrap2D, input)
|
||||
solve(Wrap2D::new(input), input)
|
||||
}
|
||||
|
||||
fn part_2(input: &Input) -> Output {
|
||||
let wrapper = Wrap2D;
|
||||
solve(&wrapper, input)
|
||||
// sample
|
||||
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