diff --git a/CMakeLists.txt b/CMakeLists.txt index 6577ad8..fd948f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,10 @@ cmake_minimum_required(VERSION 2.8) include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/include/") add_definitions(-std=c++1y) -find_package(Boost REQUIRED COMPONENTS) +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}) diff --git a/include/dynamic_grid.hpp b/include/dynamic_grid.hpp index 62cf732..681165c 100644 --- a/include/dynamic_grid.hpp +++ b/include/dynamic_grid.hpp @@ -17,9 +17,9 @@ struct DynamicGrid{ using Position = std::pair; template - DynamicGrid(int W, int H, Input it) - : W(W) - , H(H) + DynamicGrid(int width, int height, Input it) + : W(width) + , H(height) , grid(W*H, 0) { std::copy_n(it, W*H, std::begin(grid)); diff --git a/include/rules.hpp b/include/rules.hpp new file mode 100644 index 0000000..653fb37 --- /dev/null +++ b/include/rules.hpp @@ -0,0 +1,14 @@ +#pragma once + +struct BasicRules { + BasicRules(int minimal_cluster_size) + : min_size(minimal_cluster_size) + {} + + auto min_cluster_size() const { + return min_size; + } + +private: + int min_size; +}; diff --git a/include/solver.hpp b/include/solver.hpp index 1c2cbea..1e64851 100644 --- a/include/solver.hpp +++ b/include/solver.hpp @@ -1,35 +1,52 @@ #pragma once #include "clusters.hpp" -#include +#include -template +template struct Solution { - std::deque taps; + using Trace = std::vector; + Trace current_trace; + std::vector solution_traces; }; -template -auto solve_impl(Field const & field, Solution & s){ - for(auto&& p : field.all_positions()){ - if(!field.empty(p)) goto analyse; - } - return true; - - analyse: - for(auto&& c : all_clusters(field)){ - if(c.size() < 3) continue; - auto new_field = make_empty(field, c); - if(solve_impl(new_field, s)) { - s.taps.push_front(*c.begin()); - return true; - } - } - return false; +template +auto pop(std::vector & v){ + v.erase(v.end()-1); } -template -auto solve(Field const & field){ - Solution s; - solve_impl(field, s); +template +auto solve_impl(Grid const & grid, Rules const & rules, Solution & s){ + // are we done yet? + for(auto&& p : grid.all_positions()){ + if(!grid.empty(p)) goto analyse; + } + // 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; + + // remove the cluster + s.current_trace.push_back(*c.begin()); + auto new_grid = make_empty(grid, c); + + // recurse + solve_impl(new_grid, rules, s); + + // go on with next cluster + pop(s.current_trace); + } + return; +} + +template +auto solve(Grid const & grid, Rules const & rules){ + Solution s; + s.current_trace.reserve(10); + solve_impl(grid, rules, s); return s; } diff --git a/main.cpp b/main.cpp index 18830f6..c3e51f8 100644 --- a/main.cpp +++ b/main.cpp @@ -3,31 +3,75 @@ #include "clusters.hpp" #include "solver.hpp" #include "generator.hpp" +#include "rules.hpp" + +#include #include #include #include #include +#include -int main(){ - using namespace std; +int main(int argc, char** argv){ + namespace po = boost::program_options; + + int w = 5; + int h = 5; + int c = 3; + int explosion_size = 3; + + // Describe program options + po::options_description opts; + opts.add_options() + ("w", po::value(&w), "width of puzzles") + ("h", po::value(&h), "height of puzzles") + ("c", po::value(&c), "number of colors") + ("explosion_size", po::value(&explosion_size), "minimum explosion size") + ("help", po::bool_switch(), "show this help"); + + // Parse and store them in a vm + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, opts), vm); + po::notify(vm); + + if(vm["help"].as()){ + std::cout << "Puzzle Wuzzle Generator, version " << __DATE__ << std::endl; + std::cout << opts << std::endl; + return 0; + } + +#define OUT(x) std::cout << #x << " = " << x << "\n" + OUT(w); OUT(h); OUT(c); OUT(explosion_size); +#undef OUT std::random_device rd; std::mt19937 gen(rd()); - int c = 0; - while(true){ - std::cout << "puzzle " << ++c << std::endl; - auto field = random_dynamic_grid(5, 2, 3, gen); + BasicRules rules(explosion_size); - field.print(std::cout); + int count = 50000; + int solvable = 0; + int unsolvable = 0; + while(--count){ + auto field = random_dynamic_grid(w, h, c, gen); - auto solution = solve(field); - for(auto&& t : solution.taps){ - std::cout << "(" << t.first << ", " << t.second << ")\n"; - } - if(!solution.taps.empty()){ - break; +// std::cout << "\n= puzzle " << count << " =\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"; +// } + } else { + ++unsolvable; +// std::cout << "no solutions\n"; } } + + std::cout << solvable << " solvable\n"; + std::cout << unsolvable << " unsolvable\n"; }