1
Fork 0

Adds source file for grid. Adds verbose option.

This commit is contained in:
Joshua Moerman 2014-02-16 11:36:19 +01:00
parent b61b6c04ff
commit f4ef3d2837
9 changed files with 185 additions and 139 deletions

View file

@ -1,13 +1,13 @@
project(PuzzleWuzzleGenerator) project(PuzzleWuzzleGenerator)
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 2.8)
include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/include/") include_directories("${PROJECT_SOURCE_DIR}/include/")
add_definitions(-std=c++1y) add_definitions(-std=c++1y)
find_package(Boost REQUIRED COMPONENTS program_options system) find_package(Boost REQUIRED COMPONENTS program_options system)
include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
set(libs ${libs} ${Boost_LIBRARIES}) set(libs ${libs} ${Boost_LIBRARIES})
aux_source_directory(. SRC_LIST) set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_executable(${PROJECT_NAME} ${SRC_LIST}) add_subdirectory("lib")
target_link_libraries(${PROJECT_NAME} ${libs}) add_subdirectory("src")

View file

@ -34,14 +34,18 @@ auto cluster_at(Field const & field, typename Field::Position const & p){
} }
template <typename Field> template <typename Field, typename Rules>
auto all_clusters(Field const & field){ auto all_clusters(Field const & field, Rules const & rules){
boost::container::flat_set<decltype(cluster_at(field, std::declval<typename Field::Position>()))> ret; boost::container::flat_set<decltype(cluster_at(field, std::declval<typename Field::Position>()))> ret;
ret.reserve(10); ret.reserve(10);
for(auto&& p : field.all_positions()){ for(auto&& p : field.all_positions()){
if(field.empty(p)) continue; if(field.empty(p)) continue;
ret.insert(cluster_at(field, p)); ret.insert(cluster_at(field, p));
} }
for(auto it = ret.begin(); it != ret.end();){
if (it->size() < rules.min_cluster_size()) it = ret.erase(it);
else ++it;
}
return ret; return ret;
} }

View file

@ -7,7 +7,7 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <iostream> #include <iosfwd>
#include <functional> #include <functional>
// Position {0,0} is bottom left. // Position {0,0} is bottom left.
@ -16,13 +16,11 @@
struct DynamicGrid{ struct DynamicGrid{
using Position = std::pair<int, int>; using Position = std::pair<int, int>;
template <typename Input> DynamicGrid(int width, int height, std::vector<int> && data);
DynamicGrid(int width, int height, Input it)
: W(width) //! \brief Returns an array of all valid positions (may be empty)
, H(height) auto const & all_positions() const{
, grid(W*H, 0) return positions;
{
std::copy_n(it, W*H, std::begin(grid));
} }
//! \brief Returns true if p represents a valid position //! \brief Returns true if p represents a valid position
@ -31,130 +29,56 @@ struct DynamicGrid{
&& p.first < W && p.second < H; && p.first < W && p.second < H;
} }
//! \brief Get (mutable) value at p
auto & get(Position const & p){
assert(valid(p));
return grid[static_cast<size_t>(p.first + p.second*W)];
}
//! \brief Get value at p
auto const& get(Position const & p) const {
assert(valid(p));
return grid[static_cast<size_t>(p.first + p.second*W)];
}
//! \brief Returns true if p represents an empty cell //! \brief Returns true if p represents an empty cell
auto empty(Position const & p) const { auto empty(Position const & p) const {
assert(valid(p)); assert(valid(p));
return get(p) == 0; return get(p) == 0;
} }
//! \brief Return true if the whole grid is empty
auto empty() const {
for(auto&& p : all_positions()){
if(!empty(p)) return false;
}
return true;
}
//! \brief Returns all neighbors of p //! \brief Returns all neighbors of p
//! Only returns valid neighbors (and p does not need to be valid) //! Only returns valid neighbors (and p does not need to be valid)
auto neighbors(Position const & p) const { small_vector<Position, 4> neighbors(Position const & p) const;
small_vector<Position, 4> ret;
static Position nbs[] = { //! \brief Let the block fall (all the way)
{-1, 0}, {1, 0}, {0, 1}, {0, -1} void collapse();
};
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 Get (mutable) value at p
int & get(Position const & p){
assert(valid(p));
return grid[static_cast<size_t>(p.first + p.second*W)];
}
//! \brief Get value at p
int 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 //! \brief Pretty prints grid to out
void print(std::ostream& out) const { 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: private:
int W; int W;
int H; int H;
std::vector<int> grid; std::vector<int> grid;
std::vector<Position> positions;
//! \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 for all_positions()
static auto all_positions_impl(int W, int H) {
std::vector<Position> r;
for(int y = 0; y < H; ++y)
for(int x = 0; x < W; ++x)
r.emplace_back(x, y);
return r;
}
public:
//! \brief Returns an array of all valid positions
auto const & all_positions() const {
static auto v = all_positions_impl(W, H);
return v;
}
private:
//! \brief Single vertical collapsing step //! \brief Single vertical collapsing step
auto vcollapse(){ 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 //! \brief Single horizontal collapsing step
auto hcollapse(){ 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; //! \brief Returns true if the whole column at x is empty
for(; x2 < W; ++x2){ auto empty_column(int x);
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;
}
public:
//! \brief Let the block fall (all the way)
auto collapse(){
while(vcollapse());
while(hcollapse());
}
}; };
template <typename URNG> template <typename URNG>
@ -162,5 +86,5 @@ auto random_dynamic_grid(int W, int H, int C, URNG&& r){
std::uniform_int_distribution<int> dis(1, C); std::uniform_int_distribution<int> dis(1, C);
std::vector<int> v(W*H); std::vector<int> v(W*H);
std::generate_n(std::begin(v), W*H, [&]{ return dis(r); }); std::generate_n(std::begin(v), W*H, [&]{ return dis(r); });
return DynamicGrid(W, H, std::begin(v)); return DynamicGrid(W, H, std::move(v));
} }

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
struct BasicRules { struct BasicRules {
BasicRules(int minimal_cluster_size) BasicRules(unsigned int minimal_cluster_size)
: min_size(minimal_cluster_size) : min_size(minimal_cluster_size)
{} {}
@ -10,5 +10,5 @@ struct BasicRules {
} }
private: private:
int min_size; unsigned int min_size;
}; };

View file

@ -17,19 +17,19 @@ auto pop(std::vector<T> & v){
template <typename Grid, typename Rules> template <typename Grid, typename Rules>
auto solve_impl(Grid const & grid, Rules const & rules, Solution<Grid> & s){ auto solve_impl(Grid const & grid, Rules const & rules, Solution<Grid> & s){
// are we done yet? if(grid.empty()){
for(auto&& p : grid.all_positions()){ // solved path
if(!grid.empty(p)) goto analyse; s.solution_traces.push_back(s.current_trace);
return;
} }
// yes -> record the solution
s.solution_traces.push_back(s.current_trace);
return;
// no -> try all clusters auto&& clusters = all_clusters(grid, rules);
analyse: if(clusters.empty()){
for(auto&& c : all_clusters(grid)){ // unsolvable path
if(c.size() < rules.min_cluster_size()) continue; return;
}
for(auto&& c : clusters){
// remove the cluster // remove the cluster
s.current_trace.push_back(*c.begin()); s.current_trace.push_back(*c.begin());
auto new_grid = make_empty(grid, c); auto new_grid = make_empty(grid, c);

4
lib/CMakeLists.txt Normal file
View file

@ -0,0 +1,4 @@
file(GLOB sources "*.cpp")
add_library(common STATIC ${sources})
target_link_libraries(common ${libs})

96
lib/dynamic_grid.cpp Normal file
View file

@ -0,0 +1,96 @@
#include "dynamic_grid.hpp"
#include <iostream>
//! \brief Helper function for all_positions()
static auto all_positions_impl(int W, int H) {
std::vector<DynamicGrid::Position> r;
for(int y = 0; y < H; ++y)
for(int x = 0; x < W; ++x)
r.emplace_back(x, y);
return r;
}
DynamicGrid::DynamicGrid(int width, int height, std::vector<int> && data)
: W(width), H(height), grid(std::move(data)) {
assert(grid.size() == W*H);
positions = all_positions_impl(W, H);
}
small_vector<DynamicGrid::Position, 4>
DynamicGrid::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;
}
auto DynamicGrid::empty_column(int x){
for(auto y = 0; y < H; ++y){
if(!empty({x, y})){
return false;
}
}
return true;
}
auto DynamicGrid::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;
}
auto DynamicGrid::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;
}
void DynamicGrid::collapse(){
while(vcollapse());
while(hcollapse());
}
void DynamicGrid::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' : ' ');
}
}
}

8
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,8 @@
file(GLOB sources "*.cpp")
foreach(source ${sources})
get_filename_component(exec ${source} NAME_WE)
add_executable(${exec} ${source})
target_link_libraries(${exec} common ${libs})
endforeach()

View file

@ -20,6 +20,7 @@ int main(int argc, char** argv){
int h = 5; int h = 5;
int c = 3; int c = 3;
int explosion_size = 3; int explosion_size = 3;
int n = 1000;
// Describe program options // Describe program options
po::options_description opts; po::options_description opts;
@ -28,6 +29,8 @@ int main(int argc, char** argv){
("h", po::value(&h), "height of puzzles") ("h", po::value(&h), "height of puzzles")
("c", po::value(&c), "number of colors") ("c", po::value(&c), "number of colors")
("explosion_size", po::value(&explosion_size), "minimum explosion size") ("explosion_size", po::value(&explosion_size), "minimum explosion size")
("n", po::value(&n), "number of grids to test")
("verbose", po::bool_switch(), "prints a lot")
("help", po::bool_switch(), "show this help"); ("help", po::bool_switch(), "show this help");
// Parse and store them in a vm // Parse and store them in a vm
@ -41,6 +44,8 @@ int main(int argc, char** argv){
return 0; return 0;
} }
bool verbose = vm["verbose"].as<bool>();
#define OUT(x) std::cout << #x << " = " << x << "\n" #define OUT(x) std::cout << #x << " = " << x << "\n"
OUT(w); OUT(h); OUT(c); OUT(explosion_size); OUT(w); OUT(h); OUT(c); OUT(explosion_size);
#undef OUT #undef OUT
@ -50,25 +55,30 @@ int main(int argc, char** argv){
BasicRules rules(explosion_size); BasicRules rules(explosion_size);
int count = 50000;
int solvable = 0; int solvable = 0;
int unsolvable = 0; int unsolvable = 0;
while(--count){ while(--n){
auto field = random_dynamic_grid(w, h, c, gen); auto field = random_dynamic_grid(w, h, c, gen);
// std::cout << "\n= puzzle " << count << " =\n"; if(verbose){
// field.print(std::cout); std::cout << "\n= puzzle " << n << " =\n";
field.print(std::cout);
}
auto solution = solve(field, rules); auto solution = solve(field, rules);
if(!solution.solution_traces.empty()){ if(!solution.solution_traces.empty()){
++solvable; ++solvable;
// std::cout << solution.solution_traces.size() << " solutions\n"; if(verbose){
// for(auto&& t : solution.solution_traces[0]){ std::cout << solution.solution_traces.size() << " solutions\n";
// std::cout << "(" << t.first << ", " << t.second << ")\n"; for(auto&& t : solution.solution_traces[0]){
// } std::cout << "(" << t.first << ", " << t.second << ")\n";
}
}
} else { } else {
++unsolvable; ++unsolvable;
// std::cout << "no solutions\n"; if(verbose){
std::cout << "no solutions\n";
}
} }
} }