diff lisp/games/xmine.el @ 118:7d55a9ba150c r20-1b11

Import from CVS: tag r20-1b11
author cvs
date Mon, 13 Aug 2007 09:24:17 +0200
parents
children cca96a509cfe
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/games/xmine.el	Mon Aug 13 09:24:17 2007 +0200
@@ -0,0 +1,735 @@
+;;; xmine.el --- Mine game for XEmacs
+
+;; Author:     Jens Lautenbacher <jens@lemming0.lem.uni-karlsruhe.de>
+;; Keywords:   games
+;; Version:    1.2
+
+(defconst xmine-version-number "1.2" "XEmacs Mine version number.")
+(defconst xmine-version (format "XEmacs Mine v%s by Jens Lautenbacher © 1997"
+			       xmine-version-number)
+  "Full XEmacs Mine version number.")
+
+;; This file is part of XEmacs.
+
+;; XEmacs 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, or (at your option)
+;; any later version.
+
+;; XEmacs 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 XEmacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; Commentary: This is a complete reimplementation of the classical
+;; mine searching game known from various OS/GUIs under names like
+;; xmine, minesweeper etc.
+
+;; The idea to implement this in elisp is from
+;; Jacques Duthen <duthen@cegelec-red.fr>,
+;; the author of the original mine game for GNU Emacs. This version
+;; has to the best of my knowledge no code in common with his version,
+;; but cudos go to him for first starting this...
+;;
+;; I mainly wrote this as an example how graphics handling in XEmacs
+;; is possible. I think I did it the right way, using an extension to
+;; the annotation mechanism and via extensive use of `slots' (realized
+;; as properties of extents) to hold the data in the object itself.
+;; (Of course this is not true. The keyboard handling is controlled from
+;; the "outside" of the objects. But at one time during development
+;; before hacking the keyboard controls the code really _was_ nice...
+;; now it's a bad messing with slots and controls from the outside)
+;;
+;; Code:
+;;
+;;; First of all we'll define the needed varibles.
+
+(defgroup xmine nil
+  "The well known mine searching game."
+  :group 'games)
+
+(defcustom xmine-width 25
+  "The width of the mine field"
+  :group 'xmine
+  :type 'integer)
+
+(defcustom xmine-height 20
+  "The height of the mine field"
+  :group 'xmine
+  :type 'integer)
+
+(defcustom xmine-glyph-dir (concat data-directory "mine/")
+  "The directory where the mine glyphs reside"
+  :group 'xmine
+  :type 'directory)
+
+(defface xmine-hidden-face
+  '((t
+     (:background "blue")))
+  "The face used for hidden tiles on ttys"
+  :group 'xmine)
+
+(defface xmine-flagged-face
+  '((t
+     (:background "red")))
+  "The face used for flagged tiles on ttys"
+  :group 'xmine)
+
+(defface xmine-number-face
+  '((t
+     (:background "green")))
+  "The face used for unhidden, numbered tiles on ttys"
+  :group 'xmine)
+
+
+(defvar xmine-pad-glyph
+  (make-glyph
+   (if (and (eq window-system 'x) (featurep 'xpm))
+       (concat xmine-glyph-dir "pad.xpm")
+     "      ")))
+
+(defvar xmine-title-glyph
+  (make-glyph
+   (if (and (eq window-system 'x) (featurep 'xpm))
+       (concat xmine-glyph-dir "splash.xpm")
+     "------------------ XEmacs XMine ------------------")))
+
+(defvar xmine-glyph-production-list
+  '(("xmine-new-up"            "new_up.gif"               "new"  nil               )
+    ("xmine-new-down"          "new_down.gif"             "NEW"  nil               )
+    ("xmine-quit-up"           "quit_up.gif"              "quit" nil               )
+    ("xmine-quit-down"         "quit_down.gif"   	  "QUIT" nil               )
+    ("xmine-up-glyph"          "empty_16_up.gif"          "@ "   xmine-hidden-face )
+    ("xmine-up-sel-glyph"      "empty_16_up_sel.gif"      "@<"   xmine-hidden-face )
+    ("xmine-down-glyph"        "empty_16_down.gif"        "? "   nil               ) 
+    ("xmine-flagged-glyph"     "flagged_16_up.gif"        "! "   xmine-flagged-face)
+    ("xmine-flagged-sel-glyph" "flagged_16_up_sel.gif"    "!<"   xmine-flagged-face)
+    ("xmine-mine-glyph"        "bomb_16_flat.gif"         "* "   nil               )
+    ("xmine-mine-sel-glyph"    "bomb_16_flat.gif"         "*<"   nil               )
+    ("xmine-trapped-glyph"     "bomb_trapped_16_flat.gif" "X "   nil               )
+    ("xmine-0-glyph"           "empty_16_flat.gif"        ". "   nil               )
+    ("xmine-0-sel-glyph"       "empty_16_flat_sel.gif"    ".<"   nil               )
+    ("xmine-1-glyph"           "1_16_flat.gif"            "1 "   xmine-number-face )
+    ("xmine-1-sel-glyph"       "1_16_flat_sel.gif"        "1<"   xmine-number-face )
+    ("xmine-2-glyph"           "2_16_flat.gif"            "2 "   xmine-number-face )
+    ("xmine-2-sel-glyph"       "2_16_flat_sel.gif"        "2<"   xmine-number-face )
+    ("xmine-3-glyph"           "3_16_flat.gif"            "3 "   xmine-number-face )
+    ("xmine-3-sel-glyph"       "3_16_flat_sel.gif"        "3<"   xmine-number-face )
+    ("xmine-4-glyph"           "4_16_flat.gif"            "4 "   xmine-number-face )
+    ("xmine-4-sel-glyph"       "4_16_flat_sel.gif"        "4<"   xmine-number-face )
+    ("xmine-5-glyph"           "5_16_flat.gif"            "5 "   xmine-number-face )
+    ("xmine-5-sel-glyph"       "5_16_flat_sel.gif"        "5<"   xmine-number-face )
+    ("xmine-6-glyph"           "6_16_flat.gif"            "6 "   xmine-number-face )
+    ("xmine-6-sel-glyph"       "6_16_flat_sel.gif"        "6<"   xmine-number-face )
+    ("xmine-7-glyph"           "7_16_flat.gif"            "7 "   xmine-number-face )
+    ("xmine-7-sel-glyph"       "7_16_flat_sel.gif"        "7<"   xmine-number-face )
+    ("xmine-8-glyph"           "8_16_flat.gif"            "8 "   xmine-number-face )
+    ("xmine-8-sel-glyph"       "8_16_flat_sel.gif"        "8<"   xmine-number-face )))
+
+(defun xmine-generate-glyphs ()
+  (let ((list xmine-glyph-production-list)
+	elem var gif text face)
+    (while (setq elem (pop list))
+      (setq var  (car    elem)
+	    gif  (cadr   elem)
+	    text (caddr  elem)
+	    face (cadddr elem))
+      (set (intern var)
+	   (make-glyph (if (eq window-system 'x)
+			   (concat xmine-glyph-dir gif)
+			 text)))
+      (if face
+	  (set-glyph-face (eval (intern-soft var)) face)))))
+
+(xmine-generate-glyphs)
+
+(defvar xmine-key-sel-button nil)
+
+(defun xmine-up-glyph (ext)
+  (if (equal ext xmine-key-sel-button)
+      (progn
+	(set-extent-property ext 'xmine-non-selected-glyph xmine-up-glyph)
+	xmine-up-sel-glyph)
+    xmine-up-glyph))
+
+(defun xmine-flagged-glyph (ext)
+  (if (equal ext xmine-key-sel-button)
+      (progn
+	(set-extent-property ext 'xmine-non-selected-glyph xmine-flagged-glyph)
+	xmine-flagged-sel-glyph)
+    xmine-flagged-glyph))
+
+(defcustom xmine-%-of-mines 12
+  "The percentage of tiles that should be mines."
+  :group 'xmine
+  :type 'integer)
+
+(defcustom xmine-balloon-list (list "What are you waiting for?"
+				    "Push me!"
+				    "Come on. Don't sleep."
+				    "Are you sure?"
+				    "Are you sleeping?"
+				    "Yes! Do it!"
+				    "I'm getting bored."
+				    "You will NEVER beat me.")
+  "(Random) texts for the balloon-help property of the tiles"
+  :group 'xmine
+  :type '(repeat (string)))
+
+(defcustom xmine-background "white"
+  "The background color of XMine's buffer.
+Many colors will not blend nicely with the logo. Shades of light grey are
+preferred if you don't want to use white."
+  :group 'xmine
+  :type 'color)
+
+(defvar xmine-keymap nil)
+
+(if xmine-keymap ()
+  (setq xmine-keymap (make-sparse-keymap))
+  (suppress-keymap xmine-keymap)
+  (define-key xmine-keymap [up] 'xmine-key-up)
+  (define-key xmine-keymap [down] 'xmine-key-down)
+  (define-key xmine-keymap [right] 'xmine-key-right)
+  (define-key xmine-keymap [left] 'xmine-key-left)
+  (define-key xmine-keymap "e" 'xmine-key-up)
+  (define-key xmine-keymap "c" 'xmine-key-down)
+  (define-key xmine-keymap "f" 'xmine-key-right)
+  (define-key xmine-keymap "s" 'xmine-key-left)
+  (define-key xmine-keymap "w" 'xmine-key-up-left)
+  (define-key xmine-keymap "x" 'xmine-key-down-left)
+  (define-key xmine-keymap "r" 'xmine-key-up-right)
+  (define-key xmine-keymap "v" 'xmine-key-down-right)
+  (define-key xmine-keymap [return] 'xmine-key-action3)
+  (define-key xmine-keymap "d" 'xmine-key-action3)
+  (define-key xmine-keymap [(shift space)] 'xmine-key-action2)
+  (define-key xmine-keymap "a" 'xmine-key-action2)
+  (define-key xmine-keymap [space] 'xmine-key-action1)
+  (define-key xmine-keymap [Q] 'xmine-key-quit)
+  (define-key xmine-keymap [N] 'xmine-key-new))
+
+(defvar xmine-number-of-flagged 0)
+
+(defvar xmine-number-of-opened 0)
+  
+(defvar xmine-number-of-mines 0)
+
+(defvar xmine-field nil)
+
+(defvar xmine-buffer nil)
+
+(defvar xmine-quit-ann nil)
+
+(defvar xmine-new-ann nil)
+
+(defvar xmine-count-ann nil)
+
+(defvar xmine-count-glyph (make-glyph "Mines: 00"))
+
+(defvar xmine-mode-hook nil
+  "*Hook called by `xmine-mode-hook'.")
+
+;; the next function is more or less stolen from annotation.el and
+;; modified to fit in our scheme were all three buttons should trigger
+;; actions
+
+(defun xmine-activate-function-button (event)
+  (interactive "e")
+  (let* ((extent (event-glyph-extent event))
+	 (button (event-button event))
+	 (action (intern (concat "action" (number-to-string button))))
+	 (mouse-down t)
+	 (up-glyph nil))
+    ;; make the glyph look pressed
+    (cond ((annotation-down-glyph extent)
+	   (setq up-glyph (annotation-glyph extent))
+	   (set-annotation-glyph extent (annotation-down-glyph extent))))
+    (while mouse-down
+      (setq event (next-event event))
+      (if (button-release-event-p event)
+	  (setq mouse-down nil)))
+    ;; make the glyph look released
+    (cond ((annotation-down-glyph extent)
+	   (set-annotation-glyph extent up-glyph)))
+    (if (eq extent (event-glyph-extent event))
+	(if (extent-property extent action)
+	    (funcall (extent-property extent action) extent)))))
+
+;;; Here we define the button object's constructor function
+
+(defun xmine-button-create (x y type)
+  (let ((ext (make-annotation
+	      xmine-up-glyph nil 'text nil nil xmine-down-glyph nil)))
+    (set-extent-property ext 'action1 'xmine-action1)
+    (set-extent-property ext 'action2 'xmine-beep)
+    (set-extent-property ext 'action3 'xmine-action3)
+    (set-extent-property ext 'xmine-glyph (xmine-type-to-glyph type))
+    (set-extent-property ext 'xmine-sel-glyph (xmine-type-to-sel-glyph type)) 
+    (set-extent-property ext 'xmine-type type)
+    (set-extent-property ext 'xmine-x x)
+    (set-extent-property ext 'xmine-y y)
+    (set-extent-property ext 'xmine-flagged nil)
+    (set-extent-property ext 'xmine-hidden t)
+    (set-extent-property ext 'end-open t)
+    (set-extent-property ext 'balloon-help (xmine-balloon-text))
+    (aset xmine-field (+ (* (1- y) xmine-width) (1- x)) ext)))
+
+;;; ...and this is the second global function to change a
+;;; button object. It is only needed during creation of the board.
+
+(defun xmine-button-change-type (ext type)
+  (set-extent-property ext 'xmine-glyph (xmine-type-to-glyph type))
+  (set-extent-property ext 'xmine-sel-glyph (xmine-type-to-sel-glyph type)) 
+  (set-extent-property ext 'xmine-type type))
+
+;;; some needed predicates.
+
+(defun xmine-flat-button-p (ext)
+  (and ext
+       (not (extent-property ext 'xmine-hidden))
+       (equal "0" (extent-property ext 'xmine-type))))
+
+(defun xmine-mine-button-p (ext)
+  (and ext
+       (equal "mine" (extent-property ext 'xmine-type))))
+
+;;; the next three functions are helper functions used inside a button
+;;; object.
+
+(defun xmine-balloon-text ()
+  (nth (random (length xmine-balloon-list)) xmine-balloon-list))
+
+(defun xmine-beep (&rest forget)
+  (beep))
+
+(defun xmine-type-to-glyph (type)
+  (eval (intern-soft (concat "xmine-" type "-glyph"))))
+
+(defun xmine-type-to-sel-glyph (type)
+  (eval (intern-soft (concat "xmine-" type "-sel-glyph"))))
+
+;;; the next three functions are the main functions that are used
+;;; inside the button objects and which are bound to the 'action1,
+;;; 'action2 and 'action3 slots respectively
+
+(defun xmine-action1 (ext &optional no-repaint force)
+  "This unhides a hidden button"
+  (if (or force
+	  (not (extent-property ext 'xmine-flagged)))
+      (progn
+	(if (and (not force)
+		 (extent-property ext 'xmine-hidden))
+	    (setq xmine-number-of-opened (1+ xmine-number-of-opened)))
+	(set-extent-property ext 'xmine-hidden nil)
+	(set-annotation-glyph ext (if (equal ext xmine-key-sel-button)
+				      (progn
+					(set-extent-property
+					 ext 'xmine-non-selected-glyph
+					 (extent-property ext 'xmine-glyph))
+					(extent-property ext 'xmine-sel-glyph))
+				    (extent-property ext 'xmine-glyph)))
+	(set-extent-property ext 'action3 nil)
+	(set-extent-property ext 'action1 nil)
+	(set-extent-property ext 'balloon-help nil)
+	(set-extent-property ext 'action2 'xmine-action2)
+	(if (not no-repaint)
+	    (progn
+	      (xmine-field-repaint ext)
+	      (if (xmine-game-solved-p) (xmine-end-game)))))))
+
+(defun xmine-action2 (ext)
+  "This unhides all hidden neighbours of a button.
+It is meant as convenience function you can use if you're sure that
+you've marked all mines around the button correctly (or you're sure
+there isn't one)"
+  (let ((list (xmine-get-neighbours ext))
+	next)
+    (while (setq next (pop list))
+      (if (not (xmine-flat-button-p next)) (xmine-action1 next)))))
+
+(defun xmine-action3 (ext)
+  "This toggles the flagged status of a button.
+You flag a button if you know - or think - that there's a mine under it"
+(if (extent-property ext 'xmine-flagged)
+      (progn
+	(set-annotation-glyph ext (xmine-up-glyph ext))
+	(set-extent-property ext 'action1 'xmine-action1)
+	(set-extent-property ext 'xmine-flagged nil)
+	(setq xmine-number-of-flagged (1- xmine-number-of-flagged))
+	(set-annotation-glyph xmine-count-ann
+			      (make-glyph
+			       (format "Mines: %2d"
+				       (- xmine-number-of-mines
+					    xmine-number-of-flagged)))))
+    (if (= xmine-number-of-flagged xmine-number-of-mines)
+	(progn 
+	  (beep)
+	  (message
+	   "Impossible. You seem to have marked too many tiles as mines?"))
+      (set-annotation-glyph ext (xmine-flagged-glyph ext))
+      (set-extent-property ext 'action1 nil)
+      (set-extent-property ext 'xmine-flagged t)
+      (setq xmine-number-of-flagged (1+ xmine-number-of-flagged))
+      (if (xmine-game-solved-p) (xmine-end-game)
+	(set-annotation-glyph xmine-count-ann
+			      (make-glyph
+			       (format "Mines: %2d"
+				       (- xmine-number-of-mines
+					  xmine-number-of-flagged))))))))
+
+
+;;; what to do after a button is unhidden: We (maybe) have to repaint
+;;; parts of the board. This is done here recursively.
+
+(defun xmine-field-repaint (ext)
+  (let* ((flatp  (xmine-flat-button-p ext))
+	 (minep  (xmine-mine-button-p ext))
+	 (neighbours (xmine-get-neighbours ext))
+	 (max-lisp-eval-depth (* 8 xmine-width xmine-height))
+	 next-ext ext-list)
+    (cond (flatp
+	   (while (setq next-ext (pop neighbours))
+	     (if (extent-property next-ext 'xmine-hidden)
+		 (progn
+		   (xmine-action1 next-ext 'no-repaint)
+		   (and (equal "0" (extent-property next-ext 'xmine-type))
+			(push next-ext ext-list)))))
+	   (while ext-list
+	     (setq next-ext (pop ext-list))
+	     (xmine-field-repaint next-ext)))
+	  (minep
+	   (set-extent-property ext 'xmine-glyph xmine-trapped-glyph)
+	   (set-extent-property ext 'xmine-sel-glyph xmine-trapped-glyph)
+	   (xmine-show-all)
+	   (xmine-end-game-trapped)))))
+
+
+(defun xmine-get-neighbours (ext)
+  "This gives back a list of all neighbours of a button, correctly
+  handling buttons at the side or corner of course"
+(let* ((x (extent-property ext 'xmine-x))
+	 (y (extent-property ext 'xmine-y))
+	 next-coord next list
+	 (neighbours  (list (list (1- x) (1+ y))
+			    (list     x  (1+ y))
+			    (list (1+ x) (1+ y))
+			    (list (1- x) (1- y))
+			    (list     x  (1- y))
+			    (list (1+ x) (1- y))
+			    (list (1+ x)     y)
+			    (list (1- x)     y))))
+    (while (setq next-coord (pop neighbours))
+      (if (setq next (xmine-field-button-at (car next-coord)
+					    (cadr next-coord)))
+	  (push next list)))
+    list))
+    
+
+;;; the next four functions are used to know if we're at the end of
+;;; the game (either successfully or exploded) and do the approbate
+;;; action
+
+(defun xmine-game-solved-p ()
+  "You have solved the game successfully if the number of flagged
+mines plus the number of unhidden buttons equals width*height of the field"
+  (equal (+ xmine-number-of-flagged xmine-number-of-opened)
+	 (* xmine-width xmine-height)))
+
+(defun xmine-end-game ()
+  (beep)
+  (set-annotation-glyph xmine-count-ann
+			(make-glyph " Solved. ")))
+
+(defun xmine-end-game-trapped ()
+  (beep)
+  (set-annotation-glyph xmine-count-ann
+			(make-glyph "++ RIP ++")))
+
+(defun xmine-show-all ()
+  (let ((list (append xmine-field nil))
+	next)
+    (while (setq next (pop list))
+      (xmine-action1 next 'no-repaint 'force))))
+
+
+(defun xmine-field-button-at (x y)
+  "This function gives back the button at a given coordinate pair (x y)
+It is only used during creation of the board and when getting the
+neighbours of a button (and for keyboard handling...), as we don't
+want to use coordinates in the main loop, only the button object
+itself should be referenced. Of course the use of this function could
+be avoided in xmine-get-neighbours by storing the neighbour buttons
+directly in the button, but this seems to be a bit oversized for this
+little game."
+  (if (or (> x xmine-width)  (< x 1)
+	  (> y xmine-height) (< y 1)) nil
+    (aref xmine-field (+ (* (1- y) xmine-width) (1- x)))))
+
+(defun xmine-mode ()
+"A mode for playing the well known mine searching game.
+
+   `\\<annotation-local-map-default>\\[xmine-activate-function-button1]' or `\\<xmine-keymap>\\[xmine-key-action1]' unhides a tile,
+   `\\<annotation-local-map-default>\\[xmine-activate-function-button2]' or `\\<xmine-keymap>\\[xmine-key-action2]' unhides all neighbours of a tile,
+   `\\<annotation-local-map-default>\\[xmine-activate-function-button3]' or `\\<xmine-keymap>\\[xmine-key-action3]' (un)flagges a tile to hold a mine.
+
+   `\\[xmine-key-new]' starts a new game.
+   `\\[xmine-key-quit]' ends a game.
+
+All keybindings (with alternatives) currently in effect:
+   \\{xmine-keymap}
+
+The rules are quite easy: You start by unhiding (random) tiles. An unhidden
+tile showing a number tells you something about the number of mines in it's
+neighborhood, where the neighborhood are all 8 tiles (or less if it's
+at a border) around the tile.
+
+E.g. a \"1\" shows you that there is only one mine in the neighborhood of
+this tile. Empty tiles have no mines around them, and empty tiles in
+the neighborhood of another empty tile are all automatically unhidden
+if you unhide one of them. You need to find a strategy to use the
+information you have from the numbers to \"flag\" the tiles with mines
+under them and unhide all other tiles. If you correctly made this
+without accidently unhiding a mine, you've won.
+
+If you are sure you have correctly flagged all mines around a unhidden tile,
+you can use Button-2 or \\[xmine-key-action2] on it to unhide all it's
+neighbors. But beware: If you made a mistake by flagging the wrong mines,
+you'll blow up! 
+
+Have Fun."
+  (interactive)
+  (xmine-field-create))
+
+(fset 'xmine 'xmine-mode)
+
+(defun xmine-field-create ()
+  "We create the playing board here."
+  (let ((width 1)
+	(height 1)
+	(pop-up-windows nil)
+	total)
+    (xmine-buffer-init)
+    (pop-to-buffer xmine-buffer)
+    (setq total (* xmine-height xmine-width))
+    (setq xmine-field (make-vector total nil))
+    (xmine-init-mines
+     (setq xmine-number-of-mines
+	   (min 99 (round (* (/ (float xmine-%-of-mines) 100) total)))))
+    (insert "\n ")
+    (set-extent-end-glyph (make-extent (point) (point)) xmine-title-glyph)
+    (insert "\n\n")
+    (while (<= height xmine-height)
+      (insert " ")
+      (while (<= width xmine-width)
+	(if (xmine-field-button-at width height)
+	    (xmine-button-create width height "mine")
+	  (xmine-button-create width height "0"))
+	(setq width (+ width 1)))
+      (insert " \n")
+      (setq width 1)
+      (setq height (+ height 1)))
+    (insert "\n  ")
+    (set-extent-begin-glyph (make-extent (point) (point)) xmine-pad-glyph)
+    (setq xmine-new-ann
+	  (make-annotation xmine-new-up nil
+			   'text nil nil xmine-new-down nil))
+    (set-extent-property xmine-new-ann 'action1 '(lambda (&rest egal)
+						   (xmine-field-create)))
+    (set-extent-property xmine-new-ann 'action2 nil)
+    (set-extent-property xmine-new-ann 'action3 nil)
+    (set-extent-property xmine-new-ann 'end-open t)
+    (set-extent-begin-glyph (make-extent (point) (point)) xmine-pad-glyph)
+    (setq xmine-count-ann
+	  (make-annotation xmine-count-glyph nil
+			   'text nil nil nil nil))
+    (set-extent-begin-glyph (make-extent (point) (point)) xmine-pad-glyph)
+    (setq xmine-quit-ann
+	  (make-annotation xmine-quit-up nil
+			   'text nil nil xmine-quit-down nil))
+    (set-extent-property xmine-quit-ann 'action1
+			 '(lambda (&rest egal)
+			    (kill-buffer (current-buffer))))
+    (set-extent-property xmine-quit-ann 'action2 nil)
+    (set-extent-property xmine-quit-ann 'action3 nil)
+    (set-extent-property xmine-quit-ann 'end-open t)
+    (xmine-attach-numbers)
+    (setq xmine-number-of-flagged 0)
+    (setq xmine-number-of-opened 0)
+    (set-annotation-glyph xmine-count-ann
+			  (make-glyph
+			   (format "Mines: %2d" xmine-number-of-mines)))
+    (goto-char (point-min))
+    (setq buffer-read-only 't)
+    (if (eq window-system 'x)
+	(set-specifier (face-background 'default)
+		       xmine-background xmine-buffer))
+    (set-specifier (face-background 'text-cursor)
+		   xmine-background xmine-buffer)
+    (setq xmine-key-sel-button nil)
+    (xmine-select-button (xmine-field-button-at (/ xmine-width 2)
+						(/ xmine-height 2)))))
+
+
+(defun xmine-init-mines (num)
+  "A subroutine for xmine-field create.
+We randomly set a part of the nil-filled board vector with t to
+indicate the places where mines should reside."
+  (let (x y elem)
+    (random t)
+    (while (> num 0)
+      (setq x (1+ (random xmine-width)))
+      (setq y (1+ (random xmine-height)))
+      (setq elem (xmine-field-button-at x y))
+      (if (not elem)
+	  (progn
+	    (aset xmine-field (+ (* (1- y) xmine-width) (1- x)) t)
+	    (setq num (1- num)))))))
+	
+(defun xmine-attach-numbers ()
+  "A subroutine for xmine-field-create.
+The board is populated by now with empty buttons and mines. Here we
+change the correct empty buttons to \"numbered\" buttons"
+  (let 
+      ((buttons (append xmine-field nil))
+       ext)
+    (while (setq ext (pop buttons))
+      (let ((num 0)
+	    (minep (xmine-mine-button-p ext))
+	    (neighbours (xmine-get-neighbours ext))
+	    next)
+	(if (not minep)
+	    (progn
+	      (while (setq next (pop neighbours))
+		(if (xmine-mine-button-p next) (setq num (1+ num))))
+	      (if (> num 0)
+		  (xmine-button-change-type ext (number-to-string num)))))))))
+
+	    
+(defun xmine-buffer-init ()
+  "A subroutine for xmine-create-field.
+We set up the XMine buffer, set up the keymap and so on."
+  (if xmine-buffer (kill-buffer xmine-buffer))
+  (setq xmine-buffer (get-buffer-create "XEmacs Mine"))
+  (save-excursion
+    (set-buffer xmine-buffer)
+    (kill-all-local-variables)
+    (make-local-variable 'annotation-local-map-default)
+    (setq truncate-lines 't)
+    (setq major-mode 'xmine-mode)
+    (setq mode-name "XMine")
+    (put 'xmine-mode 'mode-class 'special)
+    (use-local-map xmine-keymap)
+    (buffer-disable-undo (current-buffer))
+    (setq annotation-local-map-default
+	  (let ((map (make-sparse-keymap)))
+	    (set-keymap-name map 'annotation-local-map)
+	    (define-key map 'button1 'xmine-activate-function-button)
+	    (define-key map 'button2 'xmine-activate-function-button)
+	    (define-key map 'button3 'xmine-activate-function-button)
+	    map))
+    (run-hooks 'xmine-mode-hook)))
+
+;;; The keyboard navigation.
+
+(defun xmine-select-button (ext)
+  (let ((flagged (extent-property ext 'xmine-flagged))
+	(hidden  (extent-property ext 'xmine-hidden))
+	sel-glyph)
+    (setq sel-glyph (if hidden
+		       (if flagged xmine-flagged-sel-glyph
+			 xmine-up-sel-glyph)
+		      (extent-property ext 'xmine-sel-glyph)))
+    (if xmine-key-sel-button
+	(set-annotation-glyph xmine-key-sel-button
+			      (extent-property xmine-key-sel-button
+					       'xmine-non-selected-glyph)))
+    (set-extent-property ext 'xmine-non-selected-glyph
+			 (annotation-glyph ext))
+    (set-annotation-glyph ext sel-glyph)
+    (setq xmine-key-sel-button ext)))
+
+(defun xmine-key-action1 ()
+  (interactive)
+  (let ((action (extent-property xmine-key-sel-button 'action1)))
+    (if action
+	(funcall action xmine-key-sel-button))))
+
+(defun xmine-key-action2 ()
+  (interactive)
+  (let ((action (extent-property xmine-key-sel-button 'action2)))
+    (if action
+	(funcall action xmine-key-sel-button))))
+
+(defun xmine-key-action3 ()
+  (interactive)
+  (let ((action (extent-property xmine-key-sel-button 'action3)))
+    (if action
+	(funcall action xmine-key-sel-button))))
+
+(defun xmine-key-quit ()
+  (interactive)
+  (kill-buffer (current-buffer)))
+  
+(defun xmine-key-new ()
+  (interactive)
+  (xmine-field-create))
+
+(defun xmine-key-down-right ()
+  (interactive)
+  (xmine-key-down)
+  (xmine-key-right))
+
+(defun xmine-key-down-left ()
+  (interactive)
+  (xmine-key-down)
+  (xmine-key-left))
+
+(defun xmine-key-up-right ()
+  (interactive)
+  (xmine-key-up)
+  (xmine-key-right))
+
+(defun xmine-key-up-left ()
+  (interactive)
+  (xmine-key-up)
+  (xmine-key-left))
+
+(defun xmine-key-down ()
+  (interactive)
+  (let* ((x (extent-property xmine-key-sel-button 'xmine-x))
+	 (y (extent-property xmine-key-sel-button 'xmine-y))
+	 (ext (xmine-field-button-at x (1+ y))))
+    (if ext (xmine-select-button ext)
+      (xmine-select-button (xmine-field-button-at x 1)))))
+
+(defun xmine-key-up ()
+  (interactive)
+  (let* ((x (extent-property xmine-key-sel-button 'xmine-x))
+	 (y (extent-property xmine-key-sel-button 'xmine-y))
+	 (ext (xmine-field-button-at x (1- y))))
+    (if ext (xmine-select-button ext)
+      (xmine-select-button (xmine-field-button-at x xmine-height)))))
+
+(defun xmine-key-right ()
+  (interactive)
+  (let* ((x (extent-property xmine-key-sel-button 'xmine-x))
+	 (y (extent-property xmine-key-sel-button 'xmine-y))
+	 (ext (xmine-field-button-at (1+ x) y)))
+    (if ext (xmine-select-button ext)
+      (xmine-select-button (xmine-field-button-at 1 y)))))
+
+(defun xmine-key-left ()
+  (interactive)
+  (let* ((x (extent-property xmine-key-sel-button 'xmine-x))
+	 (y (extent-property xmine-key-sel-button 'xmine-y))
+	 (ext (xmine-field-button-at (1- x) y)))
+    (if ext (xmine-select-button ext)
+      (xmine-select-button (xmine-field-button-at xmine-width y)))))
+
+(provide 'xmine)
+