h48

A prototype for an optimal Rubik's cube solver, work in progress.
git clone https://git.tronto.net/h48
Download | Log | Files | Refs | README | LICENSE

adapter.cpp (3938B)


      1 #include "adapter.h"
      2 
      3 #include <fstream>
      4 #include <sstream>
      5 #include <string>
      6 #include <vector>
      7 #include <QDebug>
      8 #include <QtConcurrent/QtConcurrent>
      9 
     10 void logWrapper(const char *str, void *data)
     11 {
     12 	auto f = *reinterpret_cast<std::function<void(std::string)>*>(data);
     13 	f(std::string{str});
     14 }
     15 
     16 NissyAdapter::NissyAdapter()
     17 {
     18 	// TODO: this list must be kept in sync with UI code, it is a bit ugly
     19 	std::vector<std::string> solverNames {
     20 		"h48h0k4",
     21 		"h48h1k2",
     22 		"h48h2k2",
     23 		"h48h3k2",
     24 		"h48h7k2",
     25 	};
     26 
     27 	for (auto s : solverNames)
     28 		initSolver(s);
     29 
     30 	writeLog = [&](std::string str) {
     31 		emit appendLog(QString::fromStdString(str));
     32 	};
     33 
     34 	nissy::set_logger(&logWrapper, &writeLog);
     35 }
     36 
     37 void NissyAdapter::initSolver(const std::string& s) {
     38 	auto se = nissy::solver::get(s);
     39 	if (std::holds_alternative<nissy::error>(se)) {
     40 		qDebug("Error loading solver!");
     41 		return;
     42 	}
     43 	auto ss = std::get<nissy::solver>(se);
     44 	solvers.push_back(ss);
     45 }
     46 
     47 bool NissyAdapter::loadSolverData(nissy::solver& solver) {
     48 	if (solver.data_checked)
     49 		return true;
     50 
     51 	std::filesystem::path filePath("./tables/" + solver.id);
     52 	if (!std::filesystem::exists(filePath)) {
     53 		logLine("Data file for solver " + solver.name + " not found, "
     54 		    "generating it...");
     55 		auto err = solver.generate_data();
     56 		if (!err.ok()) {
     57 			emit solverError(QString("Error generating data!"));
     58 			return false;
     59 		}
     60 		std::filesystem::create_directory("./tables/");
     61 		std::ofstream ofs(filePath, std::ios::binary);
     62 		ofs.write(reinterpret_cast<char *>(solver.data.data()),
     63 		    solver.size);
     64 		ofs.close();
     65 		logLine("Data generated succesfully");
     66 	} else {
     67 		logLine("Reading data for solver " + solver.name +
     68 		    " from file");
     69 		std::ifstream ifs(filePath, std::ios::binary);
     70 		solver.read_data(ifs);
     71 		ifs.close();
     72 		logLine("Data loaded");
     73 	}
     74 
     75 	logLine("Checking data integrity "
     76 	    "(this is done only once per solver per session)...");
     77 	if (!solver.check_data().ok()) {
     78 		emit solverError(QString("Error reading data!"));
     79 		return false;
     80 	}
     81 	logLine("Data checked");
     82 
     83 	return true;
     84 }
     85 
     86 Q_INVOKABLE bool NissyAdapter::isValidScramble(QString qscr)
     87 {
     88 	nissy::cube c;
     89 	return c.move(qscr.toStdString()).ok();
     90 }
     91 
     92 Q_INVOKABLE void NissyAdapter::requestSolve(
     93 	QString scramble,
     94 	QString solver,
     95 	int minmoves,
     96 	int maxmoves,
     97 	int maxsolutions,
     98 	int optimal
     99 )
    100 {
    101 	nissy::cube c;
    102 	if (!c.move(scramble.toStdString()).ok()) {
    103 		emit solverError(QString("Unexpected error: invalid scramble"));
    104 		return;
    105 	}
    106 
    107 	nissy::solver *ss = nullptr;
    108 	for (auto& s : solvers)
    109 		if (s.name == solver)
    110 			ss = &s;
    111 	if (ss == nullptr) {
    112 		std::string msg = "Error: solver '" + solver.toStdString()
    113 		    + "' not available";
    114 		emit solverError(QString::fromStdString(msg));
    115 		return;
    116 	}
    117 
    118 	SolveOptions opts{c, ss, (unsigned)minmoves, (unsigned)maxmoves,
    119 	    (unsigned)maxsolutions, (unsigned)optimal};
    120 	auto _ = QtConcurrent::run(&NissyAdapter::startSolve, this, opts);
    121 	return;
    122 }
    123 
    124 void NissyAdapter::startSolve(SolveOptions opts)
    125 {
    126 	loadSolverData(*opts.solver);
    127 
    128 	auto result = opts.solver->solve(opts.cube, nissy::nissflag::NORMAL,
    129 	    opts.minmoves, opts.maxmoves, opts.maxsolutions, opts.optimal, 8);
    130 
    131 	if (!result.err.ok()) {
    132 		std::string msg = "Error computing solutions: " +
    133 		    std::to_string(result.err.value);
    134 		emit solverError(QString::fromStdString(msg));
    135 		return;
    136 	}
    137 
    138 	auto& sols = result.solutions;
    139 	if (sols.size() == 0) {
    140 		emit solutionsReady("No solution found", "");
    141 	} else {
    142 		std::stringstream hs;
    143 		hs << "Found " << sols.size() << " solution"
    144 		    << (sols.size() > 1 ? "s:" : ":");
    145 
    146 		std::stringstream ss;
    147 		for (auto s : sols) {
    148 			auto n = nissy::count_moves(s).value;
    149 			ss << s << " (" << n << ")" << std::endl; // TODO: remove last newline
    150 		}
    151 		emit solutionsReady(QString::fromStdString(hs.str()),
    152 		   QString::fromStdString(ss.str()));
    153 	}
    154 }
    155 
    156 void NissyAdapter::logLine(std::string str)
    157 {
    158 	std::stringstream ss;
    159 	ss << str << std::endl;
    160 	writeLog(ss.str());
    161 }