1 | /* |
2 | * Copyright (C) 1995 Paul Olav Tvete <paul@troll.no> |
3 | * Copyright (C) 2000-2009 Stephan Kulow <coolo@kde.org> |
4 | * |
5 | * License of original code: |
6 | * ------------------------------------------------------------------------- |
7 | * Permission to use, copy, modify, and distribute this software and its |
8 | * documentation for any purpose and without fee is hereby granted, |
9 | * provided that the above copyright notice appear in all copies and that |
10 | * both that copyright notice and this permission notice appear in |
11 | * supporting documentation. |
12 | * |
13 | * This file is provided AS IS with no warranties of any kind. The author |
14 | * shall have no liability with respect to the infringement of copyrights, |
15 | * trade secrets or any patents by this file or any part thereof. In no |
16 | * event will the author be liable for any lost revenue or profits or |
17 | * other special, indirect and consequential damages. |
18 | * ------------------------------------------------------------------------- |
19 | * |
20 | * License of modifications/additions made after 2009-01-01: |
21 | * ------------------------------------------------------------------------- |
22 | * This program is free software; you can redistribute it and/or |
23 | * modify it under the terms of the GNU General Public License as |
24 | * published by the Free Software Foundation; either version 2 of |
25 | * the License, or (at your option) any later version. |
26 | * |
27 | * This program is distributed in the hope that it will be useful, |
28 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
29 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
30 | * GNU General Public License for more details. |
31 | * |
32 | * You should have received a copy of the GNU General Public License |
33 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
34 | * ------------------------------------------------------------------------- |
35 | */ |
36 | |
37 | #include "dealer.h" |
38 | #include "dealerinfo.h" |
39 | #include "mainwindow.h" |
40 | #include "version.h" |
41 | #include "patsolve/patsolve.h" |
42 | |
43 | #include "KCardTheme" |
44 | #include "KCardDeck" |
45 | |
46 | #include <KAboutData> |
47 | #include <KApplication> |
48 | #include <KCmdLineArgs> |
49 | #include <KDebug> |
50 | #include <KGlobal> |
51 | #include <KLocale> |
52 | #include <KMenuBar> |
53 | #include <KStandardDirs> |
54 | #include <KUrl> |
55 | |
56 | #include <QtCore/QFile> |
57 | #include <QtCore/QTime> |
58 | #include <QtGui/QResizeEvent> |
59 | #include <QtXml/QDomDocument> |
60 | |
61 | #include <climits> |
62 | #include <time.h> |
63 | |
64 | static DealerScene *getDealer( int wanted_game ) |
65 | { |
66 | foreach ( DealerInfo * di, DealerInfoList::self()->games() ) |
67 | { |
68 | if ( di->providesId( wanted_game ) ) |
69 | { |
70 | DealerScene * d = di->createGame(); |
71 | Q_ASSERT( d ); |
72 | d->setDeck( new KCardDeck( KCardTheme(), d ) ); |
73 | d->initialize(); |
74 | |
75 | if ( !d->solver() ) |
76 | { |
77 | kError() << "There is no solver for" << di->nameForId( wanted_game );; |
78 | return 0; |
79 | } |
80 | |
81 | return d; |
82 | } |
83 | } |
84 | return 0; |
85 | } |
86 | |
87 | // A function to remove all nonalphanumeric characters from a string |
88 | // and convert all letters to lowercase. |
89 | QString lowerAlphaNum( const QString & string ) |
90 | { |
91 | QString result; |
92 | for ( int i = 0; i < string.size(); ++i ) |
93 | { |
94 | QChar c = string.at( i ); |
95 | if ( c.isLetterOrNumber() ) |
96 | result += c.toLower(); |
97 | } |
98 | return result; |
99 | } |
100 | |
101 | int main( int argc, char **argv ) |
102 | { |
103 | KAboutData aboutData( "kpat" , |
104 | 0, |
105 | ki18n("KPatience" ), |
106 | KPAT_VERSION, |
107 | ki18n("KDE Patience Game" ), |
108 | KAboutData::License_GPL_V2, |
109 | ki18n("© 1995 Paul Olav Tvete\n© 2000 Stephan Kulow" ), |
110 | KLocalizedString(), |
111 | "http://games.kde.org/kpat" ); |
112 | |
113 | aboutData.addAuthor( ki18n("Paul Olav Tvete" ), |
114 | ki18n("Author of original Qt version" ), |
115 | "paul@troll.no" ); |
116 | aboutData.addAuthor( ki18n("Mario Weilguni" ), |
117 | ki18n("Initial KDE port" ), |
118 | "mweilguni@kde.org" ); |
119 | aboutData.addAuthor( ki18n("Matthias Ettrich" ), |
120 | KLocalizedString(), |
121 | "ettrich@kde.org" ); |
122 | aboutData.addAuthor( ki18n("Rodolfo Borges" ), |
123 | ki18n("New game types" ), |
124 | "barrett@9hells.org" ); |
125 | aboutData.addAuthor( ki18n("Peter H. Ruegg" ), |
126 | KLocalizedString(), |
127 | "kpat@incense.org" ); |
128 | aboutData.addAuthor( ki18n("Michael Koch" ), |
129 | ki18n("Bug fixes" ), |
130 | "koch@kde.org" ); |
131 | aboutData.addAuthor( ki18n("Marcus Meissner" ), |
132 | ki18n("Shuffle algorithm for game numbers" ), |
133 | "mm@caldera.de" ); |
134 | aboutData.addAuthor( ki18n("Tom Holroyd" ), |
135 | ki18n("Initial patience solver" ), |
136 | "tomh@kurage.nimh.nih.gov" ); |
137 | aboutData.addAuthor( ki18n("Stephan Kulow" ), |
138 | ki18n("Rewrite and current maintainer" ), |
139 | "coolo@kde.org" ); |
140 | aboutData.addAuthor( ki18n("Erik Sigra" ), |
141 | ki18n("Klondike improvements" ), |
142 | "sigra@home.se" ); |
143 | aboutData.addAuthor( ki18n("Josh Metzler" ), |
144 | ki18n("Spider implementation" ), |
145 | "joshdeb@metzlers.org" ); |
146 | aboutData.addAuthor( ki18n("Maren Pakura" ), |
147 | ki18n("Documentation" ), |
148 | "maren@kde.org" ); |
149 | aboutData.addAuthor( ki18n("Inge Wallin" ), |
150 | ki18n("Bug fixes" ), |
151 | "inge@lysator.liu.se" ); |
152 | aboutData.addAuthor( ki18n("Simon Hürlimann" ), |
153 | ki18n("Menu and toolbar work" ), |
154 | "simon.huerlimann@huerlisi.ch" ); |
155 | aboutData.addAuthor( ki18n("Parker Coates" ), |
156 | ki18n("Cleanup and polish" ), |
157 | "coates@kde.org" ); |
158 | |
159 | // Create a KLocale earlier than normal so that we can use i18n to translate |
160 | // the names of the game types in the help text. |
161 | KLocale *tmpLocale = new KLocale("kpat" ); |
162 | QMap<QString, int> indexMap; |
163 | QStringList gameList; |
164 | foreach ( const DealerInfo *di, DealerInfoList::self()->games() ) |
165 | { |
166 | KLocalizedString localizedKey = ki18n( di->untranslatedBaseName().constData() ); |
167 | const QString translatedKey = lowerAlphaNum( localizedKey.toString( tmpLocale ) ); |
168 | gameList << translatedKey; |
169 | indexMap.insert( translatedKey, di->baseId() ); |
170 | indexMap.insert( di->baseIdString(), di->baseId() ); |
171 | } |
172 | gameList.sort(); |
173 | const QString listSeparator = ki18nc( "List separator" , ", " ).toString( tmpLocale ); |
174 | delete tmpLocale; |
175 | |
176 | KCmdLineArgs::init( argc, argv, &aboutData ); |
177 | |
178 | KCmdLineOptions options; |
179 | options.add("solvegame <file>" , ki18n( "Try to find a solution to the given savegame" ) ); |
180 | options.add("solve <num>" , ki18n("Dealer to solve (debug)" )); |
181 | options.add("start <num>" , ki18n("Game range start (default 0:INT_MAX)" )); |
182 | options.add("end <num>" , ki18n("Game range end (default start:start if start given)" )); |
183 | options.add("gametype <game>" , ki18n("Skip the selection screen and load a particular game type. Valid values are: %1" ).subs(gameList.join(listSeparator))); |
184 | options.add("testdir <directory>" , ki18n( "Directory with test cases" ) ); |
185 | options.add("generate" , ki18n( "Generate random test cases" ) ); |
186 | options.add("+file" , ki18n("File to load" )); |
187 | KCmdLineArgs::addCmdLineOptions (options); |
188 | KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); |
189 | |
190 | KApplication application; |
191 | KGlobal::locale()->insertCatalog( QLatin1String( "libkdegames" )); |
192 | |
193 | QString savegame = args->getOption( "solvegame" ); |
194 | if ( !savegame.isEmpty() ) |
195 | { |
196 | QFile of(savegame); |
197 | of.open(QIODevice::ReadOnly); |
198 | QDomDocument doc; |
199 | doc.setContent(&of); |
200 | |
201 | DealerScene *f = getDealer( doc.documentElement().attribute("id" ).toInt() ); |
202 | |
203 | f->loadLegacyFile( &of ); |
204 | f->solver()->translate_layout(); |
205 | int ret = f->solver()->patsolve(); |
206 | if ( ret == Solver::SolutionExists ) |
207 | fprintf( stdout, "won\n" ); |
208 | else if ( ret == Solver::NoSolutionExists ) |
209 | fprintf( stdout, "lost\n" ); |
210 | else |
211 | fprintf( stdout, "unknown\n" ); |
212 | |
213 | return 0; |
214 | } |
215 | |
216 | QString testdir = args->getOption("testdir" ); |
217 | if ( !testdir.isEmpty() ) { |
218 | qsrand(time(0)); |
219 | if ( args->isSet("generate" ) ) { |
220 | for (int dealer = 0; dealer < 20; dealer++) { |
221 | DealerScene *f = getDealer( dealer ); |
222 | if (!f) continue; |
223 | int count = 100; |
224 | QTime mytime; |
225 | while (count) { |
226 | if (f->deck()) f->deck()->stopAnimations(); |
227 | int i = qrand() % INT_MAX; |
228 | f->startNew( i ); |
229 | mytime.start(); |
230 | f->solver()->translate_layout(); |
231 | int ret = f->solver()->patsolve(); |
232 | if ( ret == Solver::SolutionExists ) { |
233 | fprintf( stdout, "%d: %d won (%d ms)\n" , dealer, i, mytime.elapsed() ); |
234 | count--; |
235 | QFile file(QString("%1/%2-%3-1" ).arg(testdir).arg(dealer).arg(i)); |
236 | file.open( QFile::WriteOnly ); |
237 | f->saveLegacyFile( &file ); |
238 | } |
239 | else if ( ret == Solver::NoSolutionExists ) { |
240 | fprintf( stdout, "%d: %d lost (%d ms)\n" , dealer, i, mytime.elapsed() ); |
241 | count--; |
242 | QFile file(QString("%1/%2-%3-0" ).arg(testdir).arg(dealer).arg(i)); |
243 | file.open( QFile::WriteOnly ); |
244 | f->saveLegacyFile( &file ); |
245 | } else { |
246 | fprintf( stdout, "%d: %d unknown (%d ms)\n" , dealer, i, mytime.elapsed() ); |
247 | } |
248 | } |
249 | } |
250 | } |
251 | return 0; |
252 | } |
253 | |
254 | bool ok = false; |
255 | int wanted_game = -1; |
256 | if ( args->isSet( "solve" ) ) |
257 | wanted_game = args->getOption("solve" ).toInt( &ok ); |
258 | if ( ok ) |
259 | { |
260 | ok = false; |
261 | int end_index = -1; |
262 | if ( args->isSet( "end" ) ) |
263 | end_index = args->getOption("end" ).toInt( &ok ); |
264 | if ( !ok ) |
265 | end_index = -1; |
266 | ok = false; |
267 | int start_index = -1; |
268 | if ( args->isSet( "start" ) ) |
269 | start_index = args->getOption("start" ).toInt( &ok ); |
270 | if ( !ok ) { |
271 | start_index = 0; |
272 | end_index = INT_MAX; |
273 | } else { |
274 | if ( end_index == -1 ) |
275 | end_index = start_index; |
276 | } |
277 | DealerScene *f = getDealer( wanted_game ); |
278 | if ( !f ) |
279 | return 1; |
280 | |
281 | QTime mytime; |
282 | for ( int i = start_index; i <= end_index; i++ ) |
283 | { |
284 | mytime.start(); |
285 | f->deck()->stopAnimations(); |
286 | f->startNew( i ); |
287 | f->solver()->translate_layout(); |
288 | int ret = f->solver()->patsolve(); |
289 | if ( ret == Solver::SolutionExists ) |
290 | fprintf( stdout, "%d won (%d ms)\n" , i, mytime.elapsed() ); |
291 | else if ( ret == Solver::NoSolutionExists ) |
292 | fprintf( stdout, "%d lost (%d ms)\n" , i, mytime.elapsed() ); |
293 | else |
294 | fprintf( stdout, "%d unknown (%d ms)\n" , i, mytime.elapsed() ); |
295 | } |
296 | fprintf( stdout, "all_moves %ld\n" , all_moves ); |
297 | return 0; |
298 | } |
299 | |
300 | QString gametype = args->getOption("gametype" ).toLower(); |
301 | QFile savedState( KStandardDirs::locateLocal("appdata" , saved_state_file) ); |
302 | |
303 | MainWindow *w = new MainWindow; |
304 | if (args->count()) |
305 | { |
306 | if ( !w->loadGame( args->url( 0 ), true ) ) |
307 | w->slotShowGameSelectionScreen(); |
308 | } |
309 | else if (indexMap.contains(gametype)) |
310 | { |
311 | w->slotGameSelected(indexMap.value(gametype)); |
312 | } |
313 | else if (savedState.exists()) |
314 | { |
315 | if ( !w->loadGame( savedState.fileName(), false ) ) |
316 | w->slotShowGameSelectionScreen(); |
317 | } |
318 | else |
319 | { |
320 | w->slotShowGameSelectionScreen(); |
321 | } |
322 | w->show(); |
323 | |
324 | args->clear(); |
325 | return application.exec(); |
326 | } |
327 | |