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
64static 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.
89QString 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
101int 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