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)
cmake_minimum_required(VERSION 2.8)
include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/include/")
include_directories("${PROJECT_SOURCE_DIR}/include/")
add_definitions(-std=c++1y)
find_package(Boost REQUIRED COMPONENTS program_options system)
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
set(libs ${libs} ${Boost_LIBRARIES})
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} ${libs})
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_subdirectory("lib")
add_subdirectory("src")

View file

@ -34,14 +34,18 @@ auto cluster_at(Field const & field, typename Field::Position const & p){
}
template <typename Field>
auto all_clusters(Field const & field){
template <typename Field, typename Rules>
auto all_clusters(Field const & field, Rules const & rules){
boost::container::flat_set<decltype(cluster_at(field, std::declval<typename Field::Position>()))> ret;
ret.reserve(10);
for(auto&& p : field.all_positions()){
if(field.empty(p)) continue;
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;
}

View file

@ -7,7 +7,7 @@
#include <array>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iosfwd>
#include <functional>
// Position {0,0} is bottom left.
@ -16,13 +16,11 @@
struct DynamicGrid{
using Position = std::pair<int, int>;
template <typename Input>
DynamicGrid(int width, int height, Input it)
: W(width)
, H(height)
, grid(W*H, 0)
{
std::copy_n(it, W*H, std::begin(grid));
DynamicGrid(int width, int height, std::vector<int> && data);
//! \brief Returns an array of all valid positions (may be empty)
auto const & all_positions() const{
return positions;
}
//! \brief Returns true if p represents a valid position
@ -31,130 +29,56 @@ struct DynamicGrid{
&& 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
auto empty(Position const & p) const {
assert(valid(p));
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
//! Only returns valid neighbors (and p does not need to be valid)
auto neighbors(Position const & p) const {
small_vector<Position, 4> ret;
small_vector<Position, 4> neighbors(Position const & p) const;
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 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 Let the block fall (all the way)
void collapse();
//! \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' : ' ');
}
}
}
void print(std::ostream& out) const;
private:
int W;
int H;
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
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;
}
auto vcollapse();
//! \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 hcollapse();
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;
}
public:
//! \brief Let the block fall (all the way)
auto collapse(){
while(vcollapse());
while(hcollapse());
}
//! \brief Returns true if the whole column at x is empty
auto empty_column(int x);
};
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::vector<int> v(W*H);
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
struct BasicRules {
BasicRules(int minimal_cluster_size)
BasicRules(unsigned int minimal_cluster_size)
: min_size(minimal_cluster_size)
{}
@ -10,5 +10,5 @@ struct BasicRules {
}
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>
auto solve_impl(Grid const & grid, Rules const & rules, Solution<Grid> & s){
// are we done yet?
for(auto&& p : grid.all_positions()){
if(!grid.empty(p)) goto analyse;
if(grid.empty()){
// solved path
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
analyse:
for(auto&& c : all_clusters(grid)){
if(c.size() < rules.min_cluster_size()) continue;
auto&& clusters = all_clusters(grid, rules);
if(clusters.empty()){
// unsolvable path
return;
}
for(auto&& c : clusters){
// remove the cluster
s.current_trace.push_back(*c.begin());
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 c = 3;
int explosion_size = 3;
int n = 1000;
// Describe program options
po::options_description opts;
@ -28,6 +29,8 @@ int main(int argc, char** argv){
("h", po::value(&h), "height of puzzles")
("c", po::value(&c), "number of colors")
("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");
// Parse and store them in a vm
@ -41,6 +44,8 @@ int main(int argc, char** argv){
return 0;
}
bool verbose = vm["verbose"].as<bool>();
#define OUT(x) std::cout << #x << " = " << x << "\n"
OUT(w); OUT(h); OUT(c); OUT(explosion_size);
#undef OUT
@ -50,25 +55,30 @@ int main(int argc, char** argv){
BasicRules rules(explosion_size);
int count = 50000;
int solvable = 0;
int unsolvable = 0;
while(--count){
while(--n){
auto field = random_dynamic_grid(w, h, c, gen);
// std::cout << "\n= puzzle " << count << " =\n";
// field.print(std::cout);
if(verbose){
std::cout << "\n= puzzle " << n << " =\n";
field.print(std::cout);
}
auto solution = solve(field, rules);
if(!solution.solution_traces.empty()){
++solvable;
// std::cout << solution.solution_traces.size() << " solutions\n";
// for(auto&& t : solution.solution_traces[0]){
// std::cout << "(" << t.first << ", " << t.second << ")\n";
// }
if(verbose){
std::cout << solution.solution_traces.size() << " solutions\n";
for(auto&& t : solution.solution_traces[0]){
std::cout << "(" << t.first << ", " << t.second << ")\n";
}
}
} else {
++unsolvable;
// std::cout << "no solutions\n";
if(verbose){
std::cout << "no solutions\n";
}
}
}