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 | |
49 | const int CHUNKSIZE = 100; |
50 | |
51 | |
52 | Freecell::Freecell( const DealerInfo * di ) |
53 | : DealerScene( di ) |
54 | { |
55 | } |
56 | |
57 | |
58 | void 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 | |
102 | void 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 | |
117 | void 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 | |
139 | bool 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 | |
162 | bool 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 | |
181 | bool 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 | |
196 | bool 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 | |
210 | QList<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 | |
264 | static class FreecellDealerInfo : public DealerInfo |
265 | { |
266 | public: |
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 | |