Adds boost program options. Finds *all* solutions.
This commit is contained in:
parent
64952b9700
commit
b61b6c04ff
5 changed files with 117 additions and 42 deletions
|
@ -4,10 +4,10 @@ cmake_minimum_required(VERSION 2.8)
|
||||||
include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/include/")
|
include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/include/")
|
||||||
add_definitions(-std=c++1y)
|
add_definitions(-std=c++1y)
|
||||||
|
|
||||||
find_package(Boost REQUIRED COMPONENTS)
|
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)
|
aux_source_directory(. SRC_LIST)
|
||||||
add_executable(${PROJECT_NAME} ${SRC_LIST})
|
add_executable(${PROJECT_NAME} ${SRC_LIST})
|
||||||
|
target_link_libraries(${PROJECT_NAME} ${libs})
|
||||||
|
|
|
@ -17,9 +17,9 @@ struct DynamicGrid{
|
||||||
using Position = std::pair<int, int>;
|
using Position = std::pair<int, int>;
|
||||||
|
|
||||||
template <typename Input>
|
template <typename Input>
|
||||||
DynamicGrid(int W, int H, Input it)
|
DynamicGrid(int width, int height, Input it)
|
||||||
: W(W)
|
: W(width)
|
||||||
, H(H)
|
, H(height)
|
||||||
, grid(W*H, 0)
|
, grid(W*H, 0)
|
||||||
{
|
{
|
||||||
std::copy_n(it, W*H, std::begin(grid));
|
std::copy_n(it, W*H, std::begin(grid));
|
||||||
|
|
14
include/rules.hpp
Normal file
14
include/rules.hpp
Normal file
|
@ -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;
|
||||||
|
};
|
|
@ -1,35 +1,52 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "clusters.hpp"
|
#include "clusters.hpp"
|
||||||
#include <deque>
|
#include <vector>
|
||||||
|
|
||||||
template <typename Field>
|
template <typename Grid>
|
||||||
struct Solution {
|
struct Solution {
|
||||||
std::deque<typename Field::Position> taps;
|
using Trace = std::vector<typename Grid::Position>;
|
||||||
|
Trace current_trace;
|
||||||
|
std::vector<Trace> solution_traces;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Field>
|
template <typename T>
|
||||||
auto solve_impl(Field const & field, Solution<Field> & s){
|
auto pop(std::vector<T> & v){
|
||||||
for(auto&& p : field.all_positions()){
|
v.erase(v.end()-1);
|
||||||
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 <typename Field>
|
template <typename Grid, typename Rules>
|
||||||
auto solve(Field const & field){
|
auto solve_impl(Grid const & grid, Rules const & rules, Solution<Grid> & s){
|
||||||
Solution<Field> s;
|
// are we done yet?
|
||||||
solve_impl(field, s);
|
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 <typename Grid, typename Rules>
|
||||||
|
auto solve(Grid const & grid, Rules const & rules){
|
||||||
|
Solution<Grid> s;
|
||||||
|
s.current_trace.reserve(10);
|
||||||
|
solve_impl(grid, rules, s);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
70
main.cpp
70
main.cpp
|
@ -3,31 +3,75 @@
|
||||||
#include "clusters.hpp"
|
#include "clusters.hpp"
|
||||||
#include "solver.hpp"
|
#include "solver.hpp"
|
||||||
#include "generator.hpp"
|
#include "generator.hpp"
|
||||||
|
#include "rules.hpp"
|
||||||
|
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
int main(){
|
int main(int argc, char** argv){
|
||||||
using namespace std;
|
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<bool>()){
|
||||||
|
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::random_device rd;
|
||||||
std::mt19937 gen(rd());
|
std::mt19937 gen(rd());
|
||||||
|
|
||||||
int c = 0;
|
BasicRules rules(explosion_size);
|
||||||
while(true){
|
|
||||||
std::cout << "puzzle " << ++c << std::endl;
|
|
||||||
auto field = random_dynamic_grid(5, 2, 3, gen);
|
|
||||||
|
|
||||||
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);
|
// std::cout << "\n= puzzle " << count << " =\n";
|
||||||
for(auto&& t : solution.taps){
|
// field.print(std::cout);
|
||||||
std::cout << "(" << t.first << ", " << t.second << ")\n";
|
|
||||||
}
|
auto solution = solve(field, rules);
|
||||||
if(!solution.taps.empty()){
|
if(!solution.solution_traces.empty()){
|
||||||
break;
|
++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";
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue