From 80d54d4191a76967786d7faac83c7bf8e84c1bcf Mon Sep 17 00:00:00 2001 From: Eric Hameleers Date: Fri, 10 Aug 2018 15:45:06 +0200 Subject: Prepare for reverting a future hard dependency on fc-solver in kpat --- kde/patch/kpat.patch | 5 + kde/patch/kpat/kpat_no_freecell_solver_dep.patch | 1475 ++++++++++++++++++++++ 2 files changed, 1480 insertions(+) create mode 100644 kde/patch/kpat.patch create mode 100644 kde/patch/kpat/kpat_no_freecell_solver_dep.patch 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 & 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 cards = store[i]->cards(); ++ for (QList::ConstIterator it = cards.begin(); it != cards.end(); ++it) ++ output += rankToString((*it)->rank()) + suitToString((*it)->suit()) + ' '; ++ output += '\n'; ++ } ++ return output; ++} ++ + void Freecell::cardsDroppedOnPile( const QList & 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 & 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 ++ * ++ * 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 . ++ */ ++ ++#include ++#include ++ ++#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 ++ * ++ * 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 . ++ */ ++ ++#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 . + */ + ++#include ++#include ++ ++#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 ++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 + ++/* 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 + 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 firstMoves() const final; + QList winMoves() const final; +@@ -126,8 +131,7 @@ protected: + POSITION *Stack = nullptr; + QMap recu_pos; + int max_positions; +- +-private: ++protected: + QList m_firstMoves; + QList 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 . + */ + ++#include ++#include ++ ++#include "freecell-solver/fcs_user.h" ++#include "freecell-solver/fcs_cl.h" ++ + #include "simonsolver.h" + + #include "../simon.h" + + #include + ++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 + + #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 & cards ) + return true; + } + ++int countSameSuitDescendingSequences( const QList & 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 & cards ) + { +@@ -121,3 +148,37 @@ bool checkAddAlternateColorDescendingFromKing( const QList & 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 & cards ); + bool isSameSuitDescending( const QList & cards ); + bool isAlternateColorDescending( const QList & cards ); ++int countSameSuitDescendingSequences( const QList & cards ); + + bool checkAddSameSuitAscendingFromAce( const QList & oldCards, const QList & newCards ); + bool checkAddAlternateColorDescending( const QList & oldCards, const QList & newCards ); + bool checkAddAlternateColorDescendingFromKing( const QList & oldCards, const QList & 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 & 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 & 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 cards = store[i]->cards(); ++ for (QList::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 + -- cgit v1.2.3