File indexing completed on 2024-11-24 03:44:58

0001 /*
0002  * Copyright (C) 1998-2002 Tom Holroyd <tomh@kurage.nimh.nih.gov>
0003  * Copyright (C) 2006-2009 Stephan Kulow <coolo@kde.org>
0004  *
0005  * This program is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU General Public License as
0007  * published by the Free Software Foundation; either version 2 of
0008  * the License, or (at your option) any later version.
0009  *
0010  * This program is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU General Public License
0016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017  */
0018 
0019 #include "castlesolver.h"
0020 
0021 // own
0022 #include "../castle.h"
0023 #include "../settings.h"
0024 #include "patsolve-config.h"
0025 // freecell-solver
0026 #include "freecell-solver/fcs_cl.h"
0027 #include "freecell-solver/fcs_user.h"
0028 // St
0029 #include <cstdlib>
0030 #include <cstring>
0031 
0032 namespace
0033 {
0034 int m_reserves = Settings::castleReserves();
0035 int m_stacks = Settings::castleStacks();
0036 int m_decks = 1;
0037 int m_emptyStackFill = Settings::castleEmptyStackFill();
0038 int m_sequenceBuiltBy = Settings::castleSequenceBuiltBy();
0039 }
0040 
0041 /* Automove logic.  Freecell games must avoid certain types of automoves. */
0042 int CastleSolver::good_automove(int o, int r)
0043 {
0044     if (r <= 2) {
0045         return true;
0046     }
0047 
0048     if (m_sequenceBuiltBy == 1) {
0049         return true;
0050     }
0051 
0052     for (int foundation_idx = 0; foundation_idx < 4 * m_decks; ++foundation_idx) {
0053         KCard *c = deal->target[foundation_idx]->topCard();
0054         if (c) {
0055             O[translateSuit(c->suit()) >> 4] = c->rank();
0056         }
0057     }
0058     /* Check the Out piles of opposite color. */
0059 
0060     for (int i = 1 - (o & 1); i < 4 * m_decks; i += 2) {
0061         if (O[i] < r - 1) {
0062 #if 1 /* Raymond's Rule */
0063             /* Not all the N-1's of opposite color are out
0064             yet.  We can still make an automove if either
0065             both N-2's are out or the other same color N-3
0066             is out (Raymond's rule).  Note the re-use of
0067             the loop variable i.  We return here and never
0068             make it back to the outer loop. */
0069 
0070             for (i = 1 - (o & 1); i < 4 * m_decks; i += 2) {
0071                 if (O[i] < r - 2) {
0072                     return false;
0073                 }
0074             }
0075             if (O[(o + 2) & 3] < r - 3) {
0076                 return false;
0077             }
0078 
0079             return true;
0080 #else /* Horne's Rule */
0081             return false;
0082 #endif
0083         }
0084     }
0085 
0086     return true;
0087 }
0088 
0089 int CastleSolver::get_possible_moves(int *a, int *numout)
0090 {
0091     int w;
0092     card_t card;
0093     MOVE *mp;
0094 
0095     /* Check for moves from W to O. */
0096 
0097     int n = 0;
0098     mp = Possible;
0099     for (w = 0; w < m_stacks + m_reserves; ++w) {
0100         if (Wlen[w] > 0) {
0101             card = *Wp[w];
0102             int out_suit = SUIT(card);
0103             const bool empty = (O[out_suit] == NONE);
0104             if ((empty && (RANK(card) == PS_ACE)) || (!empty && (RANK(card) == O[out_suit] + 1))) {
0105                 mp->is_fcs = false;
0106                 mp->card_index = 0;
0107                 mp->from = w;
0108                 mp->to = out_suit;
0109                 mp->totype = O_Type;
0110                 mp->turn_index = -1;
0111                 mp->pri = 0; /* unused */
0112                 n++;
0113                 mp++;
0114 
0115                 /* If it's an automove, just do it. */
0116 
0117                 if (good_automove(out_suit, RANK(card))) {
0118                     *a = true;
0119                     mp[-1].pri = 127;
0120                     if (n != 1) {
0121                         Possible[0] = mp[-1];
0122                         return (*numout = 1);
0123                     }
0124                     return (*numout = n);
0125                 }
0126             }
0127         }
0128     }
0129     return (*numout = 0);
0130 }
0131 
0132 #define CMD_LINE_ARGS_NUM 2
0133 
0134 static const char *freecell_solver_cmd_line_args[CMD_LINE_ARGS_NUM] = {
0135 #if WITH_FCS_SOFT_SUSPEND
0136     "--load-config",
0137     "video-editing"
0138 #else
0139     "--load-config",
0140     "slick-rock"
0141 #endif
0142 };
0143 
0144 int CastleSolver::get_cmd_line_arg_count()
0145 {
0146     return CMD_LINE_ARGS_NUM;
0147 }
0148 
0149 const char **CastleSolver::get_cmd_line_args()
0150 {
0151     return freecell_solver_cmd_line_args;
0152 }
0153 
0154 void CastleSolver::setFcSolverGameParams()
0155 {
0156     /*
0157      * I'm using the more standard interface instead of the depracated
0158      * user_set_game one. I'd like that each function will have its
0159      * own dedicated purpose.
0160      *
0161      *     Shlomi Fish
0162      * */
0163     freecell_solver_user_set_sequence_move(solver_instance, 0);
0164 
0165     m_reserves = Settings::castleReserves();
0166     freecell_solver_user_set_num_freecells(solver_instance, m_reserves);
0167 
0168     m_stacks = Settings::castleStacks();
0169     freecell_solver_user_set_num_stacks(solver_instance, m_stacks);
0170 
0171     freecell_solver_user_set_num_decks(solver_instance, m_decks);
0172 
0173     // FCS_ES_FILLED_BY_ANY_CARD = 0, FCS_ES_FILLED_BY_KINGS_ONLY = 1,FCS_ES_FILLED_BY_NONE = 2
0174     m_emptyStackFill = Settings::castleEmptyStackFill();
0175     freecell_solver_user_set_empty_stacks_filled_by(solver_instance, m_emptyStackFill);
0176 
0177     // FCS_SEQ_BUILT_BY_ALTERNATE_COLOR = 0, FCS_SEQ_BUILT_BY_SUIT = 1, FCS_SEQ_BUILT_BY_RANK = 2
0178     m_sequenceBuiltBy = Settings::castleSequenceBuiltBy();
0179     freecell_solver_user_set_sequences_are_built_by_type(solver_instance, m_sequenceBuiltBy);
0180 }
0181 
0182 CastleSolver::CastleSolver(const Castle *dealer)
0183     : FcSolveSolver()
0184 {
0185     deal = dealer;
0186 }
0187 
0188 MoveHint CastleSolver::translateMove(const MOVE &m)
0189 {
0190     if (m.is_fcs) {
0191         fcs_move_t move = m.fcs;
0192         int cards = fcs_move_get_num_cards_in_seq(move);
0193         PatPile *from = nullptr;
0194         PatPile *to = nullptr;
0195 
0196         switch (fcs_move_get_type(move)) {
0197         case FCS_MOVE_TYPE_STACK_TO_STACK:
0198             from = deal->store[fcs_move_get_src_stack(move)];
0199             to = deal->store[fcs_move_get_dest_stack(move)];
0200             break;
0201 
0202         case FCS_MOVE_TYPE_FREECELL_TO_STACK:
0203             from = deal->freecell[fcs_move_get_src_freecell(move)];
0204             to = deal->store[fcs_move_get_dest_stack(move)];
0205             cards = 1;
0206             break;
0207 
0208         case FCS_MOVE_TYPE_FREECELL_TO_FREECELL:
0209             from = deal->freecell[fcs_move_get_src_freecell(move)];
0210             to = deal->freecell[fcs_move_get_dest_freecell(move)];
0211             cards = 1;
0212             break;
0213 
0214         case FCS_MOVE_TYPE_STACK_TO_FREECELL:
0215             from = deal->store[fcs_move_get_src_stack(move)];
0216             to = deal->freecell[fcs_move_get_dest_freecell(move)];
0217             cards = 1;
0218             break;
0219 
0220         case FCS_MOVE_TYPE_STACK_TO_FOUNDATION:
0221             from = deal->store[fcs_move_get_src_stack(move)];
0222             cards = 1;
0223             to = nullptr;
0224             break;
0225 
0226         case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION:
0227             from = deal->freecell[fcs_move_get_src_freecell(move)];
0228             cards = 1;
0229             to = nullptr;
0230         }
0231         Q_ASSERT(from);
0232         Q_ASSERT(cards <= from->cards().count());
0233         Q_ASSERT(to || cards == 1);
0234         KCard *card = from->cards()[from->cards().count() - cards];
0235 
0236         if (!to) {
0237             PatPile *target = nullptr;
0238             PatPile *empty = nullptr;
0239             for (int i = 0; i < 4 * m_decks; ++i) {
0240                 KCard *c = deal->target[i]->topCard();
0241                 if (c) {
0242                     if (c->suit() == card->suit()) {
0243                         target = deal->target[i];
0244                         break;
0245                     }
0246                 } else if (!empty)
0247                     empty = deal->target[i];
0248             }
0249             to = target ? target : empty;
0250         }
0251         Q_ASSERT(to);
0252 
0253         return MoveHint(card, to, 0);
0254     } else {
0255         // this is tricky as we need to want to build the "meta moves"
0256 
0257         PatPile *frompile = nullptr;
0258         if (m.from < m_stacks)
0259             frompile = deal->store[m.from];
0260         else
0261             frompile = deal->freecell[m.from - m_stacks];
0262         KCard *card = frompile->at(frompile->count() - m.card_index - 1);
0263 
0264         if (m.totype == O_Type) {
0265             PatPile *target = nullptr;
0266             PatPile *empty = nullptr;
0267             for (int i = 0; i < 4 * m_decks; ++i) {
0268                 KCard *c = deal->target[i]->topCard();
0269                 if (c) {
0270                     if (c->suit() == card->suit()) {
0271                         target = deal->target[i];
0272                         break;
0273                     }
0274                 } else if (!empty)
0275                     empty = deal->target[i];
0276             }
0277             if (!target)
0278                 target = empty;
0279             return MoveHint(card, target, m.pri);
0280         } else {
0281             PatPile *target = nullptr;
0282             if (m.to < m_stacks)
0283                 target = deal->store[m.to];
0284             else
0285                 target = deal->freecell[m.to - m_stacks];
0286 
0287             return MoveHint(card, target, m.pri);
0288         }
0289     }
0290 }
0291 
0292 void CastleSolver::translate_layout()
0293 {
0294     strcpy(board_as_string, deal->solverFormat().toLatin1().constData());
0295 
0296     make_solver_instance_ready();
0297     /* Read the workspace. */
0298 
0299     int total = 0;
0300     for (int w = 0; w < m_stacks; ++w) {
0301         int i = translate_pile(deal->store[w], W[w], 52 * m_decks);
0302         Wp[w] = &W[w][i - 1];
0303         Wlen[w] = i;
0304         total += i;
0305         if (w == m_stacks) {
0306             break;
0307         }
0308     }
0309 
0310     /* Temp cells may have some cards too. */
0311 
0312     for (int w = 0; w < m_reserves; ++w) {
0313         int i = translate_pile(deal->freecell[w], W[w + m_stacks], 52 * m_decks);
0314         Wp[w + m_stacks] = &W[w + m_stacks][i - 1];
0315         Wlen[w + m_stacks] = i;
0316         total += i;
0317     }
0318 
0319     /* Output piles, if any. */
0320     for (int i = 0; i < 4 * m_decks; ++i) {
0321         O[i] = NONE;
0322     }
0323     if (total != 52 * m_decks) {
0324         for (int i = 0; i < 4 * m_decks; ++i) {
0325             KCard *c = deal->target[i]->topCard();
0326             if (c) {
0327                 O[translateSuit(c->suit()) >> 4] = c->rank();
0328                 total += c->rank();
0329             }
0330         }
0331     }
0332 }