Summary
See Part 1
- The beam now can start on any edge tile
- Find the path that energizes the most tiles
Function
Luckily, this challenge can reuse 100% from Part 1. To start with this I throw
Part 1’s main loop into a function. I’ll pass the starting position, the
direction, and which function to call as arguments.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
function calculate_energized(data: tile[][], start_x: number, start_y: number, start_direction: string, start_call: Function): number{
let x: number = start_x;
let y: number = start_y;
let direction: string = start_direction;
let call: Function = start_call
let lights: light_point[] = [];
while (true){
let tile: tile|undefined = call(data, x, y);
if (tile !== undefined){
if (['\\', '/'].indexOf(tile.contents) !== -1){
const result: call_dict = mirror_map.get(`${direction}_${tile.contents}`)
call = result['func'];
direction = result['direction'];
}else if (tile.contents == '|' && !tile.reflected){
lights.push({
contents: tile.contents,
x: tile.x,
y: tile.y,
func: go_north,
direction: 'N',
});
call = go_south;
direction = 'S';
tile.reflected = true; // anti-loops
}else if (tile.contents == '-' && !tile.reflected){
lights.push({
contents: tile.contents,
x: tile.x,
y: tile.y,
func: go_east,
direction: 'E',
});
call = go_west;
direction = 'W';
tile.reflected = true; // anti-loops
}else{
// presumed a `|` or `-` previously reflected
tile = undefined;
}
}
if (tile !== undefined){
// update coordinates
x = tile.x;
y = tile.y;
}else{
if (lights.length){
// hit an edge, use a point from lights
let light: light_point = lights.pop();
if (debug && (debug_count == null || debug_count <= i )){
console.log(light);
}
// update using stored values
direction = light.direction;
call = light['func'];
x = light.x;
y = light.y;
}else{
// empty => all done
break;
}
}
};
let sum: number = 0;
data.forEach((row) => {
sum += row.filter((tile) => tile.energized).length;
});
return sum;
}
|
Horizontal Loop
All that’s left is to loop through all the combinations of edges. I started with
horizontal starts, so east or west, as they share the y
start coordinate
moving and the x
coordinate being static. The most important thing to remember
is that it must start 1 coordinate off the board. If moving east it must
start with -1
. If moving west it must be data.length
, index starts at 0.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
let sum: number = 0;
// used in both horizontal + vertical
let direction: string;
let call: Function;
let x: number;
let y: number;
// horizontal starts:
for (let i = 0; i < 2; i++){
if (i == 0){
direction = 'E';
call = go_east;
x = -1;
}else{
direction = 'W';
call = go_west;
x = data.length;
}
for (let y = 0; y < data.length; y++){
let local_sum: number = calculate_energized(data, x, y, direction, call)
if (sum < local_sum){
console.log(`(${x}, ${y}) ${direction} => Local_Sum: ${local_sum}`);
sum = local_sum
}
}
}
|
Resetting Data
As I found out, the tile’s energized
and reflected
attributes need to be
reset every loop or the sum will keep getting bigger! Here’s a quick
function that does that:
1
2
3
4
5
6
7
8
9
|
function clear_energized(data: tile[][]): tile[][]{
data.forEach((row) => {
row.forEach((tile) => {
tile.energized = false;
tile.reflected = false;
});
});
return data;
}
|
..and here is implementing it in the horizontal loop
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// Horizontal starts:
for (let i = 0; i < 2; i++){
// ...
for (let y = 0; y < data.length; y++){
let local_sum: number = calculate_energized(data, x, y, direction, call);
if (sum < local_sum){
console.log(`(${x}, ${y}) ${direction} => Local_Sum: ${local_sum}`);
sum = local_sum;
}
data = clear_energized(data);
}
}
|
Vertical Loop
Vertical is almost the same, but the start coordinate moving is the x
and the
y
coordinate is the one moving. Add it right after the horizontal start and
output the sum.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// Vertical starts:
for (let i = 0; i < 2; i++){
if (i == 0){
direction = 'S';
call = go_south;
y = -1;
}else{
direction = 'N';
call = go_north;
y = data.length;
}
for (let x = 0; x < data.length; x++){
let y: number = 0;
let local_sum: number = calculate_energized(data, x, y, direction, call);
if (sum < local_sum){
console.log(`(${x}, ${y}) ${direction} => Local_Sum: ${local_sum}`);
sum = local_sum;
}
data = clear_energized(data);
}
}
|
Conclusion
I’m surprised this worked so easily. Usually Part 2s add so many more
iterations that any Part 1 needs some rework to get it up to snuff or you’ll be
waiting for 20 seconds!- ..and that’s when you know you messed up somewhere.
I had one hangup that was resetting tile attributes, and I should have saw that
from that start.