summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author Eric Hameleers <alien@slackware.com>2018-08-10 15:45:06 +0200
committer Eric Hameleers <alien@slackware.com>2018-08-10 15:45:06 +0200
commit80d54d4191a76967786d7faac83c7bf8e84c1bcf (patch)
tree4c949ddb25df54293ed1939c942ca9eedcf18e93
parent7dfbe8f7abe48da63c2bf653c282fbe627f9a566 (diff)
downloadktown-80d54d4191a76967786d7faac83c7bf8e84c1bcf.tar.gz
ktown-80d54d4191a76967786d7faac83c7bf8e84c1bcf.tar.xz
Prepare for reverting a future hard dependency on fc-solver in kpat
-rw-r--r--kde/patch/kpat.patch5
-rw-r--r--kde/patch/kpat/kpat_no_freecell_solver_dep.patch1475
2 files changed, 1480 insertions, 0 deletions
diff --git a/kde/patch/kpat.patch b/kde/patch/kpat.patch
new file mode 100644
index 0000000..42bbdae
--- /dev/null
+++ b/kde/patch/kpat.patch
@@ -0,0 +1,5 @@
+# Commit https://cgit.kde.org/kpat.git/patch/?id=fc1d54ced6a727382599d767e55879b6843c3456
+# Introduces a hard dependency on fc-solver which in turn has new dependencies
+# So, we revert this commit to avoid dropping kpat altogether in 18.08.0.
+#cat $CWD/patch/kpat/kpat_no_freecell_solver_dep.patch | patch -p1 --reverse --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; }
+
diff --git a/kde/patch/kpat/kpat_no_freecell_solver_dep.patch b/kde/patch/kpat/kpat_no_freecell_solver_dep.patch
new file mode 100644
index 0000000..c2a0e66
--- /dev/null
+++ b/kde/patch/kpat/kpat_no_freecell_solver_dep.patch
@@ -0,0 +1,1475 @@
+Commit https://cgit.kde.org/kpat.git/patch/?id=fc1d54ced6a727382599d767e55879b6843c3456
+Introduces a hard dependency on fc-solver which in turn has new dependencies
+So, we revert this commit to avoid dropping kpat altogether
+
+From ed0e53e0888da7123f4a0d2097f8da7fb105ca18 Mon Sep 17 00:00:00 2001
+From: Fabian Kosmale <0inkane@googlemail.com>
+Date: Sun, 13 May 2018 15:14:53 +0200
+Subject: Use Freecell Solver for FreeCell and Simple Simon
+
+Summary: This uses http://fc-solve.shlomifish.org/ and prevents the looping in the existing solvers.
+
+Test Plan: Test that the solvers are working.
+
+Reviewers: #kde_games, fabiank
+
+Subscribers: kde-games-devel, aacid, #kde_games
+
+Tags: #kde_games
+
+Differential Revision: https://phabricator.kde.org/D12415
+---
+ CMakeLists.txt | 6 +-
+ dealer.cpp | 4 +
+ freecell.cpp | 35 ++++
+ freecell.h | 1 +
+ patsolve/abstract_fc_solve_solver.cpp | 239 ++++++++++++++++++++++++++
+ patsolve/abstract_fc_solve_solver.h | 52 ++++++
+ patsolve/freecellsolver.cpp | 310 +++++++++++++++++-----------------
+ patsolve/freecellsolver.h | 23 ++-
+ patsolve/patsolve.h | 12 +-
+ patsolve/simonsolver.cpp | 129 +++++++++++++-
+ patsolve/simonsolver.h | 20 ++-
+ patsolve/solverinterface.h | 2 +
+ pileutils.cpp | 61 +++++++
+ pileutils.h | 4 +
+ simon.cpp | 56 +++++-
+ simon.h | 1 +
+ 16 files changed, 779 insertions(+), 176 deletions(-)
+ create mode 100644 patsolve/abstract_fc_solve_solver.cpp
+ create mode 100644 patsolve/abstract_fc_solve_solver.h
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 8f738bf..c043c45 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -4,6 +4,8 @@ cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR)
+ set (QT_MIN_VERSION "5.7.0")
+ set (KF5_MIN_VERSION "5.30.0")
+
++include(FindPkgConfig)
++pkg_check_modules(FC_SOLVE REQUIRED libfreecell-solver)
+ find_package(ECM ${KF5_MIN_VERSION} REQUIRED CONFIG)
+ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
+
+@@ -45,7 +47,7 @@ add_subdirectory(sounds)
+ add_subdirectory(themes)
+ add_subdirectory(doc)
+
+-set(kpat_SRCS
++set(kpat_SRCS ${libfcs_SRCS}
+ main.cpp
+ dealer.cpp
+ dealerinfo.cpp
+@@ -59,6 +61,7 @@ set(kpat_SRCS
+ soundengine.cpp
+ statisticsdialog.cpp
+ view.cpp
++ patsolve/abstract_fc_solve_solver.cpp
+ patsolve/memory.cpp
+ patsolve/patsolve.cpp
+
+@@ -101,6 +104,7 @@ target_link_libraries(kpat
+ KF5::KIOCore
+ KF5KDEGames
+ kcardgame
++ ${FC_SOLVE_LIBRARIES}
+ )
+
+ install(TARGETS kpat ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
+diff --git a/dealer.cpp b/dealer.cpp
+index 7c03ebf..a2558fc 100644
+--- a/dealer.cpp
++++ b/dealer.cpp
+@@ -1724,6 +1724,10 @@ void DealerScene::startSolver()
+
+ bool DealerScene::isGameLost() const
+ {
++ if (! m_winningMoves.isEmpty())
++ {
++ return false;
++ }
+ if ( solver() )
+ {
+ if ( m_solverThread && m_solverThread->isRunning() )
+diff --git a/freecell.cpp b/freecell.cpp
+index f870cdb..9a7c278 100644
+--- a/freecell.cpp
++++ b/freecell.cpp
+@@ -111,6 +111,41 @@ void Freecell::restart( const QList<KCard*> & cards )
+ }
+
+
++QString Freecell::solverFormat() const
++{
++ QString output;
++ QString tmp;
++ for (int i = 0; i < 4 ; i++) {
++ if (target[i]->isEmpty())
++ continue;
++ tmp += suitToString(target[i]->topCard()->suit()) + '-' + rankToString(target[i]->topCard()->rank()) + ' ';
++ }
++ if (!tmp.isEmpty())
++ output += QString::fromLatin1("Foundations: %1\n").arg(tmp);
++
++ tmp.truncate(0);
++ for (int i = 0; i < 4 ; i++) {
++ if (freecell[i]->isEmpty())
++ tmp += "- ";
++ else
++ tmp += rankToString(freecell[i]->topCard()->rank()) + suitToString(freecell[i]->topCard()->suit()) + ' ';
++ }
++ if (!tmp.isEmpty())
++ {
++ QString a = QString::fromLatin1("Freecells: %1\n");
++ output += a.arg(tmp);
++ }
++
++ for (int i = 0; i < 8 ; i++)
++ {
++ QList<KCard*> cards = store[i]->cards();
++ for (QList<KCard*>::ConstIterator it = cards.begin(); it != cards.end(); ++it)
++ output += rankToString((*it)->rank()) + suitToString((*it)->suit()) + ' ';
++ output += '\n';
++ }
++ return output;
++}
++
+ void Freecell::cardsDroppedOnPile( const QList<KCard*> & cards, KCardPile * pile )
+ {
+ if ( cards.size() <= 1 )
+diff --git a/freecell.h b/freecell.h
+index 7b0b2cb..9f7d84b 100644
+--- a/freecell.h
++++ b/freecell.h
+@@ -62,6 +62,7 @@ protected slots:
+ private:
+ bool canPutStore( const KCardPile * pile, const QList<KCard*> & cards ) const;
+
++ virtual QString solverFormat() const;
+ PatPile* store[8];
+ PatPile* freecell[4];
+ PatPile* target[4];
+diff --git a/patsolve/abstract_fc_solve_solver.cpp b/patsolve/abstract_fc_solve_solver.cpp
+new file mode 100644
+index 0000000..11e5baa
+--- /dev/null
++++ b/patsolve/abstract_fc_solve_solver.cpp
+@@ -0,0 +1,239 @@
++/*
++ * Copyright (C) 2006-2009 Stephan Kulow <coolo@kde.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <stdlib.h>
++#include <string.h>
++
++#include "freecell-solver/fcs_user.h"
++#include "freecell-solver/fcs_cl.h"
++
++#include "abstract_fc_solve_solver.h"
++
++const int CHUNKSIZE = 100;
++const long int MAX_ITERS_LIMIT = 200000;
++
++#define PRINT 0
++
++/* These two routines make and unmake moves. */
++
++void FcSolveSolver::make_move(MOVE *)
++{
++ return;
++}
++
++void FcSolveSolver::undo_move(MOVE *)
++{
++ return;
++}
++
++/* Get the possible moves from a position, and store them in Possible[]. */
++SolverInterface::ExitStatus FcSolveSolver::patsolve( int _max_positions )
++{
++ int current_iters_count;
++ max_positions = (_max_positions < 0) ? MAX_ITERS_LIMIT : _max_positions;
++
++ init();
++
++ if (!solver_instance)
++ {
++ {
++ solver_instance = freecell_solver_user_alloc();
++
++ solver_ret = FCS_STATE_NOT_BEGAN_YET;
++
++ char * error_string;
++ int error_arg;
++ const char * known_parameters[1] = {NULL};
++ /* A "char *" copy instead of "const char *". */
++
++ int parse_args_ret_code = freecell_solver_user_cmd_line_parse_args(
++ solver_instance,
++ get_cmd_line_arg_count() ,
++ get_cmd_line_args(),
++ 0,
++ known_parameters,
++ NULL,
++ NULL,
++ &error_string,
++ &error_arg
++ );
++
++ Q_ASSERT(!parse_args_ret_code);
++ }
++
++ /* Not needed for Simple Simon because it's already specified in
++ * freecell_solver_cmd_line_args. TODO : abstract .
++ *
++ * Shlomi Fish
++ * */
++ setFcSolverGameParams();
++
++ current_iters_count = CHUNKSIZE;
++ freecell_solver_user_limit_iterations(solver_instance, current_iters_count);
++ }
++
++ if (solver_instance)
++ {
++ bool continue_loop = true;
++ while (continue_loop &&
++ ( (solver_ret == FCS_STATE_NOT_BEGAN_YET)
++ || (solver_ret == FCS_STATE_SUSPEND_PROCESS))
++ &&
++ (current_iters_count < MAX_ITERS_LIMIT)
++ )
++ {
++ current_iters_count += CHUNKSIZE;
++ freecell_solver_user_limit_iterations(solver_instance, current_iters_count);
++
++ if (solver_ret == FCS_STATE_NOT_BEGAN_YET)
++ {
++ solver_ret =
++ freecell_solver_user_solve_board(
++ solver_instance,
++ board_as_string
++ );
++ }
++ else
++ {
++ solver_ret = freecell_solver_user_resume_solution(solver_instance);
++ }
++ {
++ // QMutexLocker lock( &endMutex );
++ if ( m_shouldEnd )
++ {
++ continue_loop = false;
++ }
++ }
++ }
++ }
++
++ switch (solver_ret)
++ {
++ case FCS_STATE_IS_NOT_SOLVEABLE:
++ if (solver_instance)
++ {
++ freecell_solver_user_free(solver_instance);
++ solver_instance = NULL;
++ }
++ return Solver::NoSolutionExists;
++
++ case FCS_STATE_WAS_SOLVED:
++ {
++ if (solver_instance)
++ {
++ m_winMoves.clear();
++ while (freecell_solver_user_get_moves_left(solver_instance))
++ {
++ fcs_move_t move;
++ MOVE new_move;
++ const int verdict = !freecell_solver_user_get_next_move(
++ solver_instance, &move)
++ ;
++
++ Q_ASSERT (verdict);
++
++ new_move.fcs = move;
++
++ m_winMoves.append( new_move );
++ }
++
++ freecell_solver_user_free(solver_instance);
++ solver_instance = NULL;
++ }
++ return Solver::SolutionExists;
++ }
++
++ case FCS_STATE_SUSPEND_PROCESS:
++ return Solver::UnableToDetermineSolvability;
++
++ default:
++ if (solver_instance)
++ {
++ freecell_solver_user_free(solver_instance);
++ solver_instance = NULL;
++ }
++ return Solver::NoSolutionExists;
++ }
++}
++
++/* Get the possible moves from a position, and store them in Possible[]. */
++
++int FcSolveSolver::get_possible_moves(int *, int *)
++{
++ return 0;
++}
++
++bool FcSolveSolver::isWon()
++{
++ return true;
++}
++
++int FcSolveSolver::getOuts()
++{
++ return 0;
++}
++
++FcSolveSolver::FcSolveSolver()
++ : Solver()
++ , solver_instance(NULL)
++ , solver_ret(FCS_STATE_NOT_BEGAN_YET)
++ , board_as_string("")
++{
++}
++
++unsigned int FcSolveSolver::getClusterNumber()
++{
++ return 0;
++}
++
++void FcSolveSolver::print_layout()
++{
++#if 0
++ int i, w, o;
++
++ fprintf(stderr, "print-layout-begin\n");
++ for (w = 0; w < 10; ++w) {
++ Q_ASSERT( Wp[w] == &W[w][Wlen[w]-1] );
++ fprintf( stderr, "Play%d: ", w );
++ for (i = 0; i < Wlen[w]; ++i) {
++ printcard(W[w][i], stderr);
++ }
++ fputc('\n', stderr);
++ }
++ fprintf( stderr, "Off: " );
++ for (o = 0; o < 4; ++o) {
++ if ( O[o] != -1 )
++ printcard( O[o] + PS_KING, stderr);
++ }
++ fprintf(stderr, "\nprint-layout-end\n");
++#endif
++}
++
++void FcSolveSolver::unpack_cluster( unsigned int)
++{
++ return;
++}
++
++FcSolveSolver::~FcSolveSolver()
++{
++ if (solver_instance)
++ {
++ freecell_solver_user_free(solver_instance);
++ solver_instance = NULL;
++ }
++}
++
+diff --git a/patsolve/abstract_fc_solve_solver.h b/patsolve/abstract_fc_solve_solver.h
+new file mode 100644
+index 0000000..d2d072d
+--- /dev/null
++++ b/patsolve/abstract_fc_solve_solver.h
+@@ -0,0 +1,52 @@
++/*
++ * Copyright (C) 2006-2009 Stephan Kulow <coolo@kde.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef ABSTRACT_FC_SOLVE_SOLVER_H
++#define ABSTRACT_FC_SOLVE_SOLVER_H
++
++#include "patsolve.h"
++
++struct FcSolveSolver : public Solver<10>
++{
++public:
++ FcSolveSolver();
++ virtual ~FcSolveSolver();
++ virtual int get_possible_moves(int *a, int *numout);
++ virtual bool isWon();
++ virtual void make_move(MOVE *m);
++ virtual void undo_move(MOVE *m);
++ virtual int getOuts();
++ virtual unsigned int getClusterNumber();
++ virtual void translate_layout() = 0;
++ virtual void unpack_cluster( unsigned int k );
++ virtual MoveHint translateMove(const MOVE &m) = 0;
++ virtual SolverInterface::ExitStatus patsolve( int _max_positions = -1);
++ virtual void setFcSolverGameParams() = 0;
++
++ virtual void print_layout();
++
++ virtual int get_cmd_line_arg_count() = 0;
++ virtual const char * * get_cmd_line_args() = 0;
++/* Names of the cards. The ordering is defined in pat.h. */
++
++ void * solver_instance;
++ int solver_ret;
++ // More than enough space for two decks.
++ char board_as_string[4 * 13 * 2 * 4 * 3];
++};
++
++#endif // ABSTRACT_FC_SOLVE_SOLVER_H
+diff --git a/patsolve/freecellsolver.cpp b/patsolve/freecellsolver.cpp
+index 39eff50..e92000f 100644
+--- a/patsolve/freecellsolver.cpp
++++ b/patsolve/freecellsolver.cpp
+@@ -16,12 +16,18 @@
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
++#include <stdlib.h>
++#include <string.h>
++
++#include "freecell-solver/fcs_user.h"
++#include "freecell-solver/fcs_cl.h"
++
+ #include "freecellsolver.h"
+
+ #include "../freecell.h"
+
+-
+-/* Some macros used in get_possible_moves(). */
++const int CHUNKSIZE = 100;
++const long int MAX_ITERS_LIMIT = 200000;
+
+ /* The following function implements
+ (Same_suit ? (suit(a) == suit(b)) : (color(a) != color(b)))
+@@ -32,10 +38,13 @@ namespace {
+
+ /* Statistics. */
+
++#if 0
+ int FreecellSolver::Xparam[] = { 4, 1, 8, -1, 7, 11, 4, 2, 2, 1, 2 };
++#endif
+
+ /* These two routines make and unmake moves. */
+
++#if 0
+ void FreecellSolver::make_move(MOVE *m)
+ {
+ int from, to;
+@@ -85,7 +94,9 @@ void FreecellSolver::undo_move(MOVE *m)
+ Wlen[from]++;
+ hashpile(from);
+ }
++#endif
+
++#if 0
+ /* Move prioritization. Given legal, pruned moves, there are still some
+ that are a waste of time, especially in the endgame where there are lots of
+ possible moves, but few productive ones. Note that we also prioritize
+@@ -178,9 +189,11 @@ void FreecellSolver::prioritize(MOVE *mp0, int n)
+ }
+ }
+ }
++#endif
+
+ /* Automove logic. Freecell games must avoid certain types of automoves. */
+
++#if 0
+ int FreecellSolver::good_automove(int o, int r)
+ {
+ int i;
+@@ -220,148 +233,43 @@ int FreecellSolver::good_automove(int o, int r)
+
+ return true;
+ }
++#endif
+
+-/* Get the possible moves from a position, and store them in Possible[]. */
++#define CMD_LINE_ARGS_NUM 2
+
+-int FreecellSolver::get_possible_moves(int *a, int *numout)
++static const char * freecell_solver_cmd_line_args[CMD_LINE_ARGS_NUM] =
+ {
+- int i, n, t, w, o, empty, emptyw;
+- card_t card;
+- MOVE *mp;
+-
+- /* Check for moves from W to O. */
+-
+- n = 0;
+- mp = Possible;
+- for (w = 0; w < Nwpiles + Ntpiles; ++w) {
+- if (Wlen[w] > 0) {
+- card = *Wp[w];
+- o = SUIT(card);
+- empty = (O[o] == NONE);
+- if ((empty && (RANK(card) == PS_ACE)) ||
+- (!empty && (RANK(card) == O[o] + 1))) {
+- mp->card_index = 0;
+- mp->from = w;
+- mp->to = o;
+- mp->totype = O_Type;
+- mp->turn_index = -1;
+- mp->pri = 0; /* unused */
+- n++;
+- mp++;
+-
+- /* If it's an automove, just do it. */
+-
+- if (good_automove(o, RANK(card))) {
+- *a = true;
+- mp[-1].pri = 127;
+- if (n != 1) {
+- Possible[0] = mp[-1];
+- return 1;
+- }
+- return n;
+- }
+- }
+- }
+- }
+-
+- /* No more automoves, but remember if there were any moves out. */
+-
+- *a = false;
+- *numout = n;
+-
+- /* Check for moves from non-singleton W cells to one of any
+- empty W cells. */
++ "--load-config", "video-editing"
++};
+
+- emptyw = -1;
+- for (w = 0; w < Nwpiles; ++w) {
+- if (Wlen[w] == 0) {
+- emptyw = w;
+- break;
+- }
+- }
+- if (emptyw >= 0) {
+- for (i = 0; i < Nwpiles + Ntpiles; ++i) {
+- if (i == emptyw || Wlen[i] == 0) {
+- continue;
+- }
+- bool allowed = false;
+- if ( i < Nwpiles)
+- allowed = true;
+- if ( i >= Nwpiles )
+- allowed = true;
+- if ( allowed ) {
+- card = *Wp[i];
+- mp->card_index = 0;
+- mp->from = i;
+- mp->to = emptyw;
+- mp->totype = W_Type;
+- mp->turn_index = -1;
+- if ( i >= Nwpiles )
+- mp->pri = Xparam[6];
+- else
+- mp->pri = Xparam[3];
+- n++;
+- mp++;
+- }
+- }
+- }
+-
+- /* Check for moves from W to non-empty W cells. */
+-
+- for (i = 0; i < Nwpiles + Ntpiles; ++i) {
+- if (Wlen[i] > 0) {
+- card = *Wp[i];
+- for (w = 0; w < Nwpiles; ++w) {
+- if (i == w) {
+- continue;
+- }
+- if (Wlen[w] > 0 &&
+- (RANK(card) == RANK(*Wp[w]) - 1 &&
+- suitable(card, *Wp[w]))) {
+- mp->card_index = 0;
+- mp->from = i;
+- mp->to = w;
+- mp->totype = W_Type;
+- mp->turn_index = -1;
+- if ( i >= Nwpiles )
+- mp->pri = Xparam[5];
+- else
+- mp->pri = Xparam[4];
+- n++;
+- mp++;
+- }
+- }
+- }
+- }
+-
+- /* Check for moves from W to one of any empty T cells. */
+-
+- for (t = 0; t < Ntpiles; ++t) {
+- if (!Wlen[t+Nwpiles]) {
+- break;
+- }
+- }
++int FreecellSolver::get_cmd_line_arg_count()
++{
++ return CMD_LINE_ARGS_NUM;
++}
+
+- if (t < Ntpiles) {
+- for (w = 0; w < Nwpiles; ++w) {
+- if (Wlen[w] > 0) {
+- card = *Wp[w];
+- mp->card_index = 0;
+- mp->from = w;
+- mp->turn_index = -1;
+- mp->to = t+Nwpiles;
+- mp->totype = W_Type;
+- mp->pri = Xparam[7];
+- n++;
+- mp++;
+- }
+- }
+- }
++const char * * FreecellSolver::get_cmd_line_args()
++{
++ return freecell_solver_cmd_line_args;
++}
+
+
+- return n;
++void FreecellSolver::setFcSolverGameParams()
++{
++ /*
++ * I'm using the more standard interface instead of the depracated
++ * user_set_game one. I'd like that each function will have its
++ * own dedicated purpose.
++ *
++ * Shlomi Fish
++ * */
++ freecell_solver_user_set_num_freecells(solver_instance,4);
++ freecell_solver_user_set_num_stacks(solver_instance,8);
++ freecell_solver_user_set_num_decks(solver_instance,1);
++ freecell_solver_user_set_sequences_are_built_by_type(solver_instance, FCS_SEQ_BUILT_BY_ALTERNATE_COLOR);
++ freecell_solver_user_set_sequence_move(solver_instance, 0);
++ freecell_solver_user_set_empty_stacks_filled_by(solver_instance, FCS_ES_FILLED_BY_ANY_CARD);
+ }
+-
++#if 0
+ void FreecellSolver::unpack_cluster( unsigned int k )
+ {
+ /* Get the Out cells from the cluster number. */
+@@ -373,27 +281,13 @@ void FreecellSolver::unpack_cluster( unsigned int k )
+ k >>= 4;
+ O[3] = k & 0xF;
+ }
++#endif
+
+-bool FreecellSolver::isWon()
+-{
+- // maybe won?
+- for (int o = 0; o < 4; ++o) {
+- if (O[o] != PS_KING) {
+- return false;
+- }
+- }
+-
+- return true;
+-}
+-
+-int FreecellSolver::getOuts()
+-{
+- return O[0] + O[1] + O[2] + O[3];
+-}
+
+ FreecellSolver::FreecellSolver(const Freecell *dealer)
+- : Solver()
++ : FcSolveSolver()
+ {
++#if 0
+ Osuit[0] = PS_DIAMOND;
+ Osuit[1] = PS_CLUB;
+ Osuit[2] = PS_HEART;
+@@ -402,12 +296,15 @@ FreecellSolver::FreecellSolver(const Freecell *dealer)
+ Nwpiles = 8;
+ Ntpiles = 4;
+
++#endif
++
+ deal = dealer;
+ }
+
+ /* Read a layout file. Format is one pile per line, bottom to top (visible
+ card). Temp cells and Out on the last two lines, if any. */
+
++#if 0
+ void FreecellSolver::translate_layout()
+ {
+ /* Read the workspace. */
+@@ -447,9 +344,78 @@ void FreecellSolver::translate_layout()
+ }
+ }
+ }
++#endif
+
+ MoveHint FreecellSolver::translateMove( const MOVE &m )
+ {
++ fcs_move_t move = m.fcs;
++ int cards = fcs_move_get_num_cards_in_seq(move);
++ PatPile *from = 0;
++ PatPile *to = 0;
++
++ switch(fcs_move_get_type(move))
++ {
++ case FCS_MOVE_TYPE_STACK_TO_STACK:
++ from = deal->store[fcs_move_get_src_stack(move)];
++ to = deal->store[fcs_move_get_dest_stack(move)];
++ break;
++
++ case FCS_MOVE_TYPE_FREECELL_TO_STACK:
++ from = deal->freecell[fcs_move_get_src_freecell(move)];
++ to = deal->store[fcs_move_get_dest_stack(move)];
++ cards = 1;
++ break;
++
++ case FCS_MOVE_TYPE_FREECELL_TO_FREECELL:
++ from = deal->freecell[fcs_move_get_src_freecell(move)];
++ to = deal->freecell[fcs_move_get_dest_freecell(move)];
++ cards = 1;
++ break;
++
++ case FCS_MOVE_TYPE_STACK_TO_FREECELL:
++ from = deal->store[fcs_move_get_src_stack(move)];
++ to = deal->freecell[fcs_move_get_dest_freecell(move)];
++ cards = 1;
++ break;
++
++ case FCS_MOVE_TYPE_STACK_TO_FOUNDATION:
++ from = deal->store[fcs_move_get_src_stack(move)];
++ cards = 1;
++ to = 0;
++ break;
++
++ case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION:
++ from = deal->freecell[fcs_move_get_src_freecell(move)];
++ cards = 1;
++ to = 0;
++ }
++ Q_ASSERT(from);
++ Q_ASSERT(cards <= from->cards().count());
++ Q_ASSERT(to || cards == 1);
++ KCard *card = from->cards()[from->cards().count() - cards];
++
++ if (!to)
++ {
++ PatPile *target = 0;
++ PatPile *empty = 0;
++ for (int i = 0; i < 4; ++i) {
++ KCard *c = deal->target[i]->topCard();
++ if (c) {
++ if ( c->suit() == card->suit() )
++ {
++ target = deal->target[i];
++ break;
++ }
++ } else if ( !empty )
++ empty = deal->target[i];
++ }
++ to = target ? target : empty;
++ }
++ Q_ASSERT(to);
++
++ return MoveHint(card, to, 0);
++
++#if 0
+ // this is tricky as we need to want to build the "meta moves"
+
+ PatPile *frompile = nullptr;
+@@ -486,8 +452,43 @@ MoveHint FreecellSolver::translateMove( const MOVE &m )
+
+ return MoveHint( card, target, m.pri );
+ }
++#endif
+ }
+
++void FreecellSolver::translate_layout()
++{
++ strcpy(board_as_string, deal->solverFormat().toLatin1());
++
++ if (solver_instance)
++ {
++ freecell_solver_user_recycle(solver_instance);
++ solver_ret = FCS_STATE_NOT_BEGAN_YET;
++ }
++#if 0
++ /* Read the workspace. */
++ int total = 0;
++
++ for ( int w = 0; w < 10; ++w ) {
++ int i = translate_pile(deal->store[w], W[w], 52);
++ Wp[w] = &W[w][i - 1];
++ Wlen[w] = i;
++ total += i;
++ }
++
++ for (int i = 0; i < 4; ++i) {
++ O[i] = -1;
++ KCard *c = deal->target[i]->top();
++ if (c) {
++ total += 13;
++ O[i] = translateSuit( c->suit() );
++ }
++ }
++#endif
++}
++
++
++
++#if 0
+ unsigned int FreecellSolver::getClusterNumber()
+ {
+ int i = O[0] + (O[1] << 4);
+@@ -496,7 +497,9 @@ unsigned int FreecellSolver::getClusterNumber()
+ k |= i << 8;
+ return k;
+ }
++#endif
+
++#if 0
+ void FreecellSolver::print_layout()
+ {
+ int i, t, w, o;
+@@ -519,3 +522,4 @@ void FreecellSolver::print_layout()
+ }
+ fprintf(stderr, "\nprint-layout-end\n");
+ }
++#endif
+diff --git a/patsolve/freecellsolver.h b/patsolve/freecellsolver.h
+index 45ca063..99d1dbb 100644
+--- a/patsolve/freecellsolver.h
++++ b/patsolve/freecellsolver.h
+@@ -19,16 +19,17 @@
+ #ifndef FREECELLSOLVER_H
+ #define FREECELLSOLVER_H
+
+-class Freecell;
+-#include "patsolve.h"
++#include "abstract_fc_solve_solver.h"
+
+ constexpr auto Nwpiles = 8;
+ constexpr auto Ntpiles = 4;
++class Freecell;
+
+-class FreecellSolver : public Solver<Nwpiles + Ntpiles>
++class FreecellSolver : public FcSolveSolver
+ {
+ public:
+ explicit FreecellSolver(const Freecell *dealer);
++#if 0
+ int good_automove(int o, int r);
+ int get_possible_moves(int *a, int *numout) Q_DECL_OVERRIDE;
+ bool isWon() Q_DECL_OVERRIDE;
+@@ -40,8 +41,17 @@ public:
+ void translate_layout() Q_DECL_OVERRIDE;
+ void unpack_cluster( unsigned int k ) Q_DECL_OVERRIDE;
+ MoveHint translateMove(const MOVE &m) Q_DECL_OVERRIDE;
+-
+- void print_layout() Q_DECL_OVERRIDE;
++#endif
++ virtual void translate_layout();
++#if 0
++ virtual void unpack_cluster( unsigned int k );
++#endif
++ virtual MoveHint translateMove(const MOVE &m);
++ virtual void setFcSolverGameParams();
++ virtual int get_cmd_line_arg_count();
++ virtual const char * * get_cmd_line_args();
++#if 0
++ virtual void print_layout();
+
+ int Nwpiles; /* the numbers we're actually using */
+ int Ntpiles;
+@@ -51,10 +61,11 @@ public:
+ card_t O[4]; /* output piles store only the rank or NONE */
+ card_t Osuit[4];
+
+- const Freecell *deal;
+
+ static int Xparam[];
++#endif
+
++ const Freecell *deal;
+ };
+
+ #endif // FREECELLSOLVER_H
+diff --git a/patsolve/patsolve.h b/patsolve/patsolve.h
+index 03285d4..1c3a7c6 100644
+--- a/patsolve/patsolve.h
++++ b/patsolve/patsolve.h
+@@ -33,6 +33,10 @@
+
+ #include <cstdio>
+
++/* A card is represented as ( down << 6 ) + (suit << 4) + rank. */
++
++typedef quint8 card_t;
++
+ struct POSITION {
+ POSITION *queue; /* next position in the queue */
+ POSITION *parent; /* point back up the move stack */
+@@ -48,14 +52,15 @@ class MemoryManager;
+ template<size_t NumberPiles>
+ class Solver : public SolverInterface
+ {
++
+ public:
+
+ Solver();
+ virtual ~Solver();
+- ExitStatus patsolve( int max_positions = -1) final;
++ virtual ExitStatus patsolve( int max_positions = -1);
++ bool recursive(POSITION *pos = nullptr);
+ virtual void translate_layout() = 0;
+ virtual MoveHint translateMove(const MOVE &m ) = 0;
+-
+ void stopExecution() final;
+ QList<MOVE> firstMoves() const final;
+ QList<MOVE> winMoves() const final;
+@@ -126,8 +131,7 @@ protected:
+ POSITION *Stack = nullptr;
+ QMap<qint32,bool> recu_pos;
+ int max_positions;
+-
+-private:
++protected:
+ QList<MOVE> m_firstMoves;
+ QList<MOVE> m_winMoves;
+ std::atomic_bool m_shouldEnd;
+diff --git a/patsolve/simonsolver.cpp b/patsolve/simonsolver.cpp
+index a9d640c..e75dcaa 100644
+--- a/patsolve/simonsolver.cpp
++++ b/patsolve/simonsolver.cpp
+@@ -15,17 +15,26 @@
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
++#include <stdlib.h>
++#include <string.h>
++
++#include "freecell-solver/fcs_user.h"
++#include "freecell-solver/fcs_cl.h"
++
+ #include "simonsolver.h"
+
+ #include "../simon.h"
+
+ #include <QDebug>
+
++const int CHUNKSIZE = 100;
++const long int MAX_ITERS_LIMIT = 200000;
+
+ #define PRINT 0
+
+ /* These two routines make and unmake moves. */
+
++#if 0
+ void SimonSolver::make_move(MOVE *m)
+ {
+ #if PRINT
+@@ -136,13 +145,62 @@ void SimonSolver::undo_move(MOVE *m)
+ print_layout();
+ #endif
+ }
++#endif
++
++#define CMD_LINE_ARGS_NUM 4
++static const char * freecell_solver_cmd_line_args[CMD_LINE_ARGS_NUM] =
++{
++ "-g", "simple_simon", "--load-config", "the-last-mohican"
++};
++
++int SimonSolver::get_cmd_line_arg_count()
++{
++ return CMD_LINE_ARGS_NUM;
++}
++
++const char * * SimonSolver::get_cmd_line_args()
++{
++ return freecell_solver_cmd_line_args;
++}
++
++void SimonSolver::setFcSolverGameParams()
++{
++ freecell_solver_user_apply_preset(solver_instance, "simple_simon");
++}
+
++#if 0
+ /* Get the possible moves from a position, and store them in Possible[]. */
+
+ int SimonSolver::get_possible_moves(int *a, int *numout)
+ {
+ MOVE *mp;
++ int n;
++
++ mp = Possible;
++ n = 0;
++ *a = 1;
++
++ while (freecell_solver_user_get_moves_left(solver_instance))
++ {
++ fcs_move_t move;
++ fcs_move_t * move_ptr;
++ if (!freecell_solver_user_get_next_move(solver_instance, &move)) {
++ move_ptr = new fcs_move_t;
++ *move_ptr = move;
++ mp->ptr = (void *)move_ptr;
++ mp++;
++ n++;
++ }
++ else
++ {
++ Q_ASSERT(0);
++ }
++ }
++
++ *numout = n;
++ return n;
+
++#if 0
+ /* Check for moves from W to O. */
+
+ int n = 0;
+@@ -301,8 +359,11 @@ int SimonSolver::get_possible_moves(int *a, int *numout)
+ }
+
+ return n;
++#endif
+ }
++#endif
+
++#if 0
+ void SimonSolver::unpack_cluster( unsigned int k )
+ {
+ // TODO: this only works for easy
+@@ -314,7 +375,9 @@ void SimonSolver::unpack_cluster( unsigned int k )
+ O[i] = -1;
+ }
+ }
++#endif
+
++#if 0
+ bool SimonSolver::isWon()
+ {
+ // maybe won?
+@@ -324,7 +387,9 @@ bool SimonSolver::isWon()
+
+ return true;
+ }
++#endif
+
++#if 0
+ int SimonSolver::getOuts()
+ {
+ int k = 0;
+@@ -334,9 +399,10 @@ int SimonSolver::getOuts()
+
+ return k;
+ }
++#endif
+
+ SimonSolver::SimonSolver(const Simon *dealer)
+- : Solver()
++ : FcSolveSolver()
+ {
+ deal = dealer;
+ }
+@@ -346,6 +412,14 @@ card). Temp cells and Out on the last two lines, if any. */
+
+ void SimonSolver::translate_layout()
+ {
++ strcpy(board_as_string, deal->solverFormat().toLatin1());
++
++ if (solver_instance)
++ {
++ freecell_solver_user_recycle(solver_instance);
++ solver_ret = FCS_STATE_NOT_BEGAN_YET;
++ }
++#if 0
+ /* Read the workspace. */
+ int total = 0;
+
+@@ -364,8 +438,10 @@ void SimonSolver::translate_layout()
+ O[i] = translateSuit( c->suit() );
+ }
+ }
++#endif
+ }
+
++#if 0
+ unsigned int SimonSolver::getClusterNumber()
+ {
+ unsigned int k = 0;
+@@ -376,7 +452,9 @@ unsigned int SimonSolver::getClusterNumber()
+ }
+ return k;
+ }
++#endif
+
++#if 0
+ void SimonSolver::print_layout()
+ {
+ int i, w, o;
+@@ -397,9 +475,57 @@ void SimonSolver::print_layout()
+ }
+ fprintf(stderr, "\nprint-layout-end\n");
+ }
++#endif
+
+ MoveHint SimonSolver::translateMove( const MOVE &m )
+ {
++ fcs_move_t move = m.fcs;
++ int cards = fcs_move_get_num_cards_in_seq(move);
++ PatPile *from = 0;
++ PatPile *to = 0;
++
++ switch(fcs_move_get_type(move))
++ {
++ case FCS_MOVE_TYPE_STACK_TO_STACK:
++ from = deal->store[fcs_move_get_src_stack(move)];
++ to = deal->store[fcs_move_get_dest_stack(move)];
++ break;
++
++ case FCS_MOVE_TYPE_SEQ_TO_FOUNDATION:
++ from = deal->store[fcs_move_get_src_stack(move)];
++ cards = 13;
++ to = deal->target[fcs_move_get_foundation(move)];
++ break;
++
++ }
++ Q_ASSERT(from);
++ Q_ASSERT(cards <= from->cards().count());
++ Q_ASSERT(to || cards == 1);
++ KCard *card = from->cards()[from->cards().count() - cards];
++
++ if (!to)
++ {
++ PatPile *target = 0;
++ PatPile *empty = 0;
++ for (int i = 0; i < 4; ++i) {
++ KCard *c = deal->target[i]->topCard();
++ if (c) {
++ if ( c->suit() == card->suit() )
++ {
++ target = deal->target[i];
++ break;
++ }
++ } else if ( !empty )
++ empty = deal->target[i];
++ }
++ to = target ? target : empty;
++ }
++
++ Q_ASSERT(to);
++
++ return MoveHint(card, to, 0);
++
++#if 0
+ Q_ASSERT( m.from < 10 && m.to < 10 );
+
+ PatPile *frompile = deal->store[m.from];
+@@ -414,4 +540,5 @@ MoveHint SimonSolver::translateMove( const MOVE &m )
+
+ Q_ASSERT( m.to < 10 );
+ return MoveHint( card, deal->store[m.to], m.pri );
++#endif
+ }
+diff --git a/patsolve/simonsolver.h b/patsolve/simonsolver.h
+index 2d57dda..4a417b1 100644
+--- a/patsolve/simonsolver.h
++++ b/patsolve/simonsolver.h
+@@ -18,29 +18,37 @@
+ #ifndef SIMONSOLVER_H
+ #define SIMONSOLVER_H
+
+-#include "patsolve.h"
++#include "abstract_fc_solve_solver.h"
++#include "simon.h"
+ class Simon;
+
+
+-class SimonSolver : public Solver<10>
++class SimonSolver : public FcSolveSolver
+ {
+ public:
+ explicit SimonSolver(const Simon *dealer);
++#if 0
+ int get_possible_moves(int *a, int *numout) Q_DECL_OVERRIDE;
+ bool isWon() Q_DECL_OVERRIDE;
+ void make_move(MOVE *m) Q_DECL_OVERRIDE;
+ void undo_move(MOVE *m) Q_DECL_OVERRIDE;
+ int getOuts() Q_DECL_OVERRIDE;
+ unsigned int getClusterNumber() Q_DECL_OVERRIDE;
+- void translate_layout() Q_DECL_OVERRIDE;
++#endif
++ virtual void translate_layout() Q_DECL_OVERRIDE;
++ virtual MoveHint translateMove(const MOVE &m) Q_DECL_OVERRIDE;
++#if 0
+ void unpack_cluster( unsigned int k ) Q_DECL_OVERRIDE;
+- MoveHint translateMove(const MOVE &m) Q_DECL_OVERRIDE;
+-
+ void print_layout() Q_DECL_OVERRIDE;
++#endif
++ virtual void setFcSolverGameParams();
+
++ virtual int get_cmd_line_arg_count();
++ virtual const char * * get_cmd_line_args();
++#if 0
+ /* Names of the cards. The ordering is defined in pat.h. */
+-
+ int O[4];
++#endif
+ const Simon *deal;
+ };
+
+diff --git a/patsolve/solverinterface.h b/patsolve/solverinterface.h
+index d99d3b8..77fd410 100644
+--- a/patsolve/solverinterface.h
++++ b/patsolve/solverinterface.h
+@@ -4,6 +4,7 @@
+ #include <QList>
+
+ #include "../hint.h"
++#include "freecell-solver/fcs_user.h"
+
+
+ /* A card is represented as ( down << 6 ) + (suit << 4) + rank. */
+@@ -22,6 +23,7 @@ public:
+ PileType totype;
+ signed char pri; /* move priority (low priority == low value) */
+ int turn_index; /* turn the card index */
++ fcs_move_t fcs; /* A Freecell Solver move. */
+
+ bool operator<( const MOVE &m) const
+ {
+diff --git a/pileutils.cpp b/pileutils.cpp
+index 1e3da3e..609c716 100644
+--- a/pileutils.cpp
++++ b/pileutils.cpp
+@@ -48,6 +48,33 @@ bool isSameSuitAscending( const QList<KCard*> & cards )
+ return true;
+ }
+
++int countSameSuitDescendingSequences( const QList<KCard*> & cards )
++{
++ if ( cards.size() <= 1 )
++ return 0;
++
++ int suit = cards.first()->suit();
++ int lastRank = cards.first()->rank();
++
++ int count = 1;
++
++ for( int i = 1; i < cards.size(); ++i )
++ {
++ --lastRank;
++
++ if ( cards[i]->rank() != lastRank )
++ return -1;
++
++ if ( cards[i]->suit() != suit )
++ {
++ count++;
++ suit = cards[i]->suit();
++ }
++ }
++ return count;
++}
++
++
+
+ bool isSameSuitDescending( const QList<KCard*> & cards )
+ {
+@@ -121,3 +148,37 @@ bool checkAddAlternateColorDescendingFromKing( const QList<KCard*> & oldCards, c
+ && newCards.first()->rank() == oldCards.last()->rank() - 1;
+ }
+
++QString suitToString(int s) {
++ switch (s) {
++ case KCardDeck::Clubs:
++ return "C";
++ case KCardDeck::Hearts:
++ return "H";
++ case KCardDeck::Diamonds:
++ return "D";
++ case KCardDeck::Spades:
++ return "S";
++ default:
++ exit(-1);
++ }
++ return QString();
++}
++
++QString rankToString(int r)
++{
++ switch (r) {
++ case KCardDeck::King:
++ return "K";
++ case KCardDeck::Ace:
++ return "A";
++ case KCardDeck::Jack:
++ return "J";
++ case KCardDeck::Queen:
++ return "Q";
++ case KCardDeck::Ten:
++ return "T";
++ default:
++ return QString::number(r);
++ }
++}
++
+diff --git a/pileutils.h b/pileutils.h
+index 2fa1657..faa8c40 100644
+--- a/pileutils.h
++++ b/pileutils.h
+@@ -26,9 +26,13 @@ class KCard;
+ bool isSameSuitAscending( const QList<KCard*> & cards );
+ bool isSameSuitDescending( const QList<KCard*> & cards );
+ bool isAlternateColorDescending( const QList<KCard*> & cards );
++int countSameSuitDescendingSequences( const QList<KCard*> & cards );
+
+ bool checkAddSameSuitAscendingFromAce( const QList<KCard*> & oldCards, const QList<KCard*> & newCards );
+ bool checkAddAlternateColorDescending( const QList<KCard*> & oldCards, const QList<KCard*> & newCards );
+ bool checkAddAlternateColorDescendingFromKing( const QList<KCard*> & oldCards, const QList<KCard*> & newCards );
+
++extern QString suitToString(int s);
++extern QString rankToString(int r);
++
+ #endif
+diff --git a/simon.cpp b/simon.cpp
+index 8e3ef10..834dd55 100644
+--- a/simon.cpp
++++ b/simon.cpp
+@@ -111,24 +111,70 @@ bool Simon::checkAdd(const PatPile * pile, const QList<KCard*> & oldCards, const
+ {
+ if (pile->pileRole() == PatPile::Tableau)
+ {
+- return oldCards.isEmpty()
+- || oldCards.last()->rank() == newCards.first()->rank() + 1;
++ if (! (oldCards.isEmpty()
++ || oldCards.last()->rank() == newCards.first()->rank() + 1 ))
++ {
++ return false;
++ }
++
++ int seqs_count = countSameSuitDescendingSequences(newCards);
++
++ if (seqs_count < 0)
++ return false;
++
++ // This is similar to the supermoves of Freecell - we can use empty
++ // columns to temporarily hold intermediate sub-sequences which are
++ // not the same suit - only a "false" parent.
++ // Shlomi Fish
++
++ int empty_piles_count = 0;
++
++ for (int i = 0; i < 10; ++i )
++ if (store[i]->isEmpty() && ( store[i]->index() != pile->index() ))
++ empty_piles_count++;
++
++ return (seqs_count <= (1 << empty_piles_count));
+ }
+ else
+ {
+ return oldCards.isEmpty()
+ && newCards.first()->rank() == KCardDeck::King
+- && newCards.last()->rank() == KCardDeck::Ace;
++ && newCards.last()->rank() == KCardDeck::Ace
++ && isSameSuitDescending(newCards);
+ }
+ }
+
+ bool Simon::checkRemove(const PatPile * pile, const QList<KCard*> & cards) const
+ {
+- return pile->pileRole() == PatPile::Tableau
+- && isSameSuitDescending(cards);
++ if (pile->pileRole() != PatPile::Tableau)
++ return false;
++
++ int seqs_count = countSameSuitDescendingSequences(cards);
++
++ return (seqs_count >= 0);
+ }
+
++QString Simon::solverFormat() const
++{
++ QString output;
++ QString tmp;
++ for (int i = 0; i < 4 ; i++) {
++ if (target[i]->isEmpty())
++ continue;
++ tmp += suitToString(target[i]->topCard()->suit()) + "-K ";
++ }
++ if (!tmp.isEmpty())
++ output += QString::fromLatin1("Foundations: %1\n").arg(tmp);
+
++ for (int i = 0; i < 10 ; i++)
++ {
++ QList<KCard*> cards = store[i]->cards();
++ for (QList<KCard*>::ConstIterator it = cards.begin(); it != cards.end(); ++it)
++ output += rankToString((*it)->rank()) + suitToString((*it)->suit()) + ' ';
++ output += '\n';
++ }
++ return output;
++}
+
+ static class SimonDealerInfo : public DealerInfo
+ {
+diff --git a/simon.h b/simon.h
+index 83d10ab..d816f27 100644
+--- a/simon.h
++++ b/simon.h
+@@ -57,6 +57,7 @@ private:
+ PatPile* store[10];
+ PatPile* target[4];
+
++ virtual QString solverFormat() const;
+ friend class SimonSolver;
+ };
+
+--
+cgit v0.11.2
+