File indexing completed on 2024-05-12 04:04:47

0001 /*
0002  *   Copyright 2021 Michael Lang <criticaltemp@protonmail.com>
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 "castle.h"
0019 
0020 // own
0021 #include "dealerinfo.h"
0022 #include "kpat_debug.h"
0023 #include "patsolve/castlesolver.h"
0024 #include "pileutils.h"
0025 #include "settings.h"
0026 #include "speeds.h"
0027 // KF
0028 #include <KLocalizedString>
0029 #include <KSelectAction>
0030 
0031 Castle::Castle(const DealerInfo *di)
0032     : DealerScene(di)
0033 {
0034     configOptions();
0035     getSavedOptions();
0036 }
0037 
0038 void Castle::initialize()
0039 {
0040     setDeckContents(m_decks);
0041 
0042     const qreal topRowDist = 1.125;
0043     const qreal bottomRowDist = 1.125;
0044     const qreal offsetY = m_reserves > 0 ? 1.3 : 0;
0045 
0046     for (int i = 0; i < m_reserves; ++i) {
0047         freecell[i] = new PatPile(this, 1 + i, QStringLiteral("freecell%1").arg(i));
0048         freecell[i]->setPileRole(PatPile::Cell);
0049         freecell[i]->setLayoutPos(3.25 - topRowDist * (m_reserves - 1) / 2 + topRowDist * i, 0);
0050         freecell[i]->setKeyboardSelectHint(KCardPile::AutoFocusTop);
0051         freecell[i]->setKeyboardDropHint(KCardPile::AutoFocusTop);
0052     }
0053 
0054     for (int i = 0; i < m_stacks; ++i) {
0055         store[i] = new PatPile(this, 1 + i, QStringLiteral("store%1").arg(i));
0056         store[i]->setPileRole(PatPile::Tableau);
0057         store[i]->setAutoTurnTop(true);
0058         if (m_layout == 1) {
0059             store[i]->setLayoutPos(4.5 * (i % 2) + 0, offsetY + bottomRowDist * static_cast<int>((i + 0) / 2));
0060         } else {
0061             store[i]->setLayoutPos(2.5 * (i % 2) + 2, offsetY + bottomRowDist * static_cast<int>((i + 0) / 2));
0062         }
0063 
0064         if (i % 2 || m_layout == 1) {
0065             store[i]->setSpread(0.21, 0);
0066             store[i]->setRightPadding(2);
0067             store[i]->setWidthPolicy(KCardPile::GrowRight);
0068         } else {
0069             store[i]->setSpread(-0.21, 0);
0070             store[i]->setLeftPadding(3);
0071             store[i]->setWidthPolicy(KCardPile::GrowLeft);
0072         }
0073 
0074         store[i]->setKeyboardSelectHint(KCardPile::AutoFocusDeepestRemovable);
0075         store[i]->setKeyboardDropHint(KCardPile::AutoFocusTop);
0076     }
0077 
0078     for (int i = 0; i < 4 * m_decks; ++i) {
0079         target[i] = new PatPile(this, 1 + i, QStringLiteral("target%1").arg(i));
0080         target[i]->setPileRole(PatPile::Foundation);
0081         target[i]->setLayoutPos(3.25, offsetY + bottomRowDist * i);
0082         target[i]->setKeyboardSelectHint(KCardPile::NeverFocus);
0083         target[i]->setKeyboardDropHint(KCardPile::ForceFocusTop);
0084     }
0085 
0086     setActions(DealerScene::Demo | DealerScene::Hint);
0087     auto solver = new CastleSolver(this);
0088     solver->default_max_positions = Settings::castleSolverIterationsLimit();
0089     setSolver(solver);
0090     setNeededFutureMoves(4); // reserve some
0091 }
0092 
0093 QList<QAction *> Castle::configActions() const
0094 {
0095     return QList<QAction *>() << options << m_emptyStackFillOption << m_sequenceBuiltByOption << m_reservesOption << m_stacksOption << m_stackFaceupOption
0096                               << m_foundationOption << m_layoutOption;
0097 }
0098 
0099 void Castle::gameTypeChanged()
0100 {
0101     stopDemo();
0102 
0103     if (allowedToStartNewGame()) {
0104         if (m_variation != options->currentItem()) {
0105             setOptions(options->currentItem());
0106         } else {
0107             // update option selections
0108             if (m_emptyStackFill != m_emptyStackFillOption->currentItem())
0109                 m_emptyStackFill = m_emptyStackFillOption->currentItem();
0110             else if (m_sequenceBuiltBy != m_sequenceBuiltByOption->currentItem())
0111                 m_sequenceBuiltBy = m_sequenceBuiltByOption->currentItem();
0112             else if (m_reserves != m_reservesOption->currentItem())
0113                 m_reserves = m_reservesOption->currentItem();
0114             else if (m_stacks - 6 != m_stacksOption->currentItem())
0115                 m_stacks = m_stacksOption->currentItem() + 6;
0116             else if (m_stackFaceup != m_stackFaceupOption->currentItem())
0117                 m_stackFaceup = m_stackFaceupOption->currentItem();
0118             else if (m_foundation != m_foundationOption->currentItem())
0119                 m_foundation = m_foundationOption->currentItem();
0120             else if (m_layout != m_layoutOption->currentItem())
0121                 m_layout = m_layoutOption->currentItem();
0122 
0123             matchVariant();
0124         }
0125 
0126         // remove existing piles
0127         clearPatPiles();
0128 
0129         initialize();
0130         relayoutScene();
0131         startNew(gameNumber());
0132         setSavedOptions();
0133     } else {
0134         // If we're not allowed, reset the options
0135         getSavedOptions();
0136     }
0137 }
0138 
0139 void Castle::restart(const QList<KCard *> &cards)
0140 {
0141     QList<KCard *> cardList = cards;
0142 
0143     int column = 0;
0144     int fdx = 0;
0145 
0146     // Prefill aces to foundation for select game types
0147     if (m_foundation >= 1) {
0148         for (int i = 0; i < cardList.size(); ++i) {
0149             if (cardList.at(i)->rank() == KCardDeck::Ace) {
0150                 addCardForDeal(target[fdx], cardList.takeAt(i), true, target[fdx]->pos());
0151                 --i;
0152                 fdx++;
0153             }
0154         }
0155     }
0156 
0157     bool alternate = false;
0158     while (!cardList.isEmpty()) {
0159         // Add matching cards to foundation during deal for select game types
0160         if (m_foundation == 2 && fdx > 0) {
0161             KCard *card = cardList.last();
0162             int i = 0;
0163             for (i = 0; i < fdx; ++i) {
0164                 if (card->rank() == target[i]->topCard()->rank() + 1 && card->suit() == target[i]->topCard()->suit()) {
0165                     addCardForDeal(target[i], cardList.takeLast(), true, target[i]->pos());
0166                     column = (column + 1) % m_stacks;
0167                     break;
0168                 }
0169             }
0170 
0171             // Skip if card already added
0172             if (i < fdx)
0173                 continue;
0174         }
0175 
0176         bool faceUp = m_stackFaceup == 1 || cardList.length() <= m_stacks || (m_stackFaceup == 2 && alternate);
0177         addCardForDeal(store[column], cardList.takeLast(), faceUp, store[0]->pos());
0178         column = (column + 1) % m_stacks;
0179         if (column % m_stacks == 0)
0180             alternate = !alternate;
0181     }
0182 
0183     startDealAnimation();
0184 }
0185 
0186 QString Castle::solverFormat() const
0187 {
0188     QString output;
0189     QString tmp;
0190     for (int i = 0; i < 4 * m_decks; i++) {
0191         if (target[i]->isEmpty())
0192             continue;
0193         tmp += suitToString(target[i]->topCard()->suit()) + QLatin1Char('-') + rankToString(target[i]->topCard()->rank()) + QLatin1Char(' ');
0194     }
0195     if (!tmp.isEmpty())
0196         output += QStringLiteral("Foundations: %1\n").arg(tmp);
0197 
0198     tmp.truncate(0);
0199     for (int i = 0; i < m_reserves; i++) {
0200         const auto fc = freecell[i];
0201         tmp += (fc->isEmpty() ? QStringLiteral("-") : cardToRankSuitString(fc->topCard())) + QLatin1Char(' ');
0202     }
0203     if (!tmp.isEmpty()) {
0204         QString a = QStringLiteral("Freecells: %1\n");
0205         output += a.arg(tmp);
0206     }
0207 
0208     for (int i = 0; i < m_stacks; i++)
0209         cardsListToLine(output, store[i]->cards());
0210     return output;
0211 }
0212 
0213 void Castle::cardsDroppedOnPile(const QList<KCard *> &cards, KCardPile *pile)
0214 {
0215     if (cards.size() <= 1) {
0216         DealerScene::moveCardsToPile(cards, pile, DURATION_MOVE);
0217         return;
0218     }
0219 
0220     QList<KCardPile *> freeCells;
0221     for (int i = 0; i < m_reserves; ++i)
0222         if (freecell[i]->isEmpty())
0223             freeCells << freecell[i];
0224 
0225     QList<KCardPile *> freeStores;
0226     for (int i = 0; i < m_stacks; ++i)
0227         if (store[i]->isEmpty() && store[i] != pile)
0228             freeStores << store[i];
0229 
0230     multiStepMove(cards, pile, freeStores, freeCells, DURATION_MOVE);
0231 }
0232 
0233 bool Castle::tryAutomaticMove(KCard *c)
0234 {
0235     // target move
0236     if (DealerScene::tryAutomaticMove(c))
0237         return true;
0238 
0239     if (c->isAnimated())
0240         return false;
0241 
0242     if (allowedToRemove(c->pile(), c) && c == c->pile()->topCard()) {
0243         for (int i = 0; i < m_reserves; i++) {
0244             if (allowedToAdd(freecell[i], {c})) {
0245                 moveCardToPile(c, freecell[i], DURATION_MOVE);
0246                 return true;
0247             }
0248         }
0249     }
0250     return false;
0251 }
0252 
0253 bool Castle::canPutStore(const KCardPile *pile, const QList<KCard *> &cards) const
0254 {
0255     int freeCells = 0;
0256     for (int i = 0; i < m_reserves; ++i)
0257         if (freecell[i]->isEmpty())
0258             ++freeCells;
0259 
0260     int freeStores = 0;
0261     if (m_emptyStackFill == 0) {
0262         for (int i = 0; i < m_stacks; ++i)
0263             if (store[i]->isEmpty() && store[i] != pile)
0264                 ++freeStores;
0265     }
0266 
0267     if (cards.size() <= (freeCells + 1) << freeStores) {
0268         if (pile->isEmpty())
0269             return m_emptyStackFill == 0 || (m_emptyStackFill == 1 && cards.first()->rank() == KCardDeck::King);
0270         else if (m_sequenceBuiltBy == 1)
0271             return cards.first()->rank() == pile->topCard()->rank() - 1 && cards.first()->suit() == pile->topCard()->suit();
0272         else if (m_sequenceBuiltBy == 0)
0273             return cards.first()->rank() == pile->topCard()->rank() - 1 && pile->topCard()->color() != cards.first()->color();
0274         else
0275             return cards.first()->rank() == pile->topCard()->rank() - 1;
0276     } else {
0277         return false;
0278     }
0279 }
0280 
0281 bool Castle::checkAdd(const PatPile *pile, const QList<KCard *> &oldCards, const QList<KCard *> &newCards) const
0282 {
0283     switch (pile->pileRole()) {
0284     case PatPile::Tableau:
0285         return canPutStore(pile, newCards);
0286     case PatPile::Cell:
0287         return oldCards.isEmpty() && newCards.size() == 1;
0288     case PatPile::Foundation:
0289         return checkAddSameSuitAscendingFromAce(oldCards, newCards);
0290     default:
0291         return false;
0292     }
0293 }
0294 
0295 bool Castle::checkRemove(const PatPile *pile, const QList<KCard *> &cards) const
0296 {
0297     switch (pile->pileRole()) {
0298     case PatPile::Tableau:
0299         if (m_sequenceBuiltBy == 1)
0300             return isSameSuitDescending(cards);
0301         else if (m_sequenceBuiltBy == 0)
0302             return isAlternateColorDescending(cards);
0303         else
0304             return isRankDescending(cards);
0305     case PatPile::Cell:
0306         return cards.first() == pile->topCard();
0307     case PatPile::Foundation:
0308         return true;
0309     default:
0310         return false;
0311     }
0312 }
0313 
0314 static class CastleDealerInfo : public DealerInfo
0315 {
0316 public:
0317     CastleDealerInfo()
0318         : DealerInfo(kli18n("Castle"), CastleGeneralId)
0319     {
0320         addSubtype(CastleBeleagueredId, kli18n("Beleaguered Castle"));
0321         addSubtype(CastleCitadelId, kli18n("Citadel"));
0322         addSubtype(CastleExiledKingsId, kli18n("Exiled Kings"));
0323         addSubtype(CastleStreetAlleyId, kli18n("Streets and Alleys"));
0324         addSubtype(CastleSiegecraftId, kli18n("Siegecraft"));
0325         addSubtype(CastleStrongholdId, kli18n("Stronghold"));
0326         addSubtype(CastleCustomId, kli18n("Castle (Custom)"));
0327     }
0328 
0329     DealerScene *createGame() const override
0330     {
0331         return new Castle(this);
0332     }
0333 } castleDealerInfo;
0334 
0335 void Castle::matchVariant()
0336 {
0337     if (m_emptyStackFill == 0 && m_sequenceBuiltBy == 2 && m_reserves == 0 && m_stacks == 8 && m_foundation == 1)
0338         m_variation = 0;
0339     else if (m_emptyStackFill == 0 && m_sequenceBuiltBy == 2 && m_reserves == 0 && m_stacks == 8 && m_foundation == 2)
0340         m_variation = 1;
0341     else if (m_emptyStackFill == 1 && m_sequenceBuiltBy == 2 && m_reserves == 0 && m_stacks == 8 && m_foundation == 2)
0342         m_variation = 2;
0343     else if (m_emptyStackFill == 0 && m_sequenceBuiltBy == 2 && m_reserves == 0 && m_stacks == 8 && m_foundation == 0)
0344         m_variation = 3;
0345     else if (m_emptyStackFill == 0 && m_sequenceBuiltBy == 2 && m_reserves == 1 && m_stacks == 8 && m_foundation == 1)
0346         m_variation = 4;
0347     else if (m_emptyStackFill == 0 && m_sequenceBuiltBy == 2 && m_reserves == 1 && m_stacks == 8 && m_foundation == 0)
0348         m_variation = 5;
0349     else
0350         m_variation = 6;
0351 
0352     options->setCurrentItem(m_variation);
0353 }
0354 
0355 void Castle::configOptions()
0356 {
0357     options = new KSelectAction(i18n("Popular Variant Presets"), this);
0358     options->addAction(i18n("Beleaguered Castle"));
0359     options->addAction(i18n("Citadel"));
0360     options->addAction(i18n("Exiled Kings"));
0361     options->addAction(i18n("Streets and Alleys"));
0362     options->addAction(i18n("Siegecraft"));
0363     options->addAction(i18n("Stronghold"));
0364     options->addAction(i18n("Custom"));
0365 
0366     m_emptyStackFillOption = new KSelectAction(i18n("Empty Stack Fill"), this);
0367     m_emptyStackFillOption->addAction(i18n("Any (Easy)"));
0368     m_emptyStackFillOption->addAction(i18n("Kings only (Medium)"));
0369     m_emptyStackFillOption->addAction(i18n("None (Hard)"));
0370 
0371     m_sequenceBuiltByOption = new KSelectAction(i18n("Build Sequence"), this);
0372     m_sequenceBuiltByOption->addAction(i18n("Alternating Color"));
0373     m_sequenceBuiltByOption->addAction(i18n("Matching Suit"));
0374     m_sequenceBuiltByOption->addAction(i18n("Rank"));
0375 
0376     m_reservesOption = new KSelectAction(i18n("Free Cells"), this);
0377     m_reservesOption->addAction(i18n("0"));
0378     m_reservesOption->addAction(i18n("1"));
0379     m_reservesOption->addAction(i18n("2"));
0380     m_reservesOption->addAction(i18n("3"));
0381     m_reservesOption->addAction(i18n("4"));
0382 
0383     m_stacksOption = new KSelectAction(i18n("Stacks"), this);
0384     m_stacksOption->addAction(i18n("6"));
0385     m_stacksOption->addAction(i18n("7"));
0386     m_stacksOption->addAction(i18n("8"));
0387     m_stacksOption->addAction(i18n("9"));
0388     m_stacksOption->addAction(i18n("10"));
0389 
0390     m_stackFaceupOption = new KSelectAction(i18n("S&tack Options"), this);
0391     m_stackFaceupOption->addAction(i18n("Face &Down"));
0392     m_stackFaceupOption->addAction(i18n("Face &Up"));
0393     m_stackFaceupOption->addAction(i18n("Alternating Face &Up"));
0394 
0395     m_foundationOption = new KSelectAction(i18n("Foundation Deal"), this);
0396     m_foundationOption->addAction(i18n("None"));
0397     m_foundationOption->addAction(i18n("Aces"));
0398     m_foundationOption->addAction(i18n("Any"));
0399 
0400     m_layoutOption = new KSelectAction(i18n("Layout"), this);
0401     m_layoutOption->addAction(i18n("Classic"));
0402     m_layoutOption->addAction(i18n("Modern"));
0403 
0404     connect(options, &KSelectAction::indexTriggered, this, &Castle::gameTypeChanged);
0405     connect(m_emptyStackFillOption, &KSelectAction::indexTriggered, this, &Castle::gameTypeChanged);
0406     connect(m_reservesOption, &KSelectAction::indexTriggered, this, &Castle::gameTypeChanged);
0407     connect(m_sequenceBuiltByOption, &KSelectAction::indexTriggered, this, &Castle::gameTypeChanged);
0408     connect(m_stacksOption, &KSelectAction::indexTriggered, this, &Castle::gameTypeChanged);
0409     connect(m_stackFaceupOption, &KSelectAction::indexTriggered, this, &Castle::gameTypeChanged);
0410     connect(m_foundationOption, &KSelectAction::indexTriggered, this, &Castle::gameTypeChanged);
0411     connect(m_layoutOption, &KSelectAction::indexTriggered, this, &Castle::gameTypeChanged);
0412 }
0413 
0414 void Castle::setSavedOptions()
0415 {
0416     Settings::setCastleEmptyStackFill(m_emptyStackFill);
0417     Settings::setCastleSequenceBuiltBy(m_sequenceBuiltBy);
0418     Settings::setCastleReserves(m_reserves);
0419     Settings::setCastleStacks(m_stacks);
0420     Settings::setCastleStackFaceup(m_stackFaceup);
0421     Settings::setCastleFoundation(m_foundation);
0422     Settings::setCastleLayout(m_layout);
0423 }
0424 
0425 void Castle::getSavedOptions()
0426 {
0427     m_emptyStackFill = Settings::castleEmptyStackFill();
0428     m_sequenceBuiltBy = Settings::castleSequenceBuiltBy();
0429     m_reserves = Settings::castleReserves();
0430     m_stacks = Settings::castleStacks();
0431     m_stackFaceup = Settings::castleStackFaceup();
0432     m_foundation = Settings::castleFoundation();
0433     m_layout = Settings::castleLayout();
0434     m_decks = 1;
0435 
0436     if (m_stacks < 6)
0437         m_stacks = 6;
0438 
0439     matchVariant();
0440 
0441     m_emptyStackFillOption->setCurrentItem(m_emptyStackFill);
0442     m_sequenceBuiltByOption->setCurrentItem(m_sequenceBuiltBy);
0443     m_reservesOption->setCurrentItem(m_reserves);
0444     m_stacksOption->setCurrentItem(m_stacks - 6);
0445     m_stackFaceupOption->setCurrentItem(m_stackFaceup);
0446     m_foundationOption->setCurrentItem(m_foundation);
0447     m_layoutOption->setCurrentItem(m_layout);
0448 }
0449 
0450 void Castle::mapOldId(int id)
0451 {
0452     switch (id) {
0453     case DealerInfo::CastleBeleagueredId:
0454         setOptions(0);
0455         break;
0456     case DealerInfo::CastleCitadelId:
0457         setOptions(1);
0458         break;
0459     case DealerInfo::CastleExiledKingsId:
0460         setOptions(2);
0461         break;
0462     case DealerInfo::CastleStreetAlleyId:
0463         setOptions(3);
0464         break;
0465     case DealerInfo::CastleSiegecraftId:
0466         setOptions(4);
0467         break;
0468     case DealerInfo::CastleStrongholdId:
0469         setOptions(5);
0470         break;
0471     case DealerInfo::CastleCustomId:
0472         setOptions(6);
0473         break;
0474     default:
0475         // Do nothing.
0476         break;
0477     }
0478 }
0479 
0480 int Castle::oldId() const
0481 {
0482     switch (m_variation) {
0483     case 0:
0484         return DealerInfo::CastleBeleagueredId;
0485     case 1:
0486         return DealerInfo::CastleCitadelId;
0487     case 2:
0488         return DealerInfo::CastleExiledKingsId;
0489     case 3:
0490         return DealerInfo::CastleStreetAlleyId;
0491     case 4:
0492         return DealerInfo::CastleSiegecraftId;
0493     case 5:
0494         return DealerInfo::CastleStrongholdId;
0495     default:
0496         return DealerInfo::CastleCustomId;
0497     }
0498 }
0499 
0500 void Castle::setOptions(int variation)
0501 {
0502     if (variation != m_variation) {
0503         m_variation = variation;
0504         m_emptyStackFill = 0;
0505         m_sequenceBuiltBy = 2;
0506         m_reserves = 0;
0507         m_stacks = 8;
0508         m_stackFaceup = 1;
0509         m_decks = 1;
0510         m_foundation = 1;
0511 
0512         switch (m_variation) {
0513         case 0:
0514             break;
0515         case 1:
0516             m_foundation = 2;
0517             break;
0518         case 2:
0519             m_foundation = 2;
0520             m_emptyStackFill = 1;
0521             break;
0522         case 3:
0523             m_foundation = 0;
0524             break;
0525         case 4:
0526             m_reserves = 1;
0527             break;
0528         case 5:
0529             m_foundation = 0;
0530             m_reserves = 1;
0531             break;
0532         case 6:
0533             m_sequenceBuiltBy = 0;
0534             break;
0535         }
0536 
0537         m_emptyStackFillOption->setCurrentItem(m_emptyStackFill);
0538         m_sequenceBuiltByOption->setCurrentItem(m_sequenceBuiltBy);
0539         m_reservesOption->setCurrentItem(m_reserves);
0540         m_stacksOption->setCurrentItem(m_stacks - 6);
0541         m_stackFaceupOption->setCurrentItem(m_stackFaceup);
0542         m_foundationOption->setCurrentItem(m_foundation);
0543     }
0544 }
0545 
0546 #include "moc_castle.cpp"