-
Notifications
You must be signed in to change notification settings - Fork 1
/
day16.c3
205 lines (186 loc) · 5.27 KB
/
day16.c3
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
* Advent of Code 2023 day 16
*/
import std::io;
import std::time;
import std::math;
import std::collections::list;
fn void main()
{
io::printn("Advent of code, day 16.");
@pool()
{
Map map = load_global_map();
// Simple benchmarking with Clock, "mark" returns the last duration and resets the clock
Clock c = clock::now();
io::printfn("* Sum part 1: %d - completed in %s", part1(map), c.mark());
io::printfn("* Sum part 2: %d - completed in %s", part2(map), c.mark());
};
}
// Let's build a map with operator overloading, this is actually borrowed from day 14
struct Map
{
char[128][] data;
int width;
}
// This is what we need to implement to have indexing and foreach on the type
fn int Map.len(self) @inline @operator(len) => self.width;
fn char[] Map.get(self, int i) @inline @operator([]) => self.data[i][:self.width];
fn Map load_global_map()
{
File f = file::open("day16.txt", "rb")!!;
defer (void)f.close();
static char[128][128] map_buffer;
// Load the file into memory
int height = 0;
int width @noinit;
while (try line = io::treadline(&f))
{
width = line.len;
map_buffer[height++][0:line.len] = line;
}
assert(height < 128 && width < 128);
return { map_buffer[:height], width };
}
const char NORTH = 0b00001;
const char EAST = 0b00010;
const char SOUTH = 0b00100;
const char WEST = 0b01000;
struct LightBeam
{
ichar[<2>] location;
char direction;
}
const ichar[<2>][*] DIRECTION_FROM_VALUE = {
[NORTH] = { 0, -1 },
[SOUTH] = { 0, 1 },
[EAST] = { 1, 0 },
[WEST] = { -1, 0 }
};
fn void trace_beam(List(<LightBeam>)* beams, LightBeam beam, Map map, Map light_map)
{
ichar[<2>] location = beam.location;
char direction = beam.direction;
ichar[<2>] velocity = DIRECTION_FROM_VALUE[direction];
int height = map.len();
int width = map.width;
// While the beam is still on the map:
while (location.x >= 0 && location.y >= 0 && location.x < width && location.y < height)
{
// Check what
char light = light_map[location.y][location.x];
// Already found light in this direction.
if (light & direction) return;
// Map the light direction.
light_map[location.y][location.x] = light | direction;
switch (map[location.y][location.x])
{
case '.':
// Do nothing
break;
case '|':
// Split if coming from east or west
if (direction == WEST || direction == EAST)
{
// Deal with the north beam later.
beams.v({ location + { 0, -1 }, NORTH });
// Trace the south beam.
velocity = DIRECTION_FROM_VALUE[direction = SOUTH];
}
case '-':
// Split if coming from north or south.
if (direction == NORTH || direction == SOUTH)
{
// Deal with the west beam later
beams.push({ location + { -1, 0 }, WEST });
// Trace the east beam.
velocity = DIRECTION_FROM_VALUE[direction = EAST];
}
case '/':
// Redirect beam clockwise.
switch (direction)
{
case EAST: velocity = DIRECTION_FROM_VALUE[direction = NORTH];
case WEST: velocity = DIRECTION_FROM_VALUE[direction = SOUTH];
case NORTH: velocity = DIRECTION_FROM_VALUE[direction = EAST];
case SOUTH: velocity = DIRECTION_FROM_VALUE[direction = WEST];
default: unreachable("Illegal direction");
}
case '\\':
// Redirect the beam counter clockwise
switch (direction)
{
case EAST: velocity = DIRECTION_FROM_VALUE[direction = SOUTH];
case WEST: velocity = DIRECTION_FROM_VALUE[direction = NORTH];
case NORTH: velocity = DIRECTION_FROM_VALUE[direction = WEST];
case SOUTH: velocity = DIRECTION_FROM_VALUE[direction = EAST];
default: unreachable("Illegal direction");
}
default:
unreachable();
}
// Move the beam
location += velocity;
}
}
// Calculate energized tiles giving a starting beam and the map.
fn int calculate_energized_tiles(Map map, LightBeam beam)
{
// Create a light map.
char[128][128] light_buffer;
Map light_map = { light_buffer[:map.len()], map.width };
// Keep a list of beams to process
List(<LightBeam>) beams;
beams.init_temp();
// Append the first beam
beams.push(beam);
// While we have beams, trace them.
while (beams.len())
{
trace_beam(&beams, beams.pop(), map, light_map);
}
// Sum all in the light map that is != zero
int sum;
foreach (line : light_map)
{
foreach (c : line) if (c) sum++;
}
return sum;
}
fn long part1(Map map)
{
// Calculate starting from { 0, 0 } beam moving east.
return calculate_energized_tiles(map, { { 0, 0 }, EAST} );
}
fn int part2(Map map)
{
ichar height = (ichar)map.len();
ichar width = (ichar)map.width;
int max = 0;
// Look for max north and south, by tracing from the bottom and top.
for (ichar i = 0; i < width; i++)
{
@pool()
{
max = math::max(
max,
calculate_energized_tiles(map, { { i, 0 }, SOUTH }),
calculate_energized_tiles(map, { { i, height - 1 }, NORTH })
);
};
}
// Look for max east and west by tracing from left and right.
for (ichar i = 0; i < height; i++)
{
@pool()
{
max = math::max(
max,
calculate_energized_tiles(map, { { 0, i }, EAST }),
calculate_energized_tiles(map, { { width - 1, i }, WEST })
);
};
}
// Return the max
return max;
}