Skip to content

Commit 11ab753

Browse files
authored
drakeerv day 15 & 16 (#79)
1 parent bb7ffa5 commit 11ab753

File tree

6 files changed

+543
-0
lines changed

6 files changed

+543
-0
lines changed

2024/15/drakeerv.v

+271
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
import os
2+
import arrays
3+
4+
enum Tile {
5+
wall
6+
box1
7+
box2
8+
empty
9+
}
10+
11+
struct Vector {
12+
pub:
13+
// Change to immutable
14+
x int
15+
y int
16+
}
17+
18+
fn (v Vector) update(other Vector) Vector {
19+
return Vector{other.x, other.y}
20+
}
21+
22+
fn (v Vector) add(other Vector) Vector {
23+
return Vector{v.x + other.x, v.y + other.y}
24+
}
25+
26+
fn (v Vector) sub(other Vector) Vector {
27+
return Vector{v.x - other.x, v.y - other.y}
28+
}
29+
30+
fn (v Vector) mul(other Vector) Vector {
31+
return Vector{v.x * other.x, v.y * other.y}
32+
}
33+
34+
fn (v Vector) eq(other Vector) bool {
35+
return v.x == other.x && v.y == other.y
36+
}
37+
38+
fn (v Vector) clone() Vector {
39+
return Vector{v.x, v.y}
40+
}
41+
42+
fn get_tile(grid [][]Tile, position Vector) Tile {
43+
return grid[position.y][position.x]
44+
}
45+
46+
fn set_tile(mut grid [][]Tile, position Vector, tile Tile) {
47+
grid[position.y][position.x] = tile
48+
}
49+
50+
fn parse_map_tile(c string) Tile {
51+
return match c {
52+
'#' { Tile.wall }
53+
'O' { Tile.box1 }
54+
'.' { Tile.empty }
55+
'@' { Tile.empty }
56+
else { panic('Invalid tile character: ${c}') }
57+
}
58+
}
59+
60+
fn parse_movement(c string) Vector {
61+
return match c {
62+
'v' { Vector{0, 1} }
63+
'^' { Vector{0, -1} }
64+
'<' { Vector{-1, 0} }
65+
'>' { Vector{1, 0} }
66+
else { panic('Invalid movement character: ${c}') }
67+
}
68+
}
69+
70+
fn find_both_boxes(grid [][]Tile, position Vector) []Vector {
71+
tile := get_tile(grid, position)
72+
if tile == .box1 {
73+
mut box2_pos := position.clone()
74+
box2_pos = box2_pos.add(Vector{1, 0})
75+
return [position, box2_pos]
76+
} else if tile == .box2 {
77+
mut box1_pos := position.clone()
78+
box1_pos = box1_pos.add(Vector{-1, 0})
79+
return [box1_pos, position]
80+
}
81+
panic('Invalid box position')
82+
}
83+
84+
fn is_movable_y(grid [][]Tile, position Vector, movement Vector) bool {
85+
tile := get_tile(grid, position)
86+
if tile == .wall {
87+
return false
88+
} else if tile == .empty {
89+
return true
90+
}
91+
92+
// Find both boxes and check if they can move
93+
boxes := find_both_boxes(grid, position)
94+
mut next_box1_pos := boxes[0].clone()
95+
mut next_box2_pos := boxes[1].clone()
96+
next_box1_pos = next_box1_pos.add(movement)
97+
next_box2_pos = next_box2_pos.add(movement)
98+
99+
return is_movable_y(grid, next_box1_pos, movement)
100+
&& is_movable_y(grid, next_box2_pos, movement)
101+
}
102+
103+
fn move_box_y(mut grid [][]Tile, box1_pos Vector, movement Vector) {
104+
next_left_pos := box1_pos.add(movement)
105+
next_right_pos := next_left_pos.add(Vector{1, 0})
106+
next_left_tile := get_tile(grid, next_left_pos)
107+
next_right_tile := get_tile(grid, next_right_pos)
108+
109+
if next_left_tile == .box1 {
110+
move_box_y(mut grid, next_left_pos, movement)
111+
} else if next_left_tile == .box2 {
112+
move_box_y(mut grid, next_left_pos.add(Vector{-1, 0}), movement)
113+
}
114+
115+
if next_right_tile == .box1 {
116+
move_box_y(mut grid, next_right_pos, movement)
117+
}
118+
119+
set_tile(mut grid, next_left_pos, .box1)
120+
set_tile(mut grid, next_right_pos, .box2)
121+
set_tile(mut grid, box1_pos, .empty)
122+
set_tile(mut grid, box1_pos.add(Vector{1, 0}), .empty)
123+
}
124+
125+
fn is_movable_x(grid [][]Tile, position Vector, movement Vector) ?Vector {
126+
tile := get_tile(grid, position)
127+
if tile == .wall {
128+
return none
129+
} else if tile == .empty {
130+
return position
131+
}
132+
133+
if tile == .box1 {
134+
return is_movable_x(grid, position.add(Vector{2, 0}), movement)
135+
} else if tile == .box2 {
136+
return is_movable_x(grid, position.add(Vector{-2, 0}), movement)
137+
}
138+
return none
139+
}
140+
141+
fn main() {
142+
data := os.read_file('movements.input')!.replace('\r', '').split('\n')
143+
separation := data.index('')
144+
145+
// Parse the grid
146+
mut grid := [][]Tile{}
147+
for row in data[..separation] {
148+
grid << row.split('').map(parse_map_tile)
149+
}
150+
151+
// Find robot position
152+
mut robot := Vector{0, 0}
153+
for y, row in data[..separation] {
154+
x := row.index('@') or { continue }
155+
robot = Vector{int(x), y}
156+
break
157+
}
158+
159+
// Parse movements
160+
movements := data[separation + 1..].filter(it != '').join('').split('').map(parse_movement)
161+
162+
// PART 1
163+
mut robot1 := robot.clone()
164+
mut grid1 := [][]Tile{len: grid.len, init: grid[index].clone()}
165+
166+
// Execute movements
167+
for movement in movements {
168+
mut next_robot_position := robot1.clone()
169+
next_robot_position = next_robot_position.add(movement)
170+
next_robot_tile := get_tile(grid1, next_robot_position)
171+
172+
if next_robot_tile == .wall {
173+
continue
174+
}
175+
176+
if next_robot_tile == .box1 {
177+
mut next_box_position := next_robot_position.clone()
178+
next_box_position = next_box_position.add(movement)
179+
mut next_box_tile := get_tile(grid1, next_box_position)
180+
181+
for next_box_tile == .box1 {
182+
next_box_position = next_box_position.add(movement)
183+
next_box_tile = get_tile(grid1, next_box_position)
184+
}
185+
186+
if next_box_tile == .wall {
187+
continue
188+
}
189+
190+
set_tile(mut grid1, next_box_position, .box1)
191+
set_tile(mut grid1, next_robot_position, .empty)
192+
}
193+
194+
robot1 = robot1.update(next_robot_position)
195+
}
196+
197+
// Calculate part 1
198+
mut part1 := 0
199+
for y, row in grid1 {
200+
for x, tile in row {
201+
if tile == .box1 {
202+
part1 += (y * 100) + x
203+
}
204+
}
205+
}
206+
println('part1: ${part1}')
207+
208+
// PART 2
209+
mut robot2 := robot.clone()
210+
robot2 = robot2.mul(Vector{2, 1})
211+
212+
mut grid2 := [][]Tile{}
213+
for row in grid {
214+
mut row_tiles := [][]Tile{}
215+
for tile in row {
216+
if tile == .box1 {
217+
row_tiles << [Tile.box1, Tile.box2]
218+
} else {
219+
row_tiles << [tile, tile]
220+
}
221+
}
222+
grid2 << arrays.flatten[Tile](row_tiles)
223+
}
224+
225+
// Execute movements for part 2
226+
for movement in movements {
227+
next_robot_position := robot2.add(movement)
228+
next_robot_tile := get_tile(grid2, next_robot_position)
229+
230+
if next_robot_tile == .wall {
231+
continue
232+
}
233+
234+
if next_robot_tile in [.box1, .box2] {
235+
if movement.x == 0 {
236+
// Vertical movement logic
237+
if !is_movable_y(grid2, next_robot_position, movement) {
238+
continue
239+
}
240+
boxes := find_both_boxes(grid2, next_robot_position)
241+
move_box_y(mut grid2, boxes[0], movement)
242+
} else {
243+
// Horizontal movement logic
244+
next_box_pos := is_movable_x(grid2, next_robot_position, movement) or { continue }
245+
246+
mut next_box_move_pos := next_box_pos
247+
mut next_box_tile := get_tile(grid2, next_box_move_pos.sub(movement))
248+
set_tile(mut grid2, next_box_pos, next_box_tile)
249+
250+
for next_box_tile != .empty {
251+
next_box_move_pos = next_box_move_pos.sub(movement)
252+
next_box_tile = get_tile(grid2, next_box_move_pos.sub(movement))
253+
set_tile(mut grid2, next_box_move_pos, next_box_tile)
254+
}
255+
}
256+
}
257+
258+
robot2 = robot2.update(next_robot_position)
259+
}
260+
261+
// Calculate part 2
262+
mut part2 := 0
263+
for y, row in grid2 {
264+
for x, tile in row {
265+
if tile == .box1 {
266+
part2 += (y * 100) + x
267+
}
268+
}
269+
}
270+
println('part2: ${part2}')
271+
}

2024/15/movements.input

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
##########
2+
#..O..O.O#
3+
#......O.#
4+
#.OO..O.O#
5+
6+
#O#..O...#
7+
#O..O..O.#
8+
#.OO.O.OO#
9+
#....O...#
10+
##########
11+
12+
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
13+
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
14+
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
15+
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
16+
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
17+
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
18+
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
19+
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
20+
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
21+
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^

0 commit comments

Comments
 (0)