mirror of
https://github.com/bensuperpc/astar.git
synced 2025-06-14 13:19:25 +02:00
82
test/CMakeLists.txt
Normal file
82
test/CMakeLists.txt
Normal file
@ -0,0 +1,82 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(astarTests LANGUAGES C CXX)
|
||||
|
||||
include(../cmake/project-is-top-level.cmake)
|
||||
include(../cmake/folders.cmake)
|
||||
|
||||
# ---- Dependencies ----
|
||||
|
||||
if(PROJECT_IS_TOP_LEVEL)
|
||||
find_package(astar REQUIRED)
|
||||
enable_testing()
|
||||
endif()
|
||||
|
||||
|
||||
function(test_bench_generator TEST_BENCH_NAME IS_TEST ADD_TO_TEST)
|
||||
if (IS_TEST)
|
||||
add_executable("${TEST_BENCH_NAME}" "source/test/${TEST_BENCH_NAME}.cpp" source/generator/generator.cpp source/generator/generator.hpp)
|
||||
else()
|
||||
add_executable("${TEST_BENCH_NAME}" "source/benchmark/${TEST_BENCH_NAME}.cpp" source/generator/generator.cpp source/generator/generator.hpp)
|
||||
endif()
|
||||
|
||||
|
||||
if (IS_TEST)
|
||||
target_link_libraries("${TEST_BENCH_NAME}" PRIVATE gtest)
|
||||
else()
|
||||
target_link_libraries("${TEST_BENCH_NAME}" PRIVATE benchmark::benchmark)
|
||||
endif()
|
||||
|
||||
target_link_libraries("${TEST_BENCH_NAME}" PRIVATE astar::astar)
|
||||
target_link_libraries("${TEST_BENCH_NAME}" PRIVATE raylib)
|
||||
target_link_libraries("${TEST_BENCH_NAME}" PRIVATE FastNoise2)
|
||||
#target_link_libraries("${TEST_BENCH_NAME}" PRIVATE spdlog::spdlog nlohmann_json::nlohmann_json)
|
||||
|
||||
#if (OpenMP_FOUND OR OpenMP_CXX_FOUND)
|
||||
# target_link_libraries("${TEST_BENCH_NAME}" PRIVATE OpenMP::OpenMP_CXX)
|
||||
#endif()
|
||||
|
||||
set_target_properties("${TEST_BENCH_NAME}"
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||
PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
)
|
||||
|
||||
#if(NOT CMAKE_BUILD_TYPE MATCHES Debug AND NOT CMAKE_BUILD_TYPE MATCHES Coverage)
|
||||
# add_test(NAME "${TEST_BENCH_NAME}" COMMAND $<TARGET_FILE:${TEST_BENCH_NAME}>)
|
||||
#elseif()
|
||||
# message(STATUS "Disable ${BENCH_NAME}, Performance benchmark test only run on Release/RelWithDebInfo/MinSizeRel")
|
||||
#endif()
|
||||
|
||||
if (ADD_TO_TEST)
|
||||
add_test(NAME "${TEST_BENCH_NAME}" COMMAND $<TARGET_FILE:${TEST_BENCH_NAME}>)
|
||||
endif()
|
||||
target_compile_features("${TEST_BENCH_NAME}" PRIVATE cxx_std_20)
|
||||
endfunction()
|
||||
|
||||
# ---- Tests ----
|
||||
|
||||
if(NOT WIN32)
|
||||
include(../cmake/lib/gtest.cmake)
|
||||
include(../cmake/lib/benchmark.cmake)
|
||||
#include(../cmake/lib/openmp.cmake)
|
||||
include(../cmake/lib/raygui.cmake)
|
||||
|
||||
include(../cmake/lib/raylib.cmake)
|
||||
include(../cmake/lib/fast_noise2.cmake)
|
||||
#include(../cmake/lib/spdlog.cmake)
|
||||
#include(../cmake/lib/json.cmake)
|
||||
include(../cmake/utile/ccache.cmake)
|
||||
|
||||
include_directories(source)
|
||||
|
||||
test_bench_generator(astar_test true true)
|
||||
test_bench_generator(astar_bench false true)
|
||||
test_bench_generator(path_finder false false)
|
||||
endif()
|
||||
|
||||
# ---- End-of-file commands ----
|
||||
|
||||
add_folders(Test)
|
195
test/source/benchmark/astar_bench.cpp
Normal file
195
test/source/benchmark/astar_bench.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include "astar/astar.hpp"
|
||||
#include "generator/generator.hpp"
|
||||
|
||||
static constexpr int64_t multiplier = 4;
|
||||
static constexpr int64_t minRange = 16;
|
||||
static constexpr int64_t maxRange = 256;
|
||||
static constexpr int64_t minThreadRange = 1;
|
||||
static constexpr int64_t maxThreadRange = 1;
|
||||
static constexpr int64_t repetitions = 1;
|
||||
|
||||
static void DoSetup([[maybe_unused]] const benchmark::State& state) {}
|
||||
|
||||
static void DoTeardown([[maybe_unused]] const benchmark::State& state) {}
|
||||
|
||||
template <IntegerType T>
|
||||
static void astar_bench(benchmark::State& state) {
|
||||
auto range = state.range(0);
|
||||
|
||||
int mapWidth = range;
|
||||
int mapHeight = range;
|
||||
|
||||
float lacunarity = 1.6f;
|
||||
float octaves = 6;
|
||||
float gain = 3.5f;
|
||||
float frequency = 1.7f;
|
||||
float weightedStrength = 0.034f;
|
||||
float multiplier = 118;
|
||||
|
||||
Generator generator(-972960945);
|
||||
benchmark::DoNotOptimize(generator);
|
||||
generator.setLacunarity(lacunarity);
|
||||
generator.setOctaves((uint32_t)octaves);
|
||||
generator.setGain(gain);
|
||||
generator.setFrequency(frequency);
|
||||
generator.setWeightedStrength(weightedStrength);
|
||||
generator.setMultiplier((uint32_t)multiplier);
|
||||
|
||||
std::vector<uint32_t> heightmap = generator.generate2dMeightmap(0, 0, 0, mapWidth, 0, mapHeight);
|
||||
benchmark::DoNotOptimize(heightmap);
|
||||
|
||||
std::vector<uint8_t> blocks = std::vector<uint8_t>(mapWidth * mapHeight, 0);
|
||||
benchmark::DoNotOptimize(blocks);
|
||||
|
||||
AStar::AStar<T, false> pathFinder;
|
||||
benchmark::DoNotOptimize(pathFinder);
|
||||
pathFinder.setWorldSize({mapWidth, mapHeight});
|
||||
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
|
||||
pathFinder.setDiagonalMovement(true);
|
||||
|
||||
for (uint64_t x = 0; x < mapWidth; x++) {
|
||||
for (uint64_t y = 0; y < mapHeight; y++) {
|
||||
uint64_t index = x + y * mapWidth;
|
||||
uint8_t value = static_cast<uint8_t>(heightmap[index]);
|
||||
|
||||
if (value < 128) {
|
||||
blocks[index] = 0;
|
||||
} else {
|
||||
blocks[index] = 1;
|
||||
pathFinder.addObstacle({static_cast<int32_t>(x), static_cast<int32_t>(y)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks[0] = 0;
|
||||
pathFinder.removeObstacle({0, 0});
|
||||
blocks[mapWidth * mapHeight - 1] = 0;
|
||||
pathFinder.removeObstacle({mapWidth - 1, mapHeight - 1});
|
||||
|
||||
AStar::Vec2i source(0, 0);
|
||||
AStar::Vec2i target(mapWidth - 1, mapHeight - 1);
|
||||
|
||||
std::vector<AStar::Vec2i> path;
|
||||
benchmark::DoNotOptimize(path);
|
||||
|
||||
for (auto _ : state) {
|
||||
path = pathFinder.findPath(source, target);
|
||||
state.PauseTiming();
|
||||
if (path.size() == 0) {
|
||||
state.SkipWithError("No path found");
|
||||
}
|
||||
state.ResumeTiming();
|
||||
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
state.SetBytesProcessed(state.iterations() * sizeof(path));
|
||||
}
|
||||
|
||||
BENCHMARK(astar_bench<uint32_t>)
|
||||
->Name("astar_bench<uint32_t>")
|
||||
->RangeMultiplier(multiplier)
|
||||
->Range(minRange, maxRange)
|
||||
->ThreadRange(minThreadRange, maxThreadRange)
|
||||
->Unit(benchmark::kNanosecond)
|
||||
->Setup(DoSetup)
|
||||
->Teardown(DoTeardown)
|
||||
->MeasureProcessCPUTime()
|
||||
->UseRealTime()
|
||||
->Repetitions(repetitions);
|
||||
|
||||
template <IntegerType T>
|
||||
static void astar_bench_fast(benchmark::State& state) {
|
||||
auto range = state.range(0);
|
||||
|
||||
int mapWidth = range;
|
||||
int mapHeight = range;
|
||||
|
||||
float lacunarity = 1.6f;
|
||||
float octaves = 6;
|
||||
float gain = 3.5f;
|
||||
float frequency = 1.7f;
|
||||
float weighted_strength = 0.034f;
|
||||
float multiplier = 118;
|
||||
|
||||
Generator generator(-972960945);
|
||||
benchmark::DoNotOptimize(generator);
|
||||
generator.setLacunarity(lacunarity);
|
||||
generator.setOctaves((uint32_t)octaves);
|
||||
generator.setGain(gain);
|
||||
generator.setFrequency(frequency);
|
||||
generator.setWeightedStrength(0.0f);
|
||||
generator.setMultiplier((uint32_t)multiplier);
|
||||
|
||||
std::vector<uint32_t> heightmap = generator.generate2dMeightmap(0, 0, 0, mapWidth, 0, mapHeight);
|
||||
benchmark::DoNotOptimize(heightmap);
|
||||
|
||||
std::vector<uint32_t> blocks = std::vector<uint32_t>(mapWidth * mapHeight, 0);
|
||||
benchmark::DoNotOptimize(blocks);
|
||||
|
||||
AStar::AStarFast<T, false, uint32_t> pathFinder;
|
||||
benchmark::DoNotOptimize(pathFinder);
|
||||
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
|
||||
pathFinder.setDiagonalMovement(true);
|
||||
|
||||
for (uint64_t x = 0; x < mapWidth; x++) {
|
||||
for (uint64_t y = 0; y < mapHeight; y++) {
|
||||
uint64_t index = x + y * mapWidth;
|
||||
uint8_t value = static_cast<uint8_t>(heightmap[index]);
|
||||
|
||||
if (value < 128) {
|
||||
blocks[index] = 0;
|
||||
} else {
|
||||
blocks[index] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks[0] = 0;
|
||||
blocks[mapWidth * mapHeight - 1] = 0;
|
||||
|
||||
AStar::Vec2i source(0, 0);
|
||||
AStar::Vec2i target(mapWidth - 1, mapHeight - 1);
|
||||
|
||||
std::vector<AStar::Vec2i> path;
|
||||
benchmark::DoNotOptimize(path);
|
||||
|
||||
for (auto _ : state) {
|
||||
path = pathFinder.findPath(source, target, blocks, {mapWidth, mapHeight});
|
||||
state.PauseTiming();
|
||||
if (path.size() == 0) {
|
||||
state.SkipWithError("No path found");
|
||||
}
|
||||
state.ResumeTiming();
|
||||
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
state.SetBytesProcessed(state.iterations() * sizeof(path));
|
||||
}
|
||||
|
||||
BENCHMARK(astar_bench_fast<uint32_t>)
|
||||
->Name("astar_bench_fast<uint32_t>")
|
||||
->RangeMultiplier(multiplier)
|
||||
->Range(minRange, maxRange)
|
||||
->ThreadRange(minThreadRange, maxThreadRange)
|
||||
->Unit(benchmark::kNanosecond)
|
||||
->Setup(DoSetup)
|
||||
->Teardown(DoTeardown)
|
||||
->MeasureProcessCPUTime()
|
||||
->UseRealTime()
|
||||
->Repetitions(repetitions);
|
||||
|
||||
// Run the benchmark
|
||||
// BENCHMARK_MAIN();
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::benchmark::Initialize(&argc, argv);
|
||||
::benchmark::RunSpecifiedBenchmarks();
|
||||
}
|
258
test/source/benchmark/path_finder.cpp
Normal file
258
test/source/benchmark/path_finder.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
#include <array> // std::array
|
||||
#include <chrono> // std::chrono::system_clock
|
||||
#include <cmath> // std::abs
|
||||
#include <cstdint> // std::uint32_t
|
||||
#include <iostream> // std::cout, std::endl
|
||||
#include <map> // std::map
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <random> // std::random_device, std::mt19937, std::uniform_int_distribution
|
||||
#include <vector> // std::vector
|
||||
|
||||
#include "astar/astar.hpp"
|
||||
#include "generator/generator.hpp"
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#define RAYGUI_IMPLEMENTATION
|
||||
extern "C" {
|
||||
#include "src/raygui.h"
|
||||
}
|
||||
|
||||
auto main() -> int {
|
||||
// Set log level for Raylib
|
||||
SetTraceLogLevel(LOG_WARNING);
|
||||
|
||||
const int screenWidth = 1920;
|
||||
const int screenHeight = 1080;
|
||||
|
||||
const int mapWidth = 192;
|
||||
const int mapHeight = 108;
|
||||
|
||||
const uint32_t targetFPS = 120;
|
||||
|
||||
const uint32_t ImageUpdatePerSecond = 30;
|
||||
|
||||
SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_MSAA_4X_HINT);
|
||||
InitWindow(screenWidth, screenHeight, "Path finder by Bensuperpc");
|
||||
|
||||
SetTargetFPS(targetFPS);
|
||||
|
||||
float lacunarity = 1.6f;
|
||||
float octaves = 6;
|
||||
float gain = 3.5f;
|
||||
float frequency = 1.7f;
|
||||
float weighted_strength = 0.034f;
|
||||
float multiplier = 118;
|
||||
|
||||
Generator generator_2(-972960945);
|
||||
generator_2.setLacunarity(lacunarity);
|
||||
generator_2.setOctaves((uint32_t)octaves);
|
||||
generator_2.setGain(gain);
|
||||
generator_2.setFrequency(frequency);
|
||||
generator_2.setWeightedStrength(0.0f);
|
||||
generator_2.setMultiplier((uint32_t)multiplier);
|
||||
|
||||
AStar::AStar<uint32_t, false> pathFinder;
|
||||
pathFinder.setWorldSize({mapWidth, mapHeight});
|
||||
pathFinder.setHeuristic(AStar::Heuristic::manhattan);
|
||||
pathFinder.setDiagonalMovement(true);
|
||||
|
||||
size_t manhattanPathSize = 0;
|
||||
size_t euclideanPathSize = 0;
|
||||
size_t euclideanNoSQRPathSize = 0;
|
||||
size_t octagonalPathSize = 0;
|
||||
size_t chebyshevPathSize = 0;
|
||||
size_t dijkstraPathSize = 0;
|
||||
|
||||
std::vector<uint32_t> heightmap;
|
||||
|
||||
heightmap = generator_2.generate2dMeightmap(0, 0, 0, mapWidth, 0, mapHeight);
|
||||
|
||||
std::vector<uint8_t> blocks = std::vector<uint8_t>(mapWidth * mapHeight, 0);
|
||||
|
||||
uint64_t framesCounter = 0;
|
||||
|
||||
bool needUpdate = true;
|
||||
|
||||
while (!WindowShouldClose()) {
|
||||
framesCounter++;
|
||||
if (IsKeyPressed(KEY_S)) {
|
||||
const std::string filename = "screenshot_" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()) + ".png";
|
||||
TakeScreenshot(filename.c_str());
|
||||
}
|
||||
if (IsKeyPressed(KEY_R)) {
|
||||
generator_2.randomizeSeed();
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (framesCounter % (targetFPS / ImageUpdatePerSecond) == 0) {
|
||||
if (needUpdate) {
|
||||
needUpdate = false;
|
||||
generator_2.setLacunarity(lacunarity);
|
||||
generator_2.setOctaves((uint32_t)octaves);
|
||||
generator_2.setGain(gain);
|
||||
generator_2.setFrequency(frequency);
|
||||
generator_2.setWeightedStrength(weighted_strength);
|
||||
generator_2.setMultiplier((uint32_t)multiplier);
|
||||
|
||||
pathFinder.clear();
|
||||
|
||||
heightmap = generator_2.generate2dMeightmap(0, 0, 0, screenWidth, 0, screenHeight);
|
||||
|
||||
for (uint64_t x = 0; x < mapWidth; x++) {
|
||||
for (uint64_t y = 0; y < mapHeight; y++) {
|
||||
uint64_t index = x + y * mapWidth;
|
||||
uint8_t value = static_cast<uint8_t>(heightmap[index]);
|
||||
|
||||
if (value < 128) {
|
||||
blocks[index] = 0;
|
||||
} else {
|
||||
blocks[index] = 1;
|
||||
pathFinder.addObstacle({static_cast<int32_t>(x), static_cast<int32_t>(y)});
|
||||
}
|
||||
}
|
||||
}
|
||||
blocks[0] = 0;
|
||||
pathFinder.removeObstacle({0, 0});
|
||||
blocks[mapWidth * mapHeight - 1] = 0;
|
||||
pathFinder.removeObstacle({mapWidth - 1, mapHeight - 1});
|
||||
|
||||
pathFinder.setHeuristic(AStar::Heuristic::manhattan);
|
||||
auto start1 = std::chrono::high_resolution_clock::now();
|
||||
auto path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
|
||||
auto stop1 = std::chrono::high_resolution_clock::now();
|
||||
if (path.empty()) {
|
||||
std::cout << "Path not found" << std::endl;
|
||||
}
|
||||
auto duration1 = std::chrono::duration_cast<std::chrono::microseconds>(stop1 - start1);
|
||||
std::cout << "Path search: " << duration1.count() << " microseconds" << std::endl;
|
||||
|
||||
manhattanPathSize = path.size();
|
||||
for (auto& i : path) {
|
||||
uint64_t index = i.x + i.y * mapWidth;
|
||||
blocks[index] = 2;
|
||||
}
|
||||
|
||||
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
|
||||
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
|
||||
euclideanPathSize = path.size();
|
||||
|
||||
for (auto& i : path) {
|
||||
uint64_t index = i.x + i.y * mapWidth;
|
||||
blocks[index] = 3;
|
||||
}
|
||||
|
||||
pathFinder.setHeuristic(AStar::Heuristic::octagonal);
|
||||
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
|
||||
octagonalPathSize = path.size();
|
||||
|
||||
for (auto& i : path) {
|
||||
uint64_t index = i.x + i.y * mapWidth;
|
||||
blocks[index] = 4;
|
||||
}
|
||||
|
||||
pathFinder.setHeuristic(AStar::Heuristic::chebyshev);
|
||||
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
|
||||
chebyshevPathSize = path.size();
|
||||
|
||||
for (auto& i : path) {
|
||||
uint64_t index = i.x + i.y * mapWidth;
|
||||
blocks[index] = 5;
|
||||
}
|
||||
|
||||
pathFinder.setHeuristic(AStar::Heuristic::euclideanNoSQR);
|
||||
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
|
||||
euclideanNoSQRPathSize = path.size();
|
||||
|
||||
for (auto& i : path) {
|
||||
uint64_t index = i.x + i.y * mapWidth;
|
||||
blocks[index] = 6;
|
||||
}
|
||||
|
||||
pathFinder.setHeuristic(AStar::Heuristic::dijkstra);
|
||||
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
|
||||
dijkstraPathSize = path.size();
|
||||
|
||||
for (auto& i : path) {
|
||||
uint64_t index = i.x + i.y * mapWidth;
|
||||
blocks[index] = 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearBackground(RAYWHITE);
|
||||
BeginDrawing();
|
||||
|
||||
// Draw white if blocks[index] == 0 else black
|
||||
int size = 10;
|
||||
for (uint64_t x = 0; x < mapWidth; x++) {
|
||||
for (uint64_t y = 0; y < mapHeight; y++) {
|
||||
uint64_t index = x + y * mapWidth;
|
||||
if (blocks[index] == 0) {
|
||||
DrawRectangle(static_cast<float>(x * size), static_cast<float>(y * size), size, size, WHITE);
|
||||
} else if (blocks[index] == 1) {
|
||||
DrawRectangle(static_cast<float>(x * size), static_cast<float>(y * size), size, size, BLACK);
|
||||
} else if (blocks[index] == 2) {
|
||||
DrawRectangle(static_cast<float>(x * size), static_cast<float>(y * size), size, size, RED);
|
||||
} else if (blocks[index] == 3) {
|
||||
DrawRectangle(static_cast<float>(x * size), static_cast<float>(y * size), size, size, GREEN);
|
||||
} else if (blocks[index] == 4) {
|
||||
DrawRectangle(static_cast<float>(x * size), static_cast<float>(y * size), size, size, BLUE);
|
||||
} else if (blocks[index] == 5) {
|
||||
DrawRectangle(static_cast<float>(x * size), static_cast<float>(y * size), size, size, YELLOW);
|
||||
} else if (blocks[index] == 6) {
|
||||
DrawRectangle(static_cast<float>(x * size), static_cast<float>(y * size), size, size, ORANGE);
|
||||
} else if (blocks[index] == 7) {
|
||||
DrawRectangle(static_cast<float>(x * size), static_cast<float>(y * size), size, size, PURPLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// display FPS
|
||||
DrawRectangle(screenWidth - 90, 10, 80, 20, Fade(SKYBLUE, 0.95f));
|
||||
DrawText(TextFormat("FPS: %02d", GetFPS()), screenWidth - 80, 15, 15, DARKGRAY);
|
||||
|
||||
DrawRectangle(0, 0, 275, 200, Fade(SKYBLUE, 0.95f));
|
||||
DrawRectangleLines(0, 0, 275, 200, BLUE);
|
||||
GuiSlider((Rectangle){70, 10, 165, 20}, "Lacunarity", TextFormat("%2.3f", lacunarity), &lacunarity, -0.0f, 5.0f);
|
||||
GuiSlider((Rectangle){70, 40, 165, 20}, "Octaves", TextFormat("%2.3f", octaves), &octaves, 1, 12);
|
||||
GuiSlider((Rectangle){70, 70, 165, 20}, "Gain", TextFormat("%2.3f", gain), &gain, -0.0f, 16.0f);
|
||||
GuiSlider((Rectangle){70, 100, 165, 20}, "Frequency", TextFormat("%2.3f", frequency), &frequency, -0.0f, 10.0f);
|
||||
GuiSlider((Rectangle){70, 130, 165, 20}, "Weight", TextFormat("%2.3f", weighted_strength), &weighted_strength, -5.0f, 5.0f);
|
||||
GuiSlider((Rectangle){70, 160, 165, 20}, "Multiplier", TextFormat("%2.3f", multiplier), &multiplier, 1, 512);
|
||||
|
||||
// display info each color for each heuristic
|
||||
DrawRectangle(0, 200, 275, 190, Fade(SKYBLUE, 0.95f));
|
||||
DrawRectangleLines(0, 200, 275, 190, BLUE);
|
||||
|
||||
std::string manhattanText = "Manhattan: " + std::to_string(manhattanPathSize);
|
||||
DrawRectangle(10, 210, 20, 20, RED);
|
||||
DrawText(manhattanText.c_str(), 40, 210, 20, DARKGRAY);
|
||||
|
||||
std::string euclideanText = "Euclidean: " + std::to_string(euclideanPathSize);
|
||||
DrawRectangle(10, 240, 20, 20, GREEN);
|
||||
DrawText(euclideanText.c_str(), 40, 240, 20, DARKGRAY);
|
||||
|
||||
std::string octagonalText = "Octagonal: " + std::to_string(octagonalPathSize);
|
||||
DrawRectangle(10, 270, 20, 20, BLUE);
|
||||
DrawText(octagonalText.c_str(), 40, 270, 20, DARKGRAY);
|
||||
|
||||
std::string chebyshevText = "Chebyshev: " + std::to_string(chebyshevPathSize);
|
||||
DrawRectangle(10, 300, 20, 20, YELLOW);
|
||||
DrawText(chebyshevText.c_str(), 40, 300, 20, DARKGRAY);
|
||||
|
||||
std::string euclideanNoSQRText = "EuclideanNoSQR: " + std::to_string(euclideanNoSQRPathSize);
|
||||
DrawRectangle(10, 330, 20, 20, ORANGE);
|
||||
DrawText(euclideanNoSQRText.c_str(), 40, 330, 20, DARKGRAY);
|
||||
|
||||
std::string dijkstraText = "Dijkstra: " + std::to_string(dijkstraPathSize);
|
||||
DrawRectangle(10, 360, 20, 20, PURPLE);
|
||||
DrawText(dijkstraText.c_str(), 40, 360, 20, DARKGRAY);
|
||||
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
CloseWindow();
|
||||
|
||||
return 0;
|
||||
}
|
310
test/source/generator/generator.cpp
Normal file
310
test/source/generator/generator.cpp
Normal file
@ -0,0 +1,310 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
// #include <omp.h>
|
||||
|
||||
#include "generator.hpp"
|
||||
|
||||
Generator::Generator(int32_t _seed) : seed(_seed) {
|
||||
fnSimplex = FastNoise::New<FastNoise::Perlin>();
|
||||
fnFractal = FastNoise::New<FastNoise::FractalFBm>();
|
||||
|
||||
fnFractal->SetSource(fnSimplex);
|
||||
fnFractal->SetOctaveCount(octaves);
|
||||
fnFractal->SetGain(gain);
|
||||
fnFractal->SetLacunarity(lacunarity);
|
||||
fnFractal->SetWeightedStrength(weighted_strength);
|
||||
}
|
||||
|
||||
Generator::Generator() {
|
||||
fnSimplex = FastNoise::New<FastNoise::Perlin>();
|
||||
fnFractal = FastNoise::New<FastNoise::FractalFBm>();
|
||||
|
||||
fnFractal->SetSource(fnSimplex);
|
||||
fnFractal->SetOctaveCount(octaves);
|
||||
fnFractal->SetGain(gain);
|
||||
fnFractal->SetLacunarity(lacunarity);
|
||||
fnFractal->SetWeightedStrength(weighted_strength);
|
||||
|
||||
randomizeSeed();
|
||||
}
|
||||
|
||||
Generator::~Generator() {}
|
||||
|
||||
void Generator::reseed(int32_t _seed) {
|
||||
this->seed = _seed;
|
||||
}
|
||||
|
||||
int32_t Generator::randomizeSeed() {
|
||||
std::random_device rd;
|
||||
std::mt19937_64 gen(rd());
|
||||
std::uniform_int_distribution<uint32_t> dis(std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max());
|
||||
this->seed = dis(gen);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
uint32_t Generator::get_seed() const {
|
||||
return seed;
|
||||
}
|
||||
|
||||
void Generator::setOctaves(uint32_t _octaves) {
|
||||
this->octaves = _octaves;
|
||||
fnFractal->SetOctaveCount(octaves);
|
||||
}
|
||||
|
||||
uint32_t Generator::getOctaves() const {
|
||||
return octaves;
|
||||
}
|
||||
|
||||
void Generator::setLacunarity(float _lacunarity) {
|
||||
this->lacunarity = _lacunarity;
|
||||
fnFractal->SetLacunarity(lacunarity);
|
||||
}
|
||||
|
||||
float Generator::getLacunarity() const {
|
||||
return lacunarity;
|
||||
}
|
||||
|
||||
void Generator::setGain(float _gain) {
|
||||
this->gain = _gain;
|
||||
fnFractal->SetGain(gain);
|
||||
}
|
||||
|
||||
float Generator::getGain() const {
|
||||
return gain;
|
||||
}
|
||||
|
||||
void Generator::setFrequency(float _frequency) {
|
||||
this->frequency = _frequency;
|
||||
}
|
||||
|
||||
float Generator::getFrequency() const {
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void Generator::setWeightedStrength(float _weighted_strength) {
|
||||
this->weighted_strength = _weighted_strength;
|
||||
fnFractal->SetWeightedStrength(weighted_strength);
|
||||
}
|
||||
|
||||
float Generator::getWeightedStrength() const {
|
||||
return weighted_strength;
|
||||
}
|
||||
|
||||
void Generator::setMultiplier(uint32_t _multiplier) {
|
||||
this->multiplier = _multiplier;
|
||||
}
|
||||
|
||||
uint32_t Generator::getMultiplier() const {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> Generator::generate2dMeightmap(const int32_t begin_x,
|
||||
[[maybe_unused]] const int32_t begin_y,
|
||||
const int32_t begin_z,
|
||||
const uint32_t size_x,
|
||||
[[maybe_unused]] const uint32_t size_y,
|
||||
const uint32_t size_z) {
|
||||
constexpr bool debug = false;
|
||||
|
||||
std::vector<uint32_t> heightmap(size_x * size_z);
|
||||
|
||||
std::vector<float> noise_output(size_x * size_z);
|
||||
|
||||
if (fnFractal.get() == nullptr) {
|
||||
std::cout << "fnFractal is nullptr" << std::endl;
|
||||
return heightmap;
|
||||
}
|
||||
|
||||
fnFractal->GenUniformGrid2D(noise_output.data(), begin_x, begin_z, size_x, size_z, frequency, seed);
|
||||
|
||||
// Convert noise_output to heightmap
|
||||
for (uint32_t i = 0; i < size_x * size_z; i++) {
|
||||
heightmap[i] = static_cast<uint32_t>((noise_output[i] + 1.0) * multiplier);
|
||||
if constexpr (debug) {
|
||||
std::cout << "i: " << i << ", value: " << noise_output[i] << ", heightmap: " << heightmap[i] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (debug) {
|
||||
// cout max and min
|
||||
auto minmax = std::minmax_element(heightmap.begin(), heightmap.end());
|
||||
std::cout << "min: " << static_cast<int32_t>(*minmax.first) << std::endl;
|
||||
std::cout << "max: " << static_cast<int32_t>(*minmax.second) << std::endl;
|
||||
}
|
||||
return heightmap;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> Generator::generate3dHeightmap(const int32_t begin_x,
|
||||
const int32_t begin_y,
|
||||
const int32_t begin_z,
|
||||
const uint32_t size_x,
|
||||
const uint32_t size_y,
|
||||
const uint32_t size_z) {
|
||||
constexpr bool debug = false;
|
||||
|
||||
std::vector<uint32_t> heightmap(size_x * size_y * size_z);
|
||||
|
||||
std::vector<float> noise_output(size_x * size_y * size_z);
|
||||
|
||||
if (fnFractal.get() == nullptr) {
|
||||
std::cout << "fnFractal is nullptr" << std::endl;
|
||||
return heightmap;
|
||||
}
|
||||
|
||||
fnFractal->GenUniformGrid3D(noise_output.data(), begin_x, begin_y, begin_z, size_x, size_y, size_z, frequency, seed);
|
||||
|
||||
// Convert noise_output to heightmap
|
||||
for (uint32_t i = 0; i < size_x * size_y * size_z; i++) {
|
||||
heightmap[i] = static_cast<uint32_t>((noise_output[i] + 1.0) * multiplier);
|
||||
if constexpr (debug) {
|
||||
std::cout << "i: " << i << ", noise_output: " << noise_output[i] << ", heightmap: " << heightmap[i] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (debug) {
|
||||
// cout max and min
|
||||
auto minmax = std::minmax_element(heightmap.begin(), heightmap.end());
|
||||
std::cout << "min: " << static_cast<int32_t>(*minmax.first) << std::endl;
|
||||
std::cout << "max: " << static_cast<int32_t>(*minmax.second) << std::endl;
|
||||
}
|
||||
|
||||
return heightmap;
|
||||
}
|
||||
|
||||
/*
|
||||
std::unique_ptr<Chunk> Generator::generateChunk(const int32_t chunk_x,
|
||||
const int32_t chunk_y,
|
||||
const int32_t chunk_z,
|
||||
const bool generate_3d_terrain) {
|
||||
const int32_t real_x = chunk_x * Chunk::chunk_size_x;
|
||||
const int32_t real_y = chunk_y * Chunk::chunk_size_y;
|
||||
const int32_t real_z = chunk_z * Chunk::chunk_size_z;
|
||||
|
||||
std::vector<Block> blocks;
|
||||
|
||||
std::unique_ptr<Chunk> _chunk = std::make_unique<Chunk>();
|
||||
|
||||
if (generate_3d_terrain) {
|
||||
blocks = std::move(generate3d(real_x, real_y, real_z, Chunk::chunk_size_x, Chunk::chunk_size_y, Chunk::chunk_size_z));
|
||||
} else {
|
||||
blocks = std::move(generate2d(real_x, real_y, real_z, Chunk::chunk_size_x, Chunk::chunk_size_y, Chunk::chunk_size_z));
|
||||
}
|
||||
|
||||
_chunk->set_blocks(blocks);
|
||||
_chunk->set_chuck_pos(chunk_x, chunk_y, chunk_z);
|
||||
|
||||
return _chunk;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Chunk>> Generator::generateChunks(const int32_t begin_chunk_x,
|
||||
const int32_t begin_chunk_y,
|
||||
const int32_t begin_chunk_z,
|
||||
const uint32_t size_x,
|
||||
const uint32_t size_y,
|
||||
const uint32_t size_z,
|
||||
const bool generate_3d_terrain) {
|
||||
constexpr bool debug = false;
|
||||
|
||||
std::vector<std::unique_ptr<Chunk>> chunks;
|
||||
chunks.reserve(size_x * size_y * size_z);
|
||||
|
||||
#pragma omp parallel for collapse(3) schedule(auto)
|
||||
for (int32_t x = begin_chunk_x; x < begin_chunk_x + size_x; x++) {
|
||||
for (int32_t z = begin_chunk_y; z < begin_chunk_y + size_z; z++) {
|
||||
for (int32_t y = begin_chunk_z; y < begin_chunk_z + size_y; y++) {
|
||||
auto gen_chunk = generateChunk(x, y, z, generate_3d_terrain);
|
||||
#pragma omp critical
|
||||
chunks.emplace_back(std::move(gen_chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
std::vector<Block> Generator::generate2d(const int32_t begin_x,
|
||||
const int32_t begin_y,
|
||||
const int32_t begin_z,
|
||||
const uint32_t size_x,
|
||||
const uint32_t size_y,
|
||||
const uint32_t size_z) {
|
||||
constexpr bool debug = false;
|
||||
|
||||
std::vector<uint32_t> heightmap;
|
||||
std::vector<Block> blocks = std::vector<Block>(size_x * size_y * size_z, Block());
|
||||
|
||||
heightmap = std::move(generate2dMeightmap(begin_x, begin_y, begin_z, size_x, size_y, size_z));
|
||||
|
||||
// Generate blocks
|
||||
for (uint32_t x = 0; x < size_x; x++) {
|
||||
for (uint32_t z = 0; z < size_z; z++) {
|
||||
// Noise value is divided by 4 to make it smaller and it is used as the height of the Block (z)
|
||||
std::vector<Block>::size_type vec_index = math::convert_to_1d(x, z, size_x, size_z);
|
||||
|
||||
uint32_t noise_value = heightmap[vec_index] / 4;
|
||||
|
||||
for (uint32_t y = 0; y < size_y; y++) {
|
||||
// Calculate real y from begin_y
|
||||
vec_index = math::convert_to_1d(x, y, z, size_x, size_y, size_z);
|
||||
|
||||
if constexpr (debug) {
|
||||
std::cout << "x: " << x << ", z: " << z << ", y: " << y << " index: " << vec_index
|
||||
<< ", noise: " << static_cast<int32_t>(noise_value) << std::endl;
|
||||
}
|
||||
|
||||
Block& current_block = blocks[vec_index];
|
||||
|
||||
// If the noise value is greater than the current Block, make it air
|
||||
if (noise_value > 120) {
|
||||
current_block.block_type = block_type::stone;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
std::vector<Block> Generator::generate3d(const int32_t begin_x,
|
||||
const int32_t begin_y,
|
||||
const int32_t begin_z,
|
||||
const uint32_t size_x,
|
||||
const uint32_t size_y,
|
||||
const uint32_t size_z) {
|
||||
constexpr bool debug = false;
|
||||
|
||||
std::vector<Block> blocks = std::vector<Block>(size_x * size_y * size_z, Block());
|
||||
|
||||
std::vector<uint32_t> heightmap = generate3dHeightmap(begin_x, begin_y, begin_z, size_x, size_y, size_z);
|
||||
|
||||
// Generate blocks
|
||||
for (uint32_t x = 0; x < size_x; x++) {
|
||||
for (uint32_t z = 0; z < size_z; z++) {
|
||||
for (uint32_t y = 0; y < size_y; y++) {
|
||||
size_t vec_index = math::convert_to_1d(x, y, z, size_x, size_y, size_z);
|
||||
const uint32_t noise_value = heightmap[vec_index];
|
||||
auto& current_block = blocks[vec_index];
|
||||
|
||||
if constexpr (debug) {
|
||||
std::cout << "x: " << x << ", z: " << z << ", y: " << y << " index: " << vec_index
|
||||
<< ", noise: " << static_cast<int32_t>(noise_value) << std::endl;
|
||||
}
|
||||
|
||||
if (noise_value > 120) {
|
||||
current_block.block_type = block_type::stone;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
*/
|
115
test/source/generator/generator.hpp
Normal file
115
test/source/generator/generator.hpp
Normal file
@ -0,0 +1,115 @@
|
||||
#ifndef WORLD_OF_CUBE_GENERATOR_HPP
|
||||
#define WORLD_OF_CUBE_GENERATOR_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
// #include <omp.h>
|
||||
|
||||
#ifndef FASTNOISE_HPP_INCLUDED
|
||||
#define FASTNOISE_HPP_INCLUDED
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang system_header
|
||||
#endif
|
||||
#include "FastNoise/FastNoise.h"
|
||||
#endif
|
||||
|
||||
class Generator {
|
||||
public:
|
||||
explicit Generator(int32_t _seed);
|
||||
|
||||
explicit Generator();
|
||||
|
||||
~Generator();
|
||||
|
||||
void reseed(int32_t _seed);
|
||||
|
||||
int32_t randomizeSeed();
|
||||
|
||||
uint32_t get_seed() const;
|
||||
|
||||
void setOctaves(uint32_t _octaves);
|
||||
|
||||
uint32_t getOctaves() const;
|
||||
|
||||
void setLacunarity(float _lacunarity);
|
||||
|
||||
float getLacunarity() const;
|
||||
|
||||
void setGain(float _gain);
|
||||
|
||||
float getGain() const;
|
||||
|
||||
void setFrequency(float _frequency);
|
||||
float getFrequency() const;
|
||||
|
||||
void setWeightedStrength(float _weighted_strength);
|
||||
float getWeightedStrength() const;
|
||||
|
||||
void setMultiplier(uint32_t _multiplier);
|
||||
|
||||
uint32_t getMultiplier() const;
|
||||
|
||||
std::vector<uint32_t> generate2dMeightmap(const int32_t begin_x,
|
||||
[[maybe_unused]] const int32_t begin_y,
|
||||
const int32_t begin_z,
|
||||
const uint32_t size_x,
|
||||
[[maybe_unused]] const uint32_t size_y,
|
||||
const uint32_t size_z);
|
||||
|
||||
std::vector<uint32_t> generate3dHeightmap(const int32_t begin_x,
|
||||
const int32_t begin_y,
|
||||
const int32_t begin_z,
|
||||
const uint32_t size_x,
|
||||
const uint32_t size_y,
|
||||
const uint32_t size_z);
|
||||
/*
|
||||
std::unique_ptr<Chunk> generateChunk(const int32_t chunk_x, const int32_t chunk_y, const int32_t chunk_z, const bool generate_3d_terrain);
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Chunk>> generateChunks(const int32_t begin_chunk_x,
|
||||
const int32_t begin_chunk_y,
|
||||
const int32_t begin_chunk_z,
|
||||
const uint32_t size_x,
|
||||
const uint32_t size_y,
|
||||
const uint32_t size_z,
|
||||
const bool generate_3d_terrain);
|
||||
|
||||
std::vector<Block> generate2d(const int32_t begin_x,
|
||||
const int32_t begin_y,
|
||||
const int32_t begin_z,
|
||||
const uint32_t size_x,
|
||||
const uint32_t size_y,
|
||||
const uint32_t size_z);
|
||||
|
||||
std::vector<Block> generate3d(const int32_t begin_x,
|
||||
const int32_t begin_y,
|
||||
const int32_t begin_z,
|
||||
const uint32_t size_x,
|
||||
const uint32_t size_y,
|
||||
const uint32_t size_z);
|
||||
*/
|
||||
private:
|
||||
// default seed
|
||||
int32_t seed = 404;
|
||||
FastNoise::SmartNode<FastNoise::Perlin> fnSimplex;
|
||||
FastNoise::SmartNode<FastNoise::FractalFBm> fnFractal;
|
||||
|
||||
int32_t octaves = 6;
|
||||
float lacunarity = 0.5f;
|
||||
float gain = 3.5f;
|
||||
float frequency = 0.4f;
|
||||
float weighted_strength = 0.0f;
|
||||
uint32_t multiplier = 128;
|
||||
};
|
||||
|
||||
#endif // WORLD_OF_CUBE_GENERATOR_HPP
|
87
test/source/test/astar_test.cpp
Normal file
87
test/source/test/astar_test.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include "astar/astar.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
TEST(AStar, basic_path_1) {
|
||||
int mapWidth = 4;
|
||||
int mapHeight = 4;
|
||||
AStar::AStar pathFinder;
|
||||
pathFinder.setWorldSize({mapWidth, mapWidth});
|
||||
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
|
||||
pathFinder.setDiagonalMovement(true);
|
||||
|
||||
AStar::Vec2i source(0, 0);
|
||||
AStar::Vec2i target(mapWidth - 1, mapHeight - 1);
|
||||
|
||||
std::cout << "AStar::AStar pathFinder;" << std::endl;
|
||||
auto path = pathFinder.findPath(source, target);
|
||||
|
||||
EXPECT_EQ(path.size(), 4);
|
||||
|
||||
for (size_t i = 0; i < path.size(); i++) {
|
||||
EXPECT_EQ(path[i].x, path.size() - i - 1);
|
||||
EXPECT_EQ(path[i].y, path.size() - i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AStar, basic_path_2) {
|
||||
int mapWidth = 10;
|
||||
int mapHeight = 10;
|
||||
AStar::AStar pathFinder;
|
||||
pathFinder.setWorldSize({mapWidth, mapWidth});
|
||||
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
|
||||
pathFinder.setDiagonalMovement(true);
|
||||
|
||||
AStar::Vec2i source(0, 0);
|
||||
AStar::Vec2i target(mapWidth - 1, mapHeight - 1);
|
||||
|
||||
auto path = pathFinder.findPath(source, target);
|
||||
|
||||
EXPECT_EQ(path.size(), 10);
|
||||
|
||||
for (size_t i = 0; i < path.size(); i++) {
|
||||
EXPECT_EQ(path[i].x, path.size() - i - 1);
|
||||
EXPECT_EQ(path[i].y, path.size() - i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AStar, basic_diagonal_path_wrong_1) {
|
||||
int mapWidth = 10;
|
||||
int mapHeight = 10;
|
||||
AStar::AStar pathFinder;
|
||||
pathFinder.setWorldSize({mapWidth, mapHeight});
|
||||
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
|
||||
pathFinder.setDiagonalMovement(true);
|
||||
|
||||
AStar::Vec2i source(0, 0);
|
||||
AStar::Vec2i target(19, 19);
|
||||
|
||||
auto path = pathFinder.findPath(source, target);
|
||||
|
||||
EXPECT_EQ(path.size(), 0);
|
||||
}
|
||||
|
||||
TEST(AStar, basic_diagonal_path_wrong_2) {
|
||||
int mapWidth = 10;
|
||||
int mapHeight = 10;
|
||||
AStar::AStar pathFinder;
|
||||
pathFinder.setWorldSize({mapWidth, mapHeight});
|
||||
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
|
||||
pathFinder.setDiagonalMovement(true);
|
||||
|
||||
pathFinder.addObstacle({0, 1});
|
||||
pathFinder.addObstacle({1, 1});
|
||||
pathFinder.addObstacle({1, 0});
|
||||
|
||||
AStar::Vec2i source(0, 0);
|
||||
AStar::Vec2i target(9, 9);
|
||||
|
||||
auto path = pathFinder.findPath(source, target);
|
||||
|
||||
EXPECT_EQ(path.size(), 0);
|
||||
}
|
||||
|
||||
auto main(int argc, char** argv) -> int {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Reference in New Issue
Block a user