diff --git a/CMakeLists.txt b/CMakeLists.txt index 17c0402..bde537c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,3 +13,26 @@ add_executable(somaesque ) #target_link_libraries(somaesque glfw GL X11 pthread Xrandr dl SDL2 glm::glm) #target_include_directories(somaesque PRIVATE src/KHR src/glad) + +# TESTING +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +FetchContent_MakeAvailable(googletest) + +enable_testing() +add_executable(tests + tests.cpp + VoxelSpace.cpp + VoxelSpace.h +) + +target_link_libraries( + tests + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(tests) diff --git a/VoxelSpace.cpp b/VoxelSpace.cpp index fa404b8..7023702 100644 --- a/VoxelSpace.cpp +++ b/VoxelSpace.cpp @@ -43,11 +43,11 @@ namespace Voxel { } } - inline auto collides(uint64_t a, uint64_t b) -> bool { + auto collides(uint64_t a, uint64_t b) -> bool { return (a | b) != (a ^ b); } - inline auto collides(Space *a, Space *b) -> bool { + auto collides(Space *a, Space *b) -> bool { return (a->space | b->space) != (a->space ^ b->space); } @@ -105,11 +105,12 @@ namespace Voxel { } 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)) { - space->space |= 1 << newIndexRotX(space->dims, x, y, z); + new_space |= 1 << newIndexRotX(space->dims, x, y, z); } } } @@ -117,14 +118,16 @@ namespace Voxel { auto temp = space->dims[1]; space->dims[1] = space->dims[2]; space->dims[2] = 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)) { - space->space |= 1 << newIndexRotY(space->dims, x, y, z); + new_space |= 1 << newIndexRotY(space->dims, x, y, z); } } } @@ -132,14 +135,16 @@ namespace Voxel { auto temp = space->dims[0]; space->dims[0] = space->dims[2]; space->dims[2] = 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)) { - space->space |= 1 << newIndexRotZ(space->dims, x, y, z); + new_space |= 1 << newIndexRotZ(space->dims, x, y, z); } } } @@ -147,6 +152,7 @@ namespace Voxel { auto temp = space->dims[0]; space->dims[0] = space->dims[1]; space->dims[1] = temp; + space->space = new_space; } inline auto isMatch(Space *a, Space *b) -> bool { @@ -203,6 +209,24 @@ namespace Voxel { 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); + pushNewUniqueSpins(&rotations, &refSpace); + rotate90Z(&refSpace); + rotate90Z(&refSpace); + pushNewUniqueSpins(&rotations, &refSpace); + return rotations; + } + auto getAllRotations(Space *space) -> std::vector { auto rotations = std::vector(); rotations.reserve(6*24); @@ -212,12 +236,12 @@ namespace Voxel { rotate90Y(&refSpace); pushXAxisSpins(&rotations, &refSpace); rotate90Y(&refSpace); - pushXAxisSpins(&rotations, &refSpace); + pushNewUniqueSpins(&rotations, &refSpace); rotate90Z(&refSpace); - pushXAxisSpins(&rotations, &refSpace); + pushNewUniqueSpins(&rotations, &refSpace); rotate90Z(&refSpace); rotate90Z(&refSpace); - pushXAxisSpins(&rotations, &refSpace); + pushNewUniqueSpins(&rotations, &refSpace); return rotations; } diff --git a/VoxelSpace.h b/VoxelSpace.h index be17eab..e82c48e 100644 --- a/VoxelSpace.h +++ b/VoxelSpace.h @@ -19,24 +19,24 @@ namespace Voxel { int dims[3]; }; - inline auto index(int dims[3], int x, int y, int z) -> int; + auto index(int dims[3], int x, int y, int z) -> int; - inline auto newIndexRotX(int dims[3], int x, int y, int z) -> int; + auto newIndexRotX(int dims[3], int x, int y, int z) -> int; - inline auto newIndexRotY(int dims[3], int x, int y, int z) -> int; + auto newIndexRotY(int dims[3], int x, int y, int z) -> int; - inline auto newIndexRotZ(int dims[3], int x, int y, int z) -> int; + auto newIndexRotZ(int dims[3], int x, int y, int z) -> int; - inline auto toggle(uint64_t space, int index) -> uint64_t; + auto toggle(uint64_t space, int index) -> uint64_t; - inline auto set(uint64_t space, int index, bool val) -> uint64_t; + auto set(uint64_t space, int index, bool val) -> uint64_t; - inline auto collides(Space *a, Space *b) -> bool; - inline auto collides(uint64_t a, uint64_t b) -> bool; + auto collides(Space *a, Space *b) -> bool; + auto collides(uint64_t a, uint64_t b) -> bool; - inline auto add(Space *a, Space *b) -> Space; + auto add(Space *a, Space *b) -> Space; - inline auto filledAt(Space *space, int index) -> bool; + auto filledAt(uint64_t space, int dims[3], int x, int y, int z) -> bool; auto getExtrema(uint64_t space, int dims[3]) -> Extrema; diff --git a/main.cpp b/main.cpp index a90ec22..3bcf2e4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -57,7 +58,6 @@ auto backtrack_solve(std::vector *polycube_input, std::vector *of auto solns = 0; auto start = offsets->at(set); auto end = offsets->at(set + 1); - std::cout << start << " " << end << "\n"; for (int i = start; i < end; i++) { auto successful_fuse = !Voxel::collides(working_solution, polycube_input->at(i)); if (successful_fuse) { @@ -72,6 +72,34 @@ auto backtrack_solve(std::vector *polycube_input, std::vector *of return solns; } +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()); + } + for (auto &rotation : soln_rotations) { + auto end = unique_solns.size(); + for (int i = 0; i < end; i++) { + if (rotation == unique_solns[i]) { + foundMatch = true; + } + } + } + if (!foundMatch) { + unique_solns.push_back(solution); + } + } + return unique_solns; +} + auto get_dims_input(int dims[3]) -> void { std::cout << "Enter dimensions separated by newlines. (x*y*z must not exceed 64)\n"; auto success = false; @@ -146,19 +174,19 @@ auto main() -> int { std::cout << "Great. Calculating solutions...\n"; auto offsets = std::vector(); - auto polycubes = std::vector(); + auto polycubes = std::vector(); polycubes.reserve(reprs.size() * 10); auto model_space = Voxel::Space{ .space={}, .dims={dims[0], dims[1], dims[2]}, }; - + offsets.push_back(polycubes.size()); auto space = model_space; space.space = reprs[0]; Voxel::cullEmptySpace(&space); - auto positions = Voxel::getUniqueRotations(&space); + auto positions = Voxel::getAllPositionsInPrism(space.space, space.dims, dims); polycubes.insert(polycubes.end(), positions.begin(), positions.end()); for (int i = 1; i < reprs.size(); i++) { @@ -166,16 +194,13 @@ auto main() -> int { auto space = model_space; space.space = reprs[i]; Voxel::cullEmptySpace(&space); - auto perms = Voxel::getUniqueRotations(&space); + auto perms = Voxel::getAllPermutationsInPrism(&space, dims); polycubes.insert(polycubes.end(), perms.begin(), perms.end()); } offsets.push_back(polycubes.size()); - auto spaces = std::vector(polycubes.size()); - std::transform(polycubes.begin(), polycubes.end(), spaces.begin(), [](auto i) { return i.space; }); - - auto solns = backtrack_solve(&spaces, &offsets); + auto solns = backtrack_solve(&polycubes, &offsets); std::cout << solns << std::endl; return 0; diff --git a/tests.cpp b/tests.cpp new file mode 100644 index 0000000..d6e184f --- /dev/null +++ b/tests.cpp @@ -0,0 +1,63 @@ +#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); + + 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 = 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 = 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); +} + +TEST(VoxelSpaces, RotatedIndices) { + auto space1 = Voxel::Space{ .space=172ull, .dims={3, 3, 3}}; + + EXPECT_EQ(Voxel::newIndexRotX(space1.dims, 0, 0, 0), 6); + EXPECT_EQ(Voxel::newIndexRotX(space1.dims, 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::newIndexRotZ(space1.dims, 1, 0, 2), 23); + EXPECT_EQ(Voxel::newIndexRotZ(space1.dims, 0, 0, 0), 18); +} + +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}}; + Voxel::rotate90X(&space1); + Voxel::rotate90Y(&space2); + Voxel::rotate90Z(&space3); + EXPECT_EQ(space1.space, 153ull); + EXPECT_EQ(space2.space, 1067040ull); + EXPECT_EQ(space3.space, 1574400ull); +} + +