diff --git a/adventofcode/2021/day/19/beacon_scanner.cpp b/adventofcode/2021/day/19/beacon_scanner.cpp new file mode 100644 index 0000000..521d367 --- /dev/null +++ b/adventofcode/2021/day/19/beacon_scanner.cpp @@ -0,0 +1,406 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct symbol { + std::bitset<2> id; + bool negative; + symbol& operator-() { + negative = !negative; + return *this; + } + bool operator==(symbol const& that) const { + return id == that.id && negative == that.negative; + } + bool operator==(char c) const { + switch (c) { + case 'a': + return id == std::bitset<2>(0); + case 'b': + return id == std::bitset<2>(1); + case 'c': + return id == std::bitset<2>(2); + default: + throw std::invalid_argument("Invalid character for symbol. It isn't one of a, b, or c."); + } + } +}; + +namespace math { + +constexpr int dim = 3; +template using vector = std::array; +using matrix = std::array, dim>; + +}; + +struct transformation { + math::matrix rotation; + math::vector translation; +}; + +std::size_t hash_value(symbol const& s) { + return s.id.to_ullong() ^ s.negative; +} + +void print(symbol const& s) { + std::cout << (s.negative ? '-' : ' '); + switch (s.id.to_ulong()) { + case 0: + std::cout << 'a'; + break; + case 1: + std::cout << 'b'; + break; + case 2: + std::cout << 'c'; + break; + default: + throw std::runtime_error("Unexpected symbol codification."); + } +} + +void print(math::vector const& a) { + for (auto const& s : a) { + print(s); + std::cout << ' '; + } +} + +void print(math::matrix A) { + for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) + std::cout << std::setw(2) << A.at(i).at(j) << (j == 2 ? "\n" : " "); +} + +// [ 1 0 0 ] +// [ 0 cos(alpha) sin(alpha) ] +// [ 0 -sin(alpha) cos(alpha) ] +template void Rx(std::array& v) { + T backup = v[1]; + v[1] = v[2]; + v[2] = -backup; +} + +// [ cos(beta) 0 -sin(beta) ] +// [ 0 1 0 ] +// [ sin(beta) 0 cos(beta) ] +template void Ry(std::array& v) { + T backup = v[0]; + v[0] = -v[2]; + v[2] = backup; +} + +// [ cos(gamma) sin(gamma) 0 ] +// [ -sin(gamma) cos(gamma) 0 ] +// [ 0 0 1 ] +template void Rz(std::array& v) { + T backup = v[0]; + v[0] = v[1]; + v[1] = -backup; +} + +// symbolic vector [a b c]^T +std::array const v = [](){ + std::array v; + v.at(0).id = 0, v.at(0).negative = false; + v.at(1).id = 1, v.at(1).negative = false; + v.at(2).id = 2, v.at(2).negative = false; + return v; + }(); + +auto generate_rotations() { + boost::unordered::unordered_flat_set> Qed; + Qed.insert(v); + std::queue> Q; + Q.push(v); + auto g = [&Q, &Qed](auto const& v, std::function&)> R) { + auto w = v; + R(w); + if (!Qed.contains(w)) { + Q.push(w); + Qed.insert(w); + } + }; + while (!Q.empty()) { + std::array const v = Q.front(); + Q.pop(); + g(v, Rx); + g(v, Ry); + g(v, Rz); + } + if (Qed.size() != 24) throw std::runtime_error("Unexpected #rotations."); + return Qed; +} + +auto rotation_vector_mapping(std::array const& item) { + auto find_symbol_position = [](auto const& item, char c){ + for (int pos = 0; pos < 3; pos++) if (item.at(pos) == c) return pos; + throw std::runtime_error("Symbol not found."); + }; + + math::matrix rotation; + rotation.fill(std::array{0, 0, 0}); + for (int offset = 0; offset < 3; offset++) { + int const pos = find_symbol_position(item, 'a'+static_cast(offset)); + rotation.at(offset).at(pos) = item.at(pos).negative ? -1 : 1; + } + return rotation; +} + +auto read_scan_report(std::istream& input_stream) { + std::string line; + std::vector> scan_report; + for (;;) { + std::getline(input_stream, line); + if (line.empty()) break; + auto const p = line.find(','); + int const x = std::stoi(line.substr(0, p)); + auto const q = line.find(',', p+1); + int const y = std::stoi(line.substr(p+1, q-p)); + int const z = std::stoi(line.substr(q+1)); + scan_report.push_back({x, y, z}); + } + return scan_report; +} + +auto read_input() { + std::vector>> scans; + std::string line; + while (std::getline(std::cin, line)) { + //assert(line.substr(0, 12) == "--- scanner "); + scans.push_back(read_scan_report(std::cin)); + } + return scans; +} + +int const mininum_overlapping_beacons = 12; + +auto operator*(math::matrix const& A, math::vector const& v) { + auto matmul = [A, v](int r){ + int c = 0; + for (int k = 0; k < 3; k++) c += A.at(r).at(k) * v.at(k); + return c; + }; + return std::make_tuple(matmul(0), matmul(1), matmul(2)); +} + +int main() { + auto const rotations = generate_rotations(); + + auto const scans = read_input(); + + std::unordered_map> A; + std::unordered_map> neighbors; + + for (size_t scan_idx_i = 0; scan_idx_i < scans.size(); scan_idx_i++) { + for (size_t scan_idx_j = scan_idx_i + 1; scan_idx_j < scans.size(); scan_idx_j++) { + + auto const& scan0 = scans.at(scan_idx_i); + auto const& scan1 = scans.at(scan_idx_j); + + bool overlap_found = false; + for (size_t i = 0; i < scan0.size() && !overlap_found; i++) for (size_t j = 0; j < scan1.size() && !overlap_found; j++) { + for (auto const& item : rotations) { + auto const rotation = rotation_vector_mapping(item); + // Apply rotation to scan1[j] + auto const [x1, y1, z1] = rotation * scan1.at(j); + // Compute shift from scan1[j] and scan0[i] + auto const dx = x1 - scan0.at(i).at(0); + auto const dy = y1 - scan0.at(i).at(1); + auto const dz = z1 - scan0.at(i).at(2); + // Assertion: + assert(scan0[i] == std::make_tuple(x1-dx, y1-dy, z1-dz)); + // Count the number of points in scan1 that after rotating and shifting + // are points in scan0 + boost::unordered::unordered_flat_set> scan0set; + for (size_t ii = 0; ii < scan0.size(); ii++) { + if (ii == i) continue; + scan0set.insert(scan0.at(ii)); + } + int matches_count = 1; // starting from 1 because scan1[j]'s been placed at scan0[i] + std::vector, std::tuple>> matches; + matches.emplace_back(scan0.at(i), scan1.at(j)); + for (size_t jj = 0; jj < scan1.size(); jj++) { + if (jj == j) continue; + auto const [xjj, yjj, zjj] = rotation * scan1.at(jj); + auto const p = std::make_tuple(xjj-dx, yjj-dy, zjj-dz); + if (scan0set.contains(p)) { + matches_count += 1; + scan0set.erase(p); + matches.emplace_back(p, scan1.at(jj)); + } + if (matches_count == mininum_overlapping_beacons) break; + } + if (matches_count == mininum_overlapping_beacons) { + overlap_found = true; + A[scan_idx_i][scan_idx_j] = {rotation, {-dx, -dy, -dz}}; + neighbors[scan_idx_i].push_back(scan_idx_j); + neighbors[scan_idx_j].push_back(scan_idx_i); + std::println("{} -> {}:", scan_idx_i, scan_idx_j); + if (scan_idx_i == 0 && scan_idx_j == 1) { + /* + std::println("Beacons relative to scanner 0:"); + for (auto const& pq : matches) std::println(" {}", pq.first); + std::println("Beacons relative to scanner 1:"); + for (auto const& pq : matches) std::println(" {}", pq.second); + */ + std::println("Scanner 1 is at {}", A[0][1].translation); + } + break; + } + } + } + + } + } + + // Is there more than one connected component? + /* + { + std::unordered_set c; + std::queue d; + c.insert(0); + d.push(0); + while (!d.empty()) { + int const n = d.front(); + d.pop(); + for (int const m : neighbors[n]) if (!c.contains(m)) { + c.insert(m); + d.push(m); + } + } + std::println("scans size: {}, #CC: {}", scans.size(), c.size()); + } + std::println(); + */ + + boost::unordered::unordered_flat_set> pointsAt0; + for (auto const& p : scans.at(0)) pointsAt0.emplace(p.at(0), p.at(1), p.at(2)); + + std::vector> scanners_positions(scans.size()); + scanners_positions.at(0) = {0, 0, 0}; + // Paths to scan0: + { + for (size_t scan_idx = 1; scan_idx < scans.size(); scan_idx++) { + std::queue>> Q; + Q.emplace(scan_idx, std::vector{}); + boost::unordered::unordered_flat_set Qed; + Qed.insert(scan_idx); + while (!Q.empty()) { + auto [node, path] = Q.front(); + Q.pop(); + for (auto const neighbor : neighbors[node]) if (!Qed.contains(neighbor)) { + if (neighbor == 0) { + std::print("{} -> ", scan_idx); + for (auto const intermediate_node : path) { + std::print("{} -> ", intermediate_node); + } + std::println("0"); + + // Validating on sample input: + { + math::vector p{0,0,0}; + auto from = scan_idx; + for (size_t path_idx = 0; path_idx <= path.size(); path_idx++) { + auto const to = path_idx == path.size() ? 0 : path[path_idx]; + std::print(" From {} to {}: ", from, to); + if (A.contains(from) && A.at(from).contains(to)) { + std::println("direct"); + // translate + for (int c = 0; c < 3; c++) { + p[c] -= A[from][to].translation[c]; + } + math::matrix inverse; + for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) { + inverse[r][c] = A[from][to].rotation[c][r]; + } + // rotate + auto const [x, y, z] = inverse * p; + p[0] = x, p[1] = y, p[2] = z; + } else { + assert(A.contains(to) && A[to].contains(from)); + std::println("inverse"); + // rotate + auto const [x, y, z] = A[to][from].rotation * p; + p[0] = x, p[1] = y, p[2] = z; + // translate + for (int c = 0; c < 3; c++) { + p[c] += A[to][from].translation[c]; + } + } + from = to; + } + std::println("Scanner {} is at {}", scan_idx, p); + scanners_positions.at(scan_idx) = p; + } + + { + for (auto const& pconst : scans.at(scan_idx)) { + auto p = pconst; + auto from = scan_idx; + for (size_t path_idx = 0; path_idx <= path.size(); path_idx++) { + auto const to = path_idx == path.size() ? 0 : path[path_idx]; + if (A.contains(from) && A.at(from).contains(to)) { + // translate + for (int c = 0; c < 3; c++) { + p[c] -= A[from][to].translation[c]; + } + math::matrix inverse; + for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) { + inverse[r][c] = A[from][to].rotation[c][r]; + } + // rotate + auto const [x, y, z] = inverse * p; + p[0] = x, p[1] = y, p[2] = z; + } else { + assert(A.contains(to) && A[to].contains(from)); + // rotate + auto const [x, y, z] = A[to][from].rotation * p; + p[0] = x, p[1] = y, p[2] = z; + // translate + for (int c = 0; c < 3; c++) { + p[c] += A[to][from].translation[c]; + } + } + from = to; + } + pointsAt0.emplace(p.at(0), p.at(1), p.at(2)); + } + } + + break; + } else { + path.push_back(neighbor); + Qed.insert(neighbor); + Q.emplace(neighbor, path); + path.pop_back(); + } + } + } + } + std::println("In total, there are {} beacons.", pointsAt0.size()); + } + + int part_two_ans = 0; + for (size_t scanner_idx_i = 0; scanner_idx_i < scanners_positions.size(); scanner_idx_i++) { + for (size_t scanner_idx_j = scanner_idx_i+1; scanner_idx_j < scanners_positions.size(); scanner_idx_j++) { + part_two_ans = std::max(part_two_ans, + std::abs(scanners_positions.at(scanner_idx_i).at(0) - scanners_positions.at(scanner_idx_j).at(0)) + + std::abs(scanners_positions.at(scanner_idx_i).at(1) - scanners_positions.at(scanner_idx_j).at(1)) + + std::abs(scanners_positions.at(scanner_idx_i).at(2) - scanners_positions.at(scanner_idx_j).at(2))); + } + } + std::println("In total, they are {} units apart.", part_two_ans); +} diff --git a/adventofcode/2021/day/19/transformation_visualizer/README.md b/adventofcode/2021/day/19/transformation_visualizer/README.md new file mode 100644 index 0000000..8d35295 --- /dev/null +++ b/adventofcode/2021/day/19/transformation_visualizer/README.md @@ -0,0 +1,6 @@ +plug_update + draw_frame(&plug->reference_frame0) + +master_sequence(arena) + scene_transformation_visualizer(arena) + task_move_scalar(arena, &plug->reference_frame0 diff --git a/adventofcode/2021/day/19/transformation_visualizer/plug.c b/adventofcode/2021/day/19/transformation_visualizer/plug.c new file mode 100644 index 0000000..fbc3171 --- /dev/null +++ b/adventofcode/2021/day/19/transformation_visualizer/plug.c @@ -0,0 +1,264 @@ +#include +#include +#include + +#include +#include + +#include "nob.h" +#include "env.h" +#include "interpolators.h" +#include "tasks.h" +#include "plug.h" + +#define PLUG(name, ret, ...) ret name(__VA_ARGS__); +LIST_OF_PLUGS +#undef PLUG + +// --- DATA STRUCTURES --- + +typedef struct { + Vector3 origin; + Vector3 i_hat; + Vector3 j_hat; + Vector3 k_hat; + float opacity; +} ReferenceFrame; + +typedef struct { + size_t size; + Arena arena_state; + Arena arena_assets; + bool finished; + + // Global Camera + Camera3D camera; + + // Scene State + int current_scene_index; + Task task; + + // Visual Elements + ReferenceFrame f0; // For TransformationVisualizer + //ReferenceFrame f1; // For MatrixTransformScene + //ReferenceFrame fa; // For CompareFramesScene + //ReferenceFrame fb; // For CompareFramesScene + +} Plug; + +static Plug *p = NULL; + +// --- HELPERS --- + +static ReferenceFrame default_frame(Vector3 origin) { + return (ReferenceFrame){ + .origin = origin, + .i_hat = (Vector3){1.0f, 0.0f, 0.0f}, + .j_hat = (Vector3){0.0f, 1.0f, 0.0f}, + .k_hat = (Vector3){0.0f, 0.0f, 1.0f}, + .opacity = 0.0f // Start invisible + }; +} + +static void draw_vector(Vector3 start, Vector3 vec, Color color, float opacity) { + Vector3 end = Vector3Add(start, vec); + Color c = ColorAlpha(color, opacity); + DrawLine3D(start, end, c); + DrawSphere(end, 0.05f, c); // Tip +} + +static void draw_frame(ReferenceFrame *f) { + if (f->opacity <= 0.01f) return; + + Color color_x = ColorAlpha(RED, f->opacity); + Color color_y = ColorAlpha(GREEN, f->opacity); + Color color_z = ColorAlpha(BLUE, f->opacity); + Color color_origin = ColorAlpha(WHITE, f->opacity); + + draw_vector(f->origin, f->i_hat, color_x, f->opacity); + draw_vector(f->origin, f->j_hat, color_y, f->opacity); + draw_vector(f->origin, f->k_hat, color_z, f->opacity); + DrawSphere(f->origin, 0.05f, color_origin); +} + +// --- TASKS --- + +static Task scene_transformation_visualizer(Arena *a) { + ReferenceFrame *f0 = &p->f0; + *f0 = default_frame((Vector3){0,0,0}); + + // Rotation Matrix: 90 deg around Z + /* + Vector3 rot_i = (Vector3){0, 1, 0}; + Vector3 rot_j = (Vector3){-1, 0, 0}; + Vector3 rot_k = (Vector3){0, 0, 1}; + */ + Vector3 rot_i = (Vector3){1, 0, 0}; + Vector3 rot_j = (Vector3){0, 1, 0}; + Vector3 rot_k = (Vector3){0, 0, 1}; + + Vector3 shift_val = (Vector3){2, 0, 0}; + Vector3 final_origin = Vector3Add(f0->origin, shift_val); + + return task_seq(a, + task_move_scalar(a, &f0->opacity, 1.0f, 1.0f, FUNC_SMOOTHSTEP), + task_wait(a, 0.5f), + task_group(a, + task_move_vec3(a, &f0->i_hat, rot_i, 1.0f, FUNC_SMOOTHSTEP), + task_move_vec3(a, &f0->j_hat, rot_j, 1.0f, FUNC_SMOOTHSTEP), + task_move_vec3(a, &f0->k_hat, rot_k, 1.0f, FUNC_SMOOTHSTEP) + ), + task_move_vec3(a, &f0->origin, final_origin, 1.0f, FUNC_SMOOTHSTEP), + task_wait(a, 2.0f) + ); +} + +/* +static Task scene_matrix_transform(Arena *a) { + ReferenceFrame *f1 = &p->f1; + *f1 = default_frame((Vector3){0,0,0}); + + // Rotation Matrix: 90 deg around Z + Vector3 target_i = (Vector3){0, 1, 0}; + Vector3 target_j = (Vector3){-1, 0, 0}; + Vector3 target_k = (Vector3){0, 0, 1}; + + Vector3 translation = (Vector3){3, 2, 1}; + Vector3 final_origin = Vector3Add(f1->origin, translation); + + return task_seq(a, + task_move_scalar(a, &f1->opacity, 1.0f, 1.0f, FUNC_SMOOTHSTEP), + task_wait(a, 0.5f), + task_group(a, + task_move_vec3(a, &f1->i_hat, target_i, 2.0f, FUNC_SMOOTHSTEP), + task_move_vec3(a, &f1->j_hat, target_j, 2.0f, FUNC_SMOOTHSTEP), + task_move_vec3(a, &f1->k_hat, target_k, 2.0f, FUNC_SMOOTHSTEP) + ), + task_wait(a, 0.5f), + task_move_vec3(a, &f1->origin, final_origin, 2.0f, FUNC_SMOOTHSTEP), + task_wait(a, 2.0f) + ); +} +*/ + +/* +static Task scene_compare_frames(Arena *a) { + ReferenceFrame *fa = &p->fa; + ReferenceFrame *fb = &p->fb; + + Vector3 pos_a = (Vector3){0,0,0}; + Vector3 pos_b = (Vector3){2,1,0}; + + *fa = default_frame(pos_a); + *fb = default_frame(pos_b); + + // Rotation Matrix: 90 deg around Z + Vector3 target_i = (Vector3){0, 1, 0}; + Vector3 target_j = (Vector3){-1, 0, 0}; + Vector3 target_k = (Vector3){0, 0, 1}; + + fb->i_hat = target_i; + fb->j_hat = target_j; + fb->k_hat = target_k; + + return task_seq(a, + task_move_scalar(a, &fa->opacity, 1.0f, 1.0f, FUNC_SMOOTHSTEP), + task_wait(a, 0.5f), + task_move_scalar(a, &fb->opacity, 0.3f, 1.0f, FUNC_SMOOTHSTEP), + task_wait(a, 0.5f), + task_group(a, + task_move_vec3(a, &fa->origin, pos_b, 3.0f, FUNC_SMOOTHSTEP), + task_move_vec3(a, &fa->i_hat, target_i, 3.0f, FUNC_SMOOTHSTEP), + task_move_vec3(a, &fa->j_hat, target_j, 3.0f, FUNC_SMOOTHSTEP), + task_move_vec3(a, &fa->k_hat, target_k, 3.0f, FUNC_SMOOTHSTEP) + ), + task_wait(a, 2.0f) + ); +} +*/ + +static Task master_sequence(Arena *a) { + return task_seq(a, + scene_transformation_visualizer(a), + task_move_scalar(a, &p->f0.opacity, 0.0f, 0.5f, FUNC_SMOOTHSTEP)/*, + scene_matrix_transform(a), + task_move_scalar(a, &p->f1.opacity, 0.0f, 0.5f, FUNC_SMOOTHSTEP), + scene_compare_frames(a), + task_move_scalar(a, &p->fa.opacity, 0.0f, 0.5f, FUNC_SMOOTHSTEP), + task_move_scalar(a, &p->fb.opacity, 0.0f, 0.5f, FUNC_SMOOTHSTEP)*/ + ); +} + +// --- PLUG IMPL --- + +static void load_assets(void) +{ + arena_reset(&p->arena_assets); + task_vtable_rebuild(&p->arena_assets); + TraceLog(LOG_INFO, "Transformation Visualizer: Assets and VTable loaded"); +} + +void plug_init(void) +{ + p = malloc(sizeof(*p)); + assert(p != NULL); + memset(p, 0, sizeof(*p)); + p->size = sizeof(*p); + + p->camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; + p->camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + p->camera.up = (Vector3){ 0.0f, 0.0f, 1.0f }; + p->camera.fovy = 45.0f; + p->camera.projection = CAMERA_PERSPECTIVE; + + load_assets(); + plug_reset(); +} + +void *plug_pre_reload(void) +{ + return p; +} + +void plug_post_reload(void *state) +{ + p = state; + if (p->size < sizeof(*p)) { + p = realloc(p, sizeof(*p)); + p->size = sizeof(*p); + } + load_assets(); +} + +void plug_update(Env env) +{ + p->finished = task_update(p->task, env); + ClearBackground(BLACK); + BeginMode3D(p->camera); + DrawGrid(10, 1.0f); + draw_frame(&p->f0); + //draw_frame(&p->f1); + //draw_frame(&p->fa); + //draw_frame(&p->fb); + EndMode3D(); +} + +void plug_reset(void) +{ + arena_reset(&p->arena_state); + p->f0 = default_frame((Vector3){0,0,0}); + //p->f1 = default_frame((Vector3){0,0,0}); + //p->fa = default_frame((Vector3){0,0,0}); + //p->fb = default_frame((Vector3){0,0,0}); + p->task = master_sequence(&p->arena_state); + p->finished = false; +} + +bool plug_finished(void) +{ + return p->finished; +} + +#define ARENA_IMPLEMENTATION +#include "arena.h" +#include "tasks.c"