Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ The CMD-program provides the following options:

* `-c` (default OFF): Compress solutions to AXHT. This is especially useful when solving in AXQT as properly merging move sequences like `D (U D)` is not entirely trivial without having all the proper move definitions at the ready.

* `-i` (default OFF): Immediately display new solutions as they are found, instead of displaying them after the timeout.
If the `-m` parameter is provided, the search stops after the timeout, otherwise the search runs
forever and can be aborted by pressing CTRL-C.

* `-l` (default -1): Maximum solutions length. The search will stop once a solution of at most this length is found. With `-1` the solver will simply search for the full time-limit and eventually return the best solution found.
* `-l` (default -1): Maximum solution length. The search will stop once a solution of at most this length is found. With `-1` the solver will simply search for the full time-limit and eventually return the best solution found.

* `-m` (default 10): Time-limit in milliseconds.
Expand Down
3 changes: 2 additions & 1 deletion makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
C=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-std=c++11 -lpthread -O3
CPPFLAGS=-std=c++11 -lpthread -O3 -DQT -DAX
# CPPFLAGS=-std=c++11 -lpthread -O3
LDFLAGS=
LDLIBS=-lpthread

Expand Down
66 changes: 53 additions & 13 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <iostream>
#include <vector>
#include <numeric>
#include <signal.h>

#include "cubie.h"
#include "coord.h"
Expand All @@ -17,7 +18,7 @@ const std::string BENCH_FILE = "bench.cubes";

void usage() {
std::cout << "Usage: ./twophase "
<< "[-c] [-l MAX_LEN = 1] [-m MILLIS = 10] [-n N_SOLS = 1] [-s N_SPLITS = 1] [-t N_THREADS = 1] [-w N_WARMUPS = 0]"
<< "[-c] [-i] [-l MAX_LEN = 1] [-m MILLIS = 10] [-n N_SOLS = 1] [-s N_SPLITS = 1] [-t N_THREADS = 1] [-w N_WARMUPS = 0]"
<< std::endl;
exit(1);
}
Expand Down Expand Up @@ -77,6 +78,20 @@ double mean(const std::vector<std::vector<int>>& sols, int (*len)(const std::vec
return total / sols.size();
}

solve::Engine *ctrl_c_handler_engine = NULL;
bool ctrl_c_handler_solving_now = false;

void ctrl_c_handler(int s)
{
if (ctrl_c_handler_engine == NULL)
exit(0);

if (!ctrl_c_handler_solving_now)
exit(0);

ctrl_c_handler_engine->abort();
}

int main(int argc, char *argv[]) {
int n_threads = 1;
int tlim = 10;
Expand All @@ -85,19 +100,26 @@ int main(int argc, char *argv[]) {
int n_splits = 1;
bool compress = false;
int n_warmups = 0;


bool display_solutions_immediately = false; // -i flag
bool infinite_search_duration = true; // only enabled if display_solutions_immediately==true
// and -m is not given
try {
int opt;
while ((opt = getopt(argc, argv, "cl:m:n:s:t:w:")) != -1) {
while ((opt = getopt(argc, argv, "cil:m:n:s:t:w:")) != -1) {
switch (opt) {
case 'c':
compress = true;
break;
case 'i':
display_solutions_immediately = true;
break;
case 'l':
max_len = std::stoi(optarg);
break;
case 'm':
tlim = std::stoi(optarg);
infinite_search_duration = false;
break;
case 'n':
if ((n_sols = std::stoi(optarg)) <= 0) {
Expand Down Expand Up @@ -130,12 +152,27 @@ int main(int argc, char *argv[]) {
} catch (...) { // catch any integer conversion errors
usage();
}
infinite_search_duration &= display_solutions_immediately;
if (infinite_search_duration)
tlim = -1;

std::cout << "This is rob-twophase v2.0; copyright Elias Frantar 2020." << std::endl << std::endl;
init();
solve::Engine solver(n_threads, tlim, n_sols, max_len, n_splits);
solve::Engine solver(n_threads, tlim, n_sols, max_len, n_splits, display_solutions_immediately, compress);
warmup(solver, n_warmups);

// register signal handler for aborting search in immediate mode
if (display_solutions_immediately)
{
ctrl_c_handler_engine = &solver;

struct sigaction sigIntHandler;
sigIntHandler.sa_handler = ctrl_c_handler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
}

std::cout << "Enter >>solve FACECUBE<< to solve, >>scramble<< to scramble or >>bench<< to benchmark." << std::endl << std::endl;

std::string mode;
Expand Down Expand Up @@ -242,21 +279,24 @@ int main(int argc, char *argv[]) {
}

auto tick = std::chrono::high_resolution_clock::now();
ctrl_c_handler_solving_now = true;
solver.solve(c, sols);
ctrl_c_handler_solving_now = false;
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now() - tick
).count() / 1000. << "ms" << std::endl;

for (std::vector<int>& sol : sols) {
int len = sol.size(); // always print uncompressed length
if (compress)
std::cout << move::compress(sol) << " ";
else {
for (int m : sol)
std::cout << move::names[m] << " ";
if (!display_solutions_immediately)
for (std::vector<int>& sol : sols) {
int len = sol.size(); // always print uncompressed length
if (compress)
std::cout << move::compress(sol) << " ";
else {
for (int m : sol)
std::cout << move::names[m] << " ";
}
std::cout << "(" << len << ")" << std::endl;
}
std::cout << "(" << len << ")" << std::endl;
}
}
}
solver.finish(); // clean exit
Expand Down
58 changes: 55 additions & 3 deletions src/solve.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "solve.h"

#include <algorithm>
#include <iostream>
#include <cstring>
#include <thread>
#include "prun.h"
Expand Down Expand Up @@ -177,8 +178,13 @@ namespace solve {

Engine::Engine(
int n_threads, int tlim,
int n_sols, int max_len, int n_splits
) : n_threads(n_threads), tlim(tlim), n_sols(n_sols), max_len(max_len), n_splits(n_splits) {
int n_sols, int max_len, int n_splits,
bool display_solutions_immediately,
bool compress
) : n_threads(n_threads), tlim(tlim),
n_sols(n_sols), max_len(max_len), n_splits(n_splits),
display_solutions_immediately(display_solutions_immediately),
compress(compress) {
int tmp = (move::COUNT1 + n_splits - 1) / n_splits; // ceil to make sure that we always include all moves
for (int i = 0; i < n_splits; i++)
masks[i] = (move::mask(1) << tmp) - 1 << tmp * i;
Expand Down Expand Up @@ -228,6 +234,8 @@ namespace solve {
cubie::cube invc;
cubie::inv(c, invc);

min_reported_length = -1;

for (int dir = 0; dir < N_DIRS; dir++) {
const cubie::cube& c1 = (dir & 1) ? invc : c; // reference is enough, we do not need to copy
int rot = sym::ROT * (dir / 2);
Expand All @@ -250,7 +258,10 @@ namespace solve {

{ // timeout
std::unique_lock<std::mutex> lock(tout_mtx);
tout_cvar.wait_for(lock, std::chrono::milliseconds(tlim), [&]{ return done; });
if (tlim == -1)
tout_cvar.wait(lock, [&] { return done; });
else
tout_cvar.wait_for(lock, std::chrono::milliseconds(tlim), [&] { return done; });
if (!done)
done = true; // if we get here, this was a timeout
}
Expand Down Expand Up @@ -281,10 +292,16 @@ namespace solve {
if (done) // prevent any type of reporting after the solver has terminated (important for threading)
return;

bool print_sol = false;

sols.push(sol); // usually we only get here if we actually have a solution that will be added
if (sols.size() > n_sols)
{
print_sol = sols.top() != sol; // only print solution if is was actually added
sols.pop();
}
if (sols.size() == n_sols) {
print_sol = true;
lenlim = sols.top().first.size(); // only search for strictly shorter solutions

if (lenlim <= max_len) { // already found a solution that is short enough
Expand All @@ -294,6 +311,41 @@ namespace solve {
tout_cvar.notify_one();
}
}

if (print_sol
&& display_solutions_immediately
&& (min_reported_length == -1 || sol.first.size() < min_reported_length)
)
{
min_reported_length = sol.first.size();
std::vector<int> res;
res.resize(sol.first.size());

int rot = sym::ROT * (sol.second / 2);
for (int j = 0; j < res.size(); j++) // undo rotation
res[j] = sym::conj_move[sol.first[j]][rot];

if (sol.second & 1)
{ // undo inversion
for (int j = 0; j < res.size(); j++)
res[j] = move::inv[res[j]];
std::reverse(res.begin(), res.end());
}
if (compress)
std::cout << move::compress(res) << " ";
else
for (int m : res)
std::cout << move::names[m] << " ";
std::cout << "(" << sol.first.size() << ")" << std::endl;
}
}

void Engine::abort()
{
std::cout << "Aborting active solve" << std::endl;
done = true;
std::lock_guard<std::mutex> lock(tout_mtx);
tout_cvar.notify_one();
}

void Engine::finish() {
Expand Down
9 changes: 8 additions & 1 deletion src/solve.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ namespace solve {
int max_len; // find solutions with at most this length; -1 means simply search for the full `tlimit`
int tlim; // search for this amount of milliseconds

bool display_solutions_immediately; // if true, print solutions as the are found, -i flag
bool compress; // as in main.cpp (use for -i flag)
int min_reported_length; // for -i flag, to ensure that only better solutions are output

coordc dirs[N_DIRS]; // search directions
move::mask masks[move::COUNT1]; // split masks
int depths[N_DIRS]; // current search depths per direction
Expand All @@ -57,12 +61,15 @@ namespace solve {
public:
Engine(
int n_threads, int tlim,
int n_sols = 1, int max_len = -1, int n_splits = 1
int n_sols = 1, int max_len = -1, int n_splits = 1,
bool display_solutions_immediately = false,
bool compress = false
);
void prepare(); // setup all threads
void solve(const cubie::cube& c, std::vector<std::vector<int>>& res); // actual solve
void finish(); // wait for all threads to shutdown (mostly for clean program exit)
void report_sol(searchres& sol); // report a solution; never call this from the outside
void abort(); // stop a correctly running solve

void thread(); // search thread

Expand Down