File indexing completed on 2024-05-26 04:08:33
0001 /* 0002 * Copyright (C) 2006-2009 Stephan Kulow <coolo@kde.org> 0003 * 0004 * This program is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU General Public License as 0006 * published by the Free Software Foundation; either version 2 of 0007 * the License, or (at your option) any later version. 0008 * 0009 * This program is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 * GNU General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU General Public License 0015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0016 */ 0017 0018 #include "yukonsolver.h" 0019 0020 // own 0021 #include "../kpat_debug.h" 0022 #include "../yukon.h" 0023 0024 /* Some macros used in get_possible_moves(). */ 0025 0026 /* The following macro implements 0027 (Same_suit ? (suit(a) == suit(b)) : (color(a) != color(b))) 0028 */ 0029 #define suitable(a, b) (COLOR(a) != COLOR(b)) 0030 0031 #define PRINT 0 0032 0033 /* These two routines make and unmake moves. */ 0034 0035 void YukonSolver::make_move(MOVE *m) 0036 { 0037 #if PRINT 0038 if (m->totype == O_Type) 0039 fprintf(stderr, "\nmake move %d from %d out (at %d)\n\n", m->card_index, m->from, m->turn_index); 0040 else 0041 fprintf(stderr, "\nmake move %d from %d to %d (%d)\n\n", m->card_index, m->from, m->to, m->turn_index); 0042 print_layout(); 0043 #else 0044 // print_layout(); 0045 #endif 0046 0047 int from, to; 0048 card_t card = NONE; 0049 0050 from = m->from; 0051 to = m->to; 0052 0053 for (int l = m->card_index; l >= 0; --l) { 0054 card = W[from][Wlen[from] - l - 1]; 0055 Wp[from]--; 0056 if (m->totype != O_Type) { 0057 Wp[to]++; 0058 *Wp[to] = card; 0059 Wlen[to]++; 0060 } 0061 } 0062 Wlen[from] -= m->card_index + 1; 0063 0064 if (m->turn_index == 0) { 0065 if (DOWN(card)) 0066 card = (SUIT(card) << 4) + RANK(card); 0067 else 0068 card += (1 << 7); 0069 W[to][Wlen[to] - m->card_index - 1] = card; 0070 } else if (m->turn_index != -1) { 0071 card_t card2 = *Wp[from]; 0072 if (DOWN(card2)) 0073 card2 = (SUIT(card2) << 4) + RANK(card2); 0074 *Wp[from] = card2; 0075 } 0076 0077 hashpile(from); 0078 /* Add to pile. */ 0079 0080 if (m->totype == O_Type) { 0081 O[to]++; 0082 Q_ASSERT(m->card_index == 0); 0083 } else { 0084 hashpile(to); 0085 } 0086 #if PRINT 0087 print_layout(); 0088 #endif 0089 } 0090 0091 void YukonSolver::undo_move(MOVE *m) 0092 { 0093 #if PRINT 0094 if (m->totype == O_Type) 0095 fprintf(stderr, "\nundo move %d from %d out (at %d)\n\n", m->card_index, m->from, m->turn_index); 0096 else 0097 fprintf(stderr, "\nundo move %d from %d to %d (%d)\n\n", m->card_index, m->from, m->to, m->turn_index); 0098 print_layout(); 0099 0100 #endif 0101 int from, to; 0102 card_t card; 0103 0104 from = m->from; 0105 to = m->to; 0106 0107 /* Add to 'from' pile. */ 0108 if (m->turn_index > 0) { 0109 card_t card2 = *Wp[from]; 0110 if (!DOWN(card2)) 0111 card2 = (SUIT(card2) << 4) + RANK(card2) + (1 << 7); 0112 *Wp[from] = card2; 0113 } 0114 0115 if (m->totype == O_Type) { 0116 card = O[to] + Osuit[to]; 0117 O[to]--; 0118 Wp[from]++; 0119 *Wp[from] = card; 0120 Wlen[from]++; 0121 } else { 0122 for (int l = m->card_index; l >= 0; --l) { 0123 card = W[to][Wlen[to] - l - 1]; 0124 Wp[from]++; 0125 *Wp[from] = card; 0126 Wlen[from]++; 0127 Wp[to]--; 0128 } 0129 Wlen[to] -= m->card_index + 1; 0130 hashpile(to); 0131 } 0132 0133 if (m->turn_index == 0) { 0134 card_t card = *Wp[from]; 0135 if (DOWN(card)) 0136 card = (SUIT(card) << 4) + RANK(card); 0137 else 0138 card += (1 << 7); 0139 *Wp[from] = card; 0140 } 0141 0142 hashpile(from); 0143 #if PRINT 0144 print_layout(); 0145 #endif 0146 } 0147 0148 /* Automove logic. Yukon games must avoid certain types of automoves. */ 0149 0150 int YukonSolver::good_automove(int o, int r) 0151 { 0152 int i; 0153 0154 if (r <= 2) { 0155 return true; 0156 } 0157 0158 /* Check the Out piles of opposite color. */ 0159 0160 for (i = 1 - (o & 1); i < 4; i += 2) { 0161 if (O[i] < r - 1) { 0162 #if 1 /* Raymond's Rule */ 0163 /* Not all the N-1's of opposite color are out 0164 yet. We can still make an automove if either 0165 both N-2's are out or the other same color N-3 0166 is out (Raymond's rule). Note the re-use of 0167 the loop variable i. We return here and never 0168 make it back to the outer loop. */ 0169 0170 for (i = 1 - (o & 1); i < 4; i += 2) { 0171 if (O[i] < r - 2) { 0172 return false; 0173 } 0174 } 0175 if (O[(o + 2) & 3] < r - 3) { 0176 return false; 0177 } 0178 0179 return true; 0180 #else /* Horne's Rule */ 0181 return false; 0182 #endif 0183 } 0184 } 0185 0186 return true; 0187 } 0188 0189 /* Get the possible moves from a position, and store them in Possible[]. */ 0190 0191 int YukonSolver::get_possible_moves(int *a, int *numout) 0192 { 0193 int w, o, empty; 0194 card_t card; 0195 MOVE *mp; 0196 0197 /* Check for moves from W to O. */ 0198 0199 int n = 0; 0200 mp = Possible; 0201 for (w = 0; w < 7; ++w) { 0202 if (Wlen[w] > 0) { 0203 card = *Wp[w]; 0204 o = SUIT(card); 0205 empty = (O[o] == NONE); 0206 if ((empty && (RANK(card) == PS_ACE)) || (!empty && (RANK(card) == O[o] + 1))) { 0207 mp->card_index = 0; 0208 mp->from = w; 0209 mp->to = o; 0210 mp->totype = O_Type; 0211 mp->pri = 3; /* unused */ 0212 mp->turn_index = -1; 0213 if (Wlen[w] > 1 && DOWN(W[w][Wlen[w] - 2])) 0214 mp->turn_index = 1; 0215 n++; 0216 mp++; 0217 0218 /* If it's an automove, just do it. */ 0219 0220 if (good_automove(o, RANK(card))) { 0221 *a = true; 0222 mp[-1].pri = 127; 0223 if (n != 1) { 0224 Possible[0] = mp[-1]; 0225 return 1; 0226 } 0227 return n; 0228 } 0229 } 0230 } 0231 } 0232 0233 /* No more automoves, but remember if there were any moves out. */ 0234 0235 *a = false; 0236 *numout = n; 0237 0238 for (int i = 0; i < 7; ++i) { 0239 int len = Wlen[i]; 0240 for (int l = 0; l < len; ++l) { 0241 card_t card = W[i][Wlen[i] - 1 - l]; 0242 if (DOWN(card)) 0243 break; 0244 0245 for (int j = 0; j < 7; ++j) { 0246 if (i == j) 0247 continue; 0248 0249 int allowed = 0; 0250 0251 if (Wlen[j] > 0 && RANK(card) == RANK(*Wp[j]) - 1 && suitable(card, *Wp[j])) { 0252 allowed = 1; 0253 if (Wlen[i] == l + 1) { 0254 allowed = 2; 0255 } else { 0256 if (DOWN(W[i][Wlen[i] - l - 2])) 0257 allowed = 3; 0258 } 0259 } 0260 if (RANK(card) == PS_KING && Wlen[j] == 0) { 0261 if (l != Wlen[i] - 1 || i == 7) 0262 allowed = 4; 0263 } 0264 // TODO: there is no point in moving if we're not opening anything 0265 // e.g. if both i and j have perfect runs below the cards 0266 #if 0 0267 fprintf( stderr, "%d %d %d\n", i, l, j ); 0268 printcard( card, stderr ); 0269 printcard( *Wp[j], stderr ); 0270 fprintf( stderr, " allowed %d\n",allowed ); 0271 #endif 0272 if (allowed) { 0273 mp->card_index = l; 0274 mp->from = i; 0275 mp->to = j; 0276 mp->totype = W_Type; 0277 mp->turn_index = -1; 0278 if (Wlen[i] > l + 1 && DOWN(W[i][Wlen[i] - l - 2])) 0279 mp->turn_index = 1; 0280 if (mp->turn_index > 0 || Wlen[i] == l + 1) 0281 mp->pri = 30; 0282 else 0283 mp->pri = 1; 0284 n++; 0285 mp++; 0286 } 0287 } 0288 } 0289 } 0290 0291 return n; 0292 } 0293 0294 void YukonSolver::unpack_cluster(unsigned int k) 0295 { 0296 /* Get the Out cells from the cluster number. */ 0297 O[0] = k & 0xF; 0298 k >>= 4; 0299 O[1] = k & 0xF; 0300 k >>= 4; 0301 O[2] = k & 0xF; 0302 k >>= 4; 0303 O[3] = k & 0xF; 0304 } 0305 0306 bool YukonSolver::isWon() 0307 { 0308 // maybe won? 0309 for (int o = 0; o < 4; ++o) { 0310 if (O[o] != PS_KING) { 0311 return false; 0312 } 0313 } 0314 0315 return true; 0316 } 0317 0318 int YukonSolver::getOuts() 0319 { 0320 return O[0] + O[1] + O[2] + O[3]; 0321 } 0322 0323 YukonSolver::YukonSolver(const Yukon *dealer) 0324 : Solver() 0325 { 0326 Osuit[0] = PS_DIAMOND; 0327 Osuit[1] = PS_CLUB; 0328 Osuit[2] = PS_HEART; 0329 Osuit[3] = PS_SPADE; 0330 0331 deal = dealer; 0332 } 0333 0334 void YukonSolver::translate_layout() 0335 { 0336 /* Read the workspace. */ 0337 0338 int total = 0; 0339 for (int w = 0; w < 7; ++w) { 0340 int i = translate_pile(deal->store[w], W[w], 52); 0341 Wp[w] = &W[w][i - 1]; 0342 Wlen[w] = i; 0343 total += i; 0344 } 0345 0346 /* Output piles, if any. */ 0347 for (int i = 0; i < 4; ++i) { 0348 O[i] = NONE; 0349 } 0350 if (total != 52) { 0351 for (int i = 0; i < 4; ++i) { 0352 KCard *c = deal->target[i]->topCard(); 0353 if (c) { 0354 O[translateSuit(c->suit()) >> 4] = c->rank(); 0355 total += c->rank(); 0356 } 0357 } 0358 } 0359 Q_ASSERT(total == 52); 0360 } 0361 0362 unsigned int YukonSolver::getClusterNumber() 0363 { 0364 int i = O[0] + (O[1] << 4); 0365 unsigned int k = i; 0366 i = O[2] + (O[3] << 4); 0367 k |= i << 8; 0368 return k; 0369 } 0370 0371 MoveHint YukonSolver::translateMove(const MOVE &m) 0372 { 0373 PatPile *frompile = nullptr; 0374 frompile = deal->store[m.from]; 0375 0376 KCard *card = frompile->at(frompile->count() - m.card_index - 1); 0377 0378 if (m.totype == O_Type) { 0379 PatPile *target = nullptr; 0380 PatPile *empty = nullptr; 0381 for (int i = 0; i < 4; ++i) { 0382 KCard *c = deal->target[i]->topCard(); 0383 if (c) { 0384 if (c->suit() == card->suit()) { 0385 target = deal->target[i]; 0386 break; 0387 } 0388 } else if (!empty) 0389 empty = deal->target[i]; 0390 } 0391 if (!target) 0392 target = empty; 0393 return MoveHint(card, target, m.pri); 0394 } else { 0395 return MoveHint(card, deal->store[m.to], m.pri); 0396 } 0397 } 0398 0399 void YukonSolver::print_layout() 0400 { 0401 int i, w, o; 0402 0403 fprintf(stderr, "print-layout-begin\n"); 0404 for (w = 0; w < 7; ++w) { 0405 fprintf(stderr, "Play%d: ", w); 0406 for (i = 0; i < Wlen[w]; ++i) { 0407 printcard(W[w][i], stderr); 0408 } 0409 fputc('\n', stderr); 0410 } 0411 fprintf(stderr, "Off: "); 0412 for (o = 0; o < 4; ++o) { 0413 printcard(O[o] + Osuit[o], stderr); 0414 } 0415 fprintf(stderr, "\nprint-layout-end\n"); 0416 }