#pragma once #include "utilities.hpp" #include #include #include #include #include // Position {0,0} is bottom left. // Position {W-1, H-1} is top right. template struct Grid{ using Position = std::pair; Grid(std::initializer_list g){ std::copy(std::begin(g), std::end(g), 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 { using Indices = std::make_index_sequence; return all_positions_impl(Indices{}); } //! \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: std::array grid; //! \brief Helper function for all_positions() template auto const & all_positions_impl(std::index_sequence) const { static const std::array ret = {{ Position{I % W, I / W}... }}; return ret; } //! \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; } }; //! \brief Helper function to create a field (T will be deducted) template auto create_rectangular_field(std::initializer_list grid){ return Grid(grid); } namespace detail { template auto random_field(URNG&& r, std::index_sequence){ std::uniform_int_distribution dis(1, 2); return create_rectangular_field({ ((void)I, dis(r))... }); } } template auto random_static_grid(URNG&& r){ using Indices = std::make_index_sequence; return detail::random_field(r, Indices{}); }