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 }