solve_day22 :: (test: bool) {
contents := read_entire_file(ifx test then "inputs/day22_test2.txt" else "inputs/day22.txt");
sections := split(contents, "\n\n");
instruction_section := sections[1];
map_lines := split(sections[0], cast(u8) #char "\n");
map_builder: String_Builder;
map_width := map_lines[0].count;
map_height := map_lines.count;
for map_lines map_width = max(map_width, it.count);
faces_x := ifx map_width > map_height then 4 else 3;
faces_y := 7 - faces_x;
face_dim := map_lines.count / faces_y;
empty_string := NewArray(map_width, u8);
memset(empty_string.data, cast(u8) #char " ", empty_string.count);
for map_lines {
print_to_builder(*map_builder, it);
if it.count < map_width {
print_to_builder(*map_builder, xx array_view(empty_string, 0, map_width - it.count));
}
}
map := builder_to_string(*map_builder);
defer free(map.data);
map2 := copy_string(map);
defer free(map2.data);
instructions := parse_instructions(instruction_section);
pos := Vec2.{
x = 0,
y = 0,
};
dir := Vec2.{
x = 1,
y = 0,
};
while map[pos.x] == #char " " pos.x += face_dim;
for instructions {
if it.type == .TURN {
dir = turn(dir, it.value);
} else {
for 0..it.value - 1 {
new_pos := pos + dir;
if new_pos.x >= map_width || new_pos.x < 0 {
new_pos.x += -dir.x * map_width;
} else if new_pos.y >= map_height || new_pos.y < 0 {
new_pos.y += -dir.y * map_height;
} else if map[new_pos.y * map_width + new_pos.x] == #char " " {
if dir.x == 1 {
new_pos.x = 0;
} else if dir.x == -1 {
new_pos.x = map_width - 1;
} else if dir.y == 1 {
new_pos.y = 0;
} else if dir.y == -1 {
new_pos.y = map_height - 1;
} else assert(false, "dir has bad values %", dir);
}
while map[new_pos.y * map_width + new_pos.x] == #char " " new_pos += dir;
//map[pos.y * map_width + pos.x] = convert_dir_to_char(dir);
if map[new_pos.y * map_width + new_pos.x] != #char "#" {
pos = new_pos;
} else {
continue;
}
}
}
}
map[pos.y * map_width + pos.x] = #char "x";
for 0..map_height - 1 {
print("%\n", slice(map, it * map_width, map_width));
}
pos += Vec2.{1,1};
print("Part 1: %\n", pos.y * 1000 + pos.x * 4 + convert_dir_to_val(dir));
cube_faces: [6]Cube_Face;
face_map: [12]s64;
memset(face_map.data, 255, size_of(s64) * face_map.count);
assert(face_dim == map_lines.count / faces_y, "Face dim does not line up: % != %", face_dim, map_lines.count / faces_y);
assert(map_lines[0].count % face_dim == 0, "Width (%) is not divisible by %", face_dim, map_lines[0].count);
assert(map_lines.count % face_dim == 0, "Height (%) is not divisible by %", face_dim, map_lines.count);
{
face_num := 0;
for y: 0..faces_y - 1 {
for x: 0..faces_x - 1 {
if map2[y * face_dim * map_width + x * face_dim] != #char " " {
cube_faces[face_num].on_map.x = face_dim * x;
cube_faces[face_num].on_map.y = face_dim * y;
face_map[y * faces_x + x] = face_num;
face_num += 1;
}
}
}
assert(face_num == 6, "Did not find enough faces %.", face_num);
}
stich_cube(cube_faces, face_map, faces_x);
pos = .{0,0};
dir = .{1,0};
current_face := 0;
for instructions {
if it.type == .TURN {
dir = turn(dir, it.value);
} else {
for 0..it.value - 1 {
new_pos := pos + dir;
new_dir := dir;
new_current_face := current_face;
if new_pos.x < 0 {
new_pos.x = face_dim - 1;
left_dir := cube_faces[current_face].neighbours[3].dir;
new_pos = convert_coords(new_pos, left_dir, face_dim);
new_dir = convert_dir(dir, left_dir);
new_current_face = cube_faces[current_face].neighbours[3].face;
} else if new_pos.x >= face_dim {
new_pos.x = 0;
right_dir := cube_faces[current_face].neighbours[1].dir;
new_pos = convert_coords(new_pos, right_dir, face_dim);
new_dir = convert_dir(dir, right_dir);
new_current_face = cube_faces[current_face].neighbours[1].face;
} else if new_pos.y < 0 {
new_pos.y = face_dim - 1;
up_dir := cube_faces[current_face].neighbours[0].dir;
new_pos = convert_coords(new_pos, up_dir, face_dim);
new_dir = convert_dir(dir, up_dir);
new_current_face = cube_faces[current_face].neighbours[0].face;
} else if new_pos.y >= face_dim {
new_pos.y = 0;
bottom_dir := cube_faces[current_face].neighbours[2].dir;
new_pos = convert_coords(new_pos, bottom_dir, face_dim);
new_dir = convert_dir(dir, bottom_dir);
print("----> %, %\n", dir, new_dir);
new_current_face = cube_faces[current_face].neighbours[2].face;
}
map_pos := Vec2.{
x = new_pos.x + cube_faces[new_current_face].on_map.x,
y = new_pos.y + cube_faces[new_current_face].on_map.y,
};
p := Vec2.{
x = pos.x + cube_faces[current_face].on_map.x,
y = pos.y + cube_faces[current_face].on_map.y,
};
if map2[map_pos.y * map_width + map_pos.x] != #char "#" {
map2[p.y * map_width + p.x] = convert_dir_to_char(dir);
pos = new_pos;
dir = new_dir;
current_face = new_current_face;
} else {
continue;
}
}
}
}
final_pos := Vec2.{
x = pos.x + cube_faces[current_face].on_map.x,
y = pos.y + cube_faces[current_face].on_map.y,
};
map2[final_pos.y * map_width + final_pos.x] = convert_dir_to_char(dir);
for 0..map_height - 1 {
print("%\n", slice(map2, it * map_width, map_width));
}
final_pos += Vec2.{1,1};
print("Part 2: %\n", final_pos.y * 1000 + final_pos.x * 4 + convert_dir_to_val(dir));
t: [1]u8;
for 1..255 {
t[0] = xx it;
print("% ", cast(string) t);
}
print("\n");
}
convert_coords :: (v: Vec2, dir: Dir, map_dim: s64) -> Vec2 {
if dir == {
case .UP; return v;
case .LEFT; return .{ x = v.y, y = map_dim - 1 - v.x };
case .DOWN; return .{ x = map_dim - 1 - v.x, y = map_dim - 1 - v.y };
case .RIGHT; return .{ x = map_dim - 1 - v.y, y = v.x };
}
assert(false, "Bad dir %", dir);
return v;
}
convert_dir :: (v: Vec2, dir: Dir) -> Vec2 {
if dir == {
case .UP; return v;
case .RIGHT; return .{ x = -v.y, y = v.x };
case .DOWN; return .{ x = -v.x, y = -v.y };
case .LEFT; return .{ x = v.y, y = -v.x };
}
assert(false, "Bad dir %", dir);
return .{};
}
stich_cube :: (faces: [6]Cube_Face, map: [12]s64, map_width: s64) {
map_height := 7 - map_width;
for x: 0..map_width - 1 {
// Map is 4 x 3 or 3 x 4. 6 because it would be 7 - map_width - 1.
for y: 0..6 - map_width {
if map[y * map_width + x] == -1 continue;
face := *faces[map[y * map_width + x]];
if y > 0 && map[(y - 1) * map_width + x] != -1 {
face.neighbours[0].dir = .UP;
face.neighbours[0].face = map[(y - 1) * map_width + x];
}
if x < map_width - 1 && map[y * map_width + x + 1] != -1 {
face.neighbours[1].dir = .UP;
face.neighbours[1].face = map[y * map_width + x + 1];
}
if y < map_height - 1 && map[(y + 1) * map_width + x] != -1 {
face.neighbours[2].dir = .UP;
face.neighbours[2].face = map[(y + 1) * map_width + x];
}
if x > 0 && map[y * map_width + x - 1] != -1 {
face.neighbours[3].dir = .UP;
face.neighbours[3].face = map[y * map_width + x - 1];
}
}
}
for 0..1 {
for *faces {
if it.neighbours[0].face != -1 {
top_neighbour := *faces[it.neighbours[0].face];
if it.neighbours[1].face != -1 {
right_neighbour := *faces[it.neighbours[1].face];
right_neighbour_top := rot_index(.UP, it.neighbours[1].dir);
right_neighbour.neighbours[right_neighbour_top].face = it.neighbours[0].face;
right_neighbour.neighbours[right_neighbour_top].dir = rot_dir(it.neighbours[0].dir - it.neighbours[1].dir, false);
top_neighbour_right := rot_index(.RIGHT, it.neighbours[0].dir);
top\ _neighbour.neighbours[top_neighbour_right].face = it.neighbours[1].face;
top\ _neighbour.neighbours[top_neighbour_right].dir = rot_dir(it.neighbours[1].dir - it.neighbours[0].dir, true);
}
if it.neighbours[3].face != -1 {
left_neighbour := *faces[it.neighbours[3].face];
left_neighbour_top := rot_index(.UP, it.neighbours[3].dir);
left_neighbour.neighbours[left_neighbour_top].face = it.neighbours[0].face;
left_neighbour.neighbours[left_neighbour_top].dir = rot_dir(it.neighbours[0].dir - it.neighbours[3].dir, true);
top_neighbour_left := rot_index(.LEFT, it.neighbours[0].dir);
top\_neighbour.neighbours[top_neighbour_left].face = it.neighbours[3].face;
top\_neighbour.neighbours[top_neighbour_left].dir = rot_dir(it.neighbours[3].dir - it.neighbours[0].dir, false);
}
}
if it.neighbours[1].face != -1 {
right_neighbour := *faces[it.neighbours[1].face];
if it.neighbours[0].face != -1 {
top_neighbour := *faces[it.neighbours[0].face];
top_neighbour_right := rot_index(.RIGHT, it.neighbours[0].dir);
top\ _neighbour.neighbours[top_neighbour_right].face = it.neighbours[1].face;
top\ _neighbour.neighbours[top_neighbour_right].dir = rot_dir(it.neighbours[1].dir - it.neighbours[0].dir, true);
right_neighbour_top := rot_index(.UP, it.neighbours[1].dir);
right_neighbour.neighbours[right_neighbour_top].face = it.neighbours[0].face;
right_neighbour.neighbours[right_neighbour_top].dir = rot_dir(it.neighbours[0].dir - it.neighbours[1].dir, false);
}
if it.neighbours[2].face != -1 {
bottom_neighbour := *faces[it.neighbours[2].face];
bottom_neigbour_right := rot_index(.RIGHT, it.neighbours[2].dir);
bottom_neighbour.neighbours[bottom_neigbour_right].face = it.neighbours[1].face;
bottom_neighbour.neighbours[bottom_neigbour_right].dir = rot_dir(it.neighbours[1].dir - it.neighbours[2].dir, false);
right_neighbour_bottom := rot_index(.DOWN, it.neighbours[1].dir);
right_neighbour.neighbours[right_neighbour_bottom].face = it.neighbours[2].face;
right_neighbour.neighbours[right_neighbour_bottom].dir = rot_dir(it.neighbours[2].dir - it.neighbours[1].dir, true);
}
}
if it.neighbours[2].face != -1 && false {
bottom_neighbour := *faces[it.neighbours[2].face];
if it.neighbours[1].face != -1 {
right_neighbour := *faces[it.neighbours[1].face];
right_neighbour_bottom := rot_index(.DOWN, it.neighbours[1].dir);
right_neighbour.neighbours[right_neighbour_bottom].face = it.neighbours[2].face;
right_neighbour.neighbours[right_neighbour_bottom].dir = rot_dir(it.neighbours[2].dir - it.neighbours[1].dir, true);
bottom_neighbour_right := rot_index(.RIGHT, it.neighbours[2].dir);
bottom\ _neighbour.neighbours[bottom_neighbour_right].face = it.neighbours[1].face;
bottom\ _neighbour.neighbours[bottom_neighbour_right].dir = rot_dir(it.neighbours[1].dir - it.neighbours[2].dir, false);
}
if it.neighbours[3].face != -1 {
left_neighbour := *faces[it.neighbours[3].face];
left_neighbour_bottom := rot_index(.DOWN, it.neighbours[3].dir);
left_neighbour.neighbours[left_neighbour_bottom].face = it.neighbours[2].face;
left_neighbour.neighbours[left_neighbour_bottom].dir = rot_dir(it.neighbours[2].dir - it.neighbours[3].dir, false);
bottom_neighbour_left := rot_index(.LEFT, it.neighbours[2].dir);
bottom\_neighbour.neighbours[bottom_neighbour_left].face = it.neighbours[3].face;
bottom\_neighbour.neighbours[bottom_neighbour_left].dir = rot_dir(it.neighbours[3].dir - it.neighbours[2].dir, true);
}
}
}
}
for faces {
print("%\n", it);
}
}
rot_index :: (dir: Dir, neighbour_dir: Dir) -> s64 {
return xx (dir + neighbour_dir) % 4;
}
rot_dir :: (dir: Dir, right: bool) -> Dir {
res := xx ((xx dir + ifx right then 1 else -1) % 4);
return xx ((ifx res > 0 then res else 4 + res) % 4);
}
convert_dir_to_val :: (dir: Vec2) -> u8 {
assert(abs(dir.x) <= 1 && abs(dir.y) <= 1, "Bad dir values %", dir);
if dir.x == 1 return 0;
if dir.x == -1 return 2;
if dir.y == 1 return 1;
if dir.y == -1 return 3;
return 0;
}
convert_dir_to_char :: (dir: Vec2) -> u8 {
assert(abs(dir.x) <= 1 && abs(dir.y) <= 1, "Bad dir values %", dir);
if dir.x == 1 return #char ">";
if dir.x == -1 return #char "<";
if dir.y == 1 return #char "v";
if dir.y == -1 return #char "^";
return 0;
}
turn :: (current: Vec2, turn: s64) -> Vec2 {
return .{
x = -current.y * turn,
y = current.x * turn,
};
}
parse_instructions :: (section: string) -> [..]Instruction {
start := 0;
instructions: [..]Instruction;
while start < section.count {
current := start;
while current < section.count && is_digit(section[current]) {
current += 1;
}
array_add(*instructions, .{
type = .WALK,
value = string_to_int(slice(section, start, current - start)),
});
if current < section.count {
array_add(*instructions, .{
type = .TURN,
value = ifx section[current] == #char "R" then 1 else -1,
});
}
start = current + 1;
}
return instructions;
}
#scope_file
Instruction :: struct {
value: s64;
type: enum {
TURN :: 0;
WALK :: 1;
} = .TURN;
}
Vec2 :: struct {
x: s64;
y: s64;
}
operator + :: (v1: Vec2, v2: Vec2) -> Vec2 {
return .{
x = v1.x + v2.x,
y = v1.y + v2.y,
};
}
Cube_Face :: struct {
on_map: Vec2;
neighbours: [4]struct {
dir: Dir = .NONE;
face: s64 = -1;
};
}
Dir :: enum {
NONE :: 9;
UP :: 0;
RIGHT :: 1;
DOWN :: 2;
LEFT :: 3;
};