File indexing completed on 2024-05-12 08:00:31
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"