diff options
Diffstat (limited to 'kmines')
57 files changed, 7044 insertions, 0 deletions
diff --git a/kmines/CHANGELOG b/kmines/CHANGELOG new file mode 100644 index 00000000..a6d3aac0 --- /dev/null +++ b/kmines/CHANGELOG @@ -0,0 +1,325 @@ +2.1.10 (25 Aug 2005) [KDE 3.5 devel] + +2.1.9a (25 Aug 2005) [KDE 3.4.3 stable] + * fix constness in solver [reported by Garrett Kajmowicz] + * fix bug: cannot load log [reported by Tobias Meyer] + +2.1.9 (27 Jun 2004) [KDE 3.3 stable] + * fix compilation of solver debugging code. + * add missing events + * replace case size option by zoom in/zoom out actions + +2.1.8c (31 May 2004) [KDE 3.2.3 stable] + * fix bug: hint not displayed [reported by Daniel Schepler] + * fix bug: log file restarted when game paused [reported by Astharoth] + +2.1.8b (26 February 2004) [KDE 3.2.1 stable] + * fix solver crash when "magic reveal" on. + * fix longstanding crash in solver dialog. + +2.1.8 (17 January 2003) [KDE 3.2 stable] + * configure button in highscores dialog + * notifications + +2.1.7e (11 May 2003) [KDE 3.1.3 stable] + * fix score trends display + +2.1.7d (30 April 2003) [KDE 3.1.2 stable] + * fix assert in custom dialog [reported by Albert Astals Cid] + +2.1.7c (17 January 2003) [KDE 3.1.1 stable] + * fix score lcd colors for custom games + +2.1.7b (3 December 2002) [KDE 3.1 stable] + * fix bug in highscores dialog in statistics and histogram tabs + * fix icons in adviser menu and in configuration dialog + * fix bug in XML configuration that was leading to a crash at program + end [report by connyosis and "Quel Qun"] + * fix bug in mean score computation + * fix bug that prevents logging solver actions + +2.1.7 (27 July 2002) + * XML configuration is working !! + * separate keyboard and general shortcuts in configuration dialog + * add "advanced" tab to highscores configuration + possibility to remove + registration + * export highscores to text file + * add (optionnal) statistics and histogram to highscores dialog + * track lost games and black marks for kmines + * histogram for kmines + * some cosmetic fixes in configuration dialog + * view/save/replay/load game log + * fix a bug in KMultiConfigItem [reported by ...] + * at game end and in case of victory, do not show mines but add flags [bug + reported by ...] + +2.1.6 (24 April 2002) + * mark/unmark actions on mouse release [patch by Thomas Capricelli] + * "magic reveal" : a new option that leave only the non-trivial cases to + solve [patch by Thomas Capricelli] + * fix obscure flicker condition : keyboard play and moving pressed mouse + outside field :) + * add home/end/pageup/pagedown keyboard actions + * fix pause menu entry state in some situation + * fix resize when case size changed + * first step for game logs + +2.1.5 (14 February 2002) + * now include a solver/adviser ! [contributed by Mikhail Kourinny] + * revamped game state management + * slightly changed settings API + +2.1.4c (1 May 2002) [KDE 3.0.1 stable] + * fixed case drawing for all styles [bug reported by kanthoney and fixed by + Maksim Orlovich] + +2.1.4 (23 January 2002) + * completely revamped API for highscores + dynamic library + * revamped settings (now includes Custom minefield) + * fixed repaint of LCDs in inactive mode + +2.1.3 (29 November 2001) + * use KConfigGroupSaver everywhere + * config entry for wwhs server url (just in case ...) + +2.1.2 (19 November 2001) + * date for each highscores and for best highscore [proposed by Jenne] + * some more reorganisation + * better custom dialog + * slightly modify the init/repaint at game start : more clean & repaint could + leak some infos about mines position (?) + * forward port the bug fix from 2.1.0b (in 2.2.x branch) + * do not bother people who want to stay anonymous with message boxes + [suggestion from Bernhard Berger] + * autoreveal does not take "?" flag into account anymore + * changing nickname will change nickname in highscores list too [suggestion + from Juliette] + +2.1.1 (16 October 2001) + * port to Qt 3.0 completed (hopefully) + * some changes in highscores (nicer dialog) + * i18n error messages for wwhs + * new smiley pixmap for pause + * clicking smiley resumes when game paused + * changes in keyboard accelators since Ctrl and Shift seems to be + forbidden as keys now ... + * count nb of clicks (reveal, autoreveal, mark) and display it in highscores. + +2.1.0b (19 November 2001) [SF 2.2 stable] + * fix a nasty bug : uncorrect flag was not shown at game over in certain + cases [thanx for the bug reports from Viira and from an anonymous admin :)] + +2.1.0 (17 July 2001) [KDE 2.2 stable] + * fixed checked entry in "show highscores" submenu + +2.0.13 (10 June 2001) [SF 2.2 devel] + * enable world-wide highscores !! + +2.0.12 (09 June 2001) [SF 2.2 devel] + * option to put the game in pause mode if the window loses focus [proposed by + Bernhard Berger] + * better default keys + fixed autoreveal display for keyboard game + * use KStdGameAction + * big revamping of highscores + use of KHighscores + * removed status bar (ugly and not very useful) + +2.0.11 (14 March 2001) + * changed highscores accelerator to CTRL + H (more standard) + * better use of session-management + +2.0.10 (23 October 2000) [KDE 2.1 stable] + * fixed mouse action in settings (autoreveal and toggle mark were inversed) + +2.0.9 (21 September 2000) [KDE 2.0 stable] + * fix obscure bug reported by Tobias Oed (was crashing version 1.0.1a !) + : mouse buttons were messing things when pressed simultaneously. + * in the same move : simplified some code and correct a small buglet due to + rounding error (with left mouse button pressed, the first line and first + column case was pressed when the mouse was moved just outside the top and + left side of the field). + +2.0.8 (7 September 2000) + * fix bug that was pausing a stopped game when calling highscores + +2.0.7 (4 September 2000) + * fixed bug in mine field drawing with some styles [found by Gerard Delafond + and Tobias Kretschmar] that induces a complete rewrite of the drawing code. + now it honors the global style (beautiful in marble style) and it even + leads to code simplification ! + * the keyboard cursor uses now the focus drawing method. + * fixed bug when field.width != field.height when exploding + * fixed font in the pause button (with non standard case size) + * fixed behaviour of button in highscores dialog when entering the winner name + [proposed by Lotta Inkovaara for ksirtet] + * keyboard is disabled by default + +2.0.6 (23 August 2000) + * use of KMainWindow (replace KTMainWindow) but this does not solve the + resizing problems :( + * pause game when high scores requested + * custom game settings are saved [feature suggested by Toan Nguyen, + Williaw Barnes and Fran�ois-Xavier Duranceau] + * solve the resizing problems by intercepting the Layout Hint event ... + (due to limitations in K/QMainWindow IMHO) + +2.0.5 (14 June 2000) + * default for mouse binding changed + * added color configuration for numbers, flag and explosion (for B&W monitors + and few-colors themes) [feature suggested by Hume Smith and Fran�ois-Xavier + Duranceau] + * moved around some things about configuration (cleaned up "defines.h") + * case state is now more clean (but some bugs might have crept in) + * properly mark all the flagged cases with no mine to error on game end + * versioning the XMLGUI file + +2.0.4 (11 April 2000) + * hack to resize correctly when menu is hidden (due to bugs + in KTMainWindow) + * nicer (and simpler) custom level dialog with KNumIntInput + * kstatusbar in place of the label + * remove the title in dialogs (more consistent with other apps) + +2.0.3 (6 April 2000) + * use kkeydialog for actions + * rationalize settings (much better now) + * animate autoreveal with keyboard + * enable/disable and change text for pause in menu + * configuration of mouse bindings [idea of FX Duranceau] + +2.0.2 (28 February 2000) + * XMLify the GUI + +2.0.1 (19 February 2000) + * KAction/KAccel integrated + * keyboard play + * can choose case size (font is scaled) + * less flicker in repainting (+ fixed a strange divide negative int by + uint thing) + * fix the pixmaps drawing so that they are not too bad in custom case sizes + +2.0.0 (14 December 1999) + * use of KDialogBase and KAboutData/KAboutDialog + * unflagged mines are shown at game's end + * hack for focus handling in highscores dialog ... + * slightly better LCDs, message label and smiley button + * fixed a bug when clicking on the frame outside the mine field + * small fixes for custom games + * "What's This" added. + +1.0.6 (21 July 1999) + * slightly better highscores dialog + * fixed a bug in flagged mines display (cannot be negative) + * the LCDs gets red when there are more flagged cases than mines + and when you have used more time than the better player. + * use of a status bar. + * the smiley moods are now XPMs [made by Andreas Zehender] + +1.0.5 (6 July 1999) + * fixed layout handling (all this "updateGeometry" and "LayoutHint" stuff) + * space bar hit cannot restart game anymore + * clean code (config) + level is saved + +1.0.4 (12 March 1999) + * porting to QT 2.0 + +1.0.3 (10 March 1999) +Mario Weilguni <[email protected]> + * fixed the bug in the level selection (showed up with Qt 2.0) + * fixed layout for Qt 2.0 + * fixed signal/slot handling for Qt 2.0 + * fixed layout with floating menubar + * level menu items are now checked + +1.0.2 (23 February 1999) + * bug fix : argh! a "brown paper bag" one [bug report by Szokovacs Robert] + +1.0.1 (2 Junuary 1999) [KDE 1.1 stable] + * bug fix : when paused a game can be continued by changing desktops or + iconifying/deiconifying without time consumption ... [bug report by + Fran�ois-Xavier Duranceau, Frank Pieczynski & Oliver Eiden] + * add printing facility : well it seems hacky to me and certainly not perfect + but it works ... [feature suggested by Tomislav Marsic] + +1.0.0 (18 June 1998) [KDE 1.0 stable] + * final cleanup before 1.0 ! + +0.6.12 (9 June 1998) + * use QLayout for "custom level dialog" and "highscore dialog" + * some cleanup + * and a fix ! (yes there was a bug still lurking : when only two + uncovered cases remain, it was possible to win on clicking on the mine) + +0.6.11 + * Added kapp->getHelpMenu() & setCaption() [Robert Williams] + +0.6.10 + * included in the CVS tree + +0.6.9 + * bugfix from Anders Widell (doesn't allow anymore to middle click on a flag) + +0.6.8 + * some polishing (game over message do not overwrite mines number & marked + mines stay marked even when the game is lost : so you can completely analyse + why you have lost :) [thanx to Christoph Rummel for pointing these to me] + +0.6.7 + * internationalization + +0.6.6 + * no more NULLs (for the sake of 64bits) + * nicer dialog boxes + * no mine on first click + +0.6.5 + * minor changes + * "kexample 0.31" compliant + +0.6.4 + * adapted to libkdecore 0.7 + * hide/show menubar and popup + * more compliant to the Style Guide + +0.6.3 + * use kdehelp + * updating of Makefile (use KDEDIR) + * use kmsgbox + +0.62 + * use KKeyCode to manage some keyboard shortcuts + +0.6 + * change the class name "Status" to "KStatus" as an include file present on my + system defines something called "Status"... + +0.5 + * use KApp for configuration and highscoring save (via KConfig) + * better look of option & highscores windows + +0.4 + * minor bug fix + * 4 spaces tabs indentation + * slight change in the "pause code" + +0.3 + * the random configuration seems to be a bit too repetitive ... + (fixed : silly me !) + * a red cross must show where the marked cases have not contained mines + * uncover case on button release, not on button push and also allow moving + the mouse with the left button pressed + * clear function with the mid*button : it should be kool :) + * a pause entry in the menu + * QLCD for timer and mines left + * cleaning of the highscore stuff (the highscore file is now in the home dir) + * option : "'?' mark" on/off (the default behaviour is on : you can change + it by editing the defines.h file) + +0.2 + * some code reorganisation + * timer starts at first click now + * it is no more possible to click on a marked or uncertain case now + * a small help + * colored pixmaps + * a better look (?) + * highscoring + * custom level diff --git a/kmines/LICENSE b/kmines/LICENSE new file mode 100644 index 00000000..6b772e1c --- /dev/null +++ b/kmines/LICENSE @@ -0,0 +1,19 @@ +KMINES : the KDE minesweeper +---------------------------- +Copyright (c) 1996-2004 Nicolas HADACEK ([email protected]) +Copyright (c) 2001 Mikhail Kourinny ([email protected]) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/kmines/Makefile.am b/kmines/Makefile.am new file mode 100644 index 00000000..e49f6501 --- /dev/null +++ b/kmines/Makefile.am @@ -0,0 +1,66 @@ +SUBDIRS = data bitmaps solver +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = kmines +kmines_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(KDE_USE_PIE) +kmines_LDADD = ./solver/libsolver.la $(LIB_KDEGAMES) +kmines_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +kmines_SOURCES = kzoommainwindow.cpp defines.cpp highscores.cpp settings.kcfgc \ + dialogs.cpp frame.cpp field.cpp status.cpp main.cpp +kmines_METASOURCES = AUTO + +rcdir = $(kde_datadir)/kmines +rc_DATA = kminesui.rc + +messages: rc.cpp + $(XGETTEXT) rc.cpp *.cpp solver/*.cpp -o $(podir)/kmines.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && echo "********************************************************" \ + && echo "" \ + && echo "This game is installed sgid \"games\" to use the" \ + && echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." \ + && echo "" \ + && echo "If the system-wide highscore file does not exist, it is" \ + && echo "created with the correct ownership and permissions. See the" \ + && echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." \ + && echo "" \ + && echo "********************************************************" \ + ) || true + +install-exec-hook: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((mkdir -p $(DESTHIGHSCORES) && chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES)) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test ${setgid} = true \ + && ((chmod 2755 $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES)) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" \ + )) || true + diff --git a/kmines/README b/kmines/README new file mode 100644 index 00000000..3371a474 --- /dev/null +++ b/kmines/README @@ -0,0 +1,16 @@ +KMINES : the KDE minesweeper +---------------------------- +Copyright (c) 1996-2004 Nicolas HADACEK ([email protected]) +Copyright (c) 2001 Mikhail Kourinny ([email protected]) +Distributed under the GNU General Public License + + +This is a very classical minesweeper written from scratch +with three predefined levels and custom levels. + + Easy : 8x8 with 10 mines + Normal : 16x16 with 40 mines + Expert : 30x16 with 99 mines + + +Requirements : up to date KDE and QT libraries. diff --git a/kmines/TODO b/kmines/TODO new file mode 100644 index 00000000..655b7b07 --- /dev/null +++ b/kmines/TODO @@ -0,0 +1,14 @@ +TODO: + + * messages from/to a named pipe for external AI [yawn...] + * icons for easy/normal/expert + * new levels ... + * flower / star shaped levels + * option for only solvable games + + * do you have any idea ? + + +KNOWN BUGS: + + * please find one ! diff --git a/kmines/bitmaps/Makefile.am b/kmines/bitmaps/Makefile.am new file mode 100644 index 00000000..3266d0c7 --- /dev/null +++ b/kmines/bitmaps/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = smile smile_happy smile_ohno smile_stress + diff --git a/kmines/bitmaps/README b/kmines/bitmaps/README new file mode 100644 index 00000000..368803c1 --- /dev/null +++ b/kmines/bitmaps/README @@ -0,0 +1 @@ +These XPMs are a contribution from Andreas Zehender. Many thanks! diff --git a/kmines/bitmaps/smile b/kmines/bitmaps/smile new file mode 100644 index 00000000..9977db89 --- /dev/null +++ b/kmines/bitmaps/smile @@ -0,0 +1,37 @@ +/* XPM */
+const char * smile_xpm[] = {
+"25 25 9 1",
+" c None",
+". c #FFFF00",
+"+ c #C0C000",
+"@ c #808000",
+"# c #404000",
+"$ c #303030",
+"% c #000000",
+"& c #585858",
+"* c #A0A0A0",
+".........................",
+".........................",
+"........+@#$%$#@+........",
+"......+&$@++.++@$&+......",
+".....+$@.........@$+.....",
+"....+$+...........+$+....",
+"...+$+.............+$+...",
+"...&@..+@@+...+@@+..@&...",
+"..+$...@%%@...@%%@...$+..",
+"..@@...@%%@...@%%@...@@..",
+"..#+...+@@+...+@@+...+#..",
+"..$+.................+$..",
+"..%...................%..",
+"..$+..+...........+..+$..",
+"..#+.+$...........$+.+#..",
+"..@@.@#+.........+#@.@@..",
+"..+$...#*.......*#...$+..",
+"...&@...@#@+++@#@...@&...",
+"...+$+...+@$#$@+...+$+...",
+"....+$+...........+$+....",
+".....+$@.........@$+.....",
+"......+&$@++.++@$&+......",
+"........+@#$%$#@+........",
+".........................",
+"........................."};
diff --git a/kmines/bitmaps/smile_happy b/kmines/bitmaps/smile_happy new file mode 100644 index 00000000..475ac3eb --- /dev/null +++ b/kmines/bitmaps/smile_happy @@ -0,0 +1,37 @@ +/* XPM */
+const char * smile_happy_xpm[] = {
+"25 25 9 1",
+" c None",
+". c #FFFF00",
+"+ c #C0C000",
+"@ c #808000",
+"# c #404000",
+"$ c #303030",
+"% c #000000",
+"& c #585858",
+"* c #A0A0A0",
+".........................",
+".........................",
+"........+@#$%$#@+........",
+"......+&$@++.++@$&+......",
+".....+$@.........@$+.....",
+"....+$+...........+$+....",
+"...+$+..+++...+++..+$+...",
+"...&@#+@#%%+.+%%#@+#@&...",
+"..+$.+#%%%%%#%%%%%#+.$+..",
+"..@@..+%%%%%+%%%%%+..@@..",
+"..#+...@#%%@.@%%#@...+#..",
+"..$+....+++...+++....+$..",
+"..%...................%..",
+"..$+..+...........+..+$..",
+"..#+.+$...........$+.+#..",
+"..@@.@#+.........+#@.@@..",
+"..+$...#*.......*#...$+..",
+"...&@...@#@+++@#@...@&...",
+"...+$+...+@$#$@+...+$+...",
+"....+$+...........+$+....",
+".....+$@.........@$+.....",
+"......+&$@++.++@$&+......",
+"........+@#$%$#@+........",
+".........................",
+"........................."};
diff --git a/kmines/bitmaps/smile_ohno b/kmines/bitmaps/smile_ohno new file mode 100644 index 00000000..fb8d0784 --- /dev/null +++ b/kmines/bitmaps/smile_ohno @@ -0,0 +1,37 @@ +/* XPM */
+const char * smile_ohno_xpm[] = {
+"25 25 9 1",
+" c None",
+". c #FFFF00",
+"+ c #C0C000",
+"@ c #808000",
+"# c #404000",
+"$ c #303030",
+"% c #000000",
+"& c #585858",
+"* c #A0A0A0",
+".........................",
+".........................",
+"........+@#$%$#@+........",
+"......+&$@++.++@$&+......",
+".....+$@.........@$+.....",
+"....+$+...........+$+....",
+"...+$+.............+$+...",
+"...&@..+@@+...+@@+..@&...",
+"..+$...@%%@...@%%@...$+..",
+"..@@...@%%@...@%%@...@@..",
+"..#+...+@@+...+@@+...+#..",
+"..$+.................+$..",
+"..%...................%..",
+"..$+.................+$..",
+"..#+.................+#..",
+"..@@.....+@$#$@+.....@@..",
+"..+$....@#@+++@#@....$+..",
+"...&@..#*.......*#..@&...",
+"...+$+.+.........+.+$+...",
+"....+$+...........+$+....",
+".....+$@.........@$+.....",
+"......+&$@++.++@$&+......",
+"........+@#$%$#@+........",
+".........................",
+"........................."};
diff --git a/kmines/bitmaps/smile_sleep b/kmines/bitmaps/smile_sleep new file mode 100644 index 00000000..695cf9bd --- /dev/null +++ b/kmines/bitmaps/smile_sleep @@ -0,0 +1,36 @@ +/* XPM */ +const char * smile_sleep_xpm[] = { +"25 25 8 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+.............+$+...", +"...&@...............@&...", +"..+$.................$+..", +"..@@...@%%.....%%@...@@..", +"..#+.................+#..", +"..$+.................+$..", +"..%...................%..", +"..$+.................+$..", +"..#+.................+#..", +"..@@.................@@..", +"..+$.....%%%%%%#.....$+..", +"...&@...............@&...", +"...+$+.............+$+...", +"....+$+...........+$+....", +".....+$@.........@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/bitmaps/smile_stress b/kmines/bitmaps/smile_stress new file mode 100644 index 00000000..57b973da --- /dev/null +++ b/kmines/bitmaps/smile_stress @@ -0,0 +1,36 @@ +/* XPM */
+const char * smile_stress_xpm[] = {
+"25 25 8 1",
+" c None",
+". c #FFFF00",
+"+ c #C0C000",
+"@ c #808000",
+"# c #404000",
+"$ c #303030",
+"% c #000000",
+"& c #585858",
+".........................",
+".........................",
+"........+@#$%$#@+........",
+"......+&$@++.++@$&+......",
+".....+$@.........@$+.....",
+"....+$+...........+$+....",
+"...+$+.............+$+...",
+"...&@..+@@+...+@@+..@&...",
+"..+$...@%%@...@%%@...$+..",
+"..@@...@%%@...@%%@...@@..",
+"..#+...+@@+...+@@+...+#..",
+"..$+.................+$..",
+"..%...................%..",
+"..$+.......+++.......+$..",
+"..#+......@#$#@......+#..",
+"..@@.....@#...#@.....@@..",
+"..+$.....#+...+#.....$+..",
+"...&@....#+...+#....@&...",
+"...+$+...@&...&@...+$+...",
+"....+$+...&#&#&...+$+....",
+".....+$@...+@+...@$+.....",
+"......+&$@++.++@$&+......",
+"........+@#$%$#@+........",
+".........................",
+"........................."};
diff --git a/kmines/data/Makefile.am b/kmines/data/Makefile.am new file mode 100644 index 00000000..b3c82d50 --- /dev/null +++ b/kmines/data/Makefile.am @@ -0,0 +1,7 @@ +KDE_ICON = kmines + +xdg_apps_DATA = kmines.desktop + +appdatadir = $(kde_datadir)/kmines +appdata_DATA = eventsrc + diff --git a/kmines/data/eventsrc b/kmines/data/eventsrc new file mode 100644 index 00000000..867865db --- /dev/null +++ b/kmines/data/eventsrc @@ -0,0 +1,988 @@ +[!Global!] +IconName=kmines +Comment=KMines +Comment[ar]=لعبة الألغام (KMines) +Comment[be]=Сапёр +Comment[bn]=কে-মাইন্স +Comment[hi]=के-माइन्स +Comment[hr]=KMine +Comment[ne]=केडीई माइन +Comment[pa]=ਕੇ-ਸਰੁੰਗ +Comment[pt_BR]=KMinas +Comment[sv]=Minröjning +Comment[ta]=கேகன்னிவெடிகள் +Comment[tg]=KСапёр +Comment[tr]=Mayın tarlası +Comment[wa]=KMenes +Comment[zh_TW]=KMine 踩地雷 + +[reveal] +Name=Reveal case +Name[ar]=أظهر القضية +Name[be]=Адкрыццё поля +Name[bg]=Разкриване +Name[bn]=মাইন প্রদর্শন করো +Name[bs]=Otkrij polja +Name[ca]=Descobreix casella +Name[cs]=Odkrýt pole +Name[cy]=Dangos cas +Name[da]=Vis felt +Name[de]=Aufdecken +Name[el]=Αποκάλυψη +Name[eo]=Malkaŝi kazon +Name[es]=Revelar el caso +Name[et]=Avab välja +Name[eu]=Erakutsi kasua +Name[fa]=آشکار شدن موقعیت +Name[fi]=Paljasta peli +Name[fr]=Révéler la case +Name[gl]=Amosar cadrado +Name[he]=גלה ריבוע +Name[hi]=केस प्रकट करें +Name[hr]=Otkrij slučaj +Name[hu]=Mező felfedése +Name[is]=Sýna tösku +Name[it]=Rivela casella +Name[ja]=開いたとき +Name[lt]=Atverti langelį +Name[lv]=Atrisināt +Name[mk]=Отворено е поле +Name[nb]=Avslør +Name[nds]=Opmaken +Name[ne]=केस प्रकट +Name[nl]=Vak openen +Name[nn]=Avslør +Name[pa]=ਰੀਵਲ ਕੇਸ +Name[pl]=Pole odsłonięte +Name[pt]=Quadrado revelado +Name[pt_BR]=Revelar quadrado +Name[ru]=Открытие поля +Name[se]=Čájet +Name[sk]=Odkryť pole +Name[sl]=Odkrij ploščico +Name[sr]=Откриј случај +Name[sr@Latn]=Otkrij slučaj +Name[sv]=Avslöja ruta +Name[ta]=நிகழ்ச்சியை வெளிப்படுத்து +Name[tg]=Кушодани ҳолат +Name[tr]=Kutu aç +Name[uk]=Відкриття комірки +Name[zh_CN]=揭开盒盖 +Name[zh_TW]=開挖方格 +Comment=Reveal case +Comment[be]=Адкрыццё поля +Comment[bg]=Разкриване +Comment[bn]=মাইন প্রদর্শন করো +Comment[bs]=Otkrij polja +Comment[ca]=Descobreix casella +Comment[cs]=Odkrýt pole +Comment[cy]=Dangos cas +Comment[da]=Vis felt +Comment[de]=Aufdecken +Comment[el]=Αποκάλυψη +Comment[eo]=Malkaŝi kazon +Comment[es]=Revelar el caso +Comment[et]=Avab välja +Comment[eu]=Erakutsi kasua +Comment[fa]=آشکار شدن موقعیت +Comment[fi]=Paljasta peli +Comment[fr]=Révéler la case +Comment[gl]=Amosar cadrado +Comment[he]=גלה ריבוע +Comment[hi]=केस प्रकट करें +Comment[hr]=Otkrij slučaj +Comment[hu]=Mező felfedése +Comment[is]=Sýna tösku +Comment[it]=Rivela casella +Comment[ja]=開いたとき +Comment[km]=ករណីបក +Comment[lt]=Atverti langelį +Comment[lv]=Atrisināt +Comment[mk]=Отворено е поле +Comment[nb]=Avslør +Comment[nds]=Opmaken +Comment[ne]=केस प्रकट +Comment[nl]=Vak openen +Comment[nn]=Avslør +Comment[pl]=Pole zaznaczone +Comment[pt]=Quadrado revelado +Comment[pt_BR]=Revelar quadrado +Comment[ru]=Открытие поля +Comment[se]=Čájet +Comment[sk]=Odkryť pole +Comment[sl]=Odkrij ploščico +Comment[sr]=Откриј случај +Comment[sr@Latn]=Otkrij slučaj +Comment[sv]=Avslöja ruta +Comment[ta]=நிகழ்ச்சியை வெளிப்படுத்து +Comment[tg]=Кушодани ҳолат +Comment[tr]=Kutuyu aç +Comment[uk]=Відкрити комірку +Comment[zh_CN]=揭开盒盖 +Comment[zh_TW]=開挖方格 +default_presentation=0 + +[autoreveal] +Name=Autoreveal case +Name[be]=Адкрыццё пустых палёў +Name[bg]=Автоматично разкриване +Name[bn]=স্বয়ংক্রিয়ভাবে মাইন প্রদর্শন করো +Name[bs]=Automatski otkrij polja +Name[ca]=Descobreix automàticament casella +Name[cs]=Automaticky odkrýt pole +Name[cy]=Dangos cas yn ymysgogol +Name[da]=Vis felt automatisk +Name[de]=Automatisch aufdecken +Name[el]=Αυτόματη αποκάλυψη +Name[eo]=Aŭtomalkaŝi kazon +Name[es]=Autorevelar el caso +Name[et]=Välja automaatne avamine +Name[eu]=Auto-erakutsi kasua +Name[fa]=آشکار شدن موقعیت به طور خودکار +Name[fi]=Automaattisesti paljasta peli +Name[fr]=Révéler automatiquement la case +Name[gl]=Auto-amosar cadrado +Name[he]=גלה ריבוע אוטומטית +Name[hi]=केस स्वयं प्रकट करें +Name[hr]=Automatski otkrij slučaj +Name[hu]=Mező automatikus felfedése +Name[is]=Sýna tösku sjálfkrafa +Name[it]=Rivela automaticamente caselle +Name[ja]=自動で開いたとき +Name[km]=ករណីបកដោយស្វ័យប្រវត្តិ +Name[lt]=Automatinis atvėrimas +Name[lv]=Automātiski atrisināt +Name[mk]=Автоматски е отворено поле +Name[nb]=Automatisk avsløring +Name[nds]=Automaatsch opmaken +Name[ne]=स्वत: प्रकट केस +Name[nl]=Vak automatisch openen +Name[nn]=Automatisk avsløring +Name[pa]=ਆਟੋਰੀਵਲ ਕੇਸ +Name[pl]=Pole odsłonięte automatycznie +Name[pt]=Auto-revelar quadrado +Name[pt_BR]=Auto-revelar quadrado +Name[ru]=Открытие пустых полей +Name[se]=Automáhtalaččat čájet +Name[sk]=Automaticky odkryť pole +Name[sl]=Samodejno odkrij ploščico +Name[sr]=Аутоматски откриј случај +Name[sr@Latn]=Automatski otkrij slučaj +Name[sv]=Avslöja ruta automatiskt +Name[ta]=நிகழ்ச்சியை தானே வெளிப்படுத்து +Name[tg]=Худкушодашудани ҳолат +Name[tr]=Otomatik kutu aç +Name[uk]=Автоматичне відкрити комірки +Name[zh_CN]=自动揭开盒盖 +Name[zh_TW]=周圍自動開挖方格 +Comment=Autoreveal case +Comment[be]=Адкрыццё пустых палёў +Comment[bg]=Автоматично разкриване +Comment[bn]=স্বয়ংক্রিয়ভাবে মাইন প্রদর্শন করো +Comment[bs]=Automatski otkrij polja +Comment[ca]=Descobreix automàticament casella +Comment[cs]=Automaticky odkrýt pole +Comment[cy]=Dangos cas yn ymysgogol +Comment[da]=Vis felt automatisk +Comment[de]=Automatisch aufdecken +Comment[el]=Αυτόματη αποκάλυψη +Comment[eo]=Aŭtomalkaŝi kazon +Comment[es]=Autorevelar el caso +Comment[et]=Välja automaatne avamine +Comment[eu]=Auto-erakutsi kasua +Comment[fa]=آشکار شدن موقعیت به طور خودکار +Comment[fi]=Automaattisesti paljasta peli +Comment[fr]=Révéler automatiquement la case +Comment[gl]=Auto-amosar cadrado +Comment[he]=גלה ריבוע אוטומטית +Comment[hi]=केस स्वयं प्रकट करें +Comment[hr]=Automatski otkrij slučaj +Comment[hu]=Mező automatikus felfedése +Comment[is]=Sýna tösku skjakrafa +Comment[it]=Rivela automaticamente caselle +Comment[ja]=自動で開いたとき +Comment[km]=ករណីបកដោយស្វ័យប្រវត្តិ +Comment[lt]=Automatinis atvėrimas +Comment[lv]=Automātiski atrisināt +Comment[mk]=Автоматски е отворено поле +Comment[nb]=Automatisk avsløring +Comment[nds]=Automaatsch opmaken +Comment[ne]=स्वत: प्रकट केस +Comment[nl]=Vak automatisch openen +Comment[nn]=Automatisk avsløring +Comment[pl]=Pole odsłonięte automatycznie +Comment[pt]=Quadrado auto-revelado +Comment[pt_BR]=Auto-revelar quadrado +Comment[ru]=Открытие пустых полей +Comment[se]=Automáhtalaččat čájet +Comment[sk]=Automaticky odkryť pole +Comment[sl]=Samodejno odkrij ploščico +Comment[sr]=Аутоматски откриј случај +Comment[sr@Latn]=Automatski otkrij slučaj +Comment[sv]=Avslöja ruta automatiskt +Comment[ta]=நிகழ்ச்சியை தானே வெளிப்படுத்து +Comment[tg]=Худкушодани ҳолат +Comment[tr]=Kutuyu otomatik aç +Comment[uk]=Автоматично відкрити комірку +Comment[zh_CN]=自动揭开盒盖 +Comment[zh_TW]=周圍自動開挖方格 +default_presentation=0 + +[mark] +Name=Mark case +Name[be]=Пазнака міны +Name[bg]=Поставяне на флаг +Name[bn]=মাইন-এ চিহ্ন দাও +Name[bs]=Označi polje +Name[ca]=Marca casella +Name[cs]=Označit pole +Name[cy]=Marcio cas +Name[da]=Markér felt +Name[de]=Markieren +Name[el]=Σημείωση κουτιού +Name[eo]=Marki kazon +Name[es]=Marcar el caso +Name[et]=Märgib välja +Name[eu]=Markatu kasua +Name[fa]=مشخص کردن موقعیت +Name[fi]=Merkkaa peli +Name[fr]=Marquer la case +Name[gl]=Marcar cadrado +Name[he]=סמן ריבוע +Name[hi]=केस चिह्नित करें +Name[hr]=Obilježi slučaj +Name[hu]=Mező megjelölése +Name[is]=Merkja tösku +Name[it]=Segna casella +Name[ja]=マークしたとき +Name[km]=ករណីសម្គាល់ +Name[lt]=Pažymėti langelį +Name[lv]=Marķēt +Name[mk]=Обележано е поле +Name[nb]=Merk +Name[nds]=Markeren +Name[ne]=चिन्ह केस +Name[nl]=Vak markeren +Name[nn]=Merk +Name[pa]=ਮਾਰਕ ਕੇਸ +Name[pl]=Pole zaznaczone +Name[pt]=Marcar quadrado +Name[pt_BR]=Marcar quadrado +Name[ru]=Отметка мины +Name[se]=Merke +Name[sk]=Označiť pole +Name[sl]=Označi ploščico +Name[sr]=Обележи случај +Name[sr@Latn]=Obeleži slučaj +Name[sv]=Markera ruta +Name[ta]=நிகழ்ச்சியை குறி +Name[tg]=Нишонакунии ҳолат +Name[tr]=Kutuyu işaretle +Name[uk]=Позначення комірки +Name[zh_CN]=标记盒子 +Name[zh_TW]=標記方格 +Comment=Mark case +Comment[be]=Пазнака міны +Comment[bg]=Поставяне на флаг +Comment[bn]=সম্ভাব্য মাইন-এ চিহ্ন দাও +Comment[bs]=Označi polje +Comment[ca]=Marca casella +Comment[cs]=Označit pole +Comment[cy]=Marcio cas +Comment[da]=Markér felt +Comment[de]=Markieren +Comment[el]=Σημείωση κουτιού +Comment[eo]=Marki kazon +Comment[es]=Marcar el caso +Comment[et]=Märgib välja +Comment[eu]=Markatu kasua +Comment[fa]=مشخص کردن موقعیت +Comment[fi]=Merkkaa peli +Comment[fr]=Marquer la case +Comment[gl]=Marcar cadrado +Comment[he]=סמן ריבוע +Comment[hi]=केस चिह्नित करें +Comment[hr]=Obilježi slučaj +Comment[hu]=Mező megjelölése +Comment[is]=Merkja tösku +Comment[it]=Segna casella +Comment[ja]=マークしたとき +Comment[km]=ករណីសម្គាល់ +Comment[lt]=Pažymėti langelį +Comment[lv]=Marķēt +Comment[mk]=Обележано е поле +Comment[nb]=Merk +Comment[nds]=Markeren +Comment[ne]=केसमा चिन्ह लगाउनुहोस् +Comment[nl]=Vak markeren +Comment[nn]=Merk +Comment[pl]=Zaznaczenie pola +Comment[pt]=Quadrado marcado +Comment[pt_BR]=Marcar quadrado +Comment[ru]=Отметка мины +Comment[se]=Merke +Comment[sk]=Označiť pole +Comment[sl]=Označi ploščico +Comment[sr]=Обележи случај +Comment[sr@Latn]=Obeleži slučaj +Comment[sv]=Markera ruta +Comment[ta]=நிகழ்ச்சியை குறி +Comment[tg]=Нишонакунии ҳолат +Comment[tr]=Kutuyu işaretle +Comment[uk]=Помітити комірку +Comment[zh_CN]=标记盒子 +Comment[zh_TW]=標記方格 +default_presentation=0 + +[unmark] +Name=Unmark case +Name[be]=Здыманне пазнакі +Name[bg]=Премахване на флаг +Name[bn]=মাইন-এ দেওয়া চিহ্ন মুছে ফেল +Name[bs]=Odznači polje +Name[ca]=Desmarca casella +Name[cs]=Zrušit označení pole +Name[cy]=Dadmarcio cas +Name[da]=Afmarkér felt +Name[de]=Markierung entfernen +Name[el]=Αναίρεση σημείωσης κουτιού +Name[eo]=Malmarki kazon +Name[es]=Desmarcar el caso +Name[et]=Eemaldab väljalt märgi +Name[eu]=Desmarkatu kasua +Name[fa]=نامشخص کردن موقعیت +Name[fi]=Poista merkki pelistä +Name[fr]=Ne plus marquer la case +Name[gl]=Desmarcar cadrado +Name[he]=הורד סימון מריבוע +Name[hi]=केस अचिह्नित करें +Name[hr]=Skini obilježje sa slučaja +Name[hu]=Mező kijelölésének megszüntetése +Name[is]=Afmerkja tösku +Name[it]=Togli segno su casella +Name[ja]=マークを外したとき +Name[km]=ករណីមិនសម្គាល់ +Name[lt]=Atžymėti langelį +Name[lv]=Noņemt marķējumu +Name[mk]=Одобележано е поле +Name[nb]=Fjern merket +Name[nds]=Markeren wegdoon +Name[ne]=केसको चिन्ह हटाउनुहोस् +Name[nl]=Vakmarkering verwijderen +Name[nn]=Fjern merke +Name[pa]=ਅਣ-ਮਾਰਕ ਕੇਸ +Name[pl]=Pole odznaczone +Name[pt]=Desmarcar quadrado +Name[pt_BR]=Desmarcar quadrado +Name[ru]=Снятие отметки +Name[se]=Váldde mearkka eret +Name[sk]=Odstrániť značku poľa +Name[sl]=Odstrani oznako +Name[sr]=Скини обележје са случаја +Name[sr@Latn]=Skini obeležje sa slučaja +Name[sv]=Avmarkera ruta +Name[ta]=நிகழ்ச்சியை குறிக்காதே +Name[tg]=Гирифтани нишонаи ҳолат +Name[tr]=Kutudaki işareti kaldır +Name[uk]=Зняття помітки з комірки +Name[zh_CN]=不标记盒子 +Name[zh_TW]=去標記方格 +Comment=Unmark case +Comment[be]=Здыманне пазнакі +Comment[bg]=Премахване на флаг +Comment[bn]=মাইন-এ দেওয়া চিহ্ন মুছে ফেল +Comment[bs]=Odznači polje +Comment[ca]=Desmarca casella +Comment[cs]=Zrušit označení pole +Comment[cy]=Dadmarcio cas +Comment[da]=Afmarkér felt +Comment[de]=Markierung entfernen +Comment[el]=Αναίρεση σημείωσης κουτιού +Comment[eo]=Malmarki kazon +Comment[es]=Desmarcar el caso +Comment[et]=Eemaldab väljalt märgi +Comment[eu]=Desmarkatu kasua +Comment[fa]=نامشخص کردن موقعیت +Comment[fi]=Poista merkki pelistä +Comment[fr]=Ne plus marquer la case +Comment[gl]=Desmarcar cadrado +Comment[he]=הורד סימון מריבוע +Comment[hi]=केस अचिह्नित करें +Comment[hr]=Skini obilježje sa slučaja +Comment[hu]=Mező megjelölésének megszüntetése +Comment[is]=Afmerkja tösku +Comment[it]=Togli segno a casella +Comment[ja]=マークを外したとき +Comment[km]=ករណីមិនសម្គាល់ +Comment[lt]=Atžymėti langelį +Comment[lv]=Noņemt marķējumu +Comment[mk]=Одобележано е поле +Comment[nb]=Fjern merket +Comment[nds]=Markeren wegdoon +Comment[ne]=केसको चिन्ह हटाउनुहोस् +Comment[nl]=Vakmarkering verwijderen +Comment[nn]=Fjern merke +Comment[pl]=Odznaczenie pola +Comment[pt]=Quadrado desmarcado +Comment[pt_BR]=Desmarcar quadrado +Comment[ru]=Снятие отметки +Comment[se]=Váldde eret mearkka +Comment[sk]=Odstrániť značku poľa +Comment[sl]=Odstrani oznako +Comment[sr]=Скини обележје са случаја +Comment[sr@Latn]=Skini obeležje sa slučaja +Comment[sv]=Avmarkera ruta +Comment[ta]=நிகழ்ச்சியை குறிக்காதே +Comment[tg]=Гирифтани нишонаи ҳолат +Comment[tr]=Kutudaki işareti kaldır +Comment[uk]=Зняти помітку з комірки +Comment[zh_CN]=不标记盒子 +Comment[zh_TW]=去標記方格 +default_presentation=0 + +[explosion] +Name=Explosion +Name[ar]=انفجار +Name[be]=Выбух +Name[bg]=Експлозия +Name[bn]=বিস্ফোরণ +Name[br]=Tarzhad +Name[bs]=Eksplozija +Name[ca]=Explosió +Name[cs]=Exploze +Name[cy]=Ffrwydriad +Name[da]=Eksplosion +Name[el]=Έκρηξη +Name[eo]=Eksplodo +Name[es]=Explosión +Name[et]=Plahvatus +Name[eu]=Eztanda +Name[fa]=انفجار +Name[fi]=Räjähdys +Name[gl]=Estoupido +Name[he]=פיצוץ +Name[hi]=धमाका +Name[hr]=Eksplozija +Name[hu]=Robbanás +Name[is]=Sprenging +Name[it]=Esplosione +Name[ja]=爆発 +Name[km]=ការផ្ទុះ +Name[ko]=폭발 +Name[lt]=Sprogimas +Name[lv]=Eksplozija +Name[mk]=Експлозија +Name[nb]=Eksplosjon +Name[nds]=Exploschoon +Name[ne]=विस्फोट +Name[nl]=Explosie +Name[nn]=Eksplosjon +Name[pa]=ਧਮਾਕੇ +Name[pl]=Wybuch +Name[pt]=Explosão +Name[pt_BR]=Explosão +Name[ro]=Explozie +Name[ru]=Взрыв +Name[se]=Eksplošuvdna +Name[sk]=Výbuch +Name[sl]=Eksplozija +Name[sr]=Експлозија +Name[sr@Latn]=Eksplozija +Name[ta]=அதிர்வெடி +Name[tg]=Таркиш +Name[tr]=Patlama +Name[uk]=Вибух +Name[uz]=Portlash +Name[uz@cyrillic]=Портлаш +Name[wa]=Esplôzion +Name[zh_CN]=爆炸 +Name[zh_TW]=爆炸 +Comment=Explosion +Comment[be]=Выбух +Comment[bg]=Експлозия +Comment[bn]=বিস্ফোরণ +Comment[br]=Tarzhad +Comment[bs]=Eksplozija +Comment[ca]=Explosió +Comment[cs]=Exploze +Comment[cy]=Ffrwydriad +Comment[da]=Eksplosion +Comment[el]=Έκρηξη +Comment[eo]=Eksplodo +Comment[es]=Explosión +Comment[et]=Plahvatus +Comment[eu]=Eztanda +Comment[fa]=انفجار +Comment[fi]=Räjähdys +Comment[gl]=Estoupido +Comment[he]=פיצוץ +Comment[hi]=धमाका +Comment[hr]=Eksplozija +Comment[hu]=Robbanás +Comment[is]=Sprenging +Comment[it]=Esplosione +Comment[ja]=爆発 +Comment[km]=ការផ្ទុះ +Comment[ko]=폭발 +Comment[lt]=Sprogimas +Comment[lv]=Eksplozija +Comment[mk]=Експлозија +Comment[nb]=Eksplosjon +Comment[nds]=Exploschoon +Comment[ne]=विस्फोट +Comment[nl]=Explosie +Comment[nn]=Eksplosjon +Comment[pa]=ਧਮਾਕਾਖੇਜ਼ +Comment[pl]=Wybuch +Comment[pt]=Explosão +Comment[pt_BR]=Explosão +Comment[ro]=Explozie +Comment[ru]=Взрыв +Comment[se]=Eksplošuvdna +Comment[sk]=Výbuch +Comment[sl]=Eksplozija +Comment[sr]=Експлозија +Comment[sr@Latn]=Eksplozija +Comment[ta]=அதிர்வெடி +Comment[tg]=Таркиш +Comment[tr]=Patlama +Comment[uk]=Вибух +Comment[uz]=Portlash +Comment[uz@cyrillic]=Портлаш +Comment[wa]=Esplôzion +Comment[zh_CN]=爆炸 +Comment[zh_TW]=爆炸 +default_presentation=0 + +[won] +Name=Game won +Name[ar]=ربحت اللعبة +Name[be]=Перамога +Name[bg]=Спечелихте +Name[bn]=খেলা জিতেছেন +Name[br]=Gounezet eo ar c'hoari +Name[bs]=Pobjeda +Name[ca]=Partida guanyada +Name[cs]=Vyhraná hra +Name[cy]=Gêm wedi ei ennill +Name[da]=Spillet vundet +Name[de]=Spiel gewonnen +Name[el]=Παιχνίδι κερδήθηκε +Name[eo]=Ludo venkita +Name[es]=Partida ganada +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa irabazi da +Name[fa]=برد بازی +Name[fi]=Peli voitettu +Name[fr]=Partie gagnée +Name[gl]=Xogo gañado +Name[he]=ניצחת! +Name[hi]=खेल में जीत हुई +Name[hr]=Igra je dobivena +Name[hu]=Győzelem +Name[is]=Leikur unninn +Name[it]=Partita vinta +Name[ja]=ゲームに勝ち +Name[km]=ល្បែងបានឈ្នះ +Name[ko]=게임에서 이김 +Name[lt]=Žaidimas laimėtas +Name[lv]=Spēle uzvarēta +Name[mk]=Играта е добиена +Name[nb]=Du vant +Name[nds]=Speel wunnen +Name[ne]=खेल जित्नु भयो +Name[nl]=Spel gewonnen +Name[nn]=Du vann +Name[pa]=ਖੇਡ ਜਿੱਤੀ +Name[pl]=Gra wygrana +Name[pt]=Jogo ganho +Name[pt_BR]=Jogo ganho +Name[ro]=Joc cîştigat +Name[ru]=Победа +Name[se]=Don vuitet +Name[sk]=Vyhraná hra +Name[sl]=Igra je dobljena +Name[sr]=Игра је добијена +Name[sr@Latn]=Igra je dobijena +Name[sv]=Du vann spelet +Name[ta]=ஆட்டம் ஜெயிக்கப்பட்டது +Name[tg]=Дар бозӣ ғолиб омадед +Name[tr]=Oyun kazanıldı +Name[uk]=Гру виграно +Name[wa]=Djeu wangnî +Name[zh_CN]=您赢了游戏 +Name[zh_TW]=遊戲獲勝 +Comment=Game won +Comment[ar]=ربحت اللعبة +Comment[be]=Перамога +Comment[bg]=Спечелихте +Comment[bn]=খেল খতম +Comment[br]=Gounezet eo ar c'hoari +Comment[bs]=Pobjeda +Comment[ca]=Partida guanyada +Comment[cs]=Vyhraná hra +Comment[cy]=Gêm wedi ei ennill +Comment[da]=Spil vundet +Comment[de]=Spiel gewonnen +Comment[el]=Παιχνίδι κερδήθηκε +Comment[eo]=Ludo venkita +Comment[es]=Partida ganada +Comment[et]=Mäng läbi, sina võitsid +Comment[eu]=Jokoa irabazi da +Comment[fa]=برد بازی +Comment[fi]=Peli voitettu +Comment[fr]=Partie gagnée +Comment[gl]=Xogo gañado +Comment[he]=ניצחת! +Comment[hi]=खेल में जीत हुई +Comment[hr]=Igra je dobivena +Comment[hu]=Győzelem +Comment[is]=Leikur unninn +Comment[it]=Partita vinta +Comment[ja]=ゲームに勝ち +Comment[km]=ល្បែងបានឈ្នះ +Comment[ko]=게임에서 이김 +Comment[lt]=Žaidimas laimėtas +Comment[lv]=Spēle ir uzvarēta +Comment[mk]=Играта е добиена +Comment[nb]=Du vant! +Comment[nds]=Speel wunnen +Comment[ne]=खेल जित्नु भयो +Comment[nl]=Spel gewonnen +Comment[nn]=Du vann +Comment[pa]=ਖੇਡ ਜਿੱਤੀ +Comment[pl]=Gra wygrana +Comment[pt]=Jogo ganho +Comment[pt_BR]=Jogo ganho +Comment[ro]=Joc cîştigat +Comment[ru]=Победа +Comment[se]=Don vuitet +Comment[sk]=Vyhraná hra +Comment[sl]=Igra je dobljena +Comment[sr]=Игра је добијена +Comment[sr@Latn]=Igra je dobijena +Comment[sv]=Du vann spelet +Comment[ta]=ஆட்டம் ஜெயிக்கப்பட்டது +Comment[tg]=Дар бозӣ ғолиб омадед +Comment[tr]=Oyun kazanıldı +Comment[uk]=Гру виграно +Comment[wa]=Djeu wangnî +Comment[zh_CN]=您赢了游戏 +Comment[zh_TW]=遊戲獲勝 +default_presentation=0 + +[lost] +Name=Game lost +Name[ar]=خسرت اللعبة +Name[be]=Параза +Name[bg]=Загубихте +Name[bn]=খেলায় হেরে গিয়েছেন +Name[br]=Kollet eo ar c'hoari +Name[bs]=Poraz +Name[ca]=Partida perduda +Name[cs]=Prohraná hra +Name[cy]=Gêm wedi ei golli +Name[da]=Spil tabt +Name[de]=Spiel verloren +Name[el]=Παιχνίδι χάθηκε +Name[eo]=Ludo malvenkita +Name[es]=Partida perdida +Name[et]=Mäng läbi, sina kaotasid +Name[eu]=Jokoa galdu da +Name[fa]=باخت بازی +Name[fi]=Peli hävitty +Name[fr]=Partie perdue +Name[gl]=Xogo perdido +Name[he]=המשחק הסתיים, הפסדת +Name[hi]=खेल में हार हुई +Name[hr]=Igra je izgubljena +Name[hu]=Vereség +Name[is]=Leik tapað +Name[it]=Partita persa +Name[ja]=ゲームに負け +Name[km]=ល្បែងបានចាញ់ +Name[ko]=게임에서 짐 +Name[lt]=Žaidimas pralaimėtas +Name[lv]=Spēle zaudēta +Name[mk]=Играта е изгубена +Name[nb]=Du tapte +Name[nds]=Speel verloren +Name[ne]=खेल हार्नु भयो +Name[nl]=Spel verloren +Name[nn]=Du tapte +Name[pa]=ਖੇਡ ਹਾਰੀ +Name[pl]=Koniec gry, przegrałeś +Name[pt]=Jogo perdido +Name[pt_BR]=Jogo perdido +Name[ro]=Joc pierdut +Name[ru]=Поражение +Name[se]=Don vuoittohallet +Name[sk]=Prehraná hra +Name[sl]=Igra je izgubljena +Name[sr]=Игра је изгубљена +Name[sr@Latn]=Igra je izgubljena +Name[sv]=Du förlorade spelet +Name[ta]=ஆட்டம் இழக்கப்பட்டது +Name[tg]=Дар бозӣ мағлуб шудед +Name[tr]=Oyun kaybedildi +Name[uk]=Гра програна +Name[wa]=Djeu pierdou +Name[zh_CN]=您输了游戏 +Name[zh_TW]=遊戲失敗 +Comment=Game lost +Comment[ar]=خسرت اللعبة +Comment[be]=Параза +Comment[bg]=Загубихте +Comment[bn]=খেলায় হেরে গিয়েছেন +Comment[br]=Koll eo ar c'hoari +Comment[bs]=Poraz +Comment[ca]=Partida perduda +Comment[cs]=Prohraná hra +Comment[cy]=Gêm wedi ei golli +Comment[da]=Spil tabt +Comment[de]=Spiel verloren +Comment[el]=Παιχνίδι χάθηκε +Comment[eo]=Ludo malvenkita +Comment[es]=Partida perdida +Comment[et]=Mäng läbi, sina kaotasid +Comment[eu]=Jokoa galdu da +Comment[fa]=باخت بازی +Comment[fi]=Peli hävitty +Comment[fr]=Partie perdue +Comment[gl]=Xogo perdido +Comment[he]=המשחק הסתיים, הפסדת +Comment[hi]=खेल में हार हुई +Comment[hr]=Igra je izgubljena +Comment[hu]=Vereség +Comment[is]=Leik tapað +Comment[it]=Partita persa +Comment[ja]=ゲームに負け +Comment[km]=ល្បែងបានចាញ់ +Comment[ko]=게임에서 짐 +Comment[lt]=Žaidimas pralaimėtas +Comment[lv]=Spēle ir zaudēta +Comment[mk]=Играта е изубена +Comment[nb]=Du tapte +Comment[nds]=Speel verloren +Comment[ne]=खेल हार्नु भयो +Comment[nl]=Spel verloren +Comment[nn]=Du tapte +Comment[pa]=ਖੇਡ ਹਾਰੀ +Comment[pl]=Koniec gry, przegrałeś +Comment[pt]=Jogo perdido +Comment[pt_BR]=Jogo perdido +Comment[ro]=Joc pierdut +Comment[ru]=Поражение +Comment[se]=Don vuoittohallet +Comment[sk]=Prehraná hra +Comment[sl]=Igra je izgubljena +Comment[sr]=Игра је изгубљена +Comment[sr@Latn]=Igra je izgubljena +Comment[sv]=Du förlorade spelet +Comment[ta]=ஆட்டம் இழக்கப்பட்டது +Comment[tg]=Дар бозӣ мағлуб шудед +Comment[tr]=Oyun kaybedildi +Comment[uk]=Гра програна +Comment[wa]=Djeu pierdou +Comment[zh_CN]=您输了游戏 +Comment[zh_TW]=遊戲失敗 +default_presentation=0 + +[set_uncertain] +Name=Set question mark +Name[be]=Пазначыць пытальнікам +Name[bg]=Поставяне на въпросителна +Name[bn]=প্রশ্নবোধক চিহ্ন দিন +Name[bs]=Postavi upitnik +Name[ca]=Marca amb un interrogant +Name[cs]=Nastavit otazník +Name[cy]=Gosod gofynnod +Name[da]=Sæt spørgsmålstegn +Name[de]=Markierung setzen +Name[el]=Προσθήκη ερωτηματικού +Name[eo]=Meti demandsignon +Name[es]=Establecer signo de interrogación +Name[et]=Pane küsimärk +Name[eu]=Ezarri galdera-marka +Name[fa]=گذاردن علامت سؤال +Name[fi]=Aseta kysymysmerkki +Name[fr]=Ajout d'un point d'interrogation +Name[he]=הצב סימן שאלה +Name[hr]=Postavi oznaku pitanja +Name[hu]=Kérdőjel beállítása +Name[is]=Setja spurningamerkið +Name[it]=Metti punto interrogativo +Name[ja]=クエスチョンマークをセットする +Name[km]=កំណត់សញ្ញាសួរ +Name[lt]=Uždėti klaustuką +Name[lv]=Novietot jautājuma zīmi +Name[mk]=Поставен е прашалник +Name[nb]=Angi spørsmåltegn +Name[nds]=Fraagteken setten +Name[ne]=प्रश्न चिन्ह सेट गर्नुहोस् +Name[nl]=Vraagteken instellen +Name[nn]=Set spørjeteikn +Name[pa]=ਸਵਾਲੀਆ ਨਿਸ਼ਾਨ ਬਣਾਓ +Name[pl]=Wstaw znak zapytania +Name[pt]=Colocar um ponto de interrogação +Name[pt_BR]=Marcar como ponto de interrogação +Name[ru]=Отметка вопросом +Name[se]=Bija jearaldatmearkka +Name[sk]=Nastaviť otáznik +Name[sl]=Postavi vprašaj +Name[sr]=Постави знак питања +Name[sr@Latn]=Postavi znak pitanja +Name[sv]=Placerade frågetecken +Name[ta]=கேள்விக்குறியை அமை +Name[uk]=Поставити знак питання +Name[zh_CN]=设置了问号 +Name[zh_TW]=設定問號 +Comment=Set question mark +Comment[be]=Пазначыць пытальнікам +Comment[bg]=Поставяне на въпросителна +Comment[bn]=প্রশ্নবোধক চিহ্ন দিন +Comment[bs]=Postavi upitnik +Comment[ca]=Marca amb interrogant +Comment[cs]=Nastavit otazník +Comment[cy]=Gosod gofynnod +Comment[da]=Sæt spørgsmålstegn +Comment[de]=Markierung setzen +Comment[el]=Προσθήκη ερωτηματικού +Comment[eo]=Meti demandsignon +Comment[es]=Establecer signo de interrogación +Comment[et]=Pane küsimärk +Comment[eu]=Ezarri galdera-marka +Comment[fa]=گذاردن علامت سؤال +Comment[fi]=Aseta kysymysmerkki +Comment[fr]=Ajout d'un point d'interrogation +Comment[he]=הצב סימן שאלה +Comment[hr]=Postavi oznaku pitanja +Comment[hu]=Kérdőjel beállítása +Comment[is]=Setja spurningamerkið +Comment[it]=Metti punto interrogativo +Comment[ja]=クエスチョンマークをセットする +Comment[km]=កំណត់សញ្ញាសួរ +Comment[lt]=Uždėti klaustuką +Comment[lv]=Novieto jautājuma zīmi +Comment[mk]=Поставен е прашалник +Comment[nb]=Angi spørsmåltegn +Comment[nds]=Fraagteken setten +Comment[ne]=प्रश्न चिन्ह सेट गर्नुहोस् +Comment[nl]=Vraagteken instellen +Comment[nn]=Set spørjeteikn +Comment[pa]=ਸਵਾਲੀਆ ਨਿਸ਼ਾਨ ਦਿਓ +Comment[pl]=Wstawia znak zapytania +Comment[pt]=Colocar um ponto de interrogação +Comment[pt_BR]=Marcar como ponto de interrogação +Comment[ru]=Отметка вопросом +Comment[se]=Bija jearaldatmearkka +Comment[sk]=Nastaviť otáznik +Comment[sl]=Postavi vprašaj +Comment[sr]=Постави знак питања +Comment[sr@Latn]=Postavi znak pitanja +Comment[sv]=Placerade frågetecken +Comment[ta]=கேள்விக்குறியை அமை +Comment[uk]=Поставити знак питання +Comment[zh_CN]=设置了问号 +Comment[zh_TW]=設定問號 +default_presentation=0 + +[unset_uncertain] +Name=Unset question mark +Name[be]=Зняць пытальнік +Name[bg]=Премахване на въпросителна +Name[bn]=প্রশ্নবোধক চিহ্ন সরিয়ে ফেলুন +Name[bs]=Isključi upitnik +Name[ca]=Desmarca l'interrogant +Name[cs]=Zrušit nastavení otazníku +Name[cy]=Dadosod gofynnod +Name[da]=Fjern spørgsmålstegn +Name[de]=Markierung entfernen +Name[el]=Αφαίρεση του ερωτηματικού +Name[eo]=Malmeti demandsignon +Name[es]=Quitar signo de interrogación +Name[et]=Eemalda küsimärk +Name[eu]=Kendu galdera-marka +Name[fa]=برداشتن علامت سؤال +Name[fi]=Poista kysymysmerkki +Name[fr]=Retrait d'un point d'interrogation +Name[he]=הורד סימן שאלה +Name[hr]=Ukloni oznaku pitanja +Name[hu]=Kérdőjel megszüntetése +Name[is]=Afsetja spurningamerkið +Name[it]=Togli punto interrogativo +Name[ja]=クエスチョンマークを外す +Name[km]=ដោះកំណត់សញ្ញាសួរ +Name[lt]=Nuimti klaustuką +Name[lv]=Noņemt jautajuma zīmi +Name[mk]=Отстранет е прашалник +Name[nb]=Fjern spørsmåltegn +Name[nds]=Fraagteken wegmaken +Name[ne]=प्रश्न चिन्ह अनसेट गर्नुहोस् +Name[nl]=Vraagteken weghalen +Name[nn]=Fjern spørjeteikn +Name[pa]=ਸਵਾਲੀਆ ਨਿਸ਼ਾਨ ਹਟਾਓ +Name[pl]=Usuń znak zapytania +Name[pt]=Retirar um ponto de interrogação +Name[pt_BR]=Desmarcar ponto de interrogação +Name[ru]=Снятие знака вопроса +Name[se]=Váldde jearaldatmearkka eret +Name[sk]=Odstrániť otáznik +Name[sl]=Odstrani vprašaj +Name[sr]=Скини знак питања +Name[sr@Latn]=Skini znak pitanja +Name[sv]=Tog bort frågetecken +Name[ta]=கேள்விக் குறி அமைக்கவில்லை +Name[tr]=Soru işaretini kaldır +Name[uk]=Зняти знак питання +Name[zh_CN]=取消了问号 +Name[zh_TW]=取消問號 +Comment=Unset question mark +Comment[be]=Зняць пытальнік +Comment[bg]=Премахване на въпросителна +Comment[bn]=প্রশ্নবোধক চিহ্ন সরিয়ে ফেলুন +Comment[bs]=Isključi upitnik +Comment[ca]=Desmarca l'interrogant +Comment[cs]=Zrušit nastavení otazníku +Comment[cy]=Dadosod gofynnod +Comment[da]=Fjern spørgsmålstegn +Comment[de]=Markierung entfernen +Comment[el]=Αφαίρεση του ερωτηματικού +Comment[eo]=Malmeti demandsignon +Comment[es]=Quitar signo de interrogación +Comment[et]=Eemalda küsimärk +Comment[eu]=Kendu galdera-marka +Comment[fa]=برداشتن علامت سؤال +Comment[fi]=Poista kysymysmerkki +Comment[fr]=Retrait d'un point d'interrogation +Comment[he]=הורד סימן שאלה +Comment[hr]=Ukloni oznaku pitanja +Comment[hu]=Kérdőjel megszüntetése +Comment[is]=Afsetja spurningamerkið +Comment[it]=Togli punto interrogativo +Comment[ja]=クエスチョンマークを外す +Comment[km]=ដោះកំណត់សញ្ញាសួរ +Comment[lt]=Nuimti klaustuką +Comment[lv]=Noņem jautājuma zīmi +Comment[mk]=Отстранет е прашалник +Comment[nb]=Fjern spørsmåltegn +Comment[nds]=Fraagteken wegmaken +Comment[ne]=प्रश्न चिन्ह अनसेट गर्नुहोस् +Comment[nl]=Vraagteken weghalen +Comment[nn]=Fjern spørjeteikn +Comment[pl]=Usuwa znak zapytania +Comment[pt]=Retirar um ponto de interrogação +Comment[pt_BR]=Desmarcar ponto de interrogação +Comment[ru]=Снятие знака вопроса +Comment[se]=Váldde jearaldatmearkka eret +Comment[sk]=Odstrániť otáznik +Comment[sl]=Odstrani vprašaj +Comment[sr]=Скини знак питања +Comment[sr@Latn]=Skini znak pitanja +Comment[sv]=Tog bort frågetecken +Comment[ta]=கேள்விக்குறி நீக்கு +Comment[tr]=Soru işaretini kaldır +Comment[uk]=Зняти знак питання +Comment[zh_CN]=取消了问号 +Comment[zh_TW]=取消問號 +default_presentation=0 diff --git a/kmines/data/hi128-app-kmines.png b/kmines/data/hi128-app-kmines.png Binary files differnew file mode 100644 index 00000000..faaab0c2 --- /dev/null +++ b/kmines/data/hi128-app-kmines.png diff --git a/kmines/data/hi16-app-kmines.png b/kmines/data/hi16-app-kmines.png Binary files differnew file mode 100644 index 00000000..06585ce6 --- /dev/null +++ b/kmines/data/hi16-app-kmines.png diff --git a/kmines/data/hi22-app-kmines.png b/kmines/data/hi22-app-kmines.png Binary files differnew file mode 100644 index 00000000..fc1ae801 --- /dev/null +++ b/kmines/data/hi22-app-kmines.png diff --git a/kmines/data/hi32-app-kmines.png b/kmines/data/hi32-app-kmines.png Binary files differnew file mode 100644 index 00000000..10740280 --- /dev/null +++ b/kmines/data/hi32-app-kmines.png diff --git a/kmines/data/hi48-app-kmines.png b/kmines/data/hi48-app-kmines.png Binary files differnew file mode 100644 index 00000000..a1905746 --- /dev/null +++ b/kmines/data/hi48-app-kmines.png diff --git a/kmines/data/hi64-app-kmines.png b/kmines/data/hi64-app-kmines.png Binary files differnew file mode 100644 index 00000000..b4f5d27f --- /dev/null +++ b/kmines/data/hi64-app-kmines.png diff --git a/kmines/data/kmines.desktop b/kmines/data/kmines.desktop new file mode 100644 index 00000000..a74b3333 --- /dev/null +++ b/kmines/data/kmines.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Name=KMines +Name[af]=Kmyne +Name[ar]=لعبة الألغام (KMines) +Name[be]=Сапёр +Name[bn]=কে-মাইন্স +Name[hi]=के-माइन्स +Name[hr]=KMine +Name[ne]=केडीई बारूद +Name[pa]=ਕੇ-ਸਰੁੰਗ +Name[pl]=Miny +Name[pt_BR]=KMinas +Name[sv]=Kmines +Name[ta]=கேகன்னிவெடிகள் +Name[tg]=KСапёр +Name[th]=กู้ระเบิด - K +Name[wa]=KMenes +Name[zh_TW]=KMines 踩地雷 +Icon=kmines +Exec=kmines -caption "%c" %i %m +Type=Application +DocPath=kmines/index.html +GenericName=Minesweeper-like Game +GenericName[be]=Гульня ў сапёра +GenericName[bg]=Мини +GenericName[bn]=মাইনসুইপার-জাতীয় খেলা +GenericName[br]=C'hoari doare Minesweeper +GenericName[bs]=Igra minskog polja +GenericName[ca]=Joc a l'estil del buscamines +GenericName[cs]=Hra s minovým polem +GenericName[cy]=Gêm tebyg i Minesweeper +GenericName[da]=Minesøger-lignende spil +GenericName[de]=Minesweeper-ähnliches Spiel +GenericName[el]=Παιχνίδι παρόμοιο με το ναρκαλιευτή +GenericName[eo]="Minesweeper"-simila ludo +GenericName[es]=Juego similar al buscaminas +GenericName[et]=Miiniväljamäng +GenericName[eu]=Mina bilatzailearen antzeko jokoa +GenericName[fa]=بازی Minesweeper-like +GenericName[fi]=Kaivostyylinen peli +GenericName[fr]=Jeu dans le style du démineur +GenericName[he]=חיקוי שולה מוקשים +GenericName[hr]=Igra s poput Minesweepera +GenericName[hu]=Aknakereső +GenericName[is]=Leikur sem líkist Minesweeper +GenericName[it]=Gioco simile a Mine +GenericName[ja]=地雷ゲーム +GenericName[km]=ល្បែងដូច Minesweeper +GenericName[ko]=지뢰찾기 게임 +GenericName[lt]=Minesweeper primenantis žaidimas +GenericName[lv]=Minesweeper līdzīga spēle +GenericName[mk]=Игра слична на Minesweeper +GenericName[nb]=Minesveiper-lignende spill +GenericName[nds]=Minesweeper-liek Speel +GenericName[ne]=बारूद हटाउने जस्तै खेल +GenericName[nl]=Mijnenveger-achtig spel +GenericName[nn]=Minesveipar-liknande spel +GenericName[pa]=ਸੁਰੰਗ ਹਟਾਓਣ ਵਰਗੀ ਖੇਡ +GenericName[pl]=Gra typu saper +GenericName[pt]=Jogo tipo Minas +GenericName[pt_BR]=Jogo parecido com Campo Minado +GenericName[ru]=Сапёр +GenericName[sk]=Hra typu Minesweeper +GenericName[sl]=Igra, podobna Minesweeperju +GenericName[sr]=Игра налик на миноловац +GenericName[sr@Latn]=Igra nalik na minolovac +GenericName[sv]=Minröjarliknande spel +GenericName[ta]=சுரங்கம்வெட்டும் விளையாட்டு +GenericName[uk]=Гра в сапера +GenericName[wa]=On djeu di tchamp d' menes +GenericName[zh_CN]=扫雷 +GenericName[zh_TW]=類似踩地雷的遊戲 +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/kmines/defines.cpp b/kmines/defines.cpp new file mode 100644 index 00000000..b008de00 --- /dev/null +++ b/kmines/defines.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "defines.h" + +#include <klocale.h> + +const char *Level::LABELS[NB_TYPES+1] = { + I18N_NOOP("Easy"), I18N_NOOP("Normal"), I18N_NOOP("Expert"), + I18N_NOOP("Custom") +}; + +const Level::Data Level::DATA[NB_TYPES] = { + { 8, 8, 10, "easy", "8x8x10", }, + {16, 16, 40, "normal", "16x16x40", }, + {30, 16, 99, "expert", "30x16x99", } +}; + +Level::Level(Type type) +{ + Q_ASSERT( type!=Custom ); + _width = DATA[type].width; + _height = DATA[type].height; + _nbMines = DATA[type].nbMines; +} + +Level::Level(uint width, uint height, uint nbMines) + : _width(width), _height(height), _nbMines(nbMines) +{ + Q_ASSERT( width>=2 && height>=2 ); + if (_nbMines > maxNbMines(width, height) ) + _nbMines = maxNbMines(width, height); +} + +Level::Type Level::type() const +{ + for (uint i=0; i<NB_TYPES; i++) + if ( _width==DATA[i].width && _height==DATA[i].height + && _nbMines==DATA[i].nbMines ) return (Type)i; + return Custom; +} + +const char *KMines::STATES[NB_STATES] = { + "playing", "paused", "gameover", "stopped", "replaying", "init" +}; diff --git a/kmines/defines.h b/kmines/defines.h new file mode 100644 index 00000000..145a29cc --- /dev/null +++ b/kmines/defines.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef DEFINES_H +#define DEFINES_H + +#include <qcolor.h> + + +class Level +{ + public: + enum Type { Easy = 0, Normal, Expert, NB_TYPES, Custom = NB_TYPES }; + static const char *LABELS[NB_TYPES+1]; + struct Data { + uint width, height, nbMines; + const char *label, *wwLabel; + }; + static const Data DATA[NB_TYPES]; + + Level(Type); + Level(uint width, uint height, uint nbMines); + + uint width() const { return _width; } + uint height() const { return _height; } + uint nbMines() const { return _nbMines; } + Type type() const; + static uint maxNbMines(uint width, uint height) { return width*height - 2;} + + bool operator ==(const Level &level) const { + return ( _width==level._width && _height==level._height && + _nbMines==level._nbMines ); + } + + private: + uint _width, _height, _nbMines; +}; + +class KMines +{ + public: + enum GameState { Playing = 0, Paused, GameOver, Stopped, Replaying, + Init, NB_STATES }; + static const char *STATES[NB_STATES]; + enum SolvingState { Regular, Advised, Solved }; + + enum CaseState { Covered, Uncovered, Uncertain, Marked, Exploded, Error }; + struct Case { + bool mine; + CaseState state; + }; + + enum NumberColor { NB_N_COLORS = 8 }; + enum Mood { Normal = 0, Stressed, Happy, Sad, Sleeping, NbMoods }; +}; + +#endif diff --git a/kmines/dialogs.cpp b/kmines/dialogs.cpp new file mode 100644 index 00000000..d02b2eea --- /dev/null +++ b/kmines/dialogs.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 1996-2003 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "dialogs.h" +#include "dialogs.moc" + +#include <qpixmap.h> +#include <qvgroupbox.h> +#include <qlayout.h> +#include <qhbox.h> +#include <qvbox.h> +#include <qgrid.h> +#include <qlabel.h> +#include <qtimer.h> +#include <qwhatsthis.h> +#include <qcheckbox.h> + +#include <klocale.h> +#include <kmessagebox.h> +#include <kcombobox.h> +#include <knuminput.h> +#include <kcolorbutton.h> +#include <kconfig.h> +#include <kapplication.h> +#include <kdialogbase.h> + +#include "settings.h" + +#include "bitmaps/smile" +#include "bitmaps/smile_happy" +#include "bitmaps/smile_ohno" +#include "bitmaps/smile_stress" +#include "bitmaps/smile_sleep" + + +//----------------------------------------------------------------------------- +const char **Smiley::XPM_NAMES[NbMoods] = { + smile_xpm, smile_stress_xpm, smile_happy_xpm, smile_ohno_xpm, + smile_sleep_xpm +}; + +void Smiley::setMood(Mood mood) +{ + QPixmap p(XPM_NAMES[mood]); + setPixmap(p); +} + +//----------------------------------------------------------------------------- +DigitalClock::DigitalClock(QWidget *parent) +: KGameLCDClock(parent, "digital_clock") +{ + setFrameStyle(Panel | Sunken); + setDefaultBackgroundColor(black); + setDefaultColor(white); +} + +KExtHighscore::Score DigitalClock::score() const +{ + KExtHighscore::Score score(KExtHighscore::Won); + score.setScore(3600 - seconds()); + score.setData("nb_actions", _nbActions); + return score; +} + +void DigitalClock::timeoutClock() +{ + KGameLCDClock::timeoutClock(); + + if ( _cheating || _customGame ) setColor(white); + else if ( _first<score() ) setColor(red); + else if ( _last<score() ) setColor(blue); + else setColor(white); +} + +void DigitalClock::start() +{ + KGameLCDClock::start(); + if ( !_cheating && !_customGame ) setColor(red); +} + +void DigitalClock::reset(bool customGame) +{ + _nbActions = 0; + _customGame = customGame; + if ( !customGame ) { + _first = KExtHighscore::firstScore(); + _last = KExtHighscore::lastScore(); + } + _cheating = false; + KGameLCDClock::reset(); + resetColor(); +} + +void DigitalClock::setCheating() +{ + _cheating = true; + setColor(white); +} + +//----------------------------------------------------------------------------- + +const uint CustomConfig::maxWidth = 50; +const uint CustomConfig::minWidth = 5; +const uint CustomConfig::maxHeight = 50; +const uint CustomConfig::minHeight = 5; + +CustomConfig::CustomConfig() + : QWidget(0, "custom_config_widget"), _block(false) +{ + QVBoxLayout *top = new QVBoxLayout(this, KDialog::spacingHint()); + + _width = new KIntNumInput(this, "kcfg_CustomWidth"); + _width->setLabel(i18n("Width:")); + _width->setRange(minWidth, maxWidth); + connect(_width, SIGNAL(valueChanged(int)), SLOT(updateNbMines())); + top->addWidget(_width); + + _height = new KIntNumInput(this, "kcfg_CustomHeight"); + _height->setLabel(i18n("Height:")); + _height->setRange(minWidth, maxWidth); + connect(_height, SIGNAL(valueChanged(int)), SLOT(updateNbMines())); + top->addWidget(_height); + + _mines = new KIntNumInput(this, "kcfg_CustomMines"); + _mines->setLabel(i18n("No. of mines:")); + _mines->setRange(1, Level::maxNbMines(maxWidth, maxHeight)); + connect(_mines, SIGNAL(valueChanged(int)), SLOT(updateNbMines())); + top->addWidget(_mines); + + top->addSpacing(2 * KDialog::spacingHint()); + + // combo to choose level + QHBoxLayout *hbox = new QHBoxLayout(top); + QLabel *label = new QLabel(i18n("Choose level:"), this); + hbox->addWidget(label); + _gameType = new KComboBox(false, this); + connect(_gameType, SIGNAL(activated(int)), SLOT(typeChosen(int))); + for (uint i=0; i<=Level::NB_TYPES; i++) + _gameType->insertItem(i18n(Level::LABELS[i])); + hbox->addWidget(_gameType); + hbox->addWidget(new QWidget(this), 1); + + top->addStretch(1); +} + +void CustomConfig::updateNbMines() +{ + if (_block) return; + _block = true; + Level l(_width->value(), _height->value(), _mines->value()); + _mines->setRange(1, Level::maxNbMines(l.width(), l.height())); + _mines->setLabel(i18n("Mines (%1%):") + .arg( (100*l.nbMines()) / (l.width() * l.height()) )); + _gameType->setCurrentItem(l.type()); + _block = false; +} + +void CustomConfig::typeChosen(int i) +{ + if (_block) return; + _block = true; + Level::Type type = (Level::Type)i; + if ( type==Level::Custom ) { + Level level = Settings::customLevel(); + _width->setValue(level.width()); + _height->setValue(level.height()); + _mines->setRange(1, Level::maxNbMines(level.width(), level.height())); + _mines->setValue(level.nbMines()); + } else { + Level level(type); + _width->setValue(level.width()); + _height->setValue(level.height()); + _mines->setRange(1, Level::maxNbMines(level.width(), level.height())); + _mines->setValue(level.nbMines()); + } + _block = false; + updateNbMines(); +} + +//----------------------------------------------------------------------------- +static const char *MOUSE_BUTTON_LABELS[Settings::EnumButton::COUNT] = { + I18N_NOOP("Left button:"), I18N_NOOP("Middle button:"), + I18N_NOOP("Right button:") +}; + +static const char *MOUSE_CONFIG_NAMES[Settings::EnumButton::COUNT] = { + "kcfg_leftMouseAction", "kcfg_midMouseAction", + "kcfg_rightMouseAction" +}; + +static const char *MOUSE_ACTION_LABELS[Settings::EnumMouseAction::COUNT-1] = { + I18N_NOOP("Reveal"), I18N_NOOP("Autoreveal"), + I18N_NOOP("Toggle Flag"), I18N_NOOP("Toggle ? Flag") +}; + +GameConfig::GameConfig() + : QWidget(0, "game_config_widget"), _magicDialogEnabled(false) +{ + QVBoxLayout *top = new QVBoxLayout(this, KDialog::spacingHint()); + + QCheckBox *cb = new QCheckBox(i18n("Enable ? mark"), this, "kcfg_UncertainMark"); + top->addWidget(cb); + + cb = new QCheckBox(i18n("Enable keyboard"), this, "kcfg_KeyboardGame"); + top->addWidget(cb); + + cb = new QCheckBox(i18n("Pause if window loses focus"), this, "kcfg_PauseFocus"); + top->addWidget(cb); + + cb = new QCheckBox(i18n("\"Magic\" reveal"), this, "kcfg_MagicReveal"); + QWhatsThis::add(cb, i18n("Set flags and reveal squares where they are trivial.")); + connect(cb, SIGNAL(toggled(bool)), SLOT(magicModified(bool))); + top->addWidget(cb); + + top->addSpacing(2 * KDialog::spacingHint()); + + QHBoxLayout *hbox = new QHBoxLayout(top); + QVGroupBox *gb = new QVGroupBox(i18n("Mouse Bindings"), this); + hbox->addWidget(gb); + QGrid *grid = new QGrid(2, gb); + grid->setSpacing(KDialog::spacingHint()); + for (uint i=0; i< Settings::EnumButton::COUNT; i++) { + (void)new QLabel(i18n(MOUSE_BUTTON_LABELS[i]), grid); + QComboBox *cb = new QComboBox(false, grid, MOUSE_CONFIG_NAMES[i]); + for (uint k=0; k< (Settings::EnumMouseAction::COUNT-1); k++) + cb->insertItem(i18n(MOUSE_ACTION_LABELS[k])); + cb->setCurrentItem(i); + } + hbox->addStretch(1); + + top->addStretch(1); +} + +void GameConfig::magicModified(bool on) +{ + if ( !_magicDialogEnabled || !on ) return; + KMessageBox::information(this, i18n("When the \"magic\" reveal is on, you lose the ability to enter the highscores."), QString::null, "magic_reveal_warning"); +} + +//----------------------------------------------------------------------------- +static const char *COLOR_LABELS[Settings::EnumType::COUNT] = { + I18N_NOOP("Flag color:"), I18N_NOOP("Explosion color:"), + I18N_NOOP("Error color:") +}; + +static const char *COLOR_CONFIG_NAMES[Settings::EnumType::COUNT] = { + "kcfg_flagColor", "kcfg_explosionColor", "kcfg_errorColor" +}; + +static const char *N_COLOR_CONFIG_NAMES[KMines::NB_N_COLORS] = { + "kcfg_MineColor0", "kcfg_MineColor1", "kcfg_MineColor2", + "kcfg_MineColor3", "kcfg_MineColor4", "kcfg_MineColor5", + "kcfg_MineColor6", "kcfg_MineColor7" +}; + +AppearanceConfig::AppearanceConfig() + : QWidget(0, "appearance_config_widget") +{ + QVBoxLayout *top = new QVBoxLayout(this, KDialog::spacingHint()); + + QHBoxLayout *hbox = new QHBoxLayout(top); + QGrid *grid = new QGrid(2, this); + grid->setSpacing(KDialog::spacingHint()); + hbox->addWidget(grid); + for (uint i=0; i<Settings::EnumType::COUNT; i++) { + (void)new QLabel(i18n(COLOR_LABELS[i]), grid); + KColorButton *cb = new KColorButton(grid, COLOR_CONFIG_NAMES[i]); + cb->setFixedWidth(100); + } + for (uint i=0; i<NB_N_COLORS; i++) { + (void)new QLabel(i18n("%n mine color:", "%n mines color:", i+1), grid); + KColorButton *cb = new KColorButton(grid, N_COLOR_CONFIG_NAMES[i]); + cb->setFixedWidth(100); + } + hbox->addStretch(1); + + top->addStretch(1); +} + diff --git a/kmines/dialogs.h b/kmines/dialogs.h new file mode 100644 index 00000000..12dde5ac --- /dev/null +++ b/kmines/dialogs.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1996-2003 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef DIALOGS_H +#define DIALOGS_H + +#include <qpushbutton.h> + +#include <kgamelcd.h> +#include <kexthighscore.h> + +#include "defines.h" +#include "settings.h" + +class KComboBox; +class KIntNumInput; + +//----------------------------------------------------------------------------- +class Smiley : public QPushButton, public KMines +{ + Q_OBJECT + public: + Smiley(QWidget *parent, const char *name = 0) + : QPushButton(QString::null, parent, name) {} + + public slots: + void setMood(Mood); + + private: + static const char **XPM_NAMES[NbMoods]; +}; + +//----------------------------------------------------------------------------- +class DigitalClock : public KGameLCDClock +{ + Q_OBJECT + public: + DigitalClock(QWidget *parent); + + void reset(bool customGame); + + bool cheating() const { return _cheating; } + uint nbActions() const { return _nbActions; } + KExtHighscore::Score score() const; + + public slots: + void start(); + void setCheating(); + void addAction() { _nbActions++; } + + private slots: + void timeoutClock(); + + private: + KExtHighscore::Score _first, _last; + uint _nbActions; + bool _cheating, _customGame; +}; + +//----------------------------------------------------------------------------- +class CustomConfig : public QWidget, public KMines +{ + Q_OBJECT + public: + CustomConfig(); + + static const uint maxWidth; + static const uint minWidth; + static const uint maxHeight; + static const uint minHeight; + + void init() { updateNbMines(); } + + private slots: + void typeChosen(int); + void updateNbMines(); + + private: + bool _block; + KIntNumInput *_width, *_height, *_mines; + KComboBox *_gameType; +}; + +//----------------------------------------------------------------------------- +class GameConfig : public QWidget, public KMines +{ + Q_OBJECT + public: + GameConfig(); + + static void saveLevel(Level::Type); + + void init() { _magicDialogEnabled = true; } + + private slots: + void magicModified(bool); + + private: + bool _magicDialogEnabled; + +}; + +class AppearanceConfig : public QWidget, public KMines +{ + Q_OBJECT + public: + AppearanceConfig(); +}; + +#endif diff --git a/kmines/field.cpp b/kmines/field.cpp new file mode 100644 index 00000000..1f3b3dfd --- /dev/null +++ b/kmines/field.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "field.h" +#include "field.moc" + +#include <math.h> + +#include <qlayout.h> +#include <qtimer.h> +#include <qpainter.h> + +#include <klocale.h> +#include <knotifyclient.h> + +#include "settings.h" +#include "solver/solver.h" +#include "dialogs.h" + + +using namespace KGrid2D; + +const Field::ActionData Field::ACTION_DATA[Nb_Actions] = { + { "Reveal", "reveal", I18N_NOOP("Case revealed") }, + { "AutoReveal", "autoreveal", I18N_NOOP("Case autorevealed") }, + { "SetFlag", "mark", I18N_NOOP("Flag set") }, + { "UnsetFlag", "unmark", I18N_NOOP("Flag unset") }, + { "SetUncertain", "set_uncertain", I18N_NOOP("Question mark set") }, + { "UnsetUncertain", "unset_uncertain", I18N_NOOP("Question mark unset") } +}; + +Field::Field(QWidget *parent) + : FieldFrame(parent), _state(Init), _solvingState(Regular), _level(Level::Easy) +{} + +void Field::readSettings() +{ + if ( inside(_cursor) ) { + QPainter p(this); + drawCase(p, _cursor); + } + if ( Settings::magicReveal() ) emit setCheating(); +} + +QSize Field::sizeHint() const +{ + return QSize(2*frameWidth() + _level.width()*Settings::caseSize(), + 2*frameWidth() + _level.height()*Settings::caseSize()); +} + +void Field::setLevel(const Level &level) +{ + _level = level; + reset(false); + adjustSize(); +} + +void Field::setReplayField(const QString &field) +{ + setState(Replaying); + initReplay(field); +} + +void Field::setState(GameState state) +{ + Q_ASSERT( state!=GameOver ); + emit gameStateChanged(state); + _state = state; +} + +void Field::reset(bool init) +{ + BaseField::reset(_level.width(), _level.height(), _level.nbMines()); + if ( init || _state==Init ) setState(Init); + else setState(Stopped); + if (Settings::magicReveal()) emit setCheating(); + _currentAction = Settings::EnumMouseAction::None; + _reveal = false; + _cursor.first = _level.width()/2; + _cursor.second = _level.height()/2; + _advisedCoord = Coord(-1, -1); + update(); +} + +void Field::paintEvent(QPaintEvent *e) +{ + QPainter painter(this); + drawFrame(&painter); + if ( _state==Paused ) return; + + Coord min = fromPoint(e->rect().topLeft()); + bound(min); + Coord max = fromPoint(e->rect().bottomRight()); + bound(max); + for (short i=min.first; i<=max.first; i++) + for (short j=min.second; j<=max.second; j++) + drawCase(painter, Coord(i,j)); +} + +void Field::changeCase(const Coord &p, CaseState newState) +{ + BaseField::changeCase(p, newState); + QPainter painter(this); + drawCase(painter, p); + if ( isActive() ) emit updateStatus( hasMine(p) ); +} + +QPoint Field::toPoint(const Coord &p) const +{ + QPoint qp; + qp.setX( p.first*Settings::caseSize() + frameWidth() ); + qp.setY( p.second*Settings::caseSize() + frameWidth() ); + return qp; +} + +Coord Field::fromPoint(const QPoint &qp) const +{ + double i = (double)(qp.x() - frameWidth()) / Settings::caseSize(); + double j = (double)(qp.y() - frameWidth()) / Settings::caseSize(); + return Coord((int)floor(i), (int)floor(j)); +} + +int Field::mapMouseButton(QMouseEvent *e) const +{ + switch (e->button()) { + case Qt::LeftButton: return Settings::mouseAction(Settings::EnumButton::left); + case Qt::MidButton: return Settings::mouseAction(Settings::EnumButton::mid); + case Qt::RightButton: return Settings::mouseAction(Settings::EnumButton::right); + default: return Settings::EnumMouseAction::ToggleFlag; + } +} + +void Field::revealActions(bool press) +{ + if ( _reveal==press ) return; // avoid flicker + _reveal = press; + + switch (_currentAction) { + case Reveal: + pressCase(_cursor, press); + break; + case AutoReveal: + pressClearFunction(_cursor, press); + break; + default: + break; + } +} + +void Field::mousePressEvent(QMouseEvent *e) +{ + if ( !isActive() || (_currentAction!=Settings::EnumMouseAction::None) ) return; + + emit setMood(Stressed); + _currentAction = mapMouseButton(e); + + Coord p = fromPoint(e->pos()); + if ( !inside(p) ) return; + placeCursor(p); + revealActions(true); +} + +void Field::mouseReleaseEvent(QMouseEvent *e) +{ + if ( !isActive() ) return; + + int tmp = _currentAction; + emit setMood(Normal); + revealActions(false); + int ma = mapMouseButton(e); + _currentAction = Settings::EnumMouseAction::None; + if ( ma!=tmp ) return; + + Coord p = fromPoint(e->pos()); + if ( !inside(p) ) return; + placeCursor(p); + + switch (ma) { + case Settings::EnumMouseAction::ToggleFlag: doMark(p); break; + case Settings::EnumMouseAction::ToggleUncertainFlag: doUmark(p); break; + case Settings::EnumMouseAction::Reveal: doReveal(p); break; + case Settings::EnumMouseAction::AutoReveal: doAutoReveal(p); break; + default: break; + } +} + +void Field::mouseMoveEvent(QMouseEvent *e) +{ + if ( !isActive() ) return; + + Coord p = fromPoint(e->pos()); + if ( p==_cursor ) return; // avoid flicker + + revealActions(false); + if ( !inside(p) ) return; + placeCursor(p); + revealActions(true); +} + +void Field::pressCase(const Coord &c, bool pressed) +{ + if ( state(c)==Covered ) { + QPainter painter(this); + drawCase(painter, c, pressed); + } +} + +void Field::pressClearFunction(const Coord &p, bool pressed) +{ + pressCase(p, pressed); + CoordList n = coveredNeighbours(p); + QPainter painter(this); + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + drawCase(painter, *it, pressed); +} + +void Field::keyboardAutoReveal() +{ + _cursor_back = _cursor; + pressClearFunction(_cursor_back, true); + QTimer::singleShot(50, this, SLOT(keyboardAutoRevealSlot())); +} + +void Field::keyboardAutoRevealSlot() +{ + pressClearFunction(_cursor_back, false); + doAutoReveal(_cursor_back); +} + +void Field::doAutoReveal(const Coord &c) +{ + if ( !isActive() ) return; + if ( state(c)!=Uncovered ) return; + emit addAction(c, AutoReveal); + resetAdvised(); + doAction(AutoReveal, c, Settings::magicReveal()); +} + +void Field::pause() +{ + switch (_state) { + case Paused: setState(Playing); break; + case Playing: setState(Paused); break; + default: return; + } + update(); +} + +void Field::moveCursor(Neighbour n) +{ + Coord c = neighbour(_cursor, n); + if ( inside(c) ) placeCursor(c); +} + +void Field::moveToEdge(Neighbour n) +{ + Coord c = toEdge(_cursor, n); + if ( inside(c) ) placeCursor(c); +} + +bool Field::doReveal(const Coord &c, CoordList *autorevealed, + bool *caseUncovered) +{ + if ( !isActive() ) return true; + if ( state(c)!=Covered ) return true; + if ( firstReveal() ) setState(Playing); + CaseState state = + doAction(Reveal, c, Settings::magicReveal(), autorevealed, caseUncovered); + emit addAction(c, Reveal); + return ( state!=Error ); +} + +void Field::doMark(const Coord &c) +{ + if ( !isActive() ) return; + ActionType action; + CaseState oldState = state(c); + switch (oldState) { + case Covered: action = SetFlag; break; + case Marked: action = (Settings::uncertainMark() ? SetUncertain : UnsetFlag); break; + case Uncertain: action = UnsetUncertain; break; + default: return; + } + CaseState newState = doAction(action, c, Settings::magicReveal()); + addMarkAction(c, newState, oldState); +} + +void Field::doUmark(const Coord &c) +{ + if ( !isActive() ) return; + ActionType action; + CaseState oldState = state(c); + switch (oldState) { + case Covered: + case Marked: action = SetUncertain; break; + case Uncertain: action = UnsetUncertain; break; + default: return; + } + CaseState newState = doAction(action, c, Settings::magicReveal()); + addMarkAction(c, newState, oldState); +} + + +KMines::CaseState Field::doAction(ActionType type, const Coord &c, + bool complete, CoordList *autorevealed, + bool *caseUncovered) +{ + resetAdvised(); + CaseState state = Error; + if ( _solvingState==Solved ) complete = false; + + KNotifyClient::event(winId(), ACTION_DATA[type].event, + i18n(ACTION_DATA[type].eventMessage)); + switch (type) { + case Reveal: + if ( !reveal(c, autorevealed, caseUncovered) ) + emit gameStateChanged(GameOver); + else { + state = Uncovered; + if (complete) completeReveal(); + } + break; + case AutoReveal: + if ( !autoReveal(c, caseUncovered) ) + emit gameStateChanged(GameOver); + else { + state = Uncovered; + if (complete) completeReveal(); + } + break; + case SetFlag: + state = Marked; + if (complete) completeReveal(); + break; + case UnsetFlag: + case UnsetUncertain: + state = Covered; + break; + case SetUncertain: + state = Uncertain; + break; + case Nb_Actions: + Q_ASSERT(false); + break; + } + + if ( state!=Error ) changeCase(c, state); + return state; +} + +void Field::addMarkAction(const Coord &c, CaseState newS, CaseState oldS) +{ + switch (newS) { + case Marked: emit addAction(c, SetFlag); return; + case Uncertain: emit addAction(c, SetUncertain); return; + default: break; + } + switch (oldS) { + case Marked: emit addAction(c, UnsetFlag); return; + case Uncertain: emit addAction(c, UnsetUncertain); return; + default: break; + } +} + +void Field::placeCursor(const Coord &p) +{ + if ( !isActive() ) return; + + Q_ASSERT( inside(p) ); + Coord old = _cursor; + _cursor = p; + if ( Settings::keyboardGame() ) { + QPainter painter(this); + drawCase(painter, old); + drawCase(painter, _cursor); + } +} + +void Field::resetAdvised() +{ + if ( !inside(_advisedCoord) ) return; + QPainter p(this); + Coord tmp = _advisedCoord; + _advisedCoord = Coord(-1, -1); + drawCase(p, tmp); +} + +void Field::setAdvised(const Coord &c, double proba) +{ + resetAdvised(); + _solvingState = Advised; + _advisedCoord = c; + _advisedProba = proba; + if ( inside(c) ) { + QPainter p(this); + drawCase(p, c); + } +} + +void Field::drawCase(QPainter &painter, const Coord &c, bool pressed) const +{ + Q_ASSERT( inside(c) ); + + QString text; + uint nbMines = 0; + PixmapType type = NoPixmap; + + switch ( state(c) ) { + case Covered: + break; + case Marked: + type = FlagPixmap; + pressed = false; + break; + case Error: + type = ErrorPixmap; + pressed = true; + break; + case Uncertain: + text = '?'; + pressed = false; + break; + case Exploded: + type = ExplodedPixmap; + pressed = true; + break; + case Uncovered: + pressed = true; + if ( hasMine(c) ) type = MinePixmap; + else { + nbMines = nbMinesAround(c); + if (nbMines) text.setNum(nbMines); + } + } + + int i = -1; + if ( c==_advisedCoord ) { + if ( _advisedProba==1 ) i = 0; + else if ( _advisedProba>0.75 ) i = 1; + else if ( _advisedProba>0.5 ) i = 2; + else if ( _advisedProba>0.25 ) i = 3; + else i = 4; + } + + bool hasFocus = ( Settings::keyboardGame() && (c==_cursor) ); + drawBox(painter, toPoint(c), pressed, type, text, nbMines, i, hasFocus); +} diff --git a/kmines/field.h b/kmines/field.h new file mode 100644 index 00000000..32b959fe --- /dev/null +++ b/kmines/field.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FIELD_H +#define FIELD_H + +#include "solver/bfield.h" +#include "frame.h" + + +//----------------------------------------------------------------------------- +class Field : public FieldFrame, public BaseField +{ + Q_OBJECT + public: + enum ActionType { Reveal = 0, AutoReveal, SetFlag, UnsetFlag, SetUncertain, + UnsetUncertain, Nb_Actions }; + struct ActionData { + const char *name, *event, *eventMessage; + }; + static const ActionData ACTION_DATA[Nb_Actions]; + + public: + Field(QWidget *parent); + + virtual QSize sizeHint() const; + + void setLevel(const Level &level); + void setReplayField(const QString &field); + const Level &level() const { return _level; } + void reset(bool init); + + GameState gameState() const { return _state; } + bool isActive() const { return _state!=Paused && _state!=GameOver; } + void pause(); + void setGameOver() { _state = GameOver; } + + void moveCursor(Neighbour); + void moveToEdge(Neighbour); + void doReveal() { doReveal(_cursor); } + void doMark() { doMark(_cursor); } + void doUmark() { doUmark(_cursor); } + void keyboardAutoReveal(); + CaseState doAction(ActionType type, const KGrid2D::Coord &c, + bool completeReveal, KGrid2D::CoordList *autorevealed = 0, + bool *caseUncovered = 0); + + void readSettings(); + + void setAdvised(const KGrid2D::Coord &c, double proba); + void setSolvingState(SolvingState state) { _solvingState = state; } + SolvingState solvingState() const { return _solvingState; } + + signals: + void updateStatus(bool); + void gameStateChanged(GameState); + void setMood(Mood); + void setCheating(); + void addAction(const KGrid2D::Coord &, Field::ActionType); + + protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + + private slots: + void keyboardAutoRevealSlot(); + + private: + GameState _state; + bool _reveal; + SolvingState _solvingState; + KGrid2D::Coord _cursor, _cursor_back, _advisedCoord; + double _advisedProba; + int _currentAction; + Level _level; + + void pressCase(const KGrid2D::Coord &, bool); + void pressClearFunction(const KGrid2D::Coord &, bool); + void placeCursor(const KGrid2D::Coord &); + void revealActions(bool press); + + void doAutoReveal(const KGrid2D::Coord &); + bool doReveal(const KGrid2D::Coord &, KGrid2D::CoordList *autorevealed = 0, + bool *caseUncovered = 0); + void doMark(const KGrid2D::Coord &); + void doUmark(const KGrid2D::Coord &); + void changeCase(const KGrid2D::Coord &, CaseState newState); + void addMarkAction(const KGrid2D::Coord &, CaseState newS, CaseState oldS); + + QPoint toPoint(const KGrid2D::Coord &) const; + KGrid2D::Coord fromPoint(const QPoint &) const; + + void drawCase(QPainter &, const KGrid2D::Coord &, + bool forcePressed = false) const; + + int mapMouseButton(QMouseEvent *) const; + void resetAdvised(); + void setState(GameState); +}; + +#endif // FIELD_H diff --git a/kmines/frame.cpp b/kmines/frame.cpp new file mode 100644 index 00000000..a502b87d --- /dev/null +++ b/kmines/frame.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "frame.h" + +#include <qpainter.h> +#include <qbitmap.h> +#include <qstyle.h> +#include <qdrawutil.h> + +#include "settings.h" + + +FieldFrame::FieldFrame(QWidget *parent) + : QFrame(parent, "field"), _button(0) +{ + setFrameStyle( QFrame::Box | QFrame::Raised ); + setLineWidth(2); + setMidLineWidth(2); +} + +void FieldFrame::adjustSize() +{ + setFixedSize(sizeHint()); + _button.resize(Settings::caseSize(), Settings::caseSize()); + + QBitmap mask; + for (uint i=0; i<Nb_Pixmap_Types; i++) { + drawPixmap(mask, (PixmapType)i, true); + drawPixmap(_pixmaps[i], (PixmapType)i, false); + _pixmaps[i].setMask(mask); + } + for (uint i=0; i<Nb_Advised; i++) { + drawAdvised(mask, i, true); + drawAdvised(_advised[i], i, false); + _advised[i].setMask(mask); + } + + QFont f = font(); + f.setPointSize(QMAX(1, Settings::caseSize()-6)); + f.setBold(true); + setFont(f); +} + +void FieldFrame::initPixmap(QPixmap &pix, bool mask) const +{ + pix.resize(Settings::caseSize(), Settings::caseSize()); + if (mask) pix.fill(color0); +} + +void FieldFrame::drawPixmap(QPixmap &pix, PixmapType type, bool mask) const +{ + initPixmap(pix, mask); + QPainter p(&pix); + + if ( type==FlagPixmap ) { + p.setWindow(0, 0, 16, 16); + p.setPen( (mask ? color1 : black) ); + p.drawLine(6, 13, 14, 13); + p.drawLine(8, 12, 12, 12); + p.drawLine(9, 11, 11, 11); + p.drawLine(10, 2, 10, 10); + if (!mask) p.setPen(black); + p.setBrush( (mask ? color1 : Settings::color(Settings::EnumType::flag)) ); + p.drawRect(4, 3, 6, 5); + return; + } + + p.setWindow(0, 0, 20, 20); + if ( type==ExplodedPixmap ) + p.fillRect(2, 2, 16, 16, (mask ? color1 : Settings::color(Settings::EnumType::explosion))); + QPen pen(mask ? color1 : black, 1); + p.setPen(pen); + p.setBrush(mask ? color1 : black); + p.drawLine(10,3,10,18); + p.drawLine(3,10,18,10); + p.drawLine(5, 5, 16, 16); + p.drawLine(5, 15, 15, 5); + p.drawEllipse(5, 5, 11, 11); + p.fillRect(8, 8, 2, 2, (mask ? color1 : white)); + if ( type==ErrorPixmap ) { + if (!mask) { + pen.setColor(Settings::color(Settings::EnumType::error)); + p.setPen(pen); + } + p.drawLine(3, 3, 17, 17); + p.drawLine(4, 3, 17, 16); + p.drawLine(3, 4, 16, 17); + p.drawLine(3, 17, 17, 3); + p.drawLine(3, 16, 16, 3); + p.drawLine(4, 17, 17, 4); + } +} + +void FieldFrame::drawAdvised(QPixmap &pix, uint i, bool mask) const +{ + initPixmap(pix, mask); + QPainter p(&pix); + p.setWindow(0, 0, 16, 16); + p.setPen( QPen(mask ? color1 : Settings::mineColor(i), 2) ); + p.drawRect(3, 3, 11, 11); +} + +void FieldFrame::drawBox(QPainter &painter, const QPoint &p, + bool pressed, PixmapType type, const QString &text, + uint nbMines, int advised, + bool hasFocus) const +{ + qDrawShadePanel(&painter, p.x(), p.y(), _button.width(), _button.height(), + colorGroup(), pressed, 2, + &colorGroup().brush(QColorGroup::Background)); + if (hasFocus) { + painter.translate(p.x(), p.y()); + QRect fbr = style().subRect(QStyle::SR_PushButtonFocusRect, &_button); + style().drawPrimitive(QStyle::PE_FocusRect, &painter, fbr, + colorGroup(), QStyle::Style_Enabled); + painter.resetXForm(); + } + + QRect r(p, _button.size()); + const QPixmap *pixmap = (type==NoPixmap ? 0 : &_pixmaps[type]); + QColor color = (nbMines==0 ? black : Settings::mineColor(nbMines-1)); + style().drawItem(&painter, r, AlignCenter, colorGroup(), true, pixmap, + text, -1, &color); + if ( advised!=-1 ) + style().drawItem(&painter, r, AlignCenter, colorGroup(), true, + &_advised[advised], QString::null); +} diff --git a/kmines/frame.h b/kmines/frame.h new file mode 100644 index 00000000..91dc1541 --- /dev/null +++ b/kmines/frame.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FRAME_H +#define FRAME_H + +#include <qframe.h> +#include <qpixmap.h> +#include <qpushbutton.h> + +#include "defines.h" + +class QPainter; + +class FieldFrame : public QFrame, public KMines +{ + public: + FieldFrame(QWidget *parent); + + protected: + enum PixmapType { FlagPixmap = 0, MinePixmap, ExplodedPixmap, + ErrorPixmap, Nb_Pixmap_Types, + NoPixmap = Nb_Pixmap_Types }; + enum { Nb_Advised = 5 }; + + void drawBox(QPainter &, const QPoint &, bool pressed, + PixmapType, const QString &text, + uint nbMines, int advised, bool hasFocus) const; + virtual void adjustSize(); + + private: + QPushButton _button; + QPixmap _pixmaps[Nb_Pixmap_Types]; + QPixmap _advised[Nb_Advised]; + + void drawPixmap(QPixmap &, PixmapType, bool mask) const; + void drawAdvised(QPixmap &, uint i, bool mask) const; + void initPixmap(QPixmap &, bool mask) const; +}; + +#endif diff --git a/kmines/highscores.cpp b/kmines/highscores.cpp new file mode 100644 index 00000000..c0e40c10 --- /dev/null +++ b/kmines/highscores.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "highscores.h" + +#include <kurl.h> +#include <klocale.h> +#include <kapplication.h> +#include <kconfig.h> + +#include "version.h" +#include "defines.h" + + +namespace KExtHighscore +{ + +ExtManager::ExtManager() + : Manager(Level::NB_TYPES) +{ + setScoreType(MinuteTime); + setWWHighscores(KURL( HOMEPAGE ), SHORT_VERSION); + setShowStatistics(true); + const uint RANGE[16] = { 1, 3120, 3180, 3240, 3300, 3360, 3420, 3480, + 3510, 3540, 3550, 3560, 3570, 3580, 3590, 3600 }; + QMemArray<uint> s; + s.duplicate(RANGE, 16); + setScoreHistogram(s, ScoreBound); + + Item *item = new Item((uint)0, i18n("Clicks"), Qt::AlignRight); + addScoreItem("nb_actions", item); +} + +QString ExtManager::gameTypeLabel(uint gameType, LabelType type) const +{ + const Level::Data &data = Level::DATA[gameType]; + switch (type) { + case Icon: + case Standard: return data.label; + case I18N: return i18n(Level::LABELS[gameType]); + case WW: return data.wwLabel; + } + return QString::null; +} + +void ExtManager::convertLegacy(uint gameType) +{ + QString group; + switch ((Level::Type)gameType) { + case Level::Easy: group = "Easy level"; break; + case Level::Normal: group = "Normal level"; break; + case Level::Expert: group = "Expert level"; break; + case Level::NB_TYPES: Q_ASSERT(false); + } + + KConfigGroupSaver cg(kapp->config(), group); + QString name = cg.config()->readEntry("Name", QString::null); + if ( name.isNull() ) return; + if ( name.isEmpty() ) name = i18n("anonymous"); + uint minutes = cg.config()->readUnsignedNumEntry("Min", 0); + uint seconds = cg.config()->readUnsignedNumEntry("Sec", 0); + int score = 3600 - (minutes*60 + seconds); + if ( score<=0 ) return; + Score s(Won); + s.setScore(score); + s.setData("name", name); + submitLegacyScore(s); +} + +bool ExtManager::isStrictlyLess(const Score &s1, const Score &s2) const +{ + if ( s1.score()==s2.score() ) + // when time is same, favour more clicks (it means auto-reveal + // didn't help so much): + return s1.data("nb_actions").toUInt()<s2.data("nb_actions").toUInt(); + return Manager::isStrictlyLess(s1, s2); +} + +} diff --git a/kmines/highscores.h b/kmines/highscores.h new file mode 100644 index 00000000..d1f577a7 --- /dev/null +++ b/kmines/highscores.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef HIGHSCORES_H +#define HIGHSCORES_H + +#include <kexthighscore.h> +#include <kdemacros.h> + +namespace KExtHighscore +{ + +class KDE_EXPORT ExtManager : public Manager +{ + public: + ExtManager(); + + private: + QString gameTypeLabel(uint gameTye, LabelType) const; + void convertLegacy(uint gameType); + bool isStrictlyLess(const Score &s1, const Score &s2) const; +}; + +} + +#endif diff --git a/kmines/kmines.kcfg b/kmines/kmines.kcfg new file mode 100644 index 00000000..382b6ccd --- /dev/null +++ b/kmines/kmines.kcfg @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="kminesrc"/> + <group name="Options"> + <entry name="CaseSize" type="Int" key="case size"> + <label>The size of a square.</label> + <min>4</min> + <max>100</max> + <default>20</default> + </entry> + <entry name="CustomWidth" type="Int" key="custom width"> + <label>The width of the playing field.</label> + <min>5</min> + <max>50</max> + <default>10</default> + </entry> + <entry name="CustomHeight" type="Int" key="custom height"> + <label>The height of the playing field.</label> + <min>5</min> + <max>50</max> + <default>10</default> + </entry> + <entry name="CustomMines" type="Int" key="custom mines"> + <label>The number of mines in the playing field.</label> + <default>20</default> + </entry> + <entry name="UncertainMark" type="Bool" key="uncertain mark"> + <label>Whether the "uncertain" marker may be used.</label> + <default>true</default> + </entry> + <entry name="KeyboardGame" type="Bool" key="keyboard game"> + <label>Whether the game can be played using the keyboard.</label> + <default>false</default> + </entry> + <entry name="PauseFocus" type="Bool" key="pause focus"> + <label>Whether the game is paused when the window loses focus.</label> + <default>true</default> + </entry> + <entry name="MagicReveal" type="Bool" key="magic reveal"> + <label>Whether to set flags and reveal squares in trivial situations.</label> + <default>false</default> + </entry> + <entry name="level" type="Enum"> + <label>The difficulty level.</label> + <default>Easy</default> + <choices> + <choice name="Easy"/> + <choice name="Normal"/> + <choice name="Expert"/> + <choice name="Custom"/> + </choices> + </entry> + + <entry name="$(Button)MouseAction" type="Enum" key="mouse $(Button)"> + <choices> + <choice name="Reveal"/> + <choice name="AutoReveal"/> + <choice name="ToggleFlag"/> + <choice name="ToggleUncertainFlag"/> + <choice name="None"/> + </choices> + <parameter name="Button" type="Enum"> + <values> + <value>left</value> + <value>mid</value> + <value>right</value> + </values> + </parameter> + <label>Mouse button actions</label> + <default param="left">Reveal</default> + <default param="mid">AutoReveal</default> + <default param="right">ToggleFlag</default> + </entry> + <entry name="$(Type)Color" type="Color" key="$(Type) color"> + <parameter name="Type" type="Enum"> + <values> + <value>flag</value> + <value>explosion</value> + <value>error</value> + </values> + </parameter> + <label>Color</label> + <default>#ff0000</default> + </entry> + <entry name="MineColor$(number)" type="Color" key="color #$(number)"> + <parameter name="number" type="Int" max="7"/> + <label>Mine Color</label> + <default param="0">#0000ff</default> + <default param="1">#008800</default> + <default param="2">#888800</default> + <default param="3">#880088</default> + <default param="4">#ff0000</default> + <default param="5">#880000</default> + <default param="6">#000000</default> + <default param="7">#000000</default> + </entry> + <entry name="MenubarVisible" type="Bool" key="menubar visible"> + <label>Whether the menubar is visible.</label> + <default>true</default> + </entry> + </group> +</kcfg> diff --git a/kmines/kminesui.rc b/kmines/kminesui.rc new file mode 100644 index 00000000..cd1ef166 --- /dev/null +++ b/kmines/kminesui.rc @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<gui name="kmines" version="21"> + +<MenuBar> + <Menu name="move"><text>&Move</text> + <Action name="solve_rate" append="move_solve_merge"/> + <Action name="log_view" /> + <Action name="log_replay" /> + <Action name="log_save" /> + <Action name="log_load" /> + </Menu> +</MenuBar> + +<Menu name="popup"> + <Action name="options_show_menubar"/> + <Separator/> + <Action name="game_new"/> + <Action name="options_choose_game_type"/> + <Separator/> + <Action name="game_pause"/> + <Action name="game_highscores"/> + <Separator/> + <Action name="game_quit"/> +</Menu> + +<State name="stopped"> + <enable> + <Action name="move_hint" /> + <Action name="move_solve" /> + <Action name="solve_rate" /> + <Action name="log_view" /> + <Action name="log_replay" /> + <Action name="log_load" /> + <Action name="log_save" /> + </enable> + <disable> + <Action name="game_pause" /> + </disable> +</State> +<State name="gameover"> + <enable> + <Action name="solve_rate" /> + <Action name="log_view" /> + <Action name="log_replay" /> + <Action name="log_load" /> + <Action name="log_save" /> + </enable> + <disable> + <Action name="move_hint" /> + <Action name="move_solve" /> + <Action name="game_pause" /> + </disable> +</State> +<State name="playing"> + <enable> + <Action name="move_hint" /> + <Action name="move_solve" /> + <Action name="game_pause" /> + </enable> + <disable> + <Action name="solve_rate" /> + <Action name="log_view" /> + <Action name="log_replay" /> + <Action name="log_load" /> + <Action name="log_save" /> + </disable> +</State> +<State name="paused"> + <disable> + <Action name="move_hint" /> + <Action name="move_solve" /> + </disable> +</State> +<State name="replaying"> + <disable> + <Action name="move_hint" /> + <Action name="move_solve" /> + <Action name="solve_rate" /> + <Action name="game_pause" /> + <Action name="log_view" /> + <Action name="log_replay" /> + <Action name="log_load" /> + <Action name="log_save" /> + </disable> +</State> +<State name="init"> + <enable> + <Action name="move_hint" /> + <Action name="move_solve" /> + <Action name="solve_rate" /> + <Action name="log_load" /> + </enable> + <disable> + <Action name="game_pause" /> + <Action name="log_view" /> + <Action name="log_replay" /> + <Action name="log_save" /> + </disable> +</State> + +</gui> diff --git a/kmines/kzoommainwindow.cpp b/kmines/kzoommainwindow.cpp new file mode 100644 index 00000000..115d5175 --- /dev/null +++ b/kmines/kzoommainwindow.cpp @@ -0,0 +1,115 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek ([email protected]) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kzoommainwindow.h" +#include "kzoommainwindow.moc" + +#include <kaction.h> +#include <kstdaction.h> +#include <kmenubar.h> +#include <kcmenumngr.h> + +KZoomMainWindow::KZoomMainWindow(uint min, uint max, uint step, const char *name) + : KMainWindow(0, name), _zoomStep(step), _minZoom(min), _maxZoom(max) +{ + installEventFilter(this); + + _zoomInAction = KStdAction::zoomIn(this, SLOT(zoomIn()), actionCollection()); + _zoomOutAction = + KStdAction::zoomOut(this, SLOT(zoomOut()), actionCollection()); + _menu = + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); +} + +void KZoomMainWindow::init(const char *popupName) +{ + // zoom + setZoom(readZoomSetting()); + + // menubar + _menu->setChecked( menubarVisibleSetting() ); + toggleMenubar(); + + // context popup + if (popupName) { + QPopupMenu *popup = + static_cast<QPopupMenu *>(factory()->container(popupName, this)); + Q_ASSERT(popup); + if (popup) KContextMenuManager::insert(this, popup); + } +} + +void KZoomMainWindow::addWidget(QWidget *widget) +{ + widget->adjustSize(); + QWidget *tlw = widget->topLevelWidget(); + KZoomMainWindow *zm = + static_cast<KZoomMainWindow *>(tlw->qt_cast("KZoomMainWindow")); + Q_ASSERT(zm); + zm->_widgets.append(widget); + connect(widget, SIGNAL(destroyed()), zm, SLOT(widgetDestroyed())); +} + +void KZoomMainWindow::widgetDestroyed() +{ + _widgets.remove(static_cast<const QWidget *>(sender())); +} + +bool KZoomMainWindow::eventFilter(QObject *o, QEvent *e) +{ + if ( e->type()==QEvent::LayoutHint ) + setFixedSize(minimumSize()); // because K/QMainWindow + // does not manage fixed central widget + // with hidden menubar... + return KMainWindow::eventFilter(o, e); +} + +void KZoomMainWindow::setZoom(uint zoom) +{ + _zoom = zoom; + writeZoomSetting(_zoom); + QPtrListIterator<QWidget> it(_widgets); + for (; it.current(); ++it) + (*it)->adjustSize();; + _zoomOutAction->setEnabled( _zoom>_minZoom ); + _zoomInAction->setEnabled( _zoom<_maxZoom ); +} + +void KZoomMainWindow::zoomIn() +{ + setZoom(_zoom + _zoomStep); +} + +void KZoomMainWindow::zoomOut() +{ + Q_ASSERT( _zoom>=_zoomStep ); + setZoom(_zoom - _zoomStep); +} + +void KZoomMainWindow::toggleMenubar() +{ + if ( _menu->isChecked() ) menuBar()->show(); + else menuBar()->hide(); +} + +bool KZoomMainWindow::queryExit() +{ + writeMenubarVisibleSetting(_menu->isChecked()); + return KMainWindow::queryExit(); +} diff --git a/kmines/kzoommainwindow.h b/kmines/kzoommainwindow.h new file mode 100644 index 00000000..da8ec96c --- /dev/null +++ b/kmines/kzoommainwindow.h @@ -0,0 +1,126 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek ([email protected]) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KZOOMMAINWINDOW_H +#define KZOOMMAINWINDOW_H + +#include <kmainwindow.h> + +class KToggleAction; + +/** + * KZoomMainWindow is a main window of fixed size. Its size can be + * modified with the "zoom in"/"zoom out" actions. + * + * It manages one or several widgets: their adjustSize() method is + * called whenever the zoom level is changed. + * The usual implementation for those widget is to redefine adjustSize() + * with code like: + * /code + * setFixedSize(newsize); + * /endcode + * + * This class also has a "show/hide menubar" action and allows the use + * of a context popup menu (useful to restore the menubar when hidden). + */ +class KZoomMainWindow : public KMainWindow +{ + Q_OBJECT +public: + /** Constructor. */ + KZoomMainWindow(uint minZoom, uint maxZoom, uint zoomStep, + const char *name = 0); + + /** Add a widget to be managed i.e. the adjustSize() method of the + * widget is called whenever the zoom is changed. + * This function assumes that the topLevelWidget() is the KZoomMainWindow. + */ + static void addWidget(QWidget *widget); + + uint zoom() const { return _zoom; } + +public slots: + void zoomIn(); + void zoomOut(); + void toggleMenubar(); + +protected: + /** You need to call this after the createGUI or setupGUI method + * is called. + * @param popupName is the name of the context popup menu as defined in + * the ui.rc file. + */ + void init(const char *popupName = 0); + + virtual void setZoom(uint zoom); + virtual bool eventFilter(QObject *o, QEvent *e); + virtual bool queryExit(); + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setZoom(zoom); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeZoomSetting(uint zoom) = 0; + + /** Youneed to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * return Settings::zoom(); + * /endcode + */ + virtual uint readZoomSetting() const = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setMenubarVisible(visible); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeMenubarVisibleSetting(bool visible) = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::menubarVisible(); + * /endcode + */ + virtual bool menubarVisibleSetting() const = 0; + +private slots: + void widgetDestroyed(); + +private: + uint _zoom, _zoomStep, _minZoom, _maxZoom; + QPtrList<QWidget> _widgets; + KAction *_zoomInAction, *_zoomOutAction; + KToggleAction *_menu; + + class KZoomMainWindowPrivate; + KZoomMainWindowPrivate *d; +}; + +#endif diff --git a/kmines/main.cpp b/kmines/main.cpp new file mode 100644 index 00000000..52620887 --- /dev/null +++ b/kmines/main.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 1996-2004 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "main.h" +#include "main.moc" + +#include <qptrvector.h> + +#include <kaccel.h> +#include <kapplication.h> +#include <klocale.h> +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <kstdaction.h> +#include <kkeydialog.h> +#include <kstdgameaction.h> +#include <kaction.h> +#include <kdebug.h> +#include <knotifyclient.h> +#include <knotifydialog.h> +#include <khighscore.h> +#include <kconfigdialog.h> + +#include "settings.h" +#include "status.h" +#include "highscores.h" +#include "version.h" +#include "dialogs.h" + +const MainWidget::KeyData MainWidget::KEY_DATA[NB_KEYS] = { +{I18N_NOOP("Move Up"), "keyboard_moveup", Key_Up, SLOT(moveUp())}, +{I18N_NOOP("Move Down"), "keyboard_movedown", Key_Down, SLOT(moveDown())}, +{I18N_NOOP("Move Right"), "keyboard_moveright", Key_Right, SLOT(moveRight())}, +{I18N_NOOP("Move Left"), "keyboard_moveleft", Key_Left, SLOT(moveLeft())}, +{I18N_NOOP("Move at Left Edge"), "keyboard_leftedge", Key_Home, SLOT(moveLeftEdge())}, +{I18N_NOOP("Move at Right Edge"), "keyboard_rightedge", Key_End, SLOT(moveRightEdge())}, +{I18N_NOOP("Move at Top Edge"), "keyboard_topedge", Key_PageUp, SLOT(moveTop())}, +{I18N_NOOP("Move at Bottom Edge"), "keyboard_bottomedge", Key_PageDown, SLOT(moveBottom())}, +{I18N_NOOP("Reveal Mine"), "keyboard_revealmine", Key_Space, SLOT(reveal())}, +{I18N_NOOP("Mark Mine"), "keyboard_markmine", Key_W, SLOT(mark())}, +{I18N_NOOP("Automatic Reveal"), "keyboard_autoreveal", Key_Return, SLOT(autoReveal())} +}; + + +MainWidget::MainWidget() + : KZoomMainWindow(4, 100, 1, "kmines") +{ + KNotifyClient::startDaemon(); + + _status = new Status(this); + connect(_status, SIGNAL(gameStateChangedSignal(KMines::GameState)), + SLOT(gameStateChanged(KMines::GameState))); + connect(_status, SIGNAL(pause()), SLOT(pause())); + + // Game & Popup + KStdGameAction::gameNew(_status, SLOT(restartGame()), actionCollection()); + _pause = KStdGameAction::pause(_status, SLOT(pauseGame()), + actionCollection()); + KStdGameAction::highscores(this, SLOT(showHighscores()), + actionCollection()); + KStdGameAction::quit(qApp, SLOT(quit()), actionCollection()); + + // keyboard + _keybCollection = new KActionCollection(this); + for (uint i=0; i<NB_KEYS; i++) { + const KeyData &d = KEY_DATA[i]; + (void)new KAction(i18n(d.label), d.keycode, _status, + d.slot, _keybCollection, d.name); + } + + // Settings + KStdAction::preferences(this, SLOT(configureSettings()), + actionCollection()); + KStdAction::keyBindings(this, SLOT(configureKeys()), actionCollection()); + KStdAction::configureNotifications(this, SLOT(configureNotifications()), + actionCollection()); + KStdGameAction::configureHighscores(this, SLOT(configureHighscores()), + actionCollection()); + // Levels + _levels = KStdGameAction::chooseGameType(0, 0, actionCollection()); + QStringList list; + for (uint i=0; i<=Level::NB_TYPES; i++) + list += i18n(Level::LABELS[i]); + _levels->setItems(list); + connect(_levels, SIGNAL(activated(int)), _status, SLOT(newGame(int))); + + // Adviser + _advise = + KStdGameAction::hint(_status, SLOT(advise()), actionCollection()); + _solve = KStdGameAction::solve(_status, SLOT(solve()), actionCollection()); + (void)new KAction(i18n("Solving Rate..."), 0, _status, SLOT(solveRate()), + actionCollection(), "solve_rate"); + + // Log + (void)new KAction(KGuiItem(i18n("View Log"), "viewmag"), 0, + _status, SLOT(viewLog()), + actionCollection(), "log_view"); + (void)new KAction(KGuiItem(i18n("Replay Log"), "player_play"), + 0, _status, SLOT(replayLog()), + actionCollection(), "log_replay"); + (void)new KAction(KGuiItem(i18n("Save Log..."), "filesave"), 0, + _status, SLOT(saveLog()), + actionCollection(), "log_save"); + (void)new KAction(KGuiItem(i18n("Load Log..."), "fileopen"), 0, + _status, SLOT(loadLog()), + actionCollection(), "log_load"); + + setupGUI( KMainWindow::Save | Create ); + readSettings(); + setCentralWidget(_status); + init("popup"); + addWidget(_status->field()); +} + +bool MainWidget::queryExit() +{ + _status->checkBlackMark(); + return KZoomMainWindow::queryExit(); +} + +void MainWidget::readSettings() +{ + settingsChanged(); + Level::Type type = (Level::Type) Settings::level(); + _levels->setCurrentItem(type); + _status->newGame(type); +} + +void MainWidget::showHighscores() +{ + KExtHighscore::show(this); +} + +void MainWidget::focusOutEvent(QFocusEvent *e) +{ + if ( Settings::pauseFocus() && e->reason()==QFocusEvent::ActiveWindow + && _status->isPlaying() ) pause(); + KMainWindow::focusOutEvent(e); +} + +void MainWidget::configureSettings() +{ + if ( KConfigDialog::showDialog("settings") ) return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self()); + GameConfig *gc = new GameConfig; + dialog->addPage(gc, i18n("Game"), "package_system"); + dialog->addPage(new AppearanceConfig, i18n("Appearance"), "style"); + CustomConfig *cc = new CustomConfig; + dialog->addPage(cc, i18n("Custom Game"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), SLOT(settingsChanged())); + dialog->show(); + cc->init(); + gc->init(); +} + +void MainWidget::configureHighscores() +{ + KExtHighscore::configure(this); +} + +void MainWidget::settingsChanged() +{ + bool enabled = Settings::keyboardGame(); + QValueList<KAction *> list = _keybCollection->actions(); + QValueList<KAction *>::Iterator it; + for (it = list.begin(); it!=list.end(); ++it) + (*it)->setEnabled(enabled); + _status->settingsChanged(); +} + +void MainWidget::configureKeys() +{ + KKeyDialog d(true, this); + d.insert(_keybCollection, i18n("Keyboard game")); + d.insert(actionCollection(), i18n("General")); + d.configure(); +} + +void MainWidget::configureNotifications() +{ + KNotifyDialog::configure(this); +} + +void MainWidget::gameStateChanged(KMines::GameState state) +{ + stateChanged(KMines::STATES[state]); + if ( state==Playing ) setFocus(); +} + +void MainWidget::pause() +{ + _pause->activate(); +} + +void MainWidget::writeZoomSetting(uint zoom) +{ + Settings::setCaseSize(zoom); + Settings::writeConfig(); +} + +uint MainWidget::readZoomSetting() const +{ + return Settings::caseSize(); +} + +void MainWidget::writeMenubarVisibleSetting(bool visible) +{ + Settings::setMenubarVisible(visible); + Settings::writeConfig(); +} + +bool MainWidget::menubarVisibleSetting() const +{ + return Settings::menubarVisible(); +} + +//---------------------------------------------------------------------------- +static const char *DESCRIPTION + = I18N_NOOP("KMines is a classic mine sweeper game"); + +int main(int argc, char **argv) +{ + KHighscore::init("kmines"); + + KAboutData aboutData("kmines", I18N_NOOP("KMines"), LONG_VERSION, + DESCRIPTION, KAboutData::License_GPL, + COPYLEFT, 0, HOMEPAGE); + aboutData.addAuthor("Nicolas Hadacek", 0, EMAIL); + aboutData.addCredit("Andreas Zehender", I18N_NOOP("Smiley pixmaps")); + aboutData.addCredit("Mikhail Kourinny", I18N_NOOP("Solver/Adviser")); + aboutData.addCredit("Thomas Capricelli", I18N_NOOP("Magic reveal mode")); + KCmdLineArgs::init(argc, argv, &aboutData); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + KExtHighscore::ExtManager manager; + + if ( a.isRestored() ) RESTORE(MainWidget) + else { + MainWidget *mw = new MainWidget; + mw->show(); + } + return a.exec(); +} diff --git a/kmines/main.h b/kmines/main.h new file mode 100644 index 00000000..21ec28b1 --- /dev/null +++ b/kmines/main.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef MAIN_H +#define MAIN_H + +#include "kzoommainwindow.h" + +#include "defines.h" + +class KAction; +class KToggleAction; +class KSelectAction; +class Status; + +class MainWidget : public KZoomMainWindow, public KMines +{ + Q_OBJECT + public: + MainWidget(); + + private slots: + void configureKeys(); + void configureSettings(); + void configureNotifications(); + void configureHighscores(); + void gameStateChanged(KMines::GameState); + void showHighscores(); + void settingsChanged(); + void pause(); + + protected: + virtual void focusOutEvent(QFocusEvent *); + virtual bool queryExit(); + + private: + Status *_status; + KToggleAction *_pause; + KSelectAction *_levels; + KAction *_advise, *_solve; + KActionCollection *_keybCollection; + + struct KeyData { + const char *label, *name; + Qt::Key keycode; + const char *slot; + }; + enum Key { NB_KEYS = 11 }; + static const KeyData KEY_DATA[NB_KEYS]; + + void readSettings(); + virtual void writeZoomSetting(uint zoom); + virtual uint readZoomSetting() const; + virtual void writeMenubarVisibleSetting(bool visible); + virtual bool menubarVisibleSetting() const; +}; + +#endif diff --git a/kmines/settings.kcfgc b/kmines/settings.kcfgc new file mode 100644 index 00000000..c863f41a --- /dev/null +++ b/kmines/settings.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=kmines.kcfg +IncludeFiles=defines.h +ClassName=Settings +Singleton=true +Mutators=level,MenubarVisible,CaseSize +CustomAdditions=true diff --git a/kmines/settings_addons.h b/kmines/settings_addons.h new file mode 100644 index 00000000..59be1ac1 --- /dev/null +++ b/kmines/settings_addons.h @@ -0,0 +1,5 @@ +public: + static Level customLevel() + { + return Level( self()->mCustomWidth, self()->mCustomHeight, self()->mCustomMines ); + } diff --git a/kmines/solver/Makefile.am b/kmines/solver/Makefile.am new file mode 100644 index 00000000..8a6696a7 --- /dev/null +++ b/kmines/solver/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/kmines/generic -I$(top_srcdir)/kmines -I$(top_srcdir)/libkdegames $(all_includes) + +noinst_LTLIBRARIES = libsolver.la +noinst_HEADERS = bfield.h solver.h headerP.h adviseFast.h adviseFull.h +libsolver_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined +libsolver_la_SOURCES = bfield.cpp solver.cpp advFastRules.cpp adviseFast.cpp \ + adviseFull.cpp +METASOURCES = solver.moc + +check_PROGRAMS = test testFast testSolve testRate +test_SOURCES = test.cpp +test_LDADD = ./libsolver.la $(LIB_KDECORE) +testFast_SOURCES = testFast.cpp +testFast_LDADD = ./libsolver.la $(LIB_KDECORE) +testSolve_SOURCES = testSolve.cpp +testSolve_LDADD = ./libsolver.la $(LIB_KDECORE) $(LIB_KDEUI) +testRate_SOURCES = testRate.cpp +testRate_LDADD = ./libsolver.la $(LIB_KDECORE) $(LIB_KDEUI) diff --git a/kmines/solver/advFastRules.cpp b/kmines/solver/advFastRules.cpp new file mode 100644 index 00000000..79c42bba --- /dev/null +++ b/kmines/solver/advFastRules.cpp @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <algorithm> +#include <assert.h> +#include "adviseFast.h" + +using std::set; + +AdviseFast::RuleSet::RuleSet(FactSet *f) : + facts(f) +{ + FactSet::iterator i; + for(i=facts->begin(); i!=facts->end(); ++i) + addGeneral(i->first); +} + +AdviseFast::RuleSet::~RuleSet(){ +} + +void AdviseFast::RuleSet::addRule(Entry const &entry) +{ + _rules.insert(entry); +} + +bool AdviseFast::RuleSet::getSurePoint(Coord *sp) +{ + if(_surePoints.empty()){ + if(!apply()) return false; + } + + CoordSet::iterator i = _surePoints.begin(); + *sp = *i; + _surePoints.erase(i); + + return true; +} + +bool AdviseFast::RuleSet::reveal(Coord what) +{ + CoordSet affectedFacts; + if(!facts->reveal(what, &affectedFacts)) + // OOPS :( + return false; + + CoordSet::iterator i; + for( i = affectedFacts.begin(); + i != affectedFacts.end(); + ++i) + this->addGeneral(*i); + + return true; +} + +void AdviseFast::RuleSet::solve() +{ + Coord p; + while(getSurePoint(&p)) { + bool res = reveal(p); + assert(res); + Q_UNUSED(res); + } +} + +bool AdviseFast::RuleSet::apply() +{ + while(!_rules.empty()){ + set<Entry>::iterator i = _rules.begin(); + std::auto_ptr<Rule> r (this->newRule(*i)); + _rules.erase(i); + + if(r->apply(&this->_surePoints)) return true; + } + + return false; +} + +AdviseFast::Rule * +AdviseFast::RuleSet::newRule(Entry const &e){ + CoordSet::const_iterator i = e.second.begin(); + Coord p, p1; + switch(e.first){ + case EMPTY: + assert(e.second.size() == 1); + return new EmptyRule(*i, this); + + case FULL: + assert(e.second.size() == 1); + return new FullRule(*i, this); + + case INCLUDE: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new InclusionRule(p, p1, this); + + case INCLUDE1: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new InclusionRule(p1, p, this); + + case INTERSECT: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new IntersectionRule(p, p1, this); + + case INTERSECT1: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new IntersectionRule(p1, p, this); + + case GENERAL: + assert(e.second.size() == 1); + return new GeneralRule(*i, this); + + default: + assert(false); + } + + // Make compiler happy + return 0; +} + +void AdviseFast::RuleSet::removeRef(Coord p){ + set<Entry>::iterator i, j; + + for( i = j = _rules.begin(); + i != _rules.end(); + i = j) + { + ++j; + if(i->second.count(p)) _rules.erase(i); + } +} + +void AdviseFast::RuleSet::addGeneral(Coord p){ + this->removeRef(p); + Entry e; + e.first = GENERAL; + e.second.insert(p); + this->addRule(e); +} + +#if defined(DEBUG) && DEBUG >= 2 +int AdviseFast::Rule::leaks = 0; +#endif + +AdviseFast::Rule::Rule(RuleSet *parent) : + _parent(parent), + _facts(parent->facts) +{ +#if defined(DEBUG) && DEBUG >= 2 + cout << "Rule::Rule, leaks = " << ++leaks << endl; +#endif +} + +AdviseFast::Rule::~Rule() +{ +#if defined(DEBUG) && DEBUG >= 2 + cout << "Rule::~Rule, leaks = " << --leaks << endl; +#endif +} + +AdviseFast::GeneralRule::GeneralRule( + Coord fact, + RuleSet *parent) : + Rule(parent), + _fact(fact) +{} + +bool AdviseFast::GeneralRule::apply(CoordSet *) +{ + +#if defined(DEBUG) && DEBUG >= 2 + operator <<( + cout << "Applying general rule ", + _fact) << endl; +#endif + + // Return if there's no more such fact + if(!_facts->count(_fact)) return false; + Fact const &f = (*_facts)[_fact]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << f << endl; +#endif + + // Insert intersection rules first + // relatedFacts -- facts which have non-zero intersection + CoordSet relatedFacts; + { + CoordSet::const_iterator i; + for( i=f.pointSet.begin(); + i!=f.pointSet.end(); + ++i){ + + CoordSet const & ps = + *_facts->getContainingFacts(*i); + relatedFacts.insert( + ps.begin(), ps.end()); + } + } + relatedFacts.erase(_fact); // ;) + + CoordSet::iterator i; + for( i=relatedFacts.begin(); + i!=relatedFacts.end(); + ++i) + { + RuleSet::Entry e; + e.second.insert(_fact); + e.second.insert(*i); + + e.first = RuleSet::INTERSECT1; _parent->addRule(e); + e.first = RuleSet::INTERSECT; _parent->addRule(e); + e.first = RuleSet::INCLUDE1; _parent->addRule(e); + e.first = RuleSet::INCLUDE; _parent->addRule(e); + } + + // Now simple rules, so that they appear first in the list + RuleSet::Entry e; e.second.insert(_fact); + e.first = RuleSet::FULL; _parent->addRule(e); + e.first = RuleSet::EMPTY; _parent->addRule(e); + + // No point revealed, so... + return false; +} + +AdviseFast::EmptyRule::EmptyRule( + Coord fact, + RuleSet *parent) : + Rule(parent), + _fact(fact) +{} + +bool AdviseFast::EmptyRule::apply( + CoordSet *surePoints) +{ + +#if defined(DEBUG) && DEBUG >= 2 + operator <<( + cout << "Applying empty rule ", + _fact) << endl; +#endif + + if(!_facts->count(_fact)) return false; + Fact const &f = (*_facts)[_fact]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << f << endl; +#endif + + // FactSet does not contain empty facts!! + assert(!f.pointSet.empty()); + + // If there are mines around, alas :( + if(f.mines) return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + surePoints->insert( + f.pointSet.begin(), + f.pointSet.end()); + + _parent->removeRef(_fact); + + return true; +} + +AdviseFast::FullRule::FullRule( + Coord fact, + RuleSet *parent) : + Rule(parent), + _fact(fact) +{} + +bool AdviseFast::FullRule::apply( + CoordSet */*surePoints*/) +{ + +#if defined(DEBUG) && DEBUG >= 2 + operator <<( + cout << "Applying full rule ", + _fact) << endl; +#endif + + if(!_facts->count(_fact)) return false; + Fact f = (*_facts)[_fact]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << f << endl; +#endif + + // FactSet does not contain empty facts!! + assert(!f.pointSet.empty()); + + // The point set is not full of mines... :( + if(f.mines != (int)f.pointSet.size()) return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + CoordSet affectedFacts; + CoordSet::iterator i; + for( i=f.pointSet.begin(); + i!=f.pointSet.end(); + ++i) + _facts->mark(*i, &affectedFacts); + + for( i=affectedFacts.begin(); + i!=affectedFacts.end(); + ++i) + _parent->addGeneral(*i); + _parent->removeRef(_fact); + + // No mines revealed + return false; +} + +AdviseFast::InclusionRule::InclusionRule( + Coord bigger, Coord smaller, + RuleSet *parent) : + Rule(parent), + _bigger(bigger), _smaller(smaller) +{} + +bool AdviseFast::InclusionRule::apply( + CoordSet */*surePoints*/) +{ + +#if defined(DEBUG) && DEBUG >= 2 + cout << "Applying inclusion rule "; + operator <<(cout, _bigger) << ' '; + operator <<(cout, _smaller) << endl; +#endif + + if(!_facts->count(_bigger)) return false; + if(!_facts->count(_smaller)) return false; + + Fact b = (*_facts)[_bigger]; + Fact s = (*_facts)[_smaller]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << b << endl << s << endl; +#endif + + assert(!s.pointSet.empty()); + + CoordSet diff; + set_difference( + s.pointSet.begin(), + s.pointSet.end(), + b.pointSet.begin(), + b.pointSet.end(), + inserter(diff, diff.begin())); + if(!diff.empty()) + // That is s is not included in b + return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + diff.clear(); + set_difference( + b.pointSet.begin(), + b.pointSet.end(), + s.pointSet.begin(), + s.pointSet.end(), + inserter(diff, diff.begin())); + + if(diff.empty()){ + _facts->deleteFact(_bigger); + _parent->removeRef(_bigger); + } else { + b.pointSet = diff; + b.mines -= s.mines; + _facts->addFact(_bigger, b); + _parent->addGeneral(_bigger); + } + + // No points revealed + return false; +} + +AdviseFast::IntersectionRule::IntersectionRule( + Coord bigger, Coord smaller, + RuleSet *parent) : + Rule(parent), + _bigger(bigger), _smaller(smaller) +{} + +bool AdviseFast::IntersectionRule::apply( + CoordSet *surePoints) +{ + +#if defined(DEBUG) && DEBUG >= 2 + cout << "Applying intersection rule "; + operator <<(cout, _bigger) << ' '; + operator <<(cout, _smaller) << endl; +#endif + + if(!_facts->count(_bigger)) return false; + if(!_facts->count(_smaller)) return false; + + Fact b = (*_facts)[_bigger]; + Fact s = (*_facts)[_smaller]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << b << endl << s << endl; +#endif + + CoordSet diff; + set_difference( + b.pointSet.begin(), + b.pointSet.end(), + s.pointSet.begin(), + s.pointSet.end(), + inserter(diff, diff.begin())); + + if((int)diff.size() != b.mines - s.mines) + // Oops :( + return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + CoordSet cross, diffs; + set_difference( + s.pointSet.begin(), + s.pointSet.end(), + b.pointSet.begin(), + b.pointSet.end(), + inserter(diffs, diffs.begin())); + set_intersection( + s.pointSet.begin(), + s.pointSet.end(), + b.pointSet.begin(), + b.pointSet.end(), + inserter(cross, cross.begin())); + + b.pointSet = diff; + b.mines -= s.mines; + _facts->addFact(_bigger, b); + + s.pointSet = cross; + _facts->addFact(_smaller, s); + + { + _parent->removeRef(_bigger); + _parent->addGeneral(_smaller); + + RuleSet::Entry e; + e.first = RuleSet::FULL; + e.second.insert(_bigger); + _parent->addRule(e); + } + + if(diffs.empty()) return false; + + // Otherwise we have something to reveal!! + surePoints->insert(diffs.begin(), diffs.end()); + return true; +} diff --git a/kmines/solver/adviseFast.cpp b/kmines/solver/adviseFast.cpp new file mode 100644 index 00000000..f15d508e --- /dev/null +++ b/kmines/solver/adviseFast.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "adviseFast.h" + +#include <algorithm> + + +using namespace AdviseFast; + +std::ostream &AdviseFast::operator <<(std::ostream &s, Fact const &f) +{ + return s << f.pointSet << "= " << f.mines; +} + +AdviseFast::FactSet::FactSet(BaseField *field) : + _field(field) +{ + Fact globalFact; globalFact.mines = field->nbMines(); + + int i, j; + for(i=field->height()-1; i>=0; --i) + for(j=field->width()-1; j>=0; --j){ + Coord p(j, i); + // #### hasMine implies isCovered (and the solver should not + // know if there is a mine :) [NH] + if ( field->isCovered(p) /*|| field->hasMine(p)*/ ) + globalFact.pointSet.insert(p); + else { + Fact f; + this->retrieveFact(p, &f); + this->addFact(p, f); + } + } + + this->addFact(Coord(-1,-1), globalFact); +} + +void AdviseFast::FactSet::retrieveFact( + Coord which, + Fact *where) +{ + where->mines = (_field->isCovered(which) ? -1 + : (int)_field->nbMinesAround(which)); + CoordList tmp = _field->coveredNeighbours(which); + for (CoordList::const_iterator it = tmp.begin(); it!=tmp.end(); ++it) + where->pointSet.insert(*it); +} + +void AdviseFast::FactSet::addFact( + Coord const &point, + Fact const &fact) +{ + if(this->count(point)) this->deleteFact(point); + + Fact &f = ((*this)[point] = fact); + + + // Remove marked points + CoordSet marked; + set_intersection( + f.pointSet.begin(), + f.pointSet.end(), + _marked.begin(), + _marked.end(), + inserter(marked, marked.begin())); + + CoordSet::iterator i; + for(i=marked.begin(); i!=marked.end(); ++i) + f.pointSet.erase(*i); + f.mines -= marked.size(); + + // Don't insert empty fact + if(f.pointSet.empty()) { this->erase(point); return;} + + for(i=f.pointSet.begin(); i!=f.pointSet.end(); ++i) + _containingFacts[*i].insert(point); +} + +void AdviseFast::FactSet::deleteFact( + Coord const &point) +{ + if(!this->count(point)) return; + CoordSet::iterator i; + Fact &f = (*this)[point]; + for(i=f.pointSet.begin(); i!=f.pointSet.end(); ++i){ + _containingFacts[*i].erase(point); + if(_containingFacts[*i].empty()) + _containingFacts.erase(*i); + } + this->erase(point); +} + +bool AdviseFast::FactSet::reveal( + Coord point, + CoordSet *affectedFacts) +{ + // Tolerate :) + if( !_field->isCovered(point) ) return true; // :) + + CoordList tmp; + if(_field->doReveal(point, &tmp, 0) == false) + // Blew up :( + return false; + + CoordSet autorevealed; + for (CoordList::const_iterator it = tmp.begin(); it!=tmp.end(); ++it) + autorevealed.insert(*it); + autorevealed.insert(point); + affectedFacts->insert(autorevealed.begin(), autorevealed.end()); + + CoordSet::const_iterator i; + for(i=autorevealed.begin(); i!=autorevealed.end(); ++i) + { + // I still think that each poing will belong to + // at least one fact, but don't want to waste time + // proving it :) + if(_containingFacts.count(*i)){ + CoordSet const &affF = _containingFacts[*i]; + affectedFacts->insert( + affF.begin(), affF.end()); + for(CoordSet::const_iterator j=affF.begin(); + j!=affF.end(); + ++j) + { + (*this)[*j].pointSet.erase(*i); + if((*this)[*j].pointSet.empty()) + this->erase(*j); + } + _containingFacts.erase(*i); + } + + Fact f; retrieveFact(*i, &f); + this->addFact(*i, f); + } + + return true; +} + +void AdviseFast::FactSet::mark( + Coord point, + CoordSet *affectedFacts) +{ + if(_marked.count(point)) return; + _marked.insert(point); + + // I still think that each poing will belong to + // at least one fact, but don't want to waste time + // proving it :) + if(_containingFacts.count(point)){ + CoordSet const &affF = _containingFacts[point]; + affectedFacts->insert(affF.begin(), affF.end()); + for(CoordSet::const_iterator i=affF.begin(); i!=affF.end(); ++i){ + (*this)[*i].pointSet.erase(point); + (*this)[*i].mines--; + if((*this)[*i].pointSet.empty()) + this->erase(*i); + } + _containingFacts.erase(point); + } + + _field->doMark(point); +} + +CoordSet const *AdviseFast::FactSet::getContainingFacts( + Coord const &point) const +{ + if(_containingFacts.count(point)) + return &const_cast<std::map<Coord, CoordSet> &>(_containingFacts) + [point]; + else return 0; +} + +std::ostream &AdviseFast::operator <<(std::ostream &s, FactSet const &fs) +{ + FactSet::const_iterator i; + for(i=fs.begin(); i!=fs.end(); ++i) + s << i->first << ": " << i->second << endl; + return s; +} + +bool AdviseFast::adviseFast( + Coord *, + FactSet *, + RuleSet *) +{ return false;} diff --git a/kmines/solver/adviseFast.h b/kmines/solver/adviseFast.h new file mode 100644 index 00000000..db3b1955 --- /dev/null +++ b/kmines/solver/adviseFast.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef adviseFast_h +#define adviseFast_h + +#include "headerP.h" + + +namespace AdviseFast { + + class GeneralRule : public Rule { + public: + GeneralRule(Coord fact, RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _fact; + }; + + class EmptyRule : public Rule { + public: + EmptyRule(Coord fact, RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _fact; + }; + + class FullRule : public Rule { + public: + FullRule(Coord fact, RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _fact; + }; + + class InclusionRule : public Rule { + public: + InclusionRule(Coord bigger, Coord smaller, + RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _bigger, _smaller; + }; + + class IntersectionRule : public Rule { + public: + IntersectionRule(Coord bigger, Coord smaller, + RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _bigger, _smaller; + }; +} + +#endif diff --git a/kmines/solver/adviseFull.cpp b/kmines/solver/adviseFull.cpp new file mode 100644 index 00000000..815ff02c --- /dev/null +++ b/kmines/solver/adviseFull.cpp @@ -0,0 +1,655 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "adviseFull.h" +#include <assert.h> +#include <stdio.h> +#include <math.h> + +using std::list; +using std::map; +using std::set; +using namespace AdviseFull; + +AdviseFull::EquationSet::EquationSet() : + _maxPointSet(0) +{} + +AdviseFull::EquationSet::EquationSet( + AdviseFast::FactSet const &facts) : + _maxPointSet(0) +{ + AdviseFast::FactSet::const_iterator i; + for(i=facts.begin(); i!=facts.end(); ++i, ++_maxPointSet){ + Equation e; + e.pointSets.insert(_maxPointSet); + e.mines = i->second.mines; + _equations.push_back(e); + + _pointSets[_maxPointSet] = i->second.pointSet; + } +} + +void AdviseFull::EquationSet::normalize() +{ + short i=0; + set<short> empty; + for(i=0; i<_maxPointSet; ++i){ + if(!_pointSets.count(i)) continue; + if(_pointSets[i].empty()){ + this->substitute(i, empty); + continue; + } + for(short j=i+1;j<_maxPointSet; ++j){ + if(!_pointSets.count(j)) continue; + if(_pointSets[j].empty()){ + this->substitute(j, empty); + continue; + } + + CoordSet intersect; + set_intersection( + _pointSets[i].begin(), + _pointSets[i].end(), + _pointSets[j].begin(), + _pointSets[j].end(), + inserter(intersect, intersect.begin())); + if(intersect.empty()) continue; + + CoordSet _i, _j; + set_difference( + _pointSets[i].begin(), + _pointSets[i].end(), + _pointSets[j].begin(), + _pointSets[j].end(), + inserter(_i, _i.begin())); + set_difference( + _pointSets[j].begin(), + _pointSets[j].end(), + _pointSets[i].begin(), + _pointSets[i].end(), + inserter(_j, _j.begin())); + + set<short> _ip, _jp; + _ip.insert(_maxPointSet); + _jp.insert(_maxPointSet); + _pointSets[_maxPointSet++] = intersect; + _ip.insert(_maxPointSet); + _pointSets[_maxPointSet++] = _i; + _jp.insert(_maxPointSet); + _pointSets[_maxPointSet++] = _j; + + this->substitute(i, _ip); + this->substitute(j, _jp); + break; + } + } +} + +void AdviseFull::EquationSet::separate( + list<EquationSet> *result) const +{ + result->clear(); // :) + + list<Equation> equations = _equations; + + while(!equations.empty()){ + // Add a new equation set to *results + result->push_back(EquationSet()); + EquationSet &workingSet = result->back(); + workingSet._maxPointSet = _maxPointSet; + + // Start iteration process + // recentlyDeceased is a set of pointSets added on the + // last iteration + workingSet._equations.push_back(equations.front()); + set<short> recentlyDeceased = equations.front().pointSets; + equations.pop_front(); + + // The iteration process + while(!recentlyDeceased.empty()){ + + // Temporary set<short> + set<short> rd; + + list<Equation>::iterator i = equations.begin(); + while(i != equations.end()){ + set<short> intersect; + set_intersection( + i->pointSets.begin(), + i->pointSets.end(), + recentlyDeceased.begin(), + recentlyDeceased.end(), + inserter(intersect, intersect.begin())); + if(intersect.empty()){ + ++i; + } else { + set_difference( + i->pointSets.begin(), + i->pointSets.end(), + intersect.begin(), + intersect.end(), + inserter(rd, rd.begin())); + workingSet._equations.push_back(*i); + i = equations.erase(i); + } + } + + // Now switch recentlyDeceased + set<short>::iterator j; + for( j = recentlyDeceased.begin(); + j != recentlyDeceased.end(); + ++j) + { + workingSet._pointSets[*j] = + (const_cast< + map<short, CoordSet> &>( + _pointSets))[*j]; + } + + recentlyDeceased = rd; + } + } +} + +map<short, CoordSet> const &AdviseFull::EquationSet::solve( + list<Solution> *results) const +{ + +#ifdef DEBUG + printf("Entering EquationSet::solve\n"); + prettyprint(); +#endif + + EquationSet eqs = *this; + + // Get the most evident solutions + Solution only; + list<Equation> &EQS = eqs._equations; + + bool success; + do { + + success = false; + list<Equation>::iterator i = EQS.begin(); + + while(i!=EQS.end()){ +#if defined(DEBUG) && DEBUG >= 2 + printf("Taking look at the equation...\n"); +#endif + // Substitute known values + { set<short>::iterator j; + set<short> known; + for( j = i->pointSets.begin(); + j != i->pointSets.end(); + ++j) + { + if(only.count(*j)){ + i->mines -= only[*j]; + known.insert(*j); + } + } + + // STL bug ?? + for( j = known.begin(); + j != known.end(); + ++j) + i->pointSets.erase(*j); + } + // From now on the equation has no known values +#if defined(DEBUG) && DEBUG >= 2 + printf("Substituted known values.\n"); +#endif + if(i->mines < 0) + // Discrepancy + return _pointSets; + + + if(i->pointSets.empty()){ +#if defined(DEBUG) && DEBUG >= 2 + printf("Empty equation.\n"); +#endif + if(i->mines){ + // No points, non-zero mine count + // No solution + return _pointSets; + } else { + i = EQS.erase(i); + continue; + } + } + + if(i->mines == 0){ + set<short>::iterator j; + for( + j=i->pointSets.begin(); + j!=i->pointSets.end(); + ++j) + only[*j] = 0; + + EQS.erase(i); + success = true; + break; + } + + if(i->pointSets.size() == 1){ + short j = *i->pointSets.begin(); + + if((int)eqs._pointSets[j].size() < i->mines) + // Discrepancy !! + return _pointSets; + + only[j] = i->mines; + + EQS.erase(i); + success = true; + break; + } + + ++i; + } + + } while(success); + + // If no equations left we have a unique solution + if(EQS.empty()){ +#ifdef DEBUG + printf("Got a single solution!\n"); +#endif + results->push_back(only); + return _pointSets; + } + + // Otherwise the first equation is not empty. + // Find the range for first element + short var = *EQS.begin()->pointSets.begin(); + std::pair<short, short> range; + range.first = 0; + range.second = eqs._pointSets[var].size(); + + // A list of equations containing var + list<list<Equation>::iterator> containers; + list<Equation>::iterator i; + for( i = EQS.begin(); + i != EQS.end(); + ++i) + { + if(i->pointSets.count(var)){ + i->pointSets.erase(var); + containers.push_back(i); + + if(i->mines < range.second) + range.second = i->mines; + + // The total size of other point sets + // in the equation + short totalsize = 0; + set<short>::iterator j; + for( j = i->pointSets.begin(); + j != i->pointSets.end(); + ++j) + totalsize += eqs._pointSets[*j].size(); + + if(range.first < i->mines - totalsize) + range.first = i->mines - totalsize; + } + } + // Found the range + + // Now set properly equation set for first recursion + list<list<Equation>::iterator>::iterator super_iter; // ;) + short varvalue = range.first; + for( super_iter = containers.begin(); + super_iter != containers.end(); + ++super_iter) + (*super_iter)->mines -= varvalue; + + // Recursive calls here + while(varvalue <= range.second){ + only[var] = varvalue; + list<Solution> tempResults; + eqs.solve(&tempResults); + + // Mix solutions with only and put them + // in *results + list<Solution>::iterator j; + for( j=tempResults.begin(); + j!=tempResults.end(); + ++j) + { + j->insert(only.begin(), only.end()); + results->push_back(*j); + } + + // Prepare next recursive call + ++varvalue; + for( super_iter = containers.begin(); + super_iter != containers.end(); + ++super_iter) + --(*super_iter)->mines; + } + + return _pointSets; +} + +void AdviseFull::EquationSet::prettyprint() const +{ + +#if defined(DEBUG) + printf("Point Sets:\n"); + map<short, CoordSet>::const_iterator i; + for(i=_pointSets.begin(); i!=_pointSets.end(); ++i){ + printf("%d:", i->first); + CoordSet::const_iterator j; + for(j=i->second.begin(); j!=i->second.end(); ++j) + printf("\t(%d,%d)\n", j->second, j->first); + } +#endif + + printf("Equations:\n"); + list<Equation>::const_iterator l; + for(l=_equations.begin(); l!=_equations.end(); ++l){ + set<short>::const_iterator j; + for(j=l->pointSets.begin(); j!=l->pointSets.end(); ++j) + printf("%d ", *j); + printf("= %d\n", l->mines); + } +} + +void AdviseFull::EquationSet::substitute( + short out, + set<short> const &in) +{ + list<Equation>::iterator i; + for( i = _equations.begin(); + i != _equations.end(); + ++i) + { + if(i->pointSets.count(out)){ + i->pointSets.erase(out); + i->pointSets.insert(in.begin(), in.end()); + } + } + + _pointSets.erase(out); +} + +bool AdviseFull::surePoints( + map<short, CoordSet> const &m, + list<EquationSet::Solution> const &l, + CoordSet *surePoints) +{ + // A set of candidates to be surePoints + list<short> sp; + { + map<short, CoordSet>::const_iterator i; + for(i=m.begin(); i!=m.end(); ++i) sp.push_back(i->first); + } + + // Scan solution list + list<EquationSet::Solution>::const_iterator i; + for(i=l.begin(); i!=l.end(); ++i){ + list<short>::iterator j = sp.begin(); + while(j != sp.end()){ + // Non-empty possibility + if((const_cast<EquationSet::Solution &>(*i))[*j]){ + j = sp.erase(j); + if(sp.empty()) // No candidates left + return false; + } else // Stay alive for now + ++j; + } + } + + // There are SOME sure points; + // Fill *surePoints + list<short>::iterator isp; + map<short, CoordSet> &mm = const_cast<map<short, CoordSet> &>(m); + for(isp = sp.begin(); isp != sp.end(); ++isp) + surePoints->insert(mm[*isp].begin(), mm[*isp].end()); + + return true; +} + +float AdviseFull::variantNumberFraction( + map<short, CoordSet> const &m, + EquationSet::Solution const ÷nd, + EquationSet::Solution const &divisor, + float fraction) +{ + short count_difference = 0; + float quotient = 1; + + EquationSet::Solution::const_iterator i; + for(i=divisor.begin(); i!=divisor.end(); ++i){ + int j = i->first; + assert(m.count(j)); + int size = (const_cast<map<short, CoordSet> &>(m))[j].size(); + int dvd = (const_cast<EquationSet::Solution &>(dividend))[j]; + int dvr = (const_cast<EquationSet::Solution &>(divisor))[j]; + + count_difference += dvd - dvr; + + if(dvd < dvr){ + dvr = size - dvr; + dvd = size - dvd; + } + while(dvr < dvd) { + float num = size-dvr++; + quotient *= num/dvr; + } + } + + // Sorry, expensive call, but I'm lazy :(( + if(count_difference){ + assert(fraction != 0.); +#if defined(DEBUG) + float correction = pow( fraction/(1-fraction), count_difference ); + cout << "Got into correction, " << + count_difference << ' ' << correction << endl; +#endif + quotient *= pow( (1-fraction)/fraction , -count_difference ); + } + +#if defined(DEBUG) && DEBUG >= 2 + printf("variantNumberFraction: %.02f.\n", quotient); +#endif + + return quotient; +} + +void AdviseFull::getProbabilities( + map<short, CoordSet> const &m, + list<EquationSet::Solution> const &l, + ProbabilityMap *probabilities, + float fraction) +{ + assert(!l.empty()); + EquationSet::Solution const &front = l.front(); + + float probabilitiesSum = 0; + map<short, float> probs; + { map<short, CoordSet>::const_iterator i; + for(i=m.begin(); i!=m.end(); ++i) + probs[i->first] = 0; + } + + list<EquationSet::Solution>::const_iterator i; + for(i=l.begin(); i!=l.end(); ++i){ + float frac = variantNumberFraction(m, *i, front, fraction); + EquationSet::Solution::const_iterator j; + for(j=i->begin(); j!=i->end(); ++j) + probs[j->first] += j->second*frac; + probabilitiesSum += frac; + } + + probabilities->clear(); + + map<short, float>::iterator j; + for(j=probs.begin(); j!= probs.end(); ++j){ + CoordSet const &ps = const_cast<map<short, CoordSet> &>(m)[j->first]; + j->second /= ps.size() * probabilitiesSum; + CoordSet::const_iterator k; + for(k=ps.begin(); k!=ps.end(); ++k) + probabilities->insert( + std::pair<float, Coord>(j->second, *k)); + } + + // That's it :) +} + +void AdviseFull::adviseFull( + AdviseFast::FactSet *facts, + CoordSet *surePoints, + ProbabilityMap *probabilities) +{ + EquationSet eqs(*facts); + +#if defined(DEBUG) && DEBUG >= 2 + eqs.prettyprint(); +#endif + + eqs.normalize(); +#if defined(DEBUG) && DEBUG >= 2 + eqs.prettyprint(); +#endif + + list<EquationSet> eqss; + eqs.separate(&eqss); +#ifdef DEBUG + {list<EquationSet>::iterator i; + for(i=eqss.begin(); i!=eqss.end(); ++i) + i->prettyprint(); + } +#endif + + + // OK, uneffective, but simple :( + surePoints->clear(); + probabilities->clear(); + + // Get a fraction; + float fraction; + { BaseField const *f = facts->getField(); + fraction = ((float)f->nbMines()) / (f->width() * f->height()); + } + + /* From now on the first equation set on the list includes + * the equation corresponding to "total" fact. This is the + * first equation on the set. + * + * Give it a special treatment ;) */ + if(!eqss.empty()) do { + EquationSet prime = eqss.front(); + EquationSet::Equation total = prime._equations.front(); + prime._equations.pop_front(); + + list<EquationSet> prime_sep; + prime.separate(&prime_sep); + + // Find a pool + list<EquationSet::Equation>::iterator i = prime._equations.begin(); + while(!prime._equations.empty()){ + set<short>::iterator j; + for( j = i->pointSets.begin(); + j != i->pointSets.end(); + ++j) + prime._pointSets.erase(*j); + i = prime._equations.erase(i); + } + + assert(prime._pointSets.size() <= 1); + if(prime._pointSets.size() == 0) break; + + short pool = prime._pointSets.begin()->first; + CoordSet const &p = prime._pointSets[pool]; +#ifdef DEBUG + cout << "Prime equation set:" << endl << + " separated into " << prime_sep.size() << endl << + " pool size is " << p.size() << endl; +#endif + // Euristic + // if( prime_sep.size () > 6 && p.size() >= prime_sep.size() * 10){ + if(p.size() < (prime_sep.size()+1) * 10) + // No special treatment!! + break; + + + // Actually, just substitute prime (!!!) + eqss.pop_front(); + eqss.insert(eqss.begin(), + prime_sep.begin(), + prime_sep.end()); + + prime._equations.clear(); + EquationSet::Equation o; + o.pointSets.insert(pool); + // #### is the convertion right ? (NH) + o.mines = (ushort)(fraction * p.size()); + // A precaution + if(o.mines == 0) o.mines = 1; // ;) + + prime._equations.push_front(o); + eqss.push_front(prime); + + +#ifdef DEBUG + cout << "Specially treated:" << endl; + { + list<EquationSet>::iterator i; + for(i=eqss.begin(); i!=eqss.end(); ++i) + i->prettyprint(); + } +#endif + } while (false); + + list<EquationSet>::const_iterator i; + for(i=eqss.begin(); i!=eqss.end(); ++i){ + CoordSet sp; ProbabilityMap pb; + + list<EquationSet::Solution> solutions; + map<short, CoordSet> const &m = i->solve(&solutions); +#ifdef DEBUG + printf("Got solutions.\n"); +#if defined(DEBUG) && DEBUG >= 2 + { list<EquationSet::Solution>::iterator i; + for( i = solutions.begin(); + i != solutions.end(); + ++i) + { + EquationSet::Solution::iterator j; + for(j=i->begin(); j!=i->end(); ++j) + printf("%d:\t%d\n", + j->first, j->second); + printf("\n"); + } + } +#endif +#endif + + //bool sure = + AdviseFull::surePoints(m, solutions, &sp); + surePoints->insert(sp.begin(), sp.end()); + + getProbabilities(m, solutions, &pb, fraction); + probabilities->insert(pb.begin(), pb.end()); + } + + // That's it + return; +} diff --git a/kmines/solver/adviseFull.h b/kmines/solver/adviseFull.h new file mode 100644 index 00000000..2b0cc97b --- /dev/null +++ b/kmines/solver/adviseFull.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ADVISE_FULL_H +#define __ADVISE_FULL_H + +#include <list> +#include <algorithm> + +#include "headerP.h" + + +namespace AdviseFull { + class EquationSet { + public: // Well, why is it necessary? + struct Equation { + std::set<short> pointSets; + short mines; + }; + typedef std::map<short, short> Solution; + + public: + EquationSet(); + EquationSet(AdviseFast::FactSet const &facts); + + std::list<Equation> _equations; + std::map<short, CoordSet> _pointSets; + + /** Make sure no _pointSets have + * non-empty intersection */ + void normalize(); + + /** Returns in *results a set of equation sets + * which can be solved separately. + * *this assumed normalized :) */ + void separate(std::list<EquationSet> *results) const; + + /** Solves... returns _pointSets. + * It's nice to have *this separated :) */ + std::map<short, CoordSet> const &solve( + std::list<Solution> *results) const; + + void prettyprint() const; + + private: + /** One more than max(_pointSets[i].first) */ + short _maxPointSet; + + /** Substitutes a pointSet in all equations */ + void substitute( + short out, + std::set<short> const &in); + }; + + bool surePoints( + std::map<short, CoordSet> const &m, + std::list<EquationSet::Solution> const &l, + CoordSet *surePoints); + + /** The fourth argument is a fraction of mines in the "pool" */ + void getProbabilities( + std::map<short, CoordSet> const &m, + std::list<EquationSet::Solution> const &l, + ProbabilityMap *probabilities, + float fraction = 0); + + /** Get the quotient of the number of variants of + * point distribution satisfying dividend and divisor + * solutions */ + /** The fourth argument is a fraction of mines in the "pool" */ + float variantNumberFraction( + std::map<short, CoordSet> const &m, + EquationSet::Solution const ÷nd, + EquationSet::Solution const &divisor, + float fraction = 0); +} + +#endif diff --git a/kmines/solver/bfield.cpp b/kmines/solver/bfield.cpp new file mode 100644 index 00000000..d6c03643 --- /dev/null +++ b/kmines/solver/bfield.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "bfield.h" + + +using namespace KGrid2D; + +BaseField::BaseField(long seed) + : _nbUncovered(0), _nbMarked(0), _nbUncertain(0), _random(seed) +{} + +CoordList BaseField::coveredNeighbours(const Coord &p) const +{ + CoordList n; + CoordList tmp = neighbours(p); + for (CoordList::const_iterator it=tmp.begin(); it!=tmp.end(); ++it) + if ( state(*it)!=Uncovered ) n.append(*it); + return n; +} + +uint BaseField::nbMinesAround(const Coord &p) const +{ + uint nb = 0; + CoordList n = neighbours(p); + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + if ( hasMine(*it) ) nb++; + return nb; +} + +void BaseField::reset(uint width, uint height, uint nbMines) +{ + _firstReveal = true; + _nbMarked = 0; + _nbUncertain = 0; + _nbUncovered = 0; + _nbMines = nbMines; + + Case tmp; + tmp.mine = false; + tmp.state = Covered; + resize(width, height); + fill(tmp); +} + +bool BaseField::checkField(uint w, uint h, uint nb, const QString &s) +{ + if ( s.length()!=w*h ) return false; + uint n = 0; + unsigned int strLength(s.length()); + for (uint i=0; i<strLength; i++) + if ( s[i]=="1" ) n++; + else if ( s[i]!="0" ) return false; + return ( n==nb ); +} + +void BaseField::initReplay(const QString &s) +{ + Q_ASSERT( checkField(width(), height(), _nbMines, s) ); + + _firstReveal = false; + + Case tmp; + tmp.state = Covered; + unsigned int strLength(s.length()); + for (uint i=0; i<strLength; i++) { + tmp.mine = ( s[i]=="1" ); + at(i) = tmp; + } +} + +void BaseField::changeState(KMines::CaseState state, int inc) +{ + switch (state) { + case Uncovered: _nbUncovered += inc; break; + case Uncertain: _nbUncertain += inc; break; + case Marked: _nbMarked += inc; break; + default: break; + } +} + +void BaseField::changeCase(const Coord &p, KMines::CaseState newState) +{ + changeState(state(p), -1); + changeState(newState, 1); + (*this)[p].state = newState; +} + +void BaseField::uncover(const Coord &p, CoordList *autorevealed) +{ + if ( state(p)!=Covered ) return; + changeCase(p, Uncovered); + + if ( nbMinesAround(p)==0 ) { + CoordList n = coveredNeighbours(p); + if (autorevealed) *autorevealed += n; + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + uncover(*it, autorevealed); + } +} + +void BaseField::showAllMines(bool won) +{ + for (uint i=0; i<size(); i++) { + Coord p = coord(i); + if ( hasMine(p) && state(p)!=Exploded && state(p)!=Marked ) { + changeCase(p, won ? Marked : Uncovered); + if ( !won ) _nbUncovered--; // not an empty case ... + } + } +} + +bool BaseField::autoReveal(const Coord &p, bool *caseUncovered) +{ + if ( state(p)!=Uncovered ) return true; + + uint nb = nbMinesAround(p); + CoordList n = neighbours(p); + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + if ( state(*it)==Marked ) nb--; + if ( nb==0 ) // number of surrounding mines == number of marks :) + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + if ( !reveal(*it, 0, caseUncovered) ) return false; + return true; +} + +bool BaseField::reveal(const Coord &p, CoordList *autorevealed, + bool *caseUncovered) +{ + if ( state(p)!=Covered ) return true; + + if (_firstReveal) { + _firstReveal = false; + // set mines positions on field ; must avoid the first + // revealed case + uint n = size() - 1; // minus one case free + Q_ASSERT( _nbMines<n ); + for(uint k=0; k<_nbMines; k++) { + uint pos = _random.getLong(n - k); + uint i = 0; + Coord tmp; + for (;;) { + tmp = coord(i); + if ( !(tmp==p) && !hasMine(tmp) ) { + if ( pos==0 ) break; + pos--; + } + i++; + } + (*this)[tmp].mine = true; + } + } + + if ( !hasMine(p) ) { + uncover(p, autorevealed); + if (caseUncovered) *caseUncovered = true; + return true; + } + + // explosion + changeCase(p, Exploded); + + // find all errors + for (uint i=0; i<size(); i++) { + Coord p = coord(i); + if ( state(p)==Marked && !hasMine(p) ) changeCase(p, Error); + } + return false; +} + +void BaseField::completeReveal() +{ + for (;;) { + bool changed = false; + for (uint i=0; i<size(); i++) { + Coord c = coord(i); + if ( state(c)!=Uncovered ) continue; + autoReveal(c, &changed); + uint nb = nbMinesAround(c); + CoordList n = neighbours(c); + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + if ( state(*it)!=Uncovered ) nb--; + if (nb) continue; + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + if ( state(*it)!=Uncovered && state(*it)!=Marked ) { + changed = true; + changeCase(*it, Marked); + } + } + if ( !changed ) break; + } +} + +void BaseField::doMark(const Coord &c) +{ + if ( state(c)!=Covered ) return; + changeCase(c, Marked); +} + +QCString BaseField::string() const +{ + QCString s(size()); + for (uint i=0; i<size(); i++) + s[i] = (hasMine(coord(i)) ? '1' : '0'); + return s; +} diff --git a/kmines/solver/bfield.h b/kmines/solver/bfield.h new file mode 100644 index 00000000..9c91d41c --- /dev/null +++ b/kmines/solver/bfield.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef BASE_FIELD_H +#define BASE_FIELD_H + +#include <qcstring.h> + +#include <krandomsequence.h> +#include <kgrid2d.h> + +#include "defines.h" + + +class BaseField : public KGrid2D::Square<KMines::Case>, public KMines +{ + public: + // seed for KRandomSequence (used by solver check programs) + BaseField(long seed = 0); + virtual ~BaseField() {} + + void reset(uint width, uint height, uint nbMines); + static bool checkField(uint width, uint height, uint nbMines, + const QString &field); + void initReplay(const QString &field); // string == "0100011011000101..." + +// -------------------------- +// interface used by the solver + uint nbMines() const { return _nbMines; } + bool isCovered(const KGrid2D::Coord &p) const + { return ( state(p)!=KMines::Uncovered ); } + uint nbMinesAround(const KGrid2D::Coord &) const; + KGrid2D::CoordList coveredNeighbours(const KGrid2D::Coord &p) const; + bool isSolved() const { return (size() - _nbUncovered)==_nbMines; } + + // return false if the case revealed contains a mine. + virtual bool doReveal(const KGrid2D::Coord &c, + KGrid2D::CoordList *autorevealed, bool *caseUncovered) + { return reveal(c, autorevealed, caseUncovered); } + virtual void doMark(const KGrid2D::Coord &); +// ------------------------- + + uint nbMarked() const { return _nbMarked; } + QCString string() const; + + void showAllMines(bool won); + + protected: + bool firstReveal() const { return _firstReveal; } + KMines::CaseState state(const KGrid2D::Coord &p) const + { return (*this)[p].state; } + bool hasMine(const KGrid2D::Coord &p) const { return (*this)[p].mine; } + virtual void changeCase(const KGrid2D::Coord &, KMines::CaseState); + bool reveal(const KGrid2D::Coord &c, + KGrid2D::CoordList *autorevealed, bool *caseUncovered); + bool autoReveal(const KGrid2D::Coord &, bool *caseUncovered); + void completeReveal(); + + private: + bool _firstReveal; + uint _nbUncovered, _nbMarked, _nbUncertain, _nbMines; + KRandomSequence _random; + + void uncover(const KGrid2D::Coord &, KGrid2D::CoordList *autoreveal); + void changeState(KMines::CaseState, int increment); +}; + +#endif diff --git a/kmines/solver/headerP.h b/kmines/solver/headerP.h new file mode 100644 index 00000000..984e3113 --- /dev/null +++ b/kmines/solver/headerP.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny ([email protected]) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __HEADERP_H +#define __HEADERP_H + +//#define DEBUG 2 + +#include <set> +#include <list> +#include <map> +#include <memory> +#include <iostream> + +#include "bfield.h" + + +using namespace KGrid2D; +using std::cout; +using std::endl; + +typedef std::set<Coord, std::less<Coord> > CoordSet; + +inline std::ostream &operator <<(std::ostream &s, const Coord &c) +{ + s << '(' << c.first << ',' << c.second << ')' << endl; + return s; +} + +inline std::ostream &operator <<(std::ostream &s, const CoordSet &set) +{ + for(CoordSet::const_iterator i=set.begin(); i!=set.end(); ++i) + s << *i; + return s; +} + +inline std::ostream &operator <<(std::ostream &s, const BaseField &f) +{ + for (uint j=0; j<f.height(); j++) { + for (uint i=0; i<f.width(); i++) { + Coord c(i, j); + if ( f.isCovered(c) ) s << "? "; + else s << f.nbMinesAround(c) << ' '; + } + s << endl; + } + return s; +} + +namespace AdviseFast { + + /** A fact - number of mines in adjacent cells */ + struct Fact { + CoordSet pointSet; + short mines; + }; + std::ostream &operator <<(std::ostream &, Fact const &); + + /** A set of facts that can be generated out of Field */ + class FactSet : public std::map<Coord, Fact> { + public: + FactSet(BaseField *); + BaseField const *getField() const { return _field;} + + /** Reveals a point on the field underlining + * Returns false on blowup !!! */ + bool reveal( + Coord what, + CoordSet *affectedFacts); + void mark( + Coord what, + CoordSet *affectedFacts); + CoordSet const *getContainingFacts( + Coord const &) + const; + /** May be used to substitute fact */ + void addFact(Coord const &, Fact const &); + void deleteFact(Coord const &); + void retrieveFact(Coord which, Fact *where); + + private: + BaseField *_field; + std::map<Coord, CoordSet> _containingFacts; + CoordSet _marked; + }; + std::ostream &operator <<(std::ostream &, FactSet const &); + + /** A Rule abstraction that can be applied. + * Applying the rule results in either modifyling the + * RuleSet which it belongs to or FactSet it is based on + * or both ;) + */ + class RuleSet; + struct Rule { + Rule(RuleSet *parent); + virtual ~Rule(); + virtual bool apply(CoordSet *surePoints) = 0; + + RuleSet *_parent; + FactSet *_facts; +#if defined(DEBUG) +# if DEBUG >= 2 + private: + static int leaks; +# endif +#endif + }; + + /** A set of rules */ + class RuleSet { + public: + enum RuleType { + EMPTY, + FULL, + INCLUDE, + INCLUDE1, + INTERSECT, + INTERSECT1, + GENERAL}; + + typedef std::pair<RuleType, CoordSet> Entry; + + RuleSet(FactSet *); + ~RuleSet(); + void addRule(Entry const &); + + /** A factory method */ + Rule *newRule(Entry const &); + + /** Remove all references to a point from RuleSet */ + void removeRef(Coord); + + /** removeRef + add a General Rule */ + void addGeneral(Coord); + + /** Returns false on blowup */ + bool reveal(Coord p); + + /** Returns false on failure */ + bool getSurePoint(Coord *sp); + /** Works until is stuck :) */ + void solve(); + + FactSet *facts; + + private: + std::set<Entry> _rules; + CoordSet _surePoints; + + /** Fills _surePoints. + * Returns false if nothing done. */ + bool apply(); + }; + + /** Returns true on success */ + bool adviseFast( + Coord *point, + FactSet *facts, + RuleSet *rules); + +} + + +namespace AdviseFull { + typedef std::multimap<float, Coord> ProbabilityMap; + + /** If there are sure free cells, + * sets surePoints, otherwise sets probabilities */ + void adviseFull( + AdviseFast::FactSet *facts, + CoordSet *surePoints, + ProbabilityMap *probabilities); + +} + +#endif diff --git a/kmines/solver/solver.cpp b/kmines/solver/solver.cpp new file mode 100644 index 00000000..00807fca --- /dev/null +++ b/kmines/solver/solver.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny ([email protected]) + * Copyright (c) 2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "solver.h" +#include "solver.moc" + +#include <algorithm> +#include <assert.h> + +#include <qtimer.h> +#include <qlayout.h> +#include <qlabel.h> +#include <kprogress.h> + +#include <klocale.h> + +#include "headerP.h" + + +//----------------------------------------------------------------------------- +class SolverPrivate +{ + public: + SolverPrivate() : facts(0), rules(0) {} + ~SolverPrivate() { + delete facts; + delete rules; + } + + AdviseFast::FactSet *facts; + AdviseFast::RuleSet *rules; +#ifdef DEBUG + unsigned long t0, t; +#endif +}; + +Solver::Solver(QObject *parent) + : QObject(parent) +{ + d = new SolverPrivate; + +#ifdef DEBUG +#define PRINT_ELAPSED(purpose) \ + d->t = time(0); \ + cout << "Spent " << d->t - d->t0 << " seconds on " purpose << endl; \ + d->t0 = d->t; + +#endif +} + +Solver::~Solver() +{ + delete d; +} + +Coord Solver::advise(BaseField &field, float &probability) +{ + Coord point; + probability = 1; + delete d->facts; + d->facts = new AdviseFast::FactSet(&field); + delete d->rules; + d->rules = new AdviseFast::RuleSet(d->facts); + + if( AdviseFast::adviseFast(&point, d->facts, d->rules) ) return point; + + CoordSet surePoints; + AdviseFull::ProbabilityMap probabilities; + AdviseFull::adviseFull(d->facts, &surePoints, &probabilities); + + // return one of the sure point (random choice to limit the tropism) [NH] + if( !surePoints.empty() ) { + KRandomSequence r; + uint k = r.getLong(surePoints.size()); + CoordSet::iterator it = surePoints.begin(); + for (uint i=0; i<k; i++) ++it; + return *it; + } + + // Just a minimum probability logic here + if( !probabilities.empty() ){ + probability = probabilities.begin()->first; + return probabilities.begin()->second; + } + + // Otherwise the Field is already solved :) + return Coord(-1,-1); +} + +void Solver::solve(BaseField &field, bool noGuess) +{ + _field = &field; + initSolve(false, noGuess); +} + +bool Solver::initSolve(bool oneStep, bool noGuess) +{ + _inOneStep = oneStep; + _noGuess = noGuess; + delete d->facts; + d->facts = new AdviseFast::FactSet(_field); + delete d->rules; + d->rules = new AdviseFast::RuleSet(d->facts); +#ifdef DEBUG + d->t0 = time(0); +#endif + return solveStep(); +} + +bool Solver::solveStep() +{ + if ( _field->isSolved() ) { + emit solvingDone(true); + return true; + } + + d->rules->solve(); + +#ifdef DEBUG + PRINT_ELAPSED("fast rules") +#endif + + if( _field->isSolved() ) { + emit solvingDone(true); + return true; + } + + CoordSet surePoints; + AdviseFull::ProbabilityMap probabilities; + AdviseFull::adviseFull(d->facts, &surePoints, &probabilities); + +#ifdef DEBUG + PRINT_ELAPSED("full rules") +#endif + + if(!surePoints.empty()){ + CoordSet::iterator i; + for(i=surePoints.begin(); i!=surePoints.end(); ++i) { + bool b = d->rules->reveal(*i); + assert(b); + } + } else if ( !_noGuess ) { +#ifdef DEBUG + cout << "Applying heuristics!" << endl; + cout << *_field << endl; +#endif + // Minimum probability logic + assert(!probabilities.empty()); +#ifdef DEBUG + AdviseFull::ProbabilityMap::iterator i=probabilities.begin(); + cout << "Probability is " << i->first << endl; +#endif + bool success = d->rules->reveal(probabilities.begin()->second); + if ( !success ) { + emit solvingDone(false); + return false; + } + } + + if (_inOneStep) return solveStep(); + else QTimer::singleShot(0, this, SLOT(solveStep())); + return false; +} + +bool Solver::solveOneStep(BaseField &field) +{ + _field = &field; + return initSolve(true, false); +} + + +//----------------------------------------------------------------------------- +SolvingRateDialog::SolvingRateDialog(const BaseField &field, QWidget *parent) + : KDialogBase(Plain, i18n("Compute Solving Rate"), Ok|Close, + Close, parent, "compute_solving_rate", true, true), + _refField(field) +{ + connect(&_solver, SIGNAL(solvingDone(bool)), SLOT(solvingDone(bool))); + + KGuiItem item = KStdGuiItem::ok(); + item.setText(i18n("Start")); + setButtonOK(item); + + QVBoxLayout *top = new QVBoxLayout(plainPage(), 0, spacingHint()); + QLabel *label = new QLabel(i18n("Width: %1").arg(field.width()), + plainPage()); + top->addWidget(label); + label = new QLabel(i18n("Height: %1").arg(field.height()), plainPage()); + top->addWidget(label); + label = new QLabel(i18n("Mines: %1 (%2%)").arg(field.nbMines()) + .arg( field.nbMines() * 100.0 / field.size()), + plainPage()); + top->addWidget(label); + + top->addSpacing(spacingHint()); + + _progress = new KProgress(NB_STEPS, plainPage()); + _progress->setTextEnabled(true); + _progress->setFormat("%v"); + top->addWidget(_progress); + + _label = new QLabel(i18n("Success rate:"), plainPage()); + top->addWidget(_label); +} + +void SolvingRateDialog::slotOk() +{ + enableButtonOK(false); + _i = 0; + _success = 0; + _progress->setValue(0); + QTimer::singleShot(0, this, SLOT(step())); +} + +void SolvingRateDialog::step() +{ + if ( _i==NB_STEPS ) { + enableButtonOK(true); + return; + } + _i++; + _field.reset(_refField.width(), _refField.height(), _refField.nbMines()); + _solver.solve(_field, false); +} + +void SolvingRateDialog::solvingDone(bool success) +{ + if (success) _success++; + _label->setText(i18n("Success rate: %1%") + .arg(_success * 100.0 / _i, 0, 'f', 3)); + _progress->advance(1); + QTimer::singleShot(0, this, SLOT(step())); +} diff --git a/kmines/solver/solver.h b/kmines/solver/solver.h new file mode 100644 index 00000000..f076874e --- /dev/null +++ b/kmines/solver/solver.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny ([email protected]) + * Copyright (c) 2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SOLVER_H +#define __SOLVER_H + +#include <kdialogbase.h> + +#include "bfield.h" + + +class QLabel; +class KProgress; +class SolverPrivate; + +class Solver : public QObject +{ + Q_OBJECT + public: + Solver(QObject *parent = 0); + ~Solver(); + + /** A method to advice a point placement */ + KGrid2D::Coord advise(BaseField &field, float &probability); + + /** Solve current mine field */ + void solve(BaseField &field, bool noGuess); + + /** Solve without signals/slot (for test programs) */ + bool solveOneStep(BaseField &field); + + signals: + void solvingDone(bool success); + + private slots: + bool solveStep(); + + private: + BaseField *_field; + bool _inOneStep, _noGuess; + SolverPrivate *d; + + bool initSolve(bool oneStep, bool noGuess); +}; + +class SolvingRateDialog : public KDialogBase +{ + Q_OBJECT + public: + SolvingRateDialog(const BaseField &field, QWidget *parent); + + private slots: + void step(); + void slotOk(); + void solvingDone(bool success); + + private: + const BaseField &_refField; + BaseField _field; + Solver _solver; + uint _i, _success; + QLabel *_label; + KProgress *_progress; + + static const uint NB_STEPS = 200; +}; + +#endif diff --git a/kmines/solver/test.cpp b/kmines/solver/test.cpp new file mode 100644 index 00000000..dd56d7a0 --- /dev/null +++ b/kmines/solver/test.cpp @@ -0,0 +1,45 @@ +/** A program to test advisory library */ + +#include "bfield.h" +#include "headerP.h" + +#define W 10 +#define H 10 + +int main(int argc, char *argv[]) +{ + long seed = (argc<2 ? time(0) : atoi(argv[1])); + cout << "seed = " << seed << endl; + + BaseField f(seed); + f.reset(W, H, 10); + + KRandomSequence random(seed); + Coord c(random.getLong(W), random.getLong(H)); + f.doReveal(c, 0, 0); + + CoordSet sp; + AdviseFull::ProbabilityMap pm; + + AdviseFast::FactSet facts(&f); + AdviseFull::adviseFull(&facts, &sp, &pm); + + float pic[H][W]; + + for(uint i=0; i<H; ++i) + for(uint j=0; j<W; ++j) pic[i][j] = -1; // unknown + pic[c.second][c.first] = -(int)f.nbMinesAround(c); + + AdviseFull::ProbabilityMap::iterator pmi; + for(pmi = pm.begin(); pmi != pm.end(); ++pmi) + pic[pmi->second.second][pmi->second.first] = pmi->first; + + QString s; + for(uint i=0;i<H;++i) { + for(uint j=0;j<W;++j) + cout << s.sprintf("%+.02f ", pic[i][j]).latin1(); + cout << endl; + } + + return 0; +} diff --git a/kmines/solver/testFast.cpp b/kmines/solver/testFast.cpp new file mode 100644 index 00000000..7dbaa757 --- /dev/null +++ b/kmines/solver/testFast.cpp @@ -0,0 +1,30 @@ +/** A program to test advisory library */ + +#include "bfield.h" +#include "headerP.h" + +#define W 10 +#define H 10 + +int main(int argc, char *argv[]) +{ + long seed = (argc < 2 ? time(0) : atoi(argv[1])); + cout << "seed = " << seed << endl; + + BaseField f(seed); + f.reset(W, H, 10); + + KRandomSequence random(seed); + Coord c(random.getLong(W), random.getLong(H)); + f.doReveal(c, 0, 0); + + AdviseFast::FactSet facts(&f); + AdviseFast::RuleSet rules(&facts); + + rules.solve(); + + cout << f << endl; + if(!f.isSolved()) cout << facts << endl; + + return 0; +} diff --git a/kmines/solver/testRate.cpp b/kmines/solver/testRate.cpp new file mode 100644 index 00000000..7fa9e90f --- /dev/null +++ b/kmines/solver/testRate.cpp @@ -0,0 +1,41 @@ +/** A program to test advisory library */ + +#include <assert.h> +#include <time.h> + +#include "bfield.h" +#include "solver.h" +#include "headerP.h" + +int main(int argc, char *argv[]) +{ + if ( argc!=4 ) + qFatal("Arguments: width height nbMines"); + + long seed = time(0); + cout << "seed = " << seed << endl; + + short W, H, M; + W = atoi(argv[1]); assert(W > 0); + H = atoi(argv[2]); assert(H > 0); + M = atoi(argv[3]); assert(M >= 0); // ;) + + BaseField field(seed); + Solver solver; + + int i, solved = 0; + for(i=0;i<1000;++i){ + field.reset(W, H, M); + + if( !solver.solveOneStep(field)){ + cout << "OOPS!!" << endl; + cout << field << endl; + } else ++solved; + + cout << "Tried " << i+1 << ", solved " << solved << endl; + } + + cout << "Solved total: " << solved << endl; + + return 0; +} diff --git a/kmines/solver/testSolve.cpp b/kmines/solver/testSolve.cpp new file mode 100644 index 00000000..61b7e570 --- /dev/null +++ b/kmines/solver/testSolve.cpp @@ -0,0 +1,33 @@ +/** A program to test advisory library */ + +#include <assert.h> +#include <time.h> + +#include "bfield.h" +#include "solver.h" +#include "headerP.h" + +int main(int argc, char *argv[]) +{ + if ( argc!=4 ) + qFatal("Arguments: width height nbMines"); + + long seed = time(0); + cout << "seed = " << seed << endl; + + short W, H, M; + W = atoi(argv[1]); assert(W > 0); + H = atoi(argv[2]); assert(H > 0); + M = atoi(argv[3]); assert(M >= 0); // ;) + + BaseField field(seed); + field.reset(W, H, M); + + Solver solver; + if( !solver.solveOneStep(field) ) cout << "OOPS!!" << endl; + else cout << "Solved!" << endl; + + cout << field << endl; + + return 0; +} diff --git a/kmines/status.cpp b/kmines/status.cpp new file mode 100644 index 00000000..a3d5f061 --- /dev/null +++ b/kmines/status.cpp @@ -0,0 +1,478 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "status.h" +#include "status.moc" + +#include <qpainter.h> +#include <qpixmap.h> +#include <qwhatsthis.h> +#include <qlayout.h> +#include <qwidgetstack.h> +#include <qtextedit.h> +#include <qtimer.h> + +#include <kapplication.h> +#include <klocale.h> +#include <kconfig.h> +#include <kmessagebox.h> +#include <kaction.h> +#include <kdebug.h> +#include <kfiledialog.h> +#include <ktempfile.h> +#include <kio/netaccess.h> +#include <knotifyclient.h> +#include <kexthighscore.h> + +#include "settings.h" +#include "solver/solver.h" +#include "dialogs.h" +#include "version.h" + + +Status::Status(QWidget *parent) + : QWidget(parent, "status"), _oldLevel(Level::Easy) +{ + _timer = new QTimer(this); + connect(_timer, SIGNAL(timeout()), SLOT(replayStep())); + + _solver = new Solver(this); + connect(_solver, SIGNAL(solvingDone(bool)), SLOT(solvingDone(bool))); + +// top layout + QGridLayout *top = new QGridLayout(this, 2, 5, 10, 10); + top->setColStretch(1, 1); + top->setColStretch(3, 1); + +// status bar + // mines left LCD + left = new KGameLCD(5, this); + left->setFrameStyle(QFrame::Panel | QFrame::Sunken); + left->setDefaultBackgroundColor(black); + left->setDefaultColor(white); + QWhatsThis::add(left, i18n("<qt>Mines left.<br/>" + "It turns <font color=\"red\">red</font> " + "when you have flagged more cases than " + "present mines.</qt>")); + top->addWidget(left, 0, 0); + + // smiley + smiley = new Smiley(this); + connect(smiley, SIGNAL(clicked()), SLOT(smileyClicked())); + smiley->setFocusPolicy(QWidget::NoFocus); + QWhatsThis::add(smiley, i18n("Press to start a new game")); + top->addWidget(smiley, 0, 2); + + // digital clock LCD + dg = new DigitalClock(this); + QWhatsThis::add(dg, i18n("<qt>Time elapsed.<br/>" + "It turns <font color=\"blue\">blue</font> " + "if it is a highscore " + "and <font color=\"red\">red</font> " + "if it is the best time.</qt>")); + top->addWidget(dg, 0, 4); + +// mines field + _fieldContainer = new QWidget(this); + QGridLayout *g = new QGridLayout(_fieldContainer, 1, 1); + _field = new Field(_fieldContainer); + _field->readSettings(); + g->addWidget(_field, 0, 0, AlignCenter); + connect( _field, SIGNAL(updateStatus(bool)), SLOT(updateStatus(bool)) ); + connect(_field, SIGNAL(gameStateChanged(GameState)), + SLOT(gameStateChangedSlot(GameState)) ); + connect(_field, SIGNAL(setMood(Mood)), smiley, SLOT(setMood(Mood))); + connect(_field, SIGNAL(setCheating()), dg, SLOT(setCheating())); + connect(_field,SIGNAL(addAction(const KGrid2D::Coord &, Field::ActionType)), + SLOT(addAction(const KGrid2D::Coord &, Field::ActionType))); + QWhatsThis::add(_field, i18n("Mines field.")); + +// resume button + _resumeContainer = new QWidget(this); + g = new QGridLayout(_resumeContainer, 1, 1); + QFont f = font(); + f.setBold(true); + QPushButton *pb + = new QPushButton(i18n("Press to Resume"), _resumeContainer); + pb->setFont(f); + connect(pb, SIGNAL(clicked()), SIGNAL(pause())); + g->addWidget(pb, 0, 0, AlignCenter); + + _stack = new QWidgetStack(this); + _stack->addWidget(_fieldContainer); + _stack->addWidget(_resumeContainer); + _stack->raiseWidget(_fieldContainer); + top->addMultiCellWidget(_stack, 1, 1, 0, 4); +} + +void Status::smileyClicked() +{ + if ( _field->gameState()==Paused ) emit pause(); + else restartGame(); +} + +void Status::newGame(int t) +{ + if ( _field->gameState()==Paused ) emit pause(); + Level::Type type = (Level::Type)t; + Settings::setLevel(type); + if ( type!=Level::Custom ) newGame( Level(type) ); + else newGame( Settings::customLevel() ); +} + +void Status::newGame(const Level &level) +{ + _timer->stop(); + if ( level.type()!=Level::Custom ) + KExtHighscore::setGameType(level.type()); + _field->setLevel(level); +} + +bool Status::checkBlackMark() +{ + bool bm = ( _field->gameState()==Playing ); + if (bm) KExtHighscore::submitScore(KExtHighscore::Lost, this); + return bm; +} + +void Status::restartGame() +{ + if ( _field->gameState()==Paused ) emit pause(); + else if ( _field->gameState()==Replaying ) { + _timer->stop(); + _field->setLevel(_oldLevel); + } else { + bool bm = checkBlackMark(); + _field->reset(bm); + } +} + +void Status::settingsChanged() +{ + _field->readSettings(); + + if ( Settings::level()!=Level::Custom ) return; + Level l = Settings::customLevel(); + if ( l==_field->level() ) return; + if ( _field->gameState()==Paused ) emit pause(); + newGame(l); +} + +void Status::updateStatus(bool mine) +{ + int r = _field->nbMines() - _field->nbMarked(); + QColor color = (r<0 && !_field->isSolved() ? red : white); + left->setColor(color); + left->display(r); + + if ( _field->isSolved() && !mine ) + gameStateChanged(GameOver, true); // ends only for wins +} + +void Status::setGameOver(bool won) +{ + if ( !won ) + KNotifyClient::event(winId(), "explosion", i18n("Explosion!")); + _field->showAllMines(won); + smiley->setMood(won ? Happy : Sad); + if ( _field->gameState()==Replaying ) return; + + _field->setGameOver(); + dg->stop(); + if ( _field->level().type()!=Level::Custom && !dg->cheating() ) { + if (won) KExtHighscore::submitScore(dg->score(), this); + else KExtHighscore::submitScore(KExtHighscore::Lost, this); + } + + KNotifyClient::event(winId(), won ? "won" : "lost", + won ? i18n("Game won!") : i18n("Game lost!")); + + // game log + _logRoot.setAttribute("count", dg->nbActions()); + + if ( Settings::magicReveal() ) + _logRoot.setAttribute("complete_reveal", "true"); + QString sa = "none"; + if ( _field->solvingState()==Solved ) sa = "solving"; + else if ( _field->solvingState()==Advised ) sa = "advising"; + _logRoot.setAttribute("solver", sa); + + QDomElement f = _log.createElement("Field"); + _logRoot.appendChild(f); + QDomText data = _log.createTextNode(_field->string()); + f.appendChild(data); +} + +void Status::setStopped() +{ + smiley->setMood(Normal); + updateStatus(false); + bool custom = ( _field->level().type()==Level::Custom ); + dg->reset(custom); + _field->setSolvingState(Regular); +} + +void Status::setPlaying() +{ + smiley->setMood(Normal); + dg->start(); + if ( _field->gameState()==Paused ) return; // do not restart game log... + + // game log + const Level &level = _field->level(); + _log = QDomDocument("kmineslog"); + _logRoot = _log.createElement("kmineslog"); + _logRoot.setAttribute("version", SHORT_VERSION); + QDateTime date = QDateTime::currentDateTime(); + _logRoot.setAttribute("date", date.toString(Qt::ISODate)); + _logRoot.setAttribute("width", level.width()); + _logRoot.setAttribute("height", level.height()); + _logRoot.setAttribute("mines", level.nbMines()); + _log.appendChild(_logRoot); + _logList = _log.createElement("ActionList"); + _logRoot.appendChild(_logList); +} + +void Status::gameStateChanged(GameState state, bool won) +{ + QWidget *w = _fieldContainer; + + switch (state) { + case Playing: + setPlaying(); + break; + case GameOver: + setGameOver(won); + break; + case Paused: + smiley->setMood(Sleeping); + dg->stop(); + w = _resumeContainer; + break; + case Stopped: + case Init: + setStopped(); + break; + case Replaying: + smiley->setMood(Normal); + break; + case NB_STATES: + Q_ASSERT(false); + break; + } + + _stack->raiseWidget(w); + emit gameStateChangedSignal(state); +} + +void Status::addAction(const KGrid2D::Coord &c, Field::ActionType type) +{ + QDomElement action = _log.createElement("Action"); + action.setAttribute("time", dg->pretty()); + action.setAttribute("column", c.first); + action.setAttribute("line", c.second); + action.setAttribute("type", Field::ACTION_DATA[type].name); + _logList.appendChild(action); + dg->addAction(); +} + +void Status::advise() +{ + int res = KMessageBox::warningContinueCancel(this, + i18n("When the solver gives " + "you advice, your score will not be added to the highscores."), + QString::null, QString::null, "advice_warning"); + if ( res==KMessageBox::Cancel ) return; + dg->setCheating(); + float probability; + KGrid2D::Coord c = _solver->advise(*_field, probability); + _field->setAdvised(c, probability); +} + +void Status::solve() +{ + dg->setCheating(); + _solver->solve(*_field, false); + _field->setSolvingState(Solved); +} + +void Status::solvingDone(bool success) +{ + if ( !success ) gameStateChanged(GameOver, false); +} + +void Status::solveRate() +{ + SolvingRateDialog sd(*_field, this); + sd.exec(); +} + +void Status::viewLog() +{ + KDialogBase d(this, "view_log", true, i18n("View Game Log"), + KDialogBase::Close, KDialogBase::Close); + QTextEdit *view = new QTextEdit(&d); + view->setReadOnly(true); + view->setTextFormat(PlainText); + view->setText(_log.toString()); + d.setMainWidget(view); + d.resize(500, 400); + d.exec(); +} + +void Status::saveLog() +{ + KURL url = KFileDialog::getSaveURL(QString::null, QString::null, this); + if ( url.isEmpty() ) return; + if ( KIO::NetAccess::exists(url, false, this) ) { + KGuiItem gi = KStdGuiItem::save(); + gi.setText(i18n("Overwrite")); + int res = KMessageBox::warningYesNo(this, + i18n("The file already exists. Overwrite?"), + i18n("File Exists"), gi, KStdGuiItem::cancel()); + if ( res==KMessageBox::No ) return; + } + KTempFile tmp; + (*tmp.textStream()) << _log.toString(); + tmp.close(); + KIO::NetAccess::upload(tmp.name(), url, this); + tmp.unlink(); +} + +void Status::loadLog() +{ + KURL url = KFileDialog::getOpenURL(QString::null, QString::null, this); + if ( url.isEmpty() ) return; + QString tmpFile; + bool success = false; + QDomDocument doc; + if( KIO::NetAccess::download(url, tmpFile, this) ) { + QFile file(tmpFile); + if ( file.open(IO_ReadOnly) ) { + int errorLine; + bool ok = doc.setContent(&file, 0, &errorLine); + if ( !ok ) { + KMessageBox::sorry(this, i18n("Cannot read XML file on line %1") + .arg(errorLine)); + return; + } + success = true; + } + KIO::NetAccess::removeTempFile(tmpFile); + + } + if ( !success ) { + KMessageBox::sorry(this, i18n("Cannot load file.")); + return; + } + + if ( !checkLog(doc) ) + KMessageBox::sorry(this, i18n("Log file not recognized.")); + else { + _log = doc; + _logRoot = doc.namedItem("kmineslog").toElement(); + emit gameStateChangedSignal(GameOver); + } +} + +bool Status::checkLog(const QDomDocument &doc) +{ + // check root element + if ( doc.doctype().name()!="kmineslog" ) return false; + QDomElement root = doc.namedItem("kmineslog").toElement(); + if ( root.isNull() ) return false; + bool ok; + uint w = root.attribute("width").toUInt(&ok); + if ( !ok || w>CustomConfig::maxWidth || w<CustomConfig::minWidth ) + return false; + uint h = root.attribute("height").toUInt(&ok); + if ( !ok || h>CustomConfig::maxHeight || h<CustomConfig::minHeight ) + return false; + uint nb = root.attribute("mines").toUInt(&ok); + if ( !ok || nb==0 || nb>Level::maxNbMines(w, h) ) return false; + + // check field + QDomElement field = root.namedItem("Field").toElement(); + if ( field.isNull() ) return false; + QString ftext = field.text(); + if ( !BaseField::checkField(w, h, nb, ftext) ) return false; + + // check action list + QDomElement list = root.namedItem("ActionList").toElement(); + if ( list.isNull() ) return false; + QDomNodeList actions = list.elementsByTagName("Action"); + if ( actions.count()==0 ) return false; + for (uint i=0; i<actions.count(); i++) { + QDomElement a = actions.item(i).toElement(); + if ( a.isNull() ) return false; + uint i0 = a.attribute("line").toUInt(&ok); + if ( !ok || i0>=h ) return false; + uint j = a.attribute("column").toUInt(&ok); + if ( !ok || j>=w ) return false; + QString type = a.attribute("type"); + uint k = 0; + for (; k<Field::Nb_Actions; k++) + if ( type==Field::ACTION_DATA[k].name ) break; + if ( k==Field::Nb_Actions ) return false; + } + + return true; +} + + +void Status::replayLog() +{ + uint w = _logRoot.attribute("width").toUInt(); + uint h = _logRoot.attribute("height").toUInt(); + uint n = _logRoot.attribute("mines").toUInt(); + Level level(w, h, n); + QDomNode f = _logRoot.namedItem("Field"); + _oldLevel = _field->level(); + newGame(level); + _field->setReplayField(f.toElement().text()); + QString s = _logRoot.attribute("complete_reveal"); + _completeReveal = ( s=="true" ); + + f = _logRoot.namedItem("ActionList"); + _actions = f.toElement().elementsByTagName("Action"); + _index = 0; + _timer->start(500); +} + +void Status::replayStep() +{ + if ( _index>=_actions.count() ) { + _timer->stop(); + _actions = QDomNodeList(); + return; + } + + _timer->changeInterval(200); + QDomElement a = _actions.item(_index).toElement(); + dg->setTime(a.attribute("time")); + uint i = a.attribute("column").toUInt(); + uint j = a.attribute("line").toUInt(); + QString type = a.attribute("type"); + for (uint k=0; k<Field::Nb_Actions; k++) + if ( type==Field::ACTION_DATA[k].name ) { + _field->doAction((Field::ActionType)k, + KGrid2D::Coord(i, j), _completeReveal); + break; + } + _index++; +} diff --git a/kmines/status.h b/kmines/status.h new file mode 100644 index 00000000..6fd06a76 --- /dev/null +++ b/kmines/status.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK ([email protected]) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef STATUS_H +#define STATUS_H + +#include <qdom.h> + +#include "field.h" + +class Smiley; +class KGameLCD; +class DigitalClock; +class Solver; +class QWidgetStack; +class QTimer; + +class Status : public QWidget, public KMines +{ + Q_OBJECT + public : + Status(QWidget *parent); + + const Level ¤tLevel() const { return _field->level(); } + bool isPlaying() const { return _field->gameState()==Playing; } + void settingsChanged(); + Field *field() { return _field; } + + bool checkBlackMark(); + + signals: + void pause(); + void gameStateChangedSignal(KMines::GameState); + + public slots: + void newGame(int type); + void restartGame(); + void updateStatus(bool); + void pauseGame() { _field->pause(); } + + void moveUp() { _field->moveCursor(KGrid2D::SquareBase::Up); } + void moveDown() { _field->moveCursor(KGrid2D::SquareBase::Down); } + void moveLeft() { _field->moveCursor(KGrid2D::SquareBase::Left); } + void moveRight() { _field->moveCursor(KGrid2D::SquareBase::Right); } + void moveLeftEdge() { _field->moveToEdge(KGrid2D::SquareBase::Left); } + void moveRightEdge() { _field->moveToEdge(KGrid2D::SquareBase::Right); } + void moveTop() { _field->moveToEdge(KGrid2D::SquareBase::Up); } + void moveBottom() { _field->moveToEdge(KGrid2D::SquareBase::Down); } + void reveal() { _field->doReveal(); } + void mark() { _field->doMark(); } + void autoReveal() { _field->keyboardAutoReveal(); } + + void advise(); + void solve(); + void solveRate(); + void addAction(const KGrid2D::Coord &, Field::ActionType type); + + void viewLog(); + void replayLog(); + void saveLog(); + void loadLog(); + + private slots: + void gameStateChangedSlot(GameState state) + { gameStateChanged(state, false); } + void smileyClicked(); + void solvingDone(bool success); + void replayStep(); + + private: + Field *_field; + QWidget *_fieldContainer, *_resumeContainer; + QWidgetStack *_stack; + + Smiley *smiley; + KGameLCD *left; + DigitalClock *dg; + Solver *_solver; + + QDomDocument _log; + QDomElement _logRoot, _logList; + QDomNodeList _actions; + uint _index; + bool _completeReveal; + Level _oldLevel; + QTimer *_timer; + + void setGameOver(bool won); + void setStopped(); + void setPlaying(); + void newGame(const Level &); + void gameStateChanged(GameState, bool won); + static bool checkLog(const QDomDocument &); +}; + +#endif // STATUS_H diff --git a/kmines/version.h b/kmines/version.h new file mode 100644 index 00000000..0e8f3568 --- /dev/null +++ b/kmines/version.h @@ -0,0 +1,5 @@ +#define SHORT_VERSION "2.1.10" +#define LONG_VERSION "2.1.10 (25 Aug 2005)" +#define COPYLEFT "(c) 1996-2005, Nicolas Hadacek\n(c) 2001, Mikhail Kourinny" +#define EMAIL "[email protected]" +#define HOMEPAGE "http://kmines.sourceforge.net/" |