File indexing completed on 2024-05-12 08:00:34

0001 /*
0002  * Copyright (C) 1995 Paul Olav Tvete <paul@troll.no>
0003  * Copyright (C) 2000-2009 Stephan Kulow <coolo@kde.org>
0004  * Copyright (C) 2010 Parker Coates <coates@kde.org>
0005  *
0006  * License of original code:
0007  * -------------------------------------------------------------------------
0008  *   Permission to use, copy, modify, and distribute this software and its
0009  *   documentation for any purpose and without fee is hereby granted,
0010  *   provided that the above copyright notice appear in all copies and that
0011  *   both that copyright notice and this permission notice appear in
0012  *   supporting documentation.
0013  *
0014  *   This file is provided AS IS with no warranties of any kind.  The author
0015  *   shall have no liability with respect to the infringement of copyrights,
0016  *   trade secrets or any patents by this file or any part thereof.  In no
0017  *   event will the author be liable for any lost revenue or profits or
0018  *   other special, indirect and consequential damages.
0019  * -------------------------------------------------------------------------
0020  *
0021  * License of modifications/additions made after 2009-01-01:
0022  * -------------------------------------------------------------------------
0023  *   This program is free software; you can redistribute it and/or
0024  *   modify it under the terms of the GNU General Public License as
0025  *   published by the Free Software Foundation; either version 2 of
0026  *   the License, or (at your option) any later version.
0027  *
0028  *   This program is distributed in the hope that it will be useful,
0029  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0030  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0031  *   GNU General Public License for more details.
0032  *
0033  *   You should have received a copy of the GNU General Public License
0034  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
0035  * -------------------------------------------------------------------------
0036  */
0037 
0038 #include "klondike.h"
0039 
0040 // own
0041 #include "dealerinfo.h"
0042 #include "patsolve/klondikesolver.h"
0043 #include "pileutils.h"
0044 #include "settings.h"
0045 #include "speeds.h"
0046 // KF
0047 #include <KLocalizedString>
0048 #include <KSelectAction>
0049 
0050 KlondikePile::KlondikePile(DealerScene *scene, int index, const QString &objectName)
0051     : PatPile(scene, index, objectName)
0052     , m_cardsToShow(1)
0053 {
0054 }
0055 
0056 void KlondikePile::setCardsToShow(int draw)
0057 {
0058     m_cardsToShow = draw;
0059 }
0060 
0061 int KlondikePile::cardsToShow() const
0062 {
0063     return m_cardsToShow;
0064 }
0065 
0066 QList<QPointF> KlondikePile::cardPositions() const
0067 {
0068     QList<QPointF> positions;
0069     QPointF currentPos(0, 0);
0070     for (int i = 0; i < count(); ++i) {
0071         positions << currentPos;
0072         if (i >= count() - m_cardsToShow)
0073             currentPos += spread();
0074     }
0075     return positions;
0076 }
0077 
0078 Klondike::Klondike(const DealerInfo *di)
0079     : DealerScene(di)
0080 {
0081 }
0082 
0083 void Klondike::initialize()
0084 {
0085     // The units of the follwoing constants are pixels
0086     const qreal hspacing = 1.0 / 6 + 0.02; // horizontal spacing between card piles
0087     const qreal vspacing = 1.0 / 4; // vertical spacing between card piles
0088 
0089     setDeckContents();
0090 
0091     easyRules = Settings::klondikeIsDrawOne();
0092 
0093     talon = new PatPile(this, 0, QStringLiteral("talon"));
0094     talon->setPileRole(PatPile::Stock);
0095     talon->setLayoutPos(0, 0);
0096     // Give the talon a low Z value to keep it out of the way during there
0097     // deal animation.
0098     talon->setZValue(-52);
0099     talon->setKeyboardSelectHint(KCardPile::NeverFocus);
0100     talon->setKeyboardDropHint(KCardPile::NeverFocus);
0101     connect(talon, &KCardPile::clicked, this, &DealerScene::drawDealRowOrRedeal);
0102 
0103     pile = new KlondikePile(this, 13, QStringLiteral("pile"));
0104     pile->setCardsToShow(easyRules ? 1 : 3);
0105     pile->setPileRole(PatPile::Waste);
0106     pile->setRightPadding(1.1);
0107     pile->setLayoutPos(1 + hspacing, 0);
0108     pile->setSpread(0.33, 0);
0109     pile->setKeyboardSelectHint(KCardPile::ForceFocusTop);
0110     pile->setKeyboardDropHint(KCardPile::NeverFocus);
0111 
0112     for (int i = 0; i < 7; ++i) {
0113         play[i] = new PatPile(this, i + 5, QStringLiteral("play%1").arg(i));
0114         play[i]->setPileRole(PatPile::Tableau);
0115         play[i]->setLayoutPos((1.0 + hspacing) * i, 1.0 + vspacing);
0116         play[i]->setAutoTurnTop(true);
0117         play[i]->setBottomPadding(play[i]->spread().y() * 5);
0118         play[i]->setHeightPolicy(KCardPile::GrowDown);
0119         play[i]->setKeyboardSelectHint(KCardPile::AutoFocusDeepestFaceUp);
0120         play[i]->setKeyboardDropHint(KCardPile::AutoFocusTop);
0121     }
0122 
0123     for (int i = 0; i < 4; ++i) {
0124         target[i] = new PatPile(this, i + 1, QStringLiteral("target%1").arg(i));
0125         target[i]->setPileRole(PatPile::Foundation);
0126         target[i]->setLayoutPos((3 + i) * (1.0 + hspacing), 0);
0127         target[i]->setKeyboardSelectHint(KCardPile::ForceFocusTop);
0128         target[i]->setKeyboardDropHint(KCardPile::ForceFocusTop);
0129     }
0130 
0131     setActions(DealerScene::Hint | DealerScene::Demo | DealerScene::Draw);
0132     setSolver(new KlondikeSolver(this, pile->cardsToShow()));
0133 
0134     options = new KSelectAction(i18n("Klondike &Options"), this);
0135     options->addAction(i18n("Draw 1"));
0136     options->addAction(i18n("Draw 3"));
0137     options->setCurrentItem(easyRules ? 0 : 1);
0138     connect(options, &KSelectAction::indexTriggered, this, &Klondike::gameTypeChanged);
0139 }
0140 
0141 bool Klondike::checkAdd(const PatPile *pile, const QList<KCard *> &oldCards, const QList<KCard *> &newCards) const
0142 {
0143     switch (pile->pileRole()) {
0144     case PatPile::Tableau:
0145         return checkAddAlternateColorDescendingFromKing(oldCards, newCards);
0146     case PatPile::Foundation:
0147         return checkAddSameSuitAscendingFromAce(oldCards, newCards);
0148     case PatPile::Waste:
0149     case PatPile::Stock:
0150     default:
0151         return false;
0152     }
0153 }
0154 
0155 bool Klondike::checkRemove(const PatPile *pile, const QList<KCard *> &cards) const
0156 {
0157     switch (pile->pileRole()) {
0158     case PatPile::Tableau:
0159         return isAlternateColorDescending(cards);
0160     case PatPile::Foundation:
0161         return easyRules && cards.first() == pile->topCard();
0162     case PatPile::Waste:
0163         return cards.first() == pile->topCard();
0164     case PatPile::Stock:
0165     default:
0166         return false;
0167     }
0168 }
0169 
0170 void Klondike::cardsMoved(const QList<KCard *> &cards, KCardPile *oldPile, KCardPile *newPile)
0171 {
0172     DealerScene::cardsMoved(cards, oldPile, newPile);
0173 
0174     Q_EMIT newCardsPossible(!talon->isEmpty() || pile->count() > 1);
0175 }
0176 
0177 QList<QAction *> Klondike::configActions() const
0178 {
0179     return QList<QAction *>() << options;
0180 }
0181 
0182 bool Klondike::newCards()
0183 {
0184     if (talon->isEmpty() && pile->count() <= 1)
0185         return false;
0186 
0187     if (talon->isEmpty()) {
0188         // Move the cards from the pile back to the deck
0189         flipCardsToPile(pile->cards(), talon, DURATION_MOVE);
0190 
0191     } else {
0192         QList<KCard *> cards = talon->topCards(pile->cardsToShow());
0193         flipCardsToPile(cards, pile, DURATION_MOVE);
0194         setKeyboardFocus(pile->topCard());
0195     }
0196 
0197     if (talon->isEmpty() && pile->count() <= 1)
0198         Q_EMIT newCardsPossible(false);
0199 
0200     // we need to look that many steps in the future to see if we can lose
0201     setNeededFutureMoves(talon->count() + pile->count());
0202 
0203     return true;
0204 }
0205 
0206 void Klondike::restart(const QList<KCard *> &cards)
0207 {
0208     QList<KCard *> cardList = cards;
0209 
0210     for (int round = 0; round < 7; ++round)
0211         for (int i = round; i < 7; ++i)
0212             addCardForDeal(play[i], cardList.takeLast(), (i == round), talon->pos());
0213 
0214     while (!cardList.isEmpty()) {
0215         KCard *c = cardList.takeFirst();
0216         c->setPos(talon->pos());
0217         c->setFaceUp(false);
0218         talon->add(c);
0219     }
0220 
0221     startDealAnimation();
0222 }
0223 
0224 void Klondike::gameTypeChanged()
0225 {
0226     stopDemo();
0227 
0228     if (allowedToStartNewGame()) {
0229         setEasy(options->currentItem() == 0);
0230         startNew(gameNumber());
0231     } else {
0232         // If we're not allowed, reset the option to
0233         // the current number of suits.
0234         options->setCurrentItem(easyRules ? 0 : 1);
0235     }
0236 }
0237 
0238 void Klondike::setGameState(const QString &state)
0239 {
0240     Q_UNUSED(state);
0241 
0242     Q_EMIT newCardsPossible(!talon->isEmpty() || pile->count() > 1);
0243 }
0244 
0245 QString Klondike::getGameOptions() const
0246 {
0247     return QString::number(pile->cardsToShow());
0248 }
0249 
0250 void Klondike::setGameOptions(const QString &options)
0251 {
0252     setEasy(options.toInt() == 1);
0253 }
0254 
0255 void Klondike::setEasy(bool _EasyRules)
0256 {
0257     if (_EasyRules != easyRules) {
0258         easyRules = _EasyRules;
0259         options->setCurrentItem(easyRules ? 0 : 1);
0260 
0261         int drawNumber = easyRules ? 1 : 3;
0262         pile->setCardsToShow(drawNumber);
0263 
0264         KCardPile::KeyboardFocusHint hint = easyRules ? KCardPile::ForceFocusTop : KCardPile::NeverFocus;
0265         for (int i = 0; i < 4; ++i)
0266             target[i]->setKeyboardSelectHint(hint);
0267 
0268         setSolver(new KlondikeSolver(this, drawNumber));
0269 
0270         Settings::setKlondikeIsDrawOne(easyRules);
0271     }
0272 }
0273 
0274 bool Klondike::drop()
0275 {
0276     bool pileempty = pile->isEmpty();
0277     if (!DealerScene::drop())
0278         return false;
0279     if (pile->isEmpty() && !pileempty)
0280         newCards();
0281     return true;
0282 }
0283 
0284 void Klondike::mapOldId(int id)
0285 {
0286     switch (id) {
0287     case DealerInfo::KlondikeDrawOneId:
0288         setEasy(true);
0289         break;
0290     case DealerInfo::KlondikeDrawThreeId:
0291         setEasy(false);
0292         break;
0293     case DealerInfo::KlondikeGeneralId:
0294     default:
0295         // Do nothing.
0296         break;
0297     }
0298 }
0299 
0300 int Klondike::oldId() const
0301 {
0302     if (easyRules)
0303         return DealerInfo::KlondikeDrawOneId;
0304     else
0305         return DealerInfo::KlondikeDrawThreeId;
0306 }
0307 
0308 static class KlondikeDealerInfo : public DealerInfo
0309 {
0310 public:
0311     KlondikeDealerInfo()
0312         : DealerInfo(kli18n("Klondike"), DealerInfo::KlondikeGeneralId)
0313     {
0314         addSubtype(KlondikeDrawOneId, kli18n("Klondike (Draw 1)"));
0315         addSubtype(KlondikeDrawThreeId, kli18n("Klondike (Draw 3)"));
0316     }
0317 
0318     DealerScene *createGame() const override
0319     {
0320         return new Klondike(this);
0321     }
0322 } klondikeDealerInfo;
0323 
0324 #include "moc_klondike.cpp"