diff --git a/.gitignore b/.gitignore index 796b96d..4700ec6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /build +/emcc_build +/debug +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index bde537c..a1c38a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,14 @@ -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.24) project(somaesque) + +set(CMAKE_BUILD_TYPE Release) +set(CMAKE_CXX_FLAGS_RELEASE "-O3") + #find_package(glfw3 3.3 REQUIRED) #find_package(glm REQUIRED) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_FLAGS -I/usr/include/SDL2) +set(CMAKE_CXX_STANDARD 20) +#set(CMAKE_CXX_FLAGS -I/usr/include/SDL2) add_executable(somaesque main.cpp @@ -32,7 +36,7 @@ add_executable(tests target_link_libraries( tests GTest::gtest_main -) + ) include(GoogleTest) gtest_discover_tests(tests) diff --git a/VoxelSpace.cpp b/VoxelSpace.cpp index 7023702..1e027a0 100644 --- a/VoxelSpace.cpp +++ b/VoxelSpace.cpp @@ -1,46 +1,48 @@ -#include "VoxelSpace.h" #include +#include #include #include #include +#include "VoxelSpace.h" namespace Voxel { - inline auto index(int dims[3], int x, int y, int z) -> int { - return (dims[1] * dims[2] * x + dims[2] * y + z); + auto index(int dim_y, int dim_z, int x, int y, int z) -> int { + return dim_y * dim_z * x + dim_z * y + z; } // [1, 0, 0] [x] [ x] // [0, 0, -1] * [y] = [-z] // [0, 1, 0] [z] [ y] - inline auto newIndexRotX(int dims[3], int x, int y, int z) -> int { - return dims[2] * dims[1] * x + dims[1] * (dims[2] - 1 - z) + y; + auto newIndexRotX(Space *space, int x, int y, int z) -> int { + return space->dim_z * space->dim_y * x + space->dim_y * (space->dim_z - 1 - z) + y; } // [ 0, 0, 1] [x] [ z] // [ 0, 1, 0] * [y] = [ y] // [-1, 0, 0] [z] [-x] - inline auto newIndexRotY(int dims[3], int x, int y, int z) -> int { - return dims[1] * dims[0] * z + dims[0] * y + (dims[0] - 1 - x); + auto newIndexRotY(Space *space, int x, int y, int z) -> int { + return space->dim_y * space->dim_x * z + space->dim_x * y + (space->dim_x - 1 - x); } // [0, -1, 0] [x] [-y] // [1, 0, 0] * [y] = [ x] // [0, 0, 1] [z] [ z] - inline auto newIndexRotZ(int dims[3], int x, int y, int z) -> int { - return dims[0] * dims[2] * (dims[1] - 1 - y) + dims[2] * x + z; + auto newIndexRotZ(Space *space, int x, int y, int z) -> int { + return space->dim_x * space->dim_z * (space->dim_y - 1 - y) + space->dim_z * x + z; } - inline auto toggle(uint64_t space, int index) -> uint64_t { - space ^= 1ull << index; + auto toggle(uint64_t space, int index) -> uint64_t { + space ^= 1ul << index; return space; } - inline auto set(uint64_t *space, int index, bool val) -> void { + auto set(uint64_t space, int index, bool val) -> uint64_t { if (val) { - *space |= 1ull << index; + space |= 1ul << index; } else { - *space &= ~(1ull << index); + space &= ~(1ul << index); } + return space; } auto collides(uint64_t a, uint64_t b) -> bool { @@ -51,25 +53,25 @@ namespace Voxel { return (a->space | b->space) != (a->space ^ b->space); } - inline auto filledAt(uint64_t space, int dims[3], int x, int y, int z) -> bool { - auto mask = 1ull << (dims[1] * dims[2] * x + dims[2] * y + z); - return (space & mask) != 0ull; + auto filledAt(Space *space, int x, int y, int z) -> bool { + auto mask = 1ul << (space->dim_y * space->dim_z * x + space->dim_z * y + z); + return (space->space & mask) != 0ul; } - auto getExtrema(uint64_t space, int dims[3]) -> Extrema { + auto getExtrema(Space *space) -> Extrema { auto extrema = Extrema{ .xMax=0, - .xMin=dims[0], + .xMin=space->dim_x, .yMax=0, - .yMin=dims[1], + .yMin=space->dim_y, .zMax=0, - .zMin=dims[2], + .zMin=space->dim_z, }; - for (int x = 0; x < dims[0]; x++) { - for (int y = 0; y < dims[1]; y++) { - for (int z = 0; z < dims[2]; z++) { - if (filledAt(space, dims, x, y, z)) { + for (int x = 0; x < space->dim_x; x++) { + for (int y = 0; y < space->dim_y; y++) { + for (int z = 0; z < space->dim_z; z++) { + if (filledAt(space, x, y, z)) { if (x > extrema.xMax) extrema.xMax = x; if (x < extrema.xMin) extrema.xMin = x; if (y > extrema.yMax) extrema.yMax = y; @@ -85,81 +87,81 @@ namespace Voxel { } auto cullEmptySpace(Space *space) -> void { - auto extrema = getExtrema(space->space, space->dims); + auto extrema = getExtrema(space); auto space_index = 0; - auto newSpace = 0ull; + auto newSpace = 0ul; for (int x = extrema.xMin; x <= extrema.xMax; x++) { for (int y = extrema.yMin; y <= extrema.yMax; y++) { for (int z = extrema.zMin; z <= extrema.zMax; z++) { - if (filledAt(space->space, space->dims, x, y, z)) { - newSpace |= 1ull << space_index; + if (filledAt(space, x, y, z)) { + newSpace |= 1ul << space_index; } space_index++; } } } - space->dims[0] = extrema.xMax - extrema.xMin + 1; - space->dims[1] = extrema.yMax - extrema.yMin + 1; - space->dims[2] = extrema.zMax - extrema.zMin + 1; + space->dim_x = extrema.xMax - extrema.xMin + 1; + space->dim_y = extrema.yMax - extrema.yMin + 1; + space->dim_z = extrema.zMax - extrema.zMin + 1; space->space = newSpace; } auto rotate90X(Space *space) -> void { - auto new_space = 0ull; - for (int x = 0; x < space->dims[0]; x++) { - for (int y = 0; y < space->dims[1]; y++) { - for (int z = 0; z < space->dims[2]; z++) { - if (filledAt(space->space, space->dims, x, y, z)) { - new_space |= 1 << newIndexRotX(space->dims, x, y, z); + auto new_space = 0ul; + for (int x = 0; x < space->dim_x; x++) { + for (int y = 0; y < space->dim_y; y++) { + for (int z = 0; z < space->dim_z; z++) { + if (filledAt(space, x, y, z)) { + new_space |= 1 << newIndexRotX(space, x, y, z); } } } } - auto temp = space->dims[1]; - space->dims[1] = space->dims[2]; - space->dims[2] = temp; + auto temp = space->dim_y; + space->dim_y = space->dim_z; + space->dim_z = temp; space->space = new_space; } auto rotate90Y(Space *space) -> void { - auto new_space = 0ull; - for (int x = 0; x < space->dims[0]; x++) { - for (int y = 0; y < space->dims[1]; y++) { - for (int z = 0; z < space->dims[2]; z++) { - if (filledAt(space->space, space->dims, x, y, z)) { - new_space |= 1 << newIndexRotY(space->dims, x, y, z); + auto new_space = 0ul; + for (int x = 0; x < space->dim_x; x++) { + for (int y = 0; y < space->dim_y; y++) { + for (int z = 0; z < space->dim_z; z++) { + if (filledAt(space, x, y, z)) { + new_space |= 1 << newIndexRotY(space, x, y, z); } } } } - auto temp = space->dims[0]; - space->dims[0] = space->dims[2]; - space->dims[2] = temp; + auto temp = space->dim_x; + space->dim_x = space->dim_z; + space->dim_z = temp; space->space = new_space; } auto rotate90Z(Space *space) -> void { - auto new_space = 0ull; - for (int x = 0; x < space->dims[0]; x++) { - for (int y = 0; y < space->dims[1]; y++) { - for (int z = 0; z < space->dims[2]; z++) { - if (filledAt(space->space, space->dims, x, y, z)) { - new_space |= 1 << newIndexRotZ(space->dims, x, y, z); + auto new_space = 0ul; + for (int x = 0; x < space->dim_x; x++) { + for (int y = 0; y < space->dim_y; y++) { + for (int z = 0; z < space->dim_z; z++) { + if (filledAt(space, x, y, z)) { + new_space |= 1 << newIndexRotZ(space, x, y, z); } } } } - auto temp = space->dims[0]; - space->dims[0] = space->dims[1]; - space->dims[1] = temp; + auto temp = space->dim_x; + space->dim_x = space->dim_y; + space->dim_y = temp; space->space = new_space; } - inline auto isMatch(Space *a, Space *b) -> bool { + auto isMatch(Space *a, Space *b) -> bool { return a->space == b->space - && a->dims[0] == b->dims[0] - && a->dims[1] == b->dims[1] - && a->dims[2] == b->dims[2]; + && a->dim_x == b->dim_x + && a->dim_y == b->dim_y + && a->dim_z == b->dim_z; } auto pushNewUniqueSpins(std::vector *existingSpaces, Space* spaceToSpin) -> void { @@ -185,7 +187,7 @@ namespace Voxel { auto pushXAxisSpins(std::vector *existingSpaces, Space* spaceToSpin) -> void { auto refSpace = *spaceToSpin; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 4; i++) { rotate90X(&refSpace); existingSpaces->push_back(refSpace); } @@ -193,30 +195,14 @@ namespace Voxel { auto getUniqueRotations(Space *space) -> std::vector { auto rotations = std::vector(); - rotations.reserve(6*24); - auto dims = space->dims; + rotations.reserve(24); auto refSpace = *space; + cullEmptySpace(&refSpace); pushNewUniqueSpins(&rotations, &refSpace); rotate90Y(&refSpace); pushNewUniqueSpins(&rotations, &refSpace); rotate90Y(&refSpace); pushNewUniqueSpins(&rotations, &refSpace); - rotate90Z(&refSpace); - pushNewUniqueSpins(&rotations, &refSpace); - rotate90Z(&refSpace); - rotate90Z(&refSpace); - pushNewUniqueSpins(&rotations, &refSpace); - return rotations; - } - - auto getUniqueRotations(Space *space) -> std::vector { - auto rotations = std::vector(); - rotations.reserve(6*24); - auto dims = space->dims; - auto refSpace = *space; - pushNewUniqueSpins(&rotations, &refSpace); - rotate90Y(&refSpace); - pushNewUniqueSpins(&rotations, &refSpace); rotate90Y(&refSpace); pushNewUniqueSpins(&rotations, &refSpace); rotate90Z(&refSpace); @@ -229,41 +215,41 @@ namespace Voxel { auto getAllRotations(Space *space) -> std::vector { auto rotations = std::vector(); - rotations.reserve(6*24); - auto dims = space->dims; + rotations.reserve(24); auto refSpace = *space; pushXAxisSpins(&rotations, &refSpace); rotate90Y(&refSpace); pushXAxisSpins(&rotations, &refSpace); rotate90Y(&refSpace); - pushNewUniqueSpins(&rotations, &refSpace); + pushXAxisSpins(&rotations, &refSpace); + rotate90Y(&refSpace); + pushXAxisSpins(&rotations, &refSpace); rotate90Z(&refSpace); - pushNewUniqueSpins(&rotations, &refSpace); + pushXAxisSpins(&rotations, &refSpace); rotate90Z(&refSpace); rotate90Z(&refSpace); - pushNewUniqueSpins(&rotations, &refSpace); + pushXAxisSpins(&rotations, &refSpace); return rotations; } - auto getAllPositionsInPrism(uint64_t space, int space_dims[3], int prism_dims[3]) -> std::vector { + auto getAllPositionsInPrism(Space *space, int prism_dims[3]) -> std::vector { auto cubePositions = std::vector(); - if (space_dims[0] > prism_dims[0] || space_dims[1] > prism_dims[1] || space_dims[2] > prism_dims[2]) { + if (space->dim_x > prism_dims[0] || space->dim_y > prism_dims[1] || space->dim_z > prism_dims[2]) { return cubePositions; } - auto xPositionCount = prism_dims[0] - space_dims[0] + 1; - auto yPositionCount = prism_dims[1] - space_dims[1] + 1; - auto zPositionCount = prism_dims[2] - space_dims[2] + 1; - cubePositions.reserve(xPositionCount + yPositionCount + zPositionCount); + auto xPositionCount = prism_dims[0] - space->dim_x + 1; + auto yPositionCount = prism_dims[1] - space->dim_y + 1; + auto zPositionCount = prism_dims[2] - space->dim_z + 1; for (int x = 0; x < xPositionCount; x++) { for (int y = 0; y < yPositionCount; y++) { for (int z = 0; z < zPositionCount; z++) { - auto new_space = space; - for (int posX = 0; posX < space_dims[0]; posX++) { - for (int posY = 0; posY < space_dims[1]; posY++) { - for (int posZ = 0; posZ < space_dims[2]; posZ++) { - auto set_val = filledAt(space, space_dims, x, y, z); - auto index_to_set = index(space_dims, x + posX, y + posY, z + posZ); - set(&new_space, index_to_set, set_val); + auto new_space = 0ul; + for (int posX = 0; posX < space->dim_x; posX++) { + for (int posY = 0; posY < space->dim_y; posY++) { + for (int posZ = 0; posZ < space->dim_z; posZ++) { + auto set_val = filledAt(space, posX, posY, posZ); + auto index_to_set = index(prism_dims[1], prism_dims[2], x + posX, y + posY, z + posZ); + new_space = set(new_space, index_to_set, set_val); } } } @@ -278,7 +264,7 @@ namespace Voxel { auto rotations = getUniqueRotations(space); auto result = std::vector(); for (auto &rotation : rotations) { - auto positions = getAllPositionsInPrism(rotation.space, rotation.dims, prism_dims); + auto positions = getAllPositionsInPrism(&rotation, prism_dims); result.insert(result.end(), positions.begin(), positions.end()); } return result; @@ -287,7 +273,7 @@ namespace Voxel { auto size(uint64_t space) -> int { auto size = 0; for (int i = 0; i < 64; i++) { - if ((space & (1ull << i)) != 0) { + if ((space & (1ul << i)) != 0) { size++; } } diff --git a/VoxelSpace.h b/VoxelSpace.h index e82c48e..d2e2e7b 100644 --- a/VoxelSpace.h +++ b/VoxelSpace.h @@ -5,6 +5,8 @@ #include namespace Voxel { + constexpr int NUM_ROTS_3D = 24; + struct Extrema { int xMax; int xMin; @@ -16,16 +18,16 @@ namespace Voxel { struct Space { uint64_t space; - int dims[3]; + int dim_x; + int dim_y; + int dim_z; }; - auto index(int dims[3], int x, int y, int z) -> int; - - auto newIndexRotX(int dims[3], int x, int y, int z) -> int; + auto newIndexRotX(Space *space, int x, int y, int z) -> int; - auto newIndexRotY(int dims[3], int x, int y, int z) -> int; + auto newIndexRotY(Space *space, int x, int y, int z) -> int; - auto newIndexRotZ(int dims[3], int x, int y, int z) -> int; + auto newIndexRotZ(Space *space, int x, int y, int z) -> int; auto toggle(uint64_t space, int index) -> uint64_t; @@ -36,9 +38,9 @@ namespace Voxel { auto add(Space *a, Space *b) -> Space; - auto filledAt(uint64_t space, int dims[3], int x, int y, int z) -> bool; + auto filledAt(Space *space, int x, int y, int z) -> bool; - auto getExtrema(uint64_t space, int dims[3]) -> Extrema; + auto getExtrema(Space *space) -> Extrema; auto cullEmptySpace(Space *space) -> void; @@ -54,7 +56,9 @@ namespace Voxel { auto getUniqueRotations(Space *space) -> std::vector; - auto getAllPositionsInPrism(uint64_t space, int space_dims[3], int prism_dims[3]) -> std::vector; + auto getAllRotations(Space *space) -> std::vector; + + auto getAllPositionsInPrism(Space *space, int prism_dims[3]) -> std::vector; auto getAllPermutationsInPrism(Space *space, int prism_dims[3]) -> std::vector; diff --git a/main.cpp b/main.cpp index 3bcf2e4..58347e3 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,6 +7,14 @@ #include #include "VoxelSpace.h" +using SomaSolution = std::vector; + +struct Solver { + std::vector* input; + std::vector* offsets; + std::vector* solutions; +}; + auto backtrack_solve_iter(std::vector *polycube_input, std::vector *offsets)-> void { auto num_inputs = offsets->size() - 1; @@ -14,7 +23,7 @@ auto backtrack_solve_iter(std::vector *polycube_input, std::vector(); auto curr_soln_stack = std::vector(); auto soln_spaces_stack = std::vector(); - soln_spaces_stack.push_back(0ull); + soln_spaces_stack.push_back(0ul); auto depth = 0; @@ -54,47 +63,76 @@ auto backtrack_solve_iter(std::vector *polycube_input, std::vector *polycube_input, std::vector *offsets, uint64_t working_solution = 0ull, int set = 0) -> int { - auto solns = 0; - auto start = offsets->at(set); - auto end = offsets->at(set + 1); +auto backtrack_solve(Solver *solver, uint64_t working_solution = 0ul, int curr_piece = 0) -> void { + auto input = solver->input; + auto offsets = solver->offsets; + auto solutions = solver->solutions; + auto start = offsets->at(curr_piece); + auto end = offsets->at(curr_piece + 1); + auto num_pieces = offsets->size() - 1; for (int i = start; i < end; i++) { - auto successful_fuse = !Voxel::collides(working_solution, polycube_input->at(i)); + auto successful_fuse = !Voxel::collides(working_solution, input->at(i)); if (successful_fuse) { - working_solution = working_solution | polycube_input->at(i); - if (set == offsets->size() - 2) { - return solns + 1; + auto new_working_solution = working_solution | input->at(i); + solutions->back().at(curr_piece) = input->at(i); + if (curr_piece == num_pieces - 1) { + auto last_soln = solutions->back(); + solutions->push_back(SomaSolution(last_soln.begin(), last_soln.end())); + return; } else { - solns += backtrack_solve(polycube_input, offsets, working_solution, set + 1); + backtrack_solve(solver, new_working_solution, curr_piece + 1); } } } - return solns; + if (curr_piece == 0) { + solutions->pop_back(); + } } -auto filter_unique(std::vector> *solutions, int dims[3]) -> std::vector> { - if (solutions->size() == 0) { - return std::vector>(); - } - auto unique_solns = std::vector>(); - for (auto &solution : unique_solns) { - auto foundMatch = false; - auto soln_rotations = std::vector>(); - for (auto &piece : solution) { - auto space = Voxel::Space{ .space=solution.at(0), .dims={ dims[0], dims[1], dims[2] } }; - auto unique_rots = Voxel::getUniqueRotations(&space); - soln_rotations.insert(soln_rotations.end(), unique_rots.begin(), unique_rots.end()); +auto get_solution_rotations(SomaSolution *solution, int dims[3]) -> std::vector { + auto result = std::vector(Voxel::NUM_ROTS_3D); + for (int piece_i = 0; piece_i < solution->size(); piece_i++) { + auto space = Voxel::Space{ + .space=solution->at(piece_i), + .dim_x=dims[0], + .dim_y=dims[1], + .dim_z=dims[2], + }; + auto piece_rotations = Voxel::getAllRotations(&space); + for (int rot_i = 0; rot_i < piece_rotations.size(); rot_i++) { + result[rot_i].push_back(piece_rotations[rot_i].space); } - for (auto &rotation : soln_rotations) { - auto end = unique_solns.size(); - for (int i = 0; i < end; i++) { - if (rotation == unique_solns[i]) { - foundMatch = true; + } + return result; +} + +auto filter_unique(std::vector *solutions, int dims[3]) -> std::vector { + if (solutions->size() == 0) { + return std::vector(); + } + auto unique_solns = std::vector{}; + for (auto &solution : *solutions) { + auto found_match = false; + for (auto &rotation : get_solution_rotations(&solution, dims)) { + for (auto &unique_soln : unique_solns) { + auto is_match = true; + for (int piece_i = 0; piece_i < unique_soln.size(); piece_i++) { + if (rotation[piece_i] != unique_soln[piece_i]) { + is_match = false; + break; + } + } + if (is_match) { + found_match = true; + break; } } + if (found_match) { + break; + } } - if (!foundMatch) { - unique_solns.push_back(solution); + if (!found_match) { + unique_solns.push_back(SomaSolution(solution)); } } return unique_solns; @@ -137,12 +175,12 @@ auto get_reprs_input(int units_required) -> std::vector { continue; } } - auto bit_repr = 0ull; + auto bit_repr = 0ul; auto i = 0; auto good_repr = true; for (auto it = input.rbegin(); it < input.rend(); it++, i++) { if (*it == '1') { - bit_repr |= 1ull << i; + bit_repr |= 1ul << i; total_units++; } else if (*it != '0' || i >= 64) { std::cout << "Input invalid. Enter a binary string only with max 64 bits." << '\n'; @@ -158,35 +196,39 @@ auto get_reprs_input(int units_required) -> std::vector { } auto main() -> int { - int dims[3] = { 3, 3, 3 }; - //get_dims_input(dims); + int dims[3]; + get_dims_input(dims); std::cout << '\n'; - //auto reprs = get_reprs_input(dims[0]*dims[1]*dims[2]); + auto reprs = get_reprs_input(dims[0]*dims[1]*dims[2]); + /* auto reprs = std::vector{ - 23ull, - 30ull, - 15ull, - 43ull, - 172ull, - 92ull, - 11ull, + 23ul, + 30ul, + 15ul, + 1043ul, + 24594ul, + 12306ul, + 11ul, }; + */ std::cout << "Great. Calculating solutions...\n"; auto offsets = std::vector(); auto polycubes = std::vector(); polycubes.reserve(reprs.size() * 10); - auto model_space = Voxel::Space{ + auto model_space = Voxel::Space{ .space={}, - .dims={dims[0], dims[1], dims[2]}, + .dim_x=dims[0], + .dim_y=dims[1], + .dim_z=dims[2], }; - offsets.push_back(polycubes.size()); + offsets.push_back(0); auto space = model_space; space.space = reprs[0]; Voxel::cullEmptySpace(&space); - auto positions = Voxel::getAllPositionsInPrism(space.space, space.dims, dims); + auto positions = Voxel::getAllPositionsInPrism(&space, dims); polycubes.insert(polycubes.end(), positions.begin(), positions.end()); for (int i = 1; i < reprs.size(); i++) { @@ -200,8 +242,17 @@ auto main() -> int { offsets.push_back(polycubes.size()); - auto solns = backtrack_solve(&polycubes, &offsets); + auto solutions = std::vector{std::vector(reprs.size())}; + auto solver = Solver{ + .input=&polycubes, + .offsets=&offsets, + .solutions=&solutions, + }; - std::cout << solns << std::endl; + backtrack_solve(&solver); + + auto filtered_solns = filter_unique(solver.solutions, dims); + + std::cout << filtered_solns.size() << std::endl; return 0; } diff --git a/tests.cpp b/tests.cpp index d6e184f..b2bd65e 100644 --- a/tests.cpp +++ b/tests.cpp @@ -1,63 +1,312 @@ #include +#include +#include #include "VoxelSpace.h" TEST(VoxelSpaces, BasicPositions) { - auto space = Voxel::Space{ .space=23ull, .dims={3, 3, 3}}; - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 0, 0, 1), true); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 1, 0, 0), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 2, 1, 2), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 1, 2, 1), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 0, 0, 0), true); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 2, 2, 1), false); + auto space = Voxel::Space{ + .space=23ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; + EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true); + EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false); + EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false); + EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false); + EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), true); + EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false); - space = Voxel::Space{ .space=30ull, .dims={3, 3, 3}}; - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 0, 0, 1), true); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 1, 0, 0), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 2, 1, 2), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 1, 2, 1), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 0, 0, 0), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 2, 2, 1), false); + space.space = 30ul; + EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true); + EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false); + EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false); + EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false); + EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), false); + EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false); - space = Voxel::Space{ .space=15ull, .dims={3, 3, 3}}; - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 0, 0, 1), true); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 1, 0, 0), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 2, 1, 2), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 1, 2, 1), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 0, 0, 0), true); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 2, 2, 1), false); + space.space = 15ul; + EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true); + EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false); + EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false); + EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false); + EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), true); + EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false); - space = Voxel::Space{ .space=23ull, .dims={3, 3, 3}}; - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 0, 0, 1), true); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 1, 0, 0), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 2, 1, 2), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 1, 2, 1), false); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 0, 0, 0), true); - EXPECT_EQ(Voxel::filledAt(space.space, space.dims, 2, 2, 1), false); + space.space = 23ul; + EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true); + EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false); + EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false); + EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false); + EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), true); + EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false); } TEST(VoxelSpaces, RotatedIndices) { - auto space1 = Voxel::Space{ .space=172ull, .dims={3, 3, 3}}; + auto space1 = Voxel::Space{ + .space=172ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; - EXPECT_EQ(Voxel::newIndexRotX(space1.dims, 0, 0, 0), 6); - EXPECT_EQ(Voxel::newIndexRotX(space1.dims, 1, 0, 1), 12); + EXPECT_EQ(Voxel::newIndexRotX(&space1, 0, 0, 0), 6); + EXPECT_EQ(Voxel::newIndexRotX(&space1, 1, 0, 1), 12); - EXPECT_EQ(Voxel::newIndexRotY(space1.dims, 0, 1, 0), 5); - EXPECT_EQ(Voxel::newIndexRotY(space1.dims, 1, 2, 0), 7); + EXPECT_EQ(Voxel::newIndexRotY(&space1, 0, 1, 0), 5); + EXPECT_EQ(Voxel::newIndexRotY(&space1, 1, 2, 0), 7); - EXPECT_EQ(Voxel::newIndexRotZ(space1.dims, 1, 0, 2), 23); - EXPECT_EQ(Voxel::newIndexRotZ(space1.dims, 0, 0, 0), 18); + EXPECT_EQ(Voxel::newIndexRotZ(&space1, 1, 0, 2), 23); + EXPECT_EQ(Voxel::newIndexRotZ(&space1, 0, 0, 0), 18); +} + +TEST(VoxelSpaces, UniqueRotations) { + auto space1 = Voxel::Space{ + .space=30ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; + + auto rotations = Voxel::getUniqueRotations(&space1); + auto expected_rots = std::vector{ + { 30ul, 1, 2, 3 }, + { 45ul, 1, 3, 2 }, + { 30ul, 3, 2, 1 }, + { 30ul, 3, 1, 2 }, + { 45ul, 3, 2, 1 }, + { 45ul, 3, 1, 2 }, + { 51ul, 1, 2, 3 }, + { 30ul, 1, 3, 2 }, + { 30ul, 2, 3, 1 }, + { 30ul, 2, 1, 3 }, + { 51ul, 2, 3, 1 }, + { 51ul, 2, 1, 3 }, + }; + + ASSERT_EQ(expected_rots.size(), rotations.size()); + + for (int i = 0; i < rotations.size(); i++) { + if (i <= expected_rots.size()) { + EXPECT_EQ(Voxel::isMatch(&expected_rots.at(i), &rotations.at(i)), true); + } + } +} + +TEST(VoxelSpaces, AllPermutationsInPrism) { + auto space1 = Voxel::Space{ + .space=30ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; + + auto prism_dims = std::array{ 3, 3, 3 }; + auto perms = Voxel::getAllPermutationsInPrism(&space1, prism_dims.begin()); + auto expected_perms = std::vector{ + 30ul, + 240ul, + 15360ul, + 122880ul, + 7864320ul, + 62914560ul, + 153ul, + 306ul, + 78336ul, + 156672ul, + 40108032ul, + 80216064ul, + 266760ul, + 533520ul, + 1067040ul, + 2134080ul, + 4268160ul, + 8536320ul, + 263682ul, + 527364ul, + 2109456ul, + 4218912ul, + 16875648ul, + 33751296ul, + 2101761ul, + 4203522ul, + 8407044ul, + 16814088ul, + 33628176ul, + 67256352ul, + 525825ul, + 1051650ul, + 4206600ul, + 8413200ul, + 33652800ul, + 67305600ul, + 51ul, + 408ul, + 26112ul, + 208896ul, + 13369344ul, + 106954752ul, + 90ul, + 180ul, + 46080ul, + 92160ul, + 23592960ul, + 47185920ul, + 4680ul, + 9360ul, + 18720ul, + 2396160ul, + 4792320ul, + 9584640ul, + 1542ul, + 12336ul, + 98688ul, + 789504ul, + 6316032ul, + 50528256ul, + 36873ul, + 73746ul, + 147492ul, + 18878976ul, + 37757952ul, + 75515904ul, + 3075ul, + 24600ul, + 196800ul, + 1574400ul, + 12595200ul, + 100761600ul, + }; + + ASSERT_EQ(expected_perms.size(), perms.size()); + + for (int i = 0; i < perms.size(); i++) { + if (i <= expected_perms.size()) { + EXPECT_EQ(expected_perms.at(i), perms.at(i)); + } + } } TEST(VoxelSpaces, RotateXYZ) { - auto space1 = Voxel::Space{ .space=30ull, .dims={3, 3, 3}}; - auto space2 = Voxel::Space{ .space=30ull, .dims={3, 3, 3}}; - auto space3 = Voxel::Space{ .space=30ull, .dims={3, 3, 3}}; + auto space1 = Voxel::Space{ + .space=30ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; + + auto space2 = Voxel::Space{ + .space=30ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; + + auto space3 = Voxel::Space{ + .space=30ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; + Voxel::rotate90X(&space1); Voxel::rotate90Y(&space2); Voxel::rotate90Z(&space3); - EXPECT_EQ(space1.space, 153ull); - EXPECT_EQ(space2.space, 1067040ull); - EXPECT_EQ(space3.space, 1574400ull); + EXPECT_EQ(space1.space, 153ul); + EXPECT_EQ(space2.space, 1067040ul); + EXPECT_EQ(space3.space, 1574400ul); +} + +TEST(VoxelSpaces, GetAllPositionsInPrism) { + int dims[] = {3, 3, 3}; + + auto space1 = Voxel::Space{ + .space=30ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; + Voxel::cullEmptySpace(&space1); + uint64_t expected_results1[] = { + 30ul, + 240ul, + 15360ul, + 122880ul, + 7864320ul, + 62914560ul, + }; + + auto space2 = Voxel::Space{ + .space=23ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; + Voxel::cullEmptySpace(&space2); + uint64_t expected_results2[] = { + 23ul, + 184ul, + 11776ul, + 94208ul, + 6029312ul, + 48234496ul, + }; + + auto space3 = Voxel::Space{ + .space=15ul, + .dim_x=3, + .dim_y=3, + .dim_z=3, + }; + Voxel::cullEmptySpace(&space3); + uint64_t expected_results3[] = { + 15ul, + 120ul, + 7680ul, + 61440ul, + 3932160ul, + 31457280ul, + }; + + auto positions1 = Voxel::getAllPositionsInPrism(&space1, dims); + auto positions2 = Voxel::getAllPositionsInPrism(&space2, dims); + auto positions3 = Voxel::getAllPositionsInPrism(&space3, dims); + auto mismatches1 = std::vector>(); + auto mismatches2 = std::vector>(); + auto mismatches3 = std::vector>(); + + for (int i = 0; i < 6; i++) { + if (positions1[i] != expected_results1[i]) { + mismatches1.push_back({ i, positions1[i], expected_results1[i] }); + } + if (positions2[i] != expected_results2[i]) { + mismatches2.push_back({ i, positions2[i], expected_results2[i] }); + } + if (positions3[i] != expected_results3[i]) { + mismatches3.push_back({ i, positions3[i], expected_results3[i] }); + } + } + EXPECT_EQ(mismatches1.size(), 0); + if (mismatches1.size() > 0) { + std::cout << "Index - Actual - Expected" << std::endl; + for (auto &mismatch : mismatches1) { + std::cout << std::get<0>(mismatch) << " - " << std::get<1>(mismatch) << " - " << std::get<2>(mismatch) << std::endl; + } + } + EXPECT_EQ(mismatches2.size(), 0); + if (mismatches2.size() > 0) { + std::cout << "Index - Actual - Expected" << std::endl; + for (auto &mismatch : mismatches2) { + std::cout << std::get<0>(mismatch) << " - " << std::get<1>(mismatch) << " - " << std::get<2>(mismatch) << std::endl; + } + } + EXPECT_EQ(mismatches3.size(), 0); + if (mismatches3.size() > 0) { + std::cout << "Index - Actual - Expected" << std::endl; + for (auto &mismatch : mismatches3) { + std::cout << "At " << std::get<0>(mismatch) << ": " << std::get<1>(mismatch) << " != " << std::get<2>(mismatch) << std::endl; + } + } }