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 }