diff --git a/include/dynamic_grid.hpp b/include/dynamic_grid.hpp new file mode 100644 index 0000000..a7a3260 --- /dev/null +++ b/include/dynamic_grid.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include "field.hpp" + +#include +#include +#include +#include +#include +#include +#include + +// Position {0,0} is bottom left. +// Position {W-1, H-1} is top right. +template +struct DynamicField{ + using Position = std::pair; + + template + DynamicField(int W, int H, Input it) + : W(W) + , H(H) + , grid(W*H, 0) + { + std::copy_n(it, W*H, std::begin(grid)); + } + + //! \brief Returns true if p represents a valid position + auto valid(Position const & p) const { + return p.first >= 0 && p.second >= 0 + && p.first < W && p.second < H; + } + + //! \brief Returns true if p represents an empty cell + auto empty(Position const & p) const { + assert(valid(p)); + return get(p) == 0; + } + + //! \brief Returns all neighbors of p + //! Only returns valid neighbors (and p does not need to be valid) + auto neighbors(Position const & p) const { + small_vector ret; + + static Position nbs[] = { + {-1, 0}, {1, 0}, {0, 1}, {0, -1} + }; + + for(auto&& n : nbs){ + auto q = Position{p.first + n.first, p.second + n.second}; + if(valid(q)){ + ret.push_back(q); + } + } + + return ret; + } + + //! \brief Let the block fall (all the way) + auto collapse(){ + while(vcollapse()); + while(hcollapse()); + } + + //! \brief Returns an array of all valid positions + auto const & all_positions() const { + static auto v = all_positions_impl(W, H); + return v; + } + + //! \brief Get (mutable) value at p + T& get(Position const & p){ + assert(valid(p)); + return grid[static_cast(p.first + p.second*W)]; + } + + //! \brief Get value at p + T const& get(Position const & p) const { + assert(valid(p)); + return grid[static_cast(p.first + p.second*W)]; + } + + //! \brief Pretty prints grid to out + void print(std::ostream& out) const { + for(auto y = H; y; --y){ + for(auto x = 0; x < W; ++x){ + out << get({x, y-1}) << (x == W-1 ? '\n' : ' '); + } + } + } + +private: + int W; + int H; + std::vector grid; + + //! \brief Helper function for all_positions() + static auto all_positions_impl(int W, int H) { + std::vector r; + for(int y = 0; y < H; ++y) + for(int x = 0; x < W; ++x) + r.emplace_back(x, y); + return r; + } + + //! \brief Single vertical collapsing step + auto vcollapse(){ + using namespace std; + bool some_change = false; + for(auto&& p : all_positions()){ + if(empty(p)) continue; + + auto q = p; + q.second--; + if(!valid(q)) continue; + if(!empty(q)) continue; + + swap(get(p), get(q)); + some_change = true; + } + + return some_change; + } + + //! \brief Single horizontal collapsing step + auto hcollapse(){ + using namespace std; + bool some_change = false; + for(auto x = 0; x < W-1; ++x){ + if(!empty_column(x)) continue; + + auto x2 = x+1; + for(; x2 < W; ++x2){ + if(!empty_column(x2)) break; + } + if(x2 == W) return some_change; + + for(auto y = 0; y < H; ++y){ + swap(get({x, y}), get({x2, y})); + } + some_change = true; + } + return some_change; + } + + //! \brief Returns true if the whole column at x is empty + auto empty_column(int x){ + for(auto y = 0; y < H; ++y){ + if(!empty({x, y})){ + return false; + } + } + return true; + } +}; + +template +auto random_dynamic_grid(int W, int H, URNG&& r){ + std::uniform_int_distribution dis(1, 2); + std::vector v(W*H); + std::generate_n(std::begin(v), W*H, [&]{ return dis(r); }); + return DynamicField(W, H, std::begin(v)); +} diff --git a/main.cpp b/main.cpp index ab16448..5ab3ae4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,5 @@ -#include "field.hpp" +#include "dynamic_grid.hpp" #include "clusters.hpp" #include "solver.hpp" #include "generator.hpp" @@ -16,7 +16,7 @@ int main(){ std::mt19937 gen(rd()); std::cout << std::endl; - auto field = random_field<3, 3>(gen); + auto field = random_dynamic_grid(3, 3, gen); field.print(std::cout);