#include "simulation.h" #include "input.h" #include #include #include #include #include #include #include namespace simulation { std::set living{}; static std::vector overpopulated{}; static std::vector underpopulated{}; static std::vector born{}; #ifdef __glibc_likely #define likely(cond_) __glibc_likely(cond_) #else #define likely(cond_) (cond_) #endif 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 } {} 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; } CellIterator &CellIterator::operator--() { --(this->state.x); if (this->state.x == this->begin.x) { this->state.x = this->end.x - 1; this->state.y = SDL_max(this->state.y - 1, this->begin.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 std::vector NeighborSet(Cell cell) { std::vector out{}; for (Cell const &c : CellRange{ { cell.x - 1, cell.y - 1 }, { cell.x + 2, cell.y + 2 } }) { if (likely(c != cell)) { out.push_back(c); } } return out; } 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 && living.contains(c)) { ++count; } } return count; } static void PopulateChanges() { overpopulated.clear(); underpopulated.clear(); born.clear(); // TODO: consider multithreading, this is highly parallelisable std::ranges::copy_if(living, std::back_inserter(overpopulated), [&](Cell const &c) -> bool { size_t const neighbors{ CountNeighbors(c) }; return neighbors > 3; }); std::ranges::copy_if(living, std::back_inserter(underpopulated), [&](Cell const &c) -> bool { size_t const neighbors{ CountNeighbors(c) }; return neighbors < 2; }); for (Cell const &cell : living) { std::vector neighbors{ NeighborSet(cell) }; std::ranges::copy_if(neighbors, std::back_inserter(born), [&](Cell const &c) -> bool { size_t const neighbors{ CountNeighbors(c) }; return !living.contains(c) && neighbors == 3; }); } } void InitializeRandom(size_t livingChance, int64_t fillArea) { 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() { for (Cell const &cell : underpopulated) { living.erase(cell); } for (Cell const &cell : overpopulated) { living.erase(cell); } for (Cell const &cell : born) { 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; SDL_GetCurrentRenderOutputSize(renderer, &w, nullptr); 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; SDL_RenderFillRect(renderer, &cellRect); } SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); for (Cell const &cell : overpopulated) { cellRect.x = (viewOffset.x + cell.x) * cellRect.w; cellRect.y = (viewOffset.y + cell.y) * cellRect.h; SDL_RenderRect(renderer, &cellRect); } SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255); for (Cell const &cell : underpopulated) { cellRect.x = (viewOffset.x + cell.x) * cellRect.w; cellRect.y = (viewOffset.y + cell.y) * cellRect.h; SDL_RenderRect(renderer, &cellRect); } SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); for (Cell const &cell : born) { cellRect.x = (viewOffset.x + cell.x) * cellRect.w; cellRect.y = (viewOffset.y + cell.y) * cellRect.h; 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; } }