diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 945cc35b7..3e1579b8d 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -343,6 +343,13 @@ target_link_libraries(libdevilutionx_codec PRIVATE libdevilutionx_log ) +add_devilutionx_object_library(libdevilutionx_crawl + crawl.cpp +) +target_link_libraries(libdevilutionx_crawl PUBLIC + tl +) + add_devilutionx_object_library(libdevilutionx_file_util utils/file_util.cpp ) @@ -398,6 +405,7 @@ target_link_libraries(libdevilutionx PUBLIC tl unordered_dense::unordered_dense libdevilutionx_codec + libdevilutionx_crawl libdevilutionx_format_int libdevilutionx_file_util libdevilutionx_parse_int diff --git a/Source/crawl.cpp b/Source/crawl.cpp new file mode 100644 index 000000000..fe561a05b --- /dev/null +++ b/Source/crawl.cpp @@ -0,0 +1,74 @@ +#include "crawl.hpp" + +#include + +#include + +#include "engine/displacement.hpp" + +namespace devilution { +namespace { + +bool CrawlFlipsX(Displacement mirrored, tl::function_ref function) +{ + for (const Displacement displacement : { mirrored.flipX(), mirrored }) { + if (!function(displacement)) + return false; + } + return true; +} + +bool CrawlFlipsY(Displacement mirrored, tl::function_ref function) +{ + for (const Displacement displacement : { mirrored, mirrored.flipY() }) { + if (!function(displacement)) + return false; + } + return true; +} + +bool CrawlFlipsXY(Displacement mirrored, tl::function_ref function) +{ + for (const Displacement displacement : { mirrored.flipX(), mirrored, mirrored.flipXY(), mirrored.flipY() }) { + if (!function(displacement)) + return false; + } + return true; +} + +} // namespace + +bool DoCrawl(unsigned radius, tl::function_ref function) +{ + if (radius == 0) + return function(Displacement { 0, 0 }); + + if (!CrawlFlipsY({ 0, static_cast(radius) }, function)) + return false; + for (unsigned i = 1; i < radius; i++) { + if (!CrawlFlipsXY({ static_cast(i), static_cast(radius) }, function)) + return false; + } + if (radius > 1) { + if (!CrawlFlipsXY({ static_cast(radius) - 1, static_cast(radius) - 1 }, function)) + return false; + } + if (!CrawlFlipsX({ static_cast(radius), 0 }, function)) + return false; + for (unsigned i = 1; i < radius; i++) { + if (!CrawlFlipsXY({ static_cast(radius), static_cast(i) }, function)) + return false; + } + return true; +} + +bool DoCrawl(unsigned minRadius, unsigned maxRadius, tl::function_ref function) +{ + for (unsigned i = minRadius; i <= maxRadius; i++) { + if (!DoCrawl(i, function)) + return false; + } + return true; +} + +} // namespace devilution diff --git a/Source/crawl.hpp b/Source/crawl.hpp new file mode 100644 index 000000000..f0146fe90 --- /dev/null +++ b/Source/crawl.hpp @@ -0,0 +1,58 @@ +#include + +#include + +#include "engine/displacement.hpp" + +namespace devilution { + +/** + * CrawlTable specifies X- and Y-coordinate deltas from a missile target coordinate. + * + * n=4 + * + * y + * ^ + * | 1 + * | 3#4 + * | 2 + * +-----> x + * + * n=16 + * + * y + * ^ + * | 314 + * | B7 8C + * | F # G + * | D9 AE + * | 526 + * +-------> x + */ + +bool DoCrawl(unsigned radius, tl::function_ref function); +bool DoCrawl(unsigned minRadius, unsigned maxRadius, tl::function_ref function); + +template +auto Crawl(unsigned radius, F function) -> std::invoke_result_t +{ + std::invoke_result_t result; + DoCrawl(radius, [&result, &function](Displacement displacement) -> bool { + result = function(displacement); + return !result; + }); + return result; +} + +template +auto Crawl(unsigned minRadius, unsigned maxRadius, F function) -> std::invoke_result_t +{ + std::invoke_result_t result; + DoCrawl(minRadius, maxRadius, [&result, &function](Displacement displacement) -> bool { + result = function(displacement); + return !result; + }); + return result; +} + +} // namespace devilution diff --git a/Source/engine/path.cpp b/Source/engine/path.cpp index a6f586e8f..ed6f87944 100644 --- a/Source/engine/path.cpp +++ b/Source/engine/path.cpp @@ -10,6 +10,7 @@ #include +#include "crawl.hpp" #include "levels/gendung.h" #include "lighting.h" #include "objects.h" diff --git a/Source/lighting.cpp b/Source/lighting.cpp index 70076359e..2345c88dd 100644 --- a/Source/lighting.cpp +++ b/Source/lighting.cpp @@ -113,33 +113,6 @@ DVL_ALWAYS_INLINE uint8_t GetLight(Point position) return dLight[position.x][position.y]; } -bool CrawlFlipsX(Displacement mirrored, tl::function_ref function) -{ - for (const Displacement displacement : { mirrored.flipX(), mirrored }) { - if (!function(displacement)) - return false; - } - return true; -} - -bool CrawlFlipsY(Displacement mirrored, tl::function_ref function) -{ - for (const Displacement displacement : { mirrored, mirrored.flipY() }) { - if (!function(displacement)) - return false; - } - return true; -} - -bool CrawlFlipsXY(Displacement mirrored, tl::function_ref function) -{ - for (const Displacement displacement : { mirrored.flipX(), mirrored, mirrored.flipXY(), mirrored.flipY() }) { - if (!function(displacement)) - return false; - } - return true; -} - bool TileAllowsLight(Point position) { if (!InDungeonBounds(position)) @@ -161,39 +134,6 @@ void DoVisionFlags(Point position, MapExplorationType doAutomap, bool visible) } // namespace -bool DoCrawl(unsigned radius, tl::function_ref function) -{ - if (radius == 0) - return function(Displacement { 0, 0 }); - - if (!CrawlFlipsY({ 0, static_cast(radius) }, function)) - return false; - for (unsigned i = 1; i < radius; i++) { - if (!CrawlFlipsXY({ static_cast(i), static_cast(radius) }, function)) - return false; - } - if (radius > 1) { - if (!CrawlFlipsXY({ static_cast(radius) - 1, static_cast(radius) - 1 }, function)) - return false; - } - if (!CrawlFlipsX({ static_cast(radius), 0 }, function)) - return false; - for (unsigned i = 1; i < radius; i++) { - if (!CrawlFlipsXY({ static_cast(radius), static_cast(i) }, function)) - return false; - } - return true; -} - -bool DoCrawl(unsigned minRadius, unsigned maxRadius, tl::function_ref function) -{ - for (unsigned i = minRadius; i <= maxRadius; i++) { - if (!DoCrawl(i, function)) - return false; - } - return true; -} - void DoUnLight(Point position, uint8_t radius) { radius++; diff --git a/Source/lighting.h b/Source/lighting.h index dd1fc72a4..f8888fbf6 100644 --- a/Source/lighting.h +++ b/Source/lighting.h @@ -86,53 +86,4 @@ void lighting_color_cycling(); constexpr int MaxCrawlRadius = 18; -/** - * CrawlTable specifies X- and Y-coordinate deltas from a missile target coordinate. - * - * n=4 - * - * y - * ^ - * | 1 - * | 3#4 - * | 2 - * +-----> x - * - * n=16 - * - * y - * ^ - * | 314 - * | B7 8C - * | F # G - * | D9 AE - * | 526 - * +-------> x - */ - -bool DoCrawl(unsigned radius, tl::function_ref function); -bool DoCrawl(unsigned minRadius, unsigned maxRadius, tl::function_ref function); - -template -auto Crawl(unsigned radius, F function) -> std::invoke_result_t -{ - std::invoke_result_t result; - DoCrawl(radius, [&result, &function](Displacement displacement) -> bool { - result = function(displacement); - return !result; - }); - return result; -} - -template -auto Crawl(unsigned minRadius, unsigned maxRadius, F function) -> std::invoke_result_t -{ - std::invoke_result_t result; - DoCrawl(minRadius, maxRadius, [&result, &function](Displacement displacement) -> bool { - result = function(displacement); - return !result; - }); - return result; -} - } // namespace devilution diff --git a/Source/lua/modules/dev/monsters.cpp b/Source/lua/modules/dev/monsters.cpp index 42b0ccd84..00855ebd2 100644 --- a/Source/lua/modules/dev/monsters.cpp +++ b/Source/lua/modules/dev/monsters.cpp @@ -6,6 +6,7 @@ #include +#include "crawl.hpp" #include "levels/gendung.h" #include "lighting.h" #include "lua/metadoc.hpp" diff --git a/Source/missiles.cpp b/Source/missiles.cpp index 8f1d2cf9a..aa611ec11 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -11,6 +11,7 @@ #include "control.h" #include "controls/plrctrls.h" +#include "crawl.hpp" #include "cursor.h" #include "dead.h" #ifdef _DEBUG diff --git a/Source/monster.cpp b/Source/monster.cpp index c2bde27e1..d93a96c46 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -18,6 +18,7 @@ #include #include "control.h" +#include "crawl.hpp" #include "cursor.h" #include "dead.h" #include "engine/load_cl2.hpp" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 38f1a4baf..987a7dd5b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,7 +24,6 @@ set(tests drlg_l4_test effects_test inv_test - lighting_test math_test missiles_test pack_test @@ -40,24 +39,30 @@ set(tests ) set(standalone_tests codec_test + crawl_test file_util_test format_int_test parse_int_test str_cat_test utf8_test ) +set(benchmarks + crawl_benchmark) include(Fixtures.cmake) -foreach(test_target ${tests} ${standalone_tests}) +foreach(test_target ${tests} ${standalone_tests} ${benchmarks}) add_executable(${test_target} "${test_target}.cpp") - gtest_discover_tests(${test_target}) set_target_properties(${test_target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) if(GPERF) target_link_libraries(${test_target} PUBLIC ${GPERFTOOLS_LIBRARIES}) endif() endforeach() +foreach(test_target ${tests} ${standalone_tests}) + gtest_discover_tests(${test_target}) +endforeach() + foreach(test_target ${tests}) target_link_libraries(${test_target} PRIVATE test_main) endforeach() @@ -67,6 +72,11 @@ foreach(test_target ${standalone_tests}) target_include_directories(${test_target} PRIVATE "${PROJECT_SOURCE_DIR}/Source") endforeach() +foreach(target ${benchmarks}) + target_link_libraries(${target} PRIVATE benchmark::benchmark benchmark::benchmark_main) + target_include_directories(${target} PRIVATE "${PROJECT_SOURCE_DIR}/Source") +endforeach() + add_library(app_fatal_for_testing OBJECT app_fatal_for_testing.cpp) target_sources(app_fatal_for_testing INTERFACE $) @@ -74,6 +84,8 @@ add_library(language_for_testing OBJECT language_for_testing.cpp) target_sources(language_for_testing INTERFACE $) target_link_libraries(codec_test PRIVATE libdevilutionx_codec app_fatal_for_testing) +target_link_libraries(crawl_test PRIVATE libdevilutionx_crawl) +target_link_libraries(crawl_benchmark PRIVATE libdevilutionx_crawl) target_link_libraries(file_util_test PRIVATE libdevilutionx_file_util app_fatal_for_testing) target_link_libraries(format_int_test PRIVATE libdevilutionx_format_int language_for_testing) target_link_libraries(parse_int_test PRIVATE libdevilutionx_parse_int) diff --git a/test/crawl_benchmark.cpp b/test/crawl_benchmark.cpp new file mode 100644 index 000000000..8c47136ad --- /dev/null +++ b/test/crawl_benchmark.cpp @@ -0,0 +1,25 @@ +#include + +#include "crawl.hpp" +#include "engine/displacement.hpp" + +namespace devilution { +namespace { + +void BM_Crawl(benchmark::State &state) +{ + const int radius = state.range(0); + for (auto _ : state) { + int sum; + Crawl(0, radius, [&sum](Displacement d) { + sum += d.deltaX + d.deltaY; + return false; + }); + benchmark::DoNotOptimize(sum); + } +} + +BENCHMARK(BM_Crawl)->RangeMultiplier(4)->Range(1, 20); + +} // namespace +} // namespace devilution diff --git a/test/lighting_test.cpp b/test/crawl_test.cpp similarity index 88% rename from test/lighting_test.cpp rename to test/crawl_test.cpp index c6c7ca7dc..a788d165c 100644 --- a/test/lighting_test.cpp +++ b/test/crawl_test.cpp @@ -3,14 +3,14 @@ #include #include -#include "control.h" -#include "lighting.h" +#include "crawl.hpp" +#include "engine/displacement.hpp" namespace devilution { namespace { using ::testing::ElementsAre; -TEST(Lighting, CrawlTables) +TEST(CrawlTest, BasicTest) { bool added[40][40]; memset(added, 0, sizeof(added)); @@ -18,6 +18,7 @@ TEST(Lighting, CrawlTables) int x = 20; int y = 20; + constexpr int MaxCrawlRadius = 18; Crawl(0, MaxCrawlRadius, [&](Displacement displacement) { int dx = x + displacement.deltaX; int dy = y + displacement.deltaY; @@ -37,7 +38,7 @@ TEST(Lighting, CrawlTables) } } -TEST(Lighting, CrawlTablesVisitationOrder) +TEST(CrawlTest, VisitationOrderTest) { std::vector order; Crawl(0, 2, [&](Displacement displacement) {