1
0
Fork 0

day(22): finish it off…

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2023-07-07 13:32:07 +02:00
parent f3b7ead65b
commit 13a0b3dbf7
Signed by: mfocko
GPG key ID: 7C47D46246790496

View file

@ -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)
}
}