From a3396bb6af88ebceaebed565d902b7af52ad228c Mon Sep 17 00:00:00 2001 From: Sara Gerretsen Date: Wed, 24 Sep 2025 22:01:55 +0200 Subject: [PATCH] feat: converted project to template --- CMakeLists.txt | 8 +- README.md | 43 ++++-- justfile | 11 +- src/application.cpp | 167 ++------------------- src/elements.h | 1 - src/main.cpp | 2 - src/simulation.cpp | 356 -------------------------------------------- src/simulation.h | 50 ------- 8 files changed, 48 insertions(+), 590 deletions(-) delete mode 100644 src/simulation.cpp delete mode 100644 src/simulation.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 96a3c22..9340897 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.21) -project(GameOfLife) +project(CHANGEME) set(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") @@ -15,11 +15,11 @@ add_subdirectory(vendor/SDL3/ EXCLUDE_FROM_ALL) set(SDLTTF_VENDORED ON) add_subdirectory(vendor/SDL3_ttf/ EXCLUDE_FROM_ALL) -add_executable(GameOfLife ${source_files}) -target_link_libraries(GameOfLife PRIVATE SDL3_ttf::SDL3_ttf SDL3::SDL3) +add_executable(CHANGEME ${source_files}) +target_link_libraries(CHANGEME PRIVATE SDL3_ttf::SDL3_ttf SDL3::SDL3) add_custom_target(copy_assets COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/ ${CMAKE_BINARY_DIR}/assets ) -add_dependencies(GameOfLife copy_assets) +add_dependencies(CHANGEME copy_assets) diff --git a/README.md b/README.md index e83c1e3..7e63075 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,26 @@ -# Conway's Game of Life SDL3 implementation +# Clay SDL3 Template + +## Using Template + +### Just use [`just`](https://just.systems/man/en/) + +Run `just set-project-name {project name}` first. + +And remember to `git remote set-url origin {repo}`. + +### Otherwise + +Modify the CMakeLists.txt manually, just replace CHANGEME with whatever your binary should be called. ## Compiling > Remember to `git submodule update --init --recursive`! -### Just use [`just`](https://just.systems/man/en/) +### Just again -Use the justfile if you have `just` and `bear` installed: +`just configure` runs cmake configuration and generates a `compile_commands.json`. -`just configure` runs cmake configuration. - -`just build` runs the cmake build and generates a `compile_commands.json` (assuming you have `bear` installed). +`just build` runs the cmake build. ### CMake works too, I guess @@ -22,10 +32,6 @@ Same as always ## Files -### simulation.h/cpp - -The Conway's Game of Life generation ticking and rendering. - ### thread_pool.h/cpp Thread pool used for parallel GoL generation ticking. @@ -54,13 +60,12 @@ Handling of input events. Entrypoint, setup, and main application loop. - ## Prerequisites -* Compiler capable of C++23 and C23. - * CMake 3.21 or higher +* Compiler capable of C++23 and C23. + ## Dependencies ### SDL3 @@ -79,9 +84,11 @@ Included as files in `vendor/clay/clay.h`; Single header library. Included as files in `vendor/renderer/` and compiled as part of the project. +> Note: Mildly modified from the official Clay_SDL3_renderer to enable more advanced styling. + ## Code Standards -* Keep program structure as simple as possible. No `Application` class or other Java-isms. Prefer namespaces with static lifetime variables. +* Keep program structure as simple as possible. No `class Application {` class or other Java-isms. Prefer namespaces with static lifetime variables. * Use STL where possible. Don't reinvent the wheel. @@ -93,12 +100,16 @@ Included as files in `vendor/renderer/` and compiled as part of the project. * const applies to the name to it's left, so it goes after the type, not `const int x;` but `int const x`. -* \* and & flush with the declaration name. `void Function(Object const &inRef)` +* \* and & flush with the declaration name. `Type const &Function(Type &inRef)` > (1) Bracket exceptions: > * using scoped_lock in arbitrary blocks, prefer tailed lisp brackets + ``` -void MyFunction() { // K&R here +struct Data { + int x{ 0 }, y{ 0 }; +}; +void MyFunction(Data const &data) { // K&R here DoAsynchronousThings(); { scoped_lock lock{ myMutex }; myVariable++; diff --git a/justfile b/justfile index f254652..a106e4d 100644 --- a/justfile +++ b/justfile @@ -1,14 +1,19 @@ build: # BUILDING - bear -- cmake --build build + cmake --build build run: - cd bin/ && DiceGui + cd bin/ && CHANGEME configure: # CONFIGURING WITH PREMAKE - cmake -S. -Bbuild + cmake -S. -Bbuild -DCMAKE_EXPORT_COMPILE_COMMANDS=ON clean: + # CLEANING BUILD ARTEFACTS rm -r bin/** rm -r build/** + +set-project-name projectname: clean + git remote set-url origin "" + sed -i "s/CHANGEME/{{projectname}}/g" ./CMakeLists.txt ./justfile diff --git a/src/application.cpp b/src/application.cpp index 829240c..decafbb 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,173 +1,24 @@ #include "application.h" #include "style.h" #include "elements.h" -#include "simulation.h" #include #include namespace application { -static bool isSimulating{ false }; -static bool lockFramerate{ true }; - -static void SetSimulatingButton(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) { - if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { - isSimulating = data == 1; - } -} - -static void RandomizeFieldButton(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) { - if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { - simulation::InitializeRandom(2, 300); - } -} - -static void StepSimulationButton(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) { - if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { - simulation::Step(); - } -} - -static void StepControl() { - CLAY_AUTO_ID(style::PanelContainer(1, { - .layout = { - .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT() }, - .childGap = 16, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - })) { - CLAY_AUTO_ID({ - .layout = { - .childGap = 16 - } - }) { - if (isSimulating) { - elements::TextButton(CLAY_STRING("Pause"), style::warningButton, &SetSimulatingButton, false); - } else { - elements::TextButton(CLAY_STRING("Start"), style::proceedButton, &SetSimulatingButton, true); - elements::TextButton(CLAY_STRING("Step"), style::actionButton, &StepSimulationButton); - } - } - elements::Toggle(CLAY_STRING("Threaded"), style::actionButton, simulation::threadedDesired); - elements::Toggle(CLAY_STRING("Show Changes"), style::actionButton, simulation::drawDebugInfo); - elements::Toggle(CLAY_STRING("Lock Speed"), style::actionButton, lockFramerate); - } -} - -static void DebugInfoLegend() { - Clay_ElementDeclaration style{style::PanelContainer(1, { - .layout = { - .padding = { - .left = 20, .right = 0, .top = 0, .bottom = 0 - }, - .childGap = 10, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - }) - }; - style.cornerRadius = { 0, style::defaultRadius, 0, style::defaultRadius }; - CLAY_AUTO_ID(style) { - CLAY_AUTO_ID({ - .layout = { - .childGap = 10, - }, - }) { - CLAY_AUTO_ID({ - .layout = { - .sizing = { CLAY_SIZING_FIXED(20), CLAY_SIZING_FIXED(20) } - }, - .border = { {255, 0, 255, 255 }, CLAY_BORDER_OUTSIDE(3) } - }); - elements::Body(CLAY_STRING("Underpopulated"), { - .textColor = style::TextColor(0) - }); - } - CLAY_AUTO_ID({ - .layout = { - .childGap = 10, - } - }) { - CLAY_AUTO_ID({ - .layout = { - .sizing = { CLAY_SIZING_FIXED(20), CLAY_SIZING_FIXED(20) } - }, - .border = { {255, 0, 0, 255 }, CLAY_BORDER_OUTSIDE(3) } - }); - elements::Body(CLAY_STRING("Overpopulated"), { - .textColor = style::TextColor(0) - }); - } - CLAY_AUTO_ID({ - .layout = { - .childGap = 10, - } - }) { - CLAY_AUTO_ID({ - .layout = { - .sizing = { CLAY_SIZING_FIXED(20), CLAY_SIZING_FIXED(20) } - }, - .border = { {0, 255, 0, 255 }, CLAY_BORDER_OUTSIDE(3) } - }); - elements::Body(CLAY_STRING("Born"), { - .textColor = style::TextColor(0) - }); - } - elements::Body(CLAY_STRING("Warning:\nDrawing simulation changes\ngreatly lowers performance."), { - .textColor = style::TextColor(2) - }); - } -} - -static void PrimaryControls() { - static bool isOpen{ true }; - if (isOpen) { - CLAY_AUTO_ID(style::LeftPanelContainer(0, { - .layout = { - .sizing = { CLAY_SIZING_FIT(.18) }, - } - })) { - CLAY_AUTO_ID({ - .layout = { - .childGap = 16, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .clip = { - true, true, Clay_GetScrollOffset() - } - }) { - StepControl(); - if (!isSimulating) { - CLAY_AUTO_ID(style::LeftPanelContainer(1, { - .layout = { .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT() } } - })) { - elements::TextButton(CLAY_STRING("Randomize"), style::actionButton, &RandomizeFieldButton); - } - } - } - } - } - if (simulation::drawDebugInfo) { - DebugInfoLegend(); - } -} - -static void TryStep() { - static uint64_t lastStep = 0; - if (isSimulating) { - double deltaTime{ (double)(SDL_GetTicks() - lastStep) * 0.001 }; - constexpr double targetDeltaTime = 1.0/24.0; - if (!lockFramerate || deltaTime > targetDeltaTime) { - lastStep = SDL_GetTicks(); - simulation::Step(); - } - } +static void SampleHeader() { + elements::Header(CLAY_STRING("Left Panel"), 2, { + .textColor = style::color::white + }); } Clay_RenderCommandArray RenderApplication() { - TryStep(); Clay_BeginLayout(); CLAY(CLAY_ID("OuterContainer"), style::Window()) { - simulation::SetSimulationHovered(Clay_Hovered()); - PrimaryControls(); + CLAY_AUTO_ID(style::PanelContainer(0, { + .layout = { .sizing = { CLAY_SIZING_PERCENT(0.15), CLAY_SIZING_GROW() } } + })) { + SampleHeader(); + } } return Clay_EndLayout(); } diff --git a/src/elements.h b/src/elements.h index f3ac09c..1f5269e 100644 --- a/src/elements.h +++ b/src/elements.h @@ -5,7 +5,6 @@ namespace elements { typedef void(*OnHoveredFn)(Clay_ElementId element, Clay_PointerData pointer, intptr_t data); - void TextButton(Clay_String text, Clay_Color color, OnHoveredFn onHovered, intptr_t onHoveredData = 0); void Toggle(Clay_String label, Clay_Color selected, bool &state); void Body(Clay_String string, Clay_TextElementConfig baseCfg = {}); diff --git a/src/main.cpp b/src/main.cpp index 136b421..543d5f0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,6 @@ #include "application.h" #include "input.h" #include "resources.h" -#include "simulation.h" #include #include #include @@ -133,7 +132,6 @@ int main(int argc, char *argv[]) { Clay_UpdateScrollContainers(true, input::scrollMotion, deltaTime); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); - simulation::Draw(renderer, 0.01); Clay_RenderCommandArray commands{ application::RenderApplication() }; SDL_Clay_RenderClayCommands(&backendData, &commands); SDL_RenderPresent(renderer); diff --git a/src/simulation.cpp b/src/simulation.cpp deleted file mode 100644 index 6c43b7b..0000000 --- a/src/simulation.cpp +++ /dev/null @@ -1,356 +0,0 @@ -#include "simulation.h" -#include "SDL3/SDL_timer.h" -#include "input.h" -#include "thread_pool.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __glibc_likely -#define likely(cond_) __glibc_likely(cond_) -#else -#define likely(cond_) (cond_) -#endif - -#define SIM_MULTITHREADING 1 - -namespace simulation { -// Encapsulates the data relevant to a GoL rule's workload -struct ThreadWorkload { - size_t seg_idx{0}, seg_len{1}; - std::mutex mtx{}; - std::vector changes{}; -}; - -bool drawDebugInfo{ true }; // draw next step's changes over current state -bool threadedDesired{ true }; // multithread generation steps -static bool actuallyThreaded{ false }; // notes whether or not currently ongoing tasks are multithreaded -size_t threadsPerTask{ std::thread::hardware_concurrency() }; - -static std::set living{}; // The set of currently alive cells - -// Ruleset workload abstraction -static std::vector> overpopulated{}; -static std::vector> underpopulated{}; -static std::vector> born{}; -// Multithreading -static unsigned tasks{ 0 }; -static std::mutex tasksMutex{}; -static std::condition_variable tasksChanged{}; -// ns timestamp of the start of the last generation change calculation -static uint64_t generationStartTime{ 0 }; - -CellIterator::CellIterator(Cell begin, Cell end) -: state{ begin } , begin{ begin }, end{ end } {} - -CellIterator::CellIterator(Cell begin, Cell end, Cell state) -: state{ state } , begin{ begin }, end{ end } {} - -static inline bool CellIsAlive(Cell const &cell) { - return living.contains(cell); -} - -CellIterator &CellIterator::operator++() { - ++(this->state.x); - if (this->state.x == this->end.x) { - this->state.x = this->begin.x; - this->state.y = SDL_min(this->state.y + 1, this->end.y); - } - return *this; -} - -bool CellIterator::operator==(CellIterator const &src) const { - return src.begin == this->begin && src.end == this->end && (src.state == this->state || src.at_end() == this->at_end()); -} - -bool CellIterator::operator!=(CellIterator const &src) const { - return src.begin != this->begin || src.end != this->end || (src.state != this->state && src.at_end() != this->at_end()); -} - -Cell const &CellIterator::operator*() const { - return state; -} - -bool CellIterator::at_end() const { - return this->state.y == this->end.y; -} - -static size_t CountNeighbors(Cell const &cell) { - size_t count{ 0 }; - for (Cell const &c : CellRange{ {cell.x - 1, cell.y - 1}, { cell.x + 2, cell.y + 2} }) { - if (c != cell && CellIsAlive(c)) { - ++count; - } - } - return count; -} - -// Block the thread until all tasks are done -// after this function, all code in this file is synchronous, until another thread is started -static void BlockUntilTasksDone() { - if (actuallyThreaded) { - std::unique_lock lock{ tasksMutex }; - while (tasks > 0) { - tasksChanged.wait(lock); - } - } else while (tasks > 0) { } -} - -static void TaskBegin() { - std::scoped_lock lock{ tasksMutex }; - tasks++; -} - -static void TaskComplete() { - if (actuallyThreaded) { - std::scoped_lock lock{ tasksMutex }; - if (--tasks == 0) { - SDL_Log("Generation Complete %lfs", (double)(SDL_GetTicksNS() - generationStartTime) * 0.000000001); - } - tasksChanged.notify_all(); - } else if (tasks == 3) { - SDL_Log("Generation complete %lfs", (double)(SDL_GetTicksNS() - generationStartTime) * 0.000000001); - tasks = 0; - } -} - -static void FindOverpopulated(std::shared_ptr wl) { - TaskBegin(); - std::scoped_lock lock{ wl->mtx }; - wl->changes.clear(); - - std::set::iterator begin{ living.begin() }; - std::set::iterator end{ living.begin() }; - - std::advance(begin, wl->seg_idx * wl->seg_len); - std::advance(end, wl->seg_idx * wl->seg_len + wl->seg_len); - - std::copy_if(begin, end, std::back_inserter(wl->changes), - [&](Cell const &c) -> bool { - return CountNeighbors(c) > 3; - }); - TaskComplete(); -} - -static void FindUnderpopulated(std::shared_ptr wl) { - TaskBegin(); - std::scoped_lock lock{ wl->mtx }; - wl->changes.clear(); - - std::set::iterator begin{ living.begin() }; - std::set::iterator end{ living.begin() }; - - std::advance(begin, wl->seg_idx * wl->seg_len); - std::advance(end, wl->seg_idx * wl->seg_len + wl->seg_len); - - std::copy_if(begin, end, std::back_inserter(wl->changes), - [&](Cell const &c) -> bool { - return CountNeighbors(c) < 2; - }); - TaskComplete(); -} - -static void FindBorn(std::shared_ptr wl) { - TaskBegin(); - std::scoped_lock lock{ wl->mtx }; - wl->changes.clear(); - - std::set::iterator itr{ living.begin() }; - - std::advance(itr, wl->seg_idx * wl->seg_len); - - std::for_each_n(itr, wl->seg_len, [&wl](Cell const ¢er){ - CellRange range{{ center.x - 1, center.y - 1 }, {center.x + 2, center.y + 2 }}; - std::copy_if(range.begin(), range.end(), std::back_inserter(wl->changes), - [&](Cell const &c) -> bool { - return !CellIsAlive(c) && CountNeighbors(c) == 3; - }); - }); - TaskComplete(); -} - -static inline void TickGenerationThreaded() { - SDL_Log("Multithreading ON"); - generationStartTime = SDL_GetTicksNS(); - size_t const seg_length{ living.size() / threadsPerTask }; - for (size_t i{ 0 }; i < threadsPerTask; ++i) { - if (overpopulated[i] == nullptr) - overpopulated[i] = std::make_shared(); - { std::scoped_lock lock{ overpopulated[i]->mtx }; - overpopulated[i]->seg_idx = i; overpopulated[i]->seg_len = seg_length; - threading::tasks.ScheduleTask(std::bind(FindOverpopulated, overpopulated[i])); - } - if (underpopulated[i] == nullptr) - underpopulated[i] = std::make_shared(); - { std::scoped_lock lock{ underpopulated[i]->mtx }; - underpopulated[i]->seg_idx = i; underpopulated[i]->seg_len = seg_length; - threading::tasks.ScheduleTask(std::bind(FindUnderpopulated, underpopulated[i])); - } - if (born[i] == nullptr) - born[i] = std::make_shared(); - { std::scoped_lock lock{ born[i]->mtx }; - born[i]->seg_idx = i; born[i]->seg_len = seg_length; - threading::tasks.ScheduleTask(std::bind(FindBorn, born[i])); - } - } -} - -static inline void TickGenerationSynchronous() { - SDL_Log("Multithreading OFF"); - generationStartTime = SDL_GetTicksNS(); - born[0]->seg_idx = underpopulated[0]->seg_idx = overpopulated[0]->seg_idx = 0; - born[0]->seg_len = underpopulated[0]->seg_len = overpopulated[0]->seg_len = living.size(); - - FindOverpopulated(overpopulated[0]); - FindUnderpopulated(underpopulated[0]); - FindBorn(born[0]); -} - -static void PopulateChanges() { - static bool first_run{ true }; - BlockUntilTasksDone(); - // for some reason three tasks per thread is faster than one task per thread, i don't get it either - if (threadedDesired) { - if (first_run || !actuallyThreaded) { - actuallyThreaded = true; - first_run = false; - born.resize(threadsPerTask); - underpopulated.resize(threadsPerTask); - overpopulated.resize(threadsPerTask); - } - TickGenerationThreaded(); - } else { - if (first_run || actuallyThreaded) { - actuallyThreaded = false; - first_run = false; - born.resize(1); - underpopulated.resize(1); - overpopulated.resize(1); - } - TickGenerationSynchronous(); - } -} - -void InitializeRandom(size_t livingChance, int64_t fillArea) { - BlockUntilTasksDone(); - living.clear(); - - Cell itr{ 0, 0 }; - while (itr.y < fillArea) { - if (std::rand() % livingChance == 0 ) { - living.insert(itr); - } - ++itr.x; - if (itr.x == fillArea) { - itr.x = 0; - ++itr.y; - } - } - PopulateChanges(); -} - -void Step() { - BlockUntilTasksDone(); - for (std::shared_ptr wl : underpopulated) { - if (wl == nullptr) continue; - std::scoped_lock l{ wl->mtx }; - for (Cell const &cell : wl->changes) - living.erase(cell); - } - for (std::shared_ptr wl : overpopulated) { - if (wl == nullptr) continue; - std::scoped_lock l{ wl->mtx }; - for (Cell const &cell : wl->changes) - living.erase(cell); - } - for (std::shared_ptr wl : born) { - if (wl == nullptr) continue; - std::scoped_lock l{ wl->mtx }; - for (Cell const &cell : wl->changes) - living.insert(cell); - } - - PopulateChanges(); -} - -static bool simulationHovered{ false }; -static SDL_FPoint viewOffset{ 0, 0 }; - -void Draw(SDL_Renderer *renderer, double cellSizePercent) { - if (simulationHovered) { - viewOffset.x += input::scrollMotion.x; - viewOffset.y += input::scrollMotion.y; - } - int w, h; - SDL_GetCurrentRenderOutputSize(renderer, &w, &h); - float const cellWidth = static_cast(w) * cellSizePercent; - SDL_FRect cellRect{ - 0, 0, cellWidth, cellWidth - }; - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - for (Cell const &cell : living) { - cellRect.x = (viewOffset.x + cell.x) * cellRect.w; - cellRect.y = (viewOffset.y + cell.y) * cellRect.h; - if (cellRect.x < -cellRect.w || cellRect.y < -cellRect.h || cellRect.x > w || cellRect.y > h) - continue; - SDL_RenderFillRect(renderer, &cellRect); - } - if (!drawDebugInfo) { - return; - } - BlockUntilTasksDone(); - for (std::shared_ptr wl : overpopulated) { - std::scoped_lock l{ wl->mtx }; - SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); - for (Cell const &cell : wl->changes) { - cellRect.x = (viewOffset.x + cell.x) * cellRect.w; - cellRect.y = (viewOffset.y + cell.y) * cellRect.h; - if (cellRect.x < -cellRect.w || cellRect.y < -cellRect.h || cellRect.x > w || cellRect.y > h) - continue; - SDL_RenderRect(renderer, &cellRect); - } - } - for (std::shared_ptr wl : underpopulated) { - std::scoped_lock l{ wl->mtx }; - SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255); - for (Cell const &cell : wl->changes) { - cellRect.x = (viewOffset.x + cell.x) * cellRect.w; - cellRect.y = (viewOffset.y + cell.y) * cellRect.h; - if (cellRect.x < -cellRect.w || cellRect.y < -cellRect.h || cellRect.x > w || cellRect.y > h) - continue; - SDL_RenderRect(renderer, &cellRect); - } - } - for (std::shared_ptr wl : born) { - std::scoped_lock l{ wl->mtx }; - SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); - for (Cell const &cell : wl->changes) { - cellRect.x = (viewOffset.x + cell.x) * cellRect.w; - cellRect.y = (viewOffset.y + cell.y) * cellRect.h; - if (cellRect.x < -cellRect.w || cellRect.y < -cellRect.h || cellRect.x > w || cellRect.y > h) - continue; - SDL_RenderRect(renderer, &cellRect); - } - } -} - -void SetSimulationHovered(bool value) { - simulationHovered = value; -} -bool operator==(Cell const &lhs, Cell const &rhs) { - return lhs.x == rhs.x && lhs.y == rhs.y; -} -bool operator!=(Cell const &lhs, Cell const &rhs) { - return lhs.x != rhs.x || lhs.y != rhs.y; -} - -bool operator<(Cell const &lhs, Cell const &rhs) { - if (lhs.y == rhs.y) return lhs.x < rhs.x; - else return lhs.y < rhs.y; -} -} diff --git a/src/simulation.h b/src/simulation.h deleted file mode 100644 index 0dbbe50..0000000 --- a/src/simulation.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef SIMULATION_H -#define SIMULATION_H - -#include -#include -#include - -namespace simulation { -struct Cell { - int64_t x{ 0 }, y{ 0 }; - -}; - -class CellIterator { -friend class CellRange; -public: CellIterator(Cell begin, Cell end); - CellIterator &operator++(); - CellIterator &operator=(CellIterator const &src); - bool operator==(CellIterator const &src) const; - bool operator!=(CellIterator const &src) const; - Cell const &operator*() const; - bool at_end() const; -private: CellIterator(Cell begin, Cell end, Cell state); - Cell state{ 0, 0 }; - Cell begin{ 0, 0 }, end{ 0, 0 }; -}; - -class CellRange { -private: - Cell beginCell{ 0, 0 }, endCell{ 0, 0 }; -public: CellRange(Cell begin, Cell end) : beginCell{ begin }, endCell{ end } {} - CellIterator begin() { return CellIterator{ beginCell, endCell }; } - CellIterator end() { return CellIterator{ beginCell, endCell, endCell }; } -}; - -extern bool drawDebugInfo; -extern bool threadedDesired; -extern size_t threadsPerTask; - -void InitializeRandom(size_t livingChance, int64_t fillArea); -void Step(); -void Draw(SDL_Renderer *renderer, double cellSizePercent); -void SetSimulationHovered(bool value); - -bool operator==(Cell const &lhs, Cell const &rhs); -bool operator!=(Cell const &lhs, Cell const &rhs); -bool operator<(Cell const &lhs, Cell const &rhs); -} - -#endif // !SIMULATION_H