1/*
2 * Copyright (C) 1995 Paul Olav Tvete <paul@troll.no>
3 * Copyright (C) 2000-2009 Stephan Kulow <coolo@kde.org>
4 * Copyright (C) 2010 Parker Coates <coates@kde.org>
5 *
6 * License of original code:
7 * -------------------------------------------------------------------------
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted,
10 * provided that the above copyright notice appear in all copies and that
11 * both that copyright notice and this permission notice appear in
12 * supporting documentation.
13 *
14 * This file is provided AS IS with no warranties of any kind. The author
15 * shall have no liability with respect to the infringement of copyrights,
16 * trade secrets or any patents by this file or any part thereof. In no
17 * event will the author be liable for any lost revenue or profits or
18 * other special, indirect and consequential damages.
19 * -------------------------------------------------------------------------
20 *
21 * License of modifications/additions made after 2009-01-01:
22 * -------------------------------------------------------------------------
23 * This program is free software; you can redistribute it and/or
24 * modify it under the terms of the GNU General Public License as
25 * published by the Free Software Foundation; either version 2 of
26 * the License, or (at your option) any later version.
27 *
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program. If not, see <http://www.gnu.org/licenses/>.
35 * -------------------------------------------------------------------------
36 */
37
38#include "klondike.h"
39
40#include "dealerinfo.h"
41#include "pileutils.h"
42#include "settings.h"
43#include "speeds.h"
44#include "patsolve/klondikesolver.h"
45
46#include <KLocale>
47#include <KSelectAction>
48
49
50KlondikePile::KlondikePile( DealerScene * scene, int index, const QString & objectName )
51 : PatPile( scene, index, objectName ),
52 m_cardsToShow( 1 )
53{
54}
55
56
57void KlondikePile::setCardsToShow( int draw )
58{
59 m_cardsToShow = draw;
60}
61
62
63int KlondikePile::cardsToShow() const
64{
65 return m_cardsToShow;
66}
67
68
69QList<QPointF> KlondikePile::cardPositions() const
70{
71 QList<QPointF> positions;
72 QPointF currentPos( 0, 0 );
73 for ( int i = 0; i < count(); ++i )
74 {
75 positions << currentPos;
76 if ( i >= count() - m_cardsToShow )
77 currentPos += spread();
78 }
79 return positions;
80}
81
82
83Klondike::Klondike( const DealerInfo * di )
84 : DealerScene( di )
85{
86}
87
88
89void Klondike::initialize()
90{
91 // The units of the follwoing constants are pixels
92 const qreal hspacing = 1.0 / 6 + 0.02; // horizontal spacing between card piles
93 const qreal vspacing = 1.0 / 4; // vertical spacing between card piles
94
95 setDeckContents();
96
97 easyRules = Settings::klondikeIsDrawOne();
98
99 talon = new PatPile( this, 0, "talon" );
100 talon->setPileRole(PatPile::Stock);
101 talon->setLayoutPos(0, 0);
102 // Give the talon a low Z value to keep it out of the way during there
103 // deal animation.
104 talon->setZValue( -52 );
105 talon->setKeyboardSelectHint( KCardPile::NeverFocus );
106 talon->setKeyboardDropHint( KCardPile::NeverFocus );
107 connect( talon, SIGNAL(clicked(KCard*)), SLOT(drawDealRowOrRedeal()) );
108
109 pile = new KlondikePile( this, 13, "pile" );
110 pile->setCardsToShow( easyRules ? 1 : 3 );
111 pile->setPileRole( PatPile::Waste );
112 pile->setRightPadding( 1.1 );
113 pile->setLayoutPos( 1 + hspacing, 0 );
114 pile->setSpread( 0.33, 0 );
115 pile->setKeyboardSelectHint( KCardPile::ForceFocusTop );
116 pile->setKeyboardDropHint( KCardPile::NeverFocus );
117
118 for( int i = 0; i < 7; ++i )
119 {
120 play[i] = new PatPile( this, i + 5, QString( "play%1" ).arg( i ));
121 play[i]->setPileRole(PatPile::Tableau);
122 play[i]->setLayoutPos((1.0 + hspacing) * i, 1.0 + vspacing);
123 play[i]->setAutoTurnTop(true);
124 play[i]->setBottomPadding( play[i]->spread().y() * 7 );
125 play[i]->setHeightPolicy( KCardPile::GrowDown );
126 play[i]->setKeyboardSelectHint( KCardPile::AutoFocusDeepestFaceUp );
127 play[i]->setKeyboardDropHint( KCardPile::AutoFocusTop );
128 }
129
130 for( int i = 0; i < 4; ++i )
131 {
132 target[i] = new PatPile( this, i + 1, QString( "target%1" ).arg( i ) );
133 target[i]->setPileRole(PatPile::Foundation);
134 target[i]->setLayoutPos((3 + i) * (1.0 + hspacing), 0);
135 target[i]->setKeyboardSelectHint( KCardPile::ForceFocusTop );
136 target[i]->setKeyboardDropHint( KCardPile::ForceFocusTop );
137 }
138
139 setActions(DealerScene::Hint | DealerScene::Demo | DealerScene::Draw);
140 setSolver( new KlondikeSolver( this, pile->cardsToShow() ) );
141 redealt = false;
142
143 options = new KSelectAction(i18n("Klondike &Options"), this );
144 options->addAction( i18n("Draw 1" ));
145 options->addAction( i18n("Draw 3" ));
146 options->setCurrentItem( easyRules ? 0 : 1 );
147 connect( options, SIGNAL(triggered(int)), SLOT(gameTypeChanged()) );
148}
149
150bool Klondike::checkAdd(const PatPile * pile, const QList<KCard*> & oldCards, const QList<KCard*> & newCards) const
151{
152 switch (pile->pileRole())
153 {
154 case PatPile::Tableau:
155 return checkAddAlternateColorDescendingFromKing(oldCards, newCards);
156 case PatPile::Foundation:
157 return checkAddSameSuitAscendingFromAce(oldCards, newCards);
158 case PatPile::Waste:
159 case PatPile::Stock:
160 default:
161 return false;
162 }
163}
164
165bool Klondike::checkRemove(const PatPile * pile, const QList<KCard*> & cards) const
166{
167 switch (pile->pileRole())
168 {
169 case PatPile::Tableau:
170 return isAlternateColorDescending(cards);
171 case PatPile::Foundation:
172 return easyRules && cards.first() == pile->topCard();
173 case PatPile::Waste:
174 return cards.first() == pile->topCard();
175 case PatPile::Stock:
176 default:
177 return false;
178 }
179}
180
181
182void Klondike::cardsMoved( const QList<KCard*> & cards, KCardPile * oldPile, KCardPile * newPile )
183{
184 DealerScene::cardsMoved( cards, oldPile, newPile );
185
186 emit newCardsPossible( !talon->isEmpty() || pile->count() > 1 );
187}
188
189
190QList<QAction*> Klondike::configActions() const
191{
192 return QList<QAction*>() << options;
193}
194
195
196bool Klondike::newCards()
197{
198 if ( talon->isEmpty() && pile->count() <= 1 )
199 return false;
200
201 if ( talon->isEmpty() )
202 {
203 // Move the cards from the pile back to the deck
204 flipCardsToPile( pile->cards(), talon, DURATION_MOVE );
205
206 redealt = true;
207 }
208 else
209 {
210 QList<KCard*> cards = talon->topCards( pile->cardsToShow() );
211 flipCardsToPile( cards, pile, DURATION_MOVE );
212 setKeyboardFocus( pile->topCard() );
213 }
214
215 if ( talon->isEmpty() && pile->count() <= 1 )
216 emit newCardsPossible( false );
217
218 // we need to look that many steps in the future to see if we can lose
219 setNeededFutureMoves( talon->count() + pile->count() );
220
221 return true;
222}
223
224
225void Klondike::restart( const QList<KCard*> & cards )
226{
227 redealt = false;
228
229 QList<KCard*> cardList = cards;
230
231 for( int round = 0; round < 7; ++round )
232 for ( int i = round; i < 7; ++i )
233 addCardForDeal( play[i], cardList.takeLast(), (i == round), talon->pos());
234
235 while ( !cardList.isEmpty() )
236 {
237 KCard * c = cardList.takeFirst();
238 c->setPos( talon->pos() );
239 c->setFaceUp( false );
240 talon->add( c );
241 }
242
243 startDealAnimation();
244}
245
246void Klondike::gameTypeChanged()
247{
248 stopDemo();
249
250 if ( allowedToStartNewGame() )
251 {
252 setEasy( options->currentItem() == 0 );
253 startNew( gameNumber() );
254 }
255 else
256 {
257 // If we're not allowed, reset the option to
258 // the current number of suits.
259 options->setCurrentItem( easyRules ? 0 : 1 );
260 }
261}
262
263
264void Klondike::setGameState( const QString & state )
265{
266 Q_UNUSED( state );
267
268 emit newCardsPossible( !talon->isEmpty() || pile->count() > 1 );
269}
270
271
272QString Klondike::getGameOptions() const
273{
274 return QString::number( pile->cardsToShow() );
275}
276
277void Klondike::setGameOptions( const QString & options )
278{
279 setEasy( options.toInt() == 1 );
280}
281
282void Klondike::setEasy( bool _EasyRules )
283{
284 if ( _EasyRules != easyRules )
285 {
286 easyRules = _EasyRules;
287 options->setCurrentItem( easyRules ? 0 : 1 );
288
289 int drawNumber = easyRules ? 1 : 3;
290 pile->setCardsToShow( drawNumber );
291
292 KCardPile::KeyboardFocusHint hint = easyRules
293 ? KCardPile::ForceFocusTop
294 : KCardPile::NeverFocus;
295 for( int i = 0; i < 4; ++i )
296 target[i]->setKeyboardSelectHint( hint );
297
298 setSolver( new KlondikeSolver( this, drawNumber ) );
299
300 Settings::setKlondikeIsDrawOne( easyRules );
301 }
302}
303
304
305bool Klondike::drop()
306{
307 bool pileempty = pile->isEmpty();
308 if (!DealerScene::drop())
309 return false;
310 if (pile->isEmpty() && !pileempty)
311 newCards();
312 return true;
313}
314
315void Klondike::mapOldId(int id)
316{
317 switch ( id )
318 {
319 case DealerInfo::KlondikeDrawOneId:
320 setEasy( true );
321 break;
322 case DealerInfo::KlondikeDrawThreeId:
323 setEasy( false );
324 break;
325 case DealerInfo::KlondikeGeneralId:
326 default:
327 // Do nothing.
328 break;
329 }
330}
331
332
333int Klondike::oldId() const
334{
335 if ( easyRules )
336 return DealerInfo::KlondikeDrawOneId;
337 else
338 return DealerInfo::KlondikeDrawThreeId;
339}
340
341
342
343
344static class KlondikeDealerInfo : public DealerInfo
345{
346public:
347 KlondikeDealerInfo()
348 : DealerInfo(I18N_NOOP("Klondike"), DealerInfo::KlondikeGeneralId)
349 {
350 addSubtype( KlondikeDrawOneId, I18N_NOOP( "Klondike (Draw 1)" ) );
351 addSubtype( KlondikeDrawThreeId, I18N_NOOP( "Klondike (Draw 3)" ) );
352 }
353
354 virtual DealerScene *createGame() const
355 {
356 return new Klondike( this );
357 }
358} klondikeDealerInfo;
359
360
361#include "klondike.moc"
362