summaryrefslogtreecommitdiffstats
path: root/kmines
diff options
context:
space:
mode:
Diffstat (limited to 'kmines')
-rw-r--r--kmines/CHANGELOG325
-rw-r--r--kmines/LICENSE19
-rw-r--r--kmines/Makefile.am66
-rw-r--r--kmines/README16
-rw-r--r--kmines/TODO14
-rw-r--r--kmines/bitmaps/Makefile.am2
-rw-r--r--kmines/bitmaps/README1
-rw-r--r--kmines/bitmaps/smile37
-rw-r--r--kmines/bitmaps/smile_happy37
-rw-r--r--kmines/bitmaps/smile_ohno37
-rw-r--r--kmines/bitmaps/smile_sleep36
-rw-r--r--kmines/bitmaps/smile_stress36
-rw-r--r--kmines/data/Makefile.am7
-rw-r--r--kmines/data/eventsrc988
-rw-r--r--kmines/data/hi128-app-kmines.pngbin0 -> 12464 bytes
-rw-r--r--kmines/data/hi16-app-kmines.pngbin0 -> 742 bytes
-rw-r--r--kmines/data/hi22-app-kmines.pngbin0 -> 3926 bytes
-rw-r--r--kmines/data/hi32-app-kmines.pngbin0 -> 2061 bytes
-rw-r--r--kmines/data/hi48-app-kmines.pngbin0 -> 3877 bytes
-rw-r--r--kmines/data/hi64-app-kmines.pngbin0 -> 5325 bytes
-rw-r--r--kmines/data/kmines.desktop75
-rw-r--r--kmines/defines.cpp60
-rw-r--r--kmines/defines.h72
-rw-r--r--kmines/dialogs.cpp294
-rw-r--r--kmines/dialogs.h125
-rw-r--r--kmines/field.cpp462
-rw-r--r--kmines/field.h118
-rw-r--r--kmines/frame.cpp143
-rw-r--r--kmines/frame.h56
-rw-r--r--kmines/highscores.cpp94
-rw-r--r--kmines/highscores.h41
-rw-r--r--kmines/kmines.kcfg105
-rw-r--r--kmines/kminesui.rc102
-rw-r--r--kmines/kzoommainwindow.cpp115
-rw-r--r--kmines/kzoommainwindow.h126
-rw-r--r--kmines/main.cpp260
-rw-r--r--kmines/main.h73
-rw-r--r--kmines/settings.kcfgc7
-rw-r--r--kmines/settings_addons.h5
-rw-r--r--kmines/solver/Makefile.am18
-rw-r--r--kmines/solver/advFastRules.cpp482
-rw-r--r--kmines/solver/adviseFast.cpp201
-rw-r--r--kmines/solver/adviseFast.h70
-rw-r--r--kmines/solver/adviseFull.cpp655
-rw-r--r--kmines/solver/adviseFull.h93
-rw-r--r--kmines/solver/bfield.cpp221
-rw-r--r--kmines/solver/bfield.h83
-rw-r--r--kmines/solver/headerP.h191
-rw-r--r--kmines/solver/solver.cpp249
-rw-r--r--kmines/solver/solver.h84
-rw-r--r--kmines/solver/test.cpp45
-rw-r--r--kmines/solver/testFast.cpp30
-rw-r--r--kmines/solver/testRate.cpp41
-rw-r--r--kmines/solver/testSolve.cpp33
-rw-r--r--kmines/status.cpp478
-rw-r--r--kmines/status.h111
-rw-r--r--kmines/version.h5
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
new file mode 100644
index 00000000..faaab0c2
--- /dev/null
+++ b/kmines/data/hi128-app-kmines.png
Binary files differ
diff --git a/kmines/data/hi16-app-kmines.png b/kmines/data/hi16-app-kmines.png
new file mode 100644
index 00000000..06585ce6
--- /dev/null
+++ b/kmines/data/hi16-app-kmines.png
Binary files differ
diff --git a/kmines/data/hi22-app-kmines.png b/kmines/data/hi22-app-kmines.png
new file mode 100644
index 00000000..fc1ae801
--- /dev/null
+++ b/kmines/data/hi22-app-kmines.png
Binary files differ
diff --git a/kmines/data/hi32-app-kmines.png b/kmines/data/hi32-app-kmines.png
new file mode 100644
index 00000000..10740280
--- /dev/null
+++ b/kmines/data/hi32-app-kmines.png
Binary files differ
diff --git a/kmines/data/hi48-app-kmines.png b/kmines/data/hi48-app-kmines.png
new file mode 100644
index 00000000..a1905746
--- /dev/null
+++ b/kmines/data/hi48-app-kmines.png
Binary files differ
diff --git a/kmines/data/hi64-app-kmines.png b/kmines/data/hi64-app-kmines.png
new file mode 100644
index 00000000..b4f5d27f
--- /dev/null
+++ b/kmines/data/hi64-app-kmines.png
Binary files differ
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>&amp;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 &dividend,
+ 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 &dividend,
+ 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 &currentLevel() 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/"