1
Fork 0
This repository has been archived on 2025-04-09. You can view files and clone it, but cannot push or open issues or pull requests.
puzzle-wuzzle-generator/include/static_grid.hpp
2014-02-18 16:45:51 +01:00

168 lines
3.7 KiB
C++

#pragma once
#include "utilities.hpp"
#include <cassert>
#include <utility>
#include <array>
#include <algorithm>
#include <iostream>
// Position {0,0} is bottom left.
// Position {W-1, H-1} is top right.
template <int W, int H, typename T>
struct Grid{
using Position = std::pair<int, int>;
Grid(std::initializer_list<T> 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<Position, 4> 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<W*H>;
return all_positions_impl(Indices{});
}
//! \brief Get (mutable) value at p
T& get(Position const & p){
assert(valid(p));
return grid[static_cast<size_t>(p.first + p.second*W)];
}
//! \brief Get value at p
T const& get(Position const & p) const {
assert(valid(p));
return grid[static_cast<size_t>(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<T, W*H> grid;
//! \brief Helper function for all_positions()
template<std::size_t... I>
auto const & all_positions_impl(std::index_sequence<I...>) const {
static const std::array<const Position, W*H> 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 <int W, int H, typename T>
auto create_rectangular_field(std::initializer_list<T> grid){
return Grid<W, H, T>(grid);
}
namespace detail {
template <int W, int H, typename URNG, size_t... I>
auto random_field(URNG&& r, std::index_sequence<I...>){
std::uniform_int_distribution<int> dis(1, 2);
return create_rectangular_field<W, H>({
((void)I, dis(r))...
});
}
}
template <int W, int H, typename URNG>
auto random_static_grid(URNG&& r){
using Indices = std::make_index_sequence<W*H>;
return detail::random_field<W, H>(r, Indices{});
}