1/*
2 * Copyright (C) 1997 Rodolfo Borges <barrett@labma.ufrj.br>
3 * Copyright (C) 1998-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 "freecell.h"
39
40#include "dealerinfo.h"
41#include "pileutils.h"
42#include "speeds.h"
43#include "patsolve/freecellsolver.h"
44
45#include <KDebug>
46#include <KLocale>
47
48
49const int CHUNKSIZE = 100;
50
51
52Freecell::Freecell( const DealerInfo * di )
53 : DealerScene( di )
54{
55}
56
57
58void Freecell::initialize()
59{
60 setDeckContents();
61
62 const qreal topRowDist = 1.08;
63 const qreal bottomRowDist = 1.13;
64 const qreal targetOffsetDist = ( 7 * bottomRowDist + 1 ) - ( 3 * topRowDist + 1 );
65
66 for ( int i = 0; i < 4; ++i )
67 {
68 freecell[i] = new PatPile ( this, 1 + 8 + i, QString( "freecell%1" ).arg( i ) );
69 freecell[i]->setPileRole(PatPile::Cell);
70 freecell[i]->setLayoutPos(topRowDist * i, 0);
71 freecell[i]->setKeyboardSelectHint( KCardPile::AutoFocusTop );
72 freecell[i]->setKeyboardDropHint( KCardPile::AutoFocusTop );
73 }
74
75 for ( int i = 0; i < 8; ++i )
76 {
77 store[i] = new PatPile( this, 1 + i, QString( "store%1" ).arg( i ) );
78 store[i]->setPileRole(PatPile::Tableau);
79 store[i]->setLayoutPos( bottomRowDist * i, 1.3 );
80 store[i]->setBottomPadding( 2.5 );
81 store[i]->setHeightPolicy( KCardPile::GrowDown );
82 store[i]->setKeyboardSelectHint( KCardPile::AutoFocusDeepestRemovable );
83 store[i]->setKeyboardDropHint( KCardPile::AutoFocusTop );
84 }
85
86 for ( int i = 0; i < 4; ++i )
87 {
88 target[i] = new PatPile(this, 1 + 8 + 4 + i, QString( "target%1" ).arg( i ));
89 target[i]->setPileRole(PatPile::Foundation);
90 target[i]->setLayoutPos(targetOffsetDist + topRowDist * i, 0);
91 target[i]->setSpread(0, 0);
92 target[i]->setKeyboardSelectHint( KCardPile::NeverFocus );
93 target[i]->setKeyboardDropHint( KCardPile::ForceFocusTop );
94 }
95
96 setActions(DealerScene::Demo | DealerScene::Hint);
97 setSolver( new FreecellSolver( this ) );
98 setNeededFutureMoves( 4 ); // reserve some
99}
100
101
102void Freecell::restart( const QList<KCard*> & cards )
103{
104 QList<KCard*> cardList = cards;
105
106 int column = 0;
107 while ( !cardList.isEmpty() )
108 {
109 addCardForDeal( store[column], cardList.takeLast(), true, store[0]->pos() );
110 column = (column + 1) % 8;
111 }
112
113 startDealAnimation();
114}
115
116
117void Freecell::cardsDroppedOnPile( const QList<KCard*> & cards, KCardPile * pile )
118{
119 if ( cards.size() <= 1 )
120 {
121 DealerScene::moveCardsToPile( cards, pile, DURATION_MOVE );
122 return;
123 }
124
125 QList<KCardPile*> freeCells;
126 for ( int i = 0; i < 4; ++i )
127 if ( freecell[i]->isEmpty() )
128 freeCells << freecell[i];
129
130 QList<KCardPile*> freeStores;
131 for ( int i = 0; i < 8; ++i )
132 if ( store[i]->isEmpty() && store[i] != pile )
133 freeStores << store[i];
134
135 multiStepMove( cards, pile, freeStores, freeCells, DURATION_MOVE );
136}
137
138
139bool Freecell::tryAutomaticMove(KCard *c)
140{
141 // target move
142 if (DealerScene::tryAutomaticMove(c))
143 return true;
144
145 if (c->isAnimated())
146 return false;
147
148 if (c == c->pile()->topCard() && c->isFaceUp())
149 {
150 for (int i = 0; i < 4; i++)
151 {
152 if (freecell[i]->isEmpty())
153 {
154 moveCardToPile( c, freecell[i], DURATION_MOVE );
155 return true;
156 }
157 }
158 }
159 return false;
160}
161
162bool Freecell::canPutStore( const KCardPile * pile, const QList<KCard*> & cards ) const
163{
164 int freeCells = 0;
165 for ( int i = 0; i < 4; ++i )
166 if ( freecell[i]->isEmpty() )
167 ++freeCells;
168
169 int freeStores = 0;
170 for ( int i = 0; i < 8; ++i )
171 if ( store[i]->isEmpty() && store[i] != pile )
172 ++freeStores;
173
174 return cards.size() <= (freeCells + 1) << freeStores
175 && ( pile->isEmpty()
176 || ( pile->topCard()->rank() == cards.first()->rank() + 1
177 && pile->topCard()->color() != cards.first()->color() ) );
178
179}
180
181bool Freecell::checkAdd(const PatPile * pile, const QList<KCard*> & oldCards, const QList<KCard*> & newCards) const
182{
183 switch (pile->pileRole())
184 {
185 case PatPile::Tableau:
186 return canPutStore(pile, newCards);
187 case PatPile::Cell:
188 return oldCards.isEmpty() && newCards.size() == 1;
189 case PatPile::Foundation:
190 return checkAddSameSuitAscendingFromAce(oldCards, newCards);
191 default:
192 return false;
193 }
194}
195
196bool Freecell::checkRemove(const PatPile * pile, const QList<KCard*> & cards) const
197{
198 switch (pile->pileRole())
199 {
200 case PatPile::Tableau:
201 return isAlternateColorDescending(cards);
202 case PatPile::Cell:
203 return cards.first() == pile->topCard();
204 case PatPile::Foundation:
205 default:
206 return false;
207 }
208}
209
210QList<MoveHint> Freecell::getHints()
211{
212 QList<MoveHint> hintList = getSolverHints();
213
214 if ( isDemoActive() )
215 return hintList;
216
217 foreach (PatPile * store, patPiles())
218 {
219 if (store->isEmpty())
220 continue;
221
222 QList<KCard*> cards = store->cards();
223 while (cards.count() && !cards.first()->isFaceUp())
224 cards.erase(cards.begin());
225
226 QList<KCard*>::Iterator iti = cards.begin();
227 while (iti != cards.end())
228 {
229 if (allowedToRemove(store, (*iti)))
230 {
231 foreach (PatPile * dest, patPiles())
232 {
233 int cardIndex = store->indexOf(*iti);
234 if (cardIndex == 0 && dest->isEmpty() && !dest->isFoundation())
235 continue;
236
237 if (!checkAdd(dest, dest->cards(), cards))
238 continue;
239
240 if ( dest->isFoundation() ) // taken care by solver
241 continue;
242
243 QList<KCard*> cardsBelow = cards.mid(0, cardIndex);
244 // if it could be here as well, then it's no use
245 if ((cardsBelow.isEmpty() && !dest->isEmpty()) || !checkAdd(store, cardsBelow, cards))
246 {
247 hintList << MoveHint( *iti, dest, 0 );
248 }
249 else if (checkPrefering( dest, dest->cards(), cards )
250 && !checkPrefering( store, cardsBelow, cards ))
251 { // if checkPrefers says so, we add it nonetheless
252 hintList << MoveHint( *iti, dest, 0 );
253 }
254 }
255 }
256 cards.erase(iti);
257 iti = cards.begin();
258 }
259 }
260 return hintList;
261}
262
263
264static class FreecellDealerInfo : public DealerInfo
265{
266public:
267 FreecellDealerInfo()
268 : DealerInfo(I18N_NOOP("Freecell"), FreecellId)
269 {}
270
271 virtual DealerScene *createGame() const
272 {
273 return new Freecell( this );
274 }
275} freecellDealerInfo;
276
277
278#include "freecell.moc"
279