Skip to content

Commit f9f027d

Browse files
committed
Solved day22
1 parent 7213da6 commit f9f027d

File tree

6 files changed

+260
-96
lines changed

6 files changed

+260
-96
lines changed

2022/src/bin/day22.rs

Lines changed: 202 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#![feature(test)]
22

33
use advent_lib::day_main;
4-
use advent_lib::direction::Direction;
4+
use advent_lib::direction::{Direction, ALL_DIRECTIONS};
55
use advent_lib::geometry::{point2, vector2, Point, Vector};
66
use advent_lib::grid::{uneven_grid_parser, Grid};
77
use advent_lib::parsing::double_line_ending;
88
use advent_macros::FromRepr;
9-
use fxhash::{FxBuildHasher, FxHashMap};
9+
use fxhash::FxHashMap;
1010
use nom_parse_macros::parse_from;
1111
use std::cmp::min;
1212
use std::ops::Neg;
@@ -25,23 +25,17 @@ enum Command {
2525
Right,
2626
}
2727

28+
#[derive(Debug, Copy, Clone)]
2829
struct Person {
29-
position: Point<2, i32>,
30-
direction: Direction,
30+
pos: Point<2, i32>,
31+
dir: Direction,
3132
}
3233

3334
impl Person {
34-
fn step(&self) -> Point<2, i32> { self.position + self.direction }
35+
fn step(&self) -> Person { Person { dir: self.dir, pos: self.pos + self.dir } }
3536

3637
fn score(&self) -> i32 {
37-
(self.position.x() + 1) * 4
38-
+ (self.position.y() + 1) * 1000
39-
+ match self.direction {
40-
East => 0,
41-
South => 1,
42-
West => 2,
43-
North => 3,
44-
}
38+
(self.pos.x() + 1) * 4 + (self.pos.y() + 1) * 1000 + i32::from(self.dir)
4539
}
4640
}
4741

@@ -60,100 +54,231 @@ struct GridAndCommands {
6054
commands: Vec<Command>,
6155
}
6256

57+
#[derive(Debug)]
58+
struct Block {
59+
grid: Grid<FieldType>,
60+
offset: Vector<2, i32>,
61+
}
62+
6363
#[derive(Debug)]
6464
struct Input {
6565
commands: Vec<Command>,
66-
block_size: usize,
67-
blocks: FxHashMap<Vector<2, i32>, Grid<FieldType>>,
68-
start_offset: Vector<2, i32>,
66+
block_size: i32,
67+
blocks: [Block; 6],
68+
block_jump_2d: BlockJumps,
69+
block_jump_3d: BlockJumps,
70+
}
71+
72+
#[derive(Debug, Copy, Clone)]
73+
enum Turn {
74+
None,
75+
Left,
76+
Right,
77+
Around,
78+
}
79+
80+
impl Turn {
81+
fn apply(&self, person: &Person, block_size: i32) -> Person {
82+
let position = person.pos + (person.dir.neg().as_vec() * block_size);
83+
match self {
84+
Self::None => Person { dir: person.dir, pos: position },
85+
Self::Left => Person {
86+
dir: person.dir.turn_left(),
87+
pos: point2(position.y(), block_size - position.x() - 1),
88+
},
89+
Self::Right => Person {
90+
dir: person.dir.turn_right(),
91+
pos: point2(block_size - position.y() - 1, position.x()),
92+
},
93+
Self::Around => Person {
94+
dir: -person.dir,
95+
pos: point2(
96+
block_size - position.x() - 1,
97+
(block_size) - position.y() - 1,
98+
),
99+
},
100+
}
101+
}
69102
}
70103

71104
fn preprocess(input: GridAndCommands) -> Input {
72-
let mut blocks = FxHashMap::with_capacity_and_hasher(10, FxBuildHasher::default());
73-
let block_size = (min(input.grid.x_range().end, input.grid.y_range().end) / 3) as usize;
105+
let mut blocks = vec![];
106+
let block_size = min(input.grid.x_range().end, input.grid.y_range().end) / 3;
74107

75-
for y in input.grid.y_range().step_by(block_size) {
76-
for x in input.grid.x_range().step_by(block_size) {
108+
for y in input.grid.y_range().step_by(block_size as usize) {
109+
for x in input.grid.x_range().step_by(block_size as usize) {
77110
if input.grid.get(point2(x, y)).cloned().unwrap_or_default() != Outside {
78-
blocks.insert(
79-
vector2(x, y),
80-
input.grid.sub_grid(x..(x + block_size as i32), y..(y + block_size as i32)),
81-
);
111+
blocks.push(Block {
112+
grid: input.grid.sub_grid(x..(x + block_size), y..(y + block_size)),
113+
offset: vector2(x, y),
114+
});
82115
}
83116
}
84117
}
85118

86-
let start_block = *blocks.keys().find(|offset| offset.y() == 0).unwrap();
119+
let blocks = blocks.try_into().expect("Expected 6 blocks as input");
120+
let block_jump_2d = calc_block_jumps_2d(&blocks);
121+
let block_jump_3d = calc_block_jumps_3d(&blocks);
87122

88-
Input { commands: input.commands, block_size, blocks, start_offset: start_block }
123+
Input {
124+
commands: input.commands,
125+
block_size,
126+
blocks: blocks,
127+
block_jump_2d,
128+
block_jump_3d,
129+
}
89130
}
90131

91-
impl Input {
92-
fn next_block_2d(
93-
&self,
94-
current_offset: &Vector<2, i32>,
95-
direction: &Direction,
96-
) -> (Vector<2, i32>, &Grid<FieldType>) {
97-
let step = direction.as_vec() * self.block_size as i32;
98-
let mut next_offset = *current_offset;
99-
let modulus = 4 * self.block_size as i32;
100-
101-
for _ in 0..5 {
102-
next_offset = next_offset + step;
103-
next_offset = vector2(
104-
(next_offset.x() + modulus) % modulus,
105-
(next_offset.y() + modulus) % modulus,
106-
);
107-
if let Some(block) = self.blocks.get(&next_offset) {
108-
return (next_offset, block);
132+
type BlockJumps = FxHashMap<(usize, Direction), (usize, Turn)>;
133+
134+
fn calc_block_jumps_2d(blocks: &[Block; 6]) -> BlockJumps {
135+
let mut jumps = FxHashMap::default();
136+
let block_size = blocks[0].grid.width();
137+
138+
for start_ix in 0..6 {
139+
let start_offset = blocks[start_ix].offset;
140+
for direction in ALL_DIRECTIONS {
141+
let step = direction.as_vec() * block_size;
142+
let modulus = 4 * block_size;
143+
let mut next_offset = start_offset;
144+
145+
for _ in 1..=4 {
146+
next_offset = (next_offset + step + vector2(modulus, modulus)) % modulus;
147+
if let Some((next_ix, _)) =
148+
blocks.iter().enumerate().find(|(_, block)| block.offset == next_offset)
149+
{
150+
jumps.insert((start_ix, direction), (next_ix, Turn::None));
151+
break;
152+
}
109153
}
110154
}
155+
}
156+
157+
return jumps;
158+
}
159+
160+
fn calc_block_jumps_3d(blocks: &[Block; 6]) -> BlockJumps {
161+
let mut jumps = FxHashMap::default();
162+
let block_size = blocks[0].grid.width();
111163

112-
panic!("No block found")
164+
if block_size == 4 {
165+
jumps.insert((0, North), (1, Turn::Around));
166+
jumps.insert((0, East), (5, Turn::Around));
167+
jumps.insert((0, South), (3, Turn::None));
168+
jumps.insert((0, West), (2, Turn::Left));
169+
170+
jumps.insert((1, North), (0, Turn::Around));
171+
jumps.insert((1, East), (2, Turn::None));
172+
jumps.insert((1, South), (4, Turn::Around));
173+
jumps.insert((1, West), (5, Turn::Left));
174+
175+
jumps.insert((2, North), (0, Turn::Right));
176+
jumps.insert((2, East), (3, Turn::None));
177+
jumps.insert((2, South), (4, Turn::Left));
178+
jumps.insert((2, West), (1, Turn::None));
179+
180+
jumps.insert((3, North), (0, Turn::None));
181+
jumps.insert((3, East), (5, Turn::Right));
182+
jumps.insert((3, South), (4, Turn::None));
183+
jumps.insert((3, West), (2, Turn::None));
184+
185+
jumps.insert((4, North), (3, Turn::None));
186+
jumps.insert((4, East), (5, Turn::None));
187+
jumps.insert((4, South), (1, Turn::Around));
188+
jumps.insert((4, West), (2, Turn::Right));
189+
190+
jumps.insert((5, North), (3, Turn::Left));
191+
jumps.insert((5, East), (0, Turn::Around));
192+
jumps.insert((5, South), (1, Turn::Right));
193+
jumps.insert((5, West), (4, Turn::None));
194+
} else {
195+
jumps.insert((0, North), (5, Turn::Right));
196+
jumps.insert((0, East), (1, Turn::None));
197+
jumps.insert((0, South), (2, Turn::None));
198+
jumps.insert((0, West), (3, Turn::Around));
199+
200+
jumps.insert((1, North), (5, Turn::None));
201+
jumps.insert((1, East), (4, Turn::Around));
202+
jumps.insert((1, South), (2, Turn::Right));
203+
jumps.insert((1, West), (0, Turn::None));
204+
205+
jumps.insert((2, North), (0, Turn::None));
206+
jumps.insert((2, East), (1, Turn::Left));
207+
jumps.insert((2, South), (4, Turn::None));
208+
jumps.insert((2, West), (3, Turn::Left));
209+
210+
jumps.insert((3, North), (2, Turn::Right));
211+
jumps.insert((3, East), (4, Turn::None));
212+
jumps.insert((3, South), (5, Turn::None));
213+
jumps.insert((3, West), (0, Turn::Around));
214+
215+
jumps.insert((4, North), (2, Turn::None));
216+
jumps.insert((4, East), (1, Turn::Around));
217+
jumps.insert((4, South), (5, Turn::Right));
218+
jumps.insert((4, West), (3, Turn::None));
219+
220+
jumps.insert((5, North), (3, Turn::None));
221+
jumps.insert((5, East), (4, Turn::Left));
222+
jumps.insert((5, South), (1, Turn::None));
223+
jumps.insert((5, West), (0, Turn::Left));
113224
}
225+
226+
return jumps;
114227
}
115228

116-
fn calculate_part1(input: &Input) -> i32 {
117-
let mut current_offset = input.start_offset;
118-
let mut current_block = input.blocks.get(&current_offset).unwrap();
119-
let mut person = Person { position: point2(0, 0), direction: East };
229+
fn handle_command(
230+
input: &Input,
231+
block_jump: &BlockJumps,
232+
command: &Command,
233+
index: &usize,
234+
person: &Person,
235+
) -> (usize, Person) {
236+
let mut person = person.clone();
237+
let mut index = *index;
120238

121-
for command in &input.commands {
122-
match command {
123-
Command::Forward(steps) => {
124-
for _ in 0..*steps {
125-
let mut next_position = person.step();
126-
match current_block.get(next_position) {
127-
Some(Empty) => person.position = next_position,
128-
Some(Wall) => break,
129-
Some(Outside) => panic!("Outside a block should not be possible"),
130-
None => {
131-
let (next_offset, next_block) =
132-
input.next_block_2d(&current_offset, &person.direction);
133-
next_position = next_position
134-
+ (person.direction.neg().as_vec() * (input.block_size as i32));
135-
136-
if next_block.get(next_position) == Some(&Wall) {
137-
break;
138-
}
139-
140-
current_offset = next_offset;
141-
current_block = next_block;
142-
person.position = next_position;
239+
match command {
240+
Command::Forward(steps) => {
241+
for _ in 0..*steps {
242+
let mut next_person = person.step();
243+
match input.blocks[index].grid.get(next_person.pos) {
244+
Some(Empty) => person = next_person,
245+
Some(Wall) => break,
246+
Some(Outside) => panic!("Outside a block should not be possible"),
247+
None => {
248+
let (next_ix, turn) = block_jump[&(index, person.dir)];
249+
next_person = turn.apply(&next_person, input.block_size);
250+
251+
if input.blocks[next_ix].grid.get(next_person.pos) == Some(&Wall) {
252+
break;
143253
}
254+
255+
(index, person) = (next_ix, next_person);
144256
}
145257
}
146258
}
147-
Command::Left => person.direction = person.direction.turn_left(),
148-
Command::Right => person.direction = person.direction.turn_right(),
149259
}
260+
Command::Left => person.dir = person.dir.turn_left(),
261+
Command::Right => person.dir = person.dir.turn_right(),
262+
}
263+
264+
(index, person)
265+
}
266+
267+
fn calculate(input: &Input, block_jump: &BlockJumps) -> i32 {
268+
let mut index = 0;
269+
let mut person = Person { pos: point2(0, 0), dir: East };
270+
271+
for command in &input.commands {
272+
(index, person) = handle_command(input, block_jump, command, &mut index, &mut person);
150273
}
151274

152-
person.position = person.position + current_offset;
275+
person.pos = person.pos + input.blocks[index].offset;
153276
person.score()
154277
}
155278

156-
fn calculate_part2(_input: &Input) -> i32 { todo!() }
279+
fn calculate_part1(input: &Input) -> i32 { calculate(input, &input.block_jump_2d) }
280+
281+
fn calculate_part2(input: &Input) -> i32 { calculate(input, &input.block_jump_3d) }
157282

158283
day_main!( preprocess => calculate_part1, calculate_part2 );
159284

@@ -166,8 +291,8 @@ mod tests {
166291
use nom::Parser;
167292
use nom_parse_trait::ParseFrom;
168293

169-
day_test!( 22, example => 6032/*, 5031 */ ; preprocess );
170-
day_test!( 22 => 197160 ; preprocess );
294+
day_test!( 22, example => 6032, 5031 ; preprocess );
295+
day_test!( 22 => 197160, 145065 ; preprocess );
171296

172297
#[test]
173298
fn test_command_parsing() {

shared/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2021"
66
[dependencies]
77
bit-vec = "0.8"
88
fxhash = "0.2"
9+
image = "0.25.8"
910
memmap2 = "0.9"
1011
nom = "8"
1112
nom-parse-trait = "0.3.2"

shared/src/direction.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,28 @@ impl Neg for Direction {
106106
}
107107
}
108108

109-
impl From<Direction> for usize {
110-
fn from(value: Direction) -> Self {
111-
match value {
112-
North => 3,
113-
East => 0,
114-
South => 1,
115-
West => 2,
109+
macro_rules! direction_into_nr {
110+
() => {};
111+
($nr_type:ty) => {
112+
impl From<Direction> for $nr_type {
113+
fn from(value: Direction) -> Self {
114+
match value {
115+
North => 3,
116+
East => 0,
117+
South => 1,
118+
West => 2,
119+
}
120+
}
116121
}
117-
}
122+
};
123+
($nr_type:ty, $($types:ty),*) => {
124+
direction_into_nr!($nr_type);
125+
direction_into_nr!($($types),*);
126+
};
118127
}
119128

129+
direction_into_nr!(i8, i16, i32, i64, u8, u16, u32, u64, usize);
130+
120131
impl<T> From<Direction> for Vector<2, T>
121132
where
122133
T: Zero + One + Neg<Output = T>,

0 commit comments

Comments
 (0)