;;; xmine.el --- Mine game for XEmacs;; Author: Jens Lautenbacher <jens@tellux.de>;; Keywords: games;; Version: 1.9;; 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 variables.(defconstxmine-version-number"1.9""XEmacs Mine version number.")(defconstxmine-version(format"XEmacs Mine v%s by Jens Lautenbacher � 1997"xmine-version-number)"Full XEmacs Mine version number.")(defgroupxminenil"The well known mine searching game.":group'games)(defcustomxmine-width25"The width of the mine field":group'xmine:type'integer)(defcustomxmine-height20"The height of the mine field":group'xmine:type'integer)(defcustomxmine-glyph-dir(locate-data-directory"mine")"The directory where the mine glyphs reside":group'xmine:type'directory)(deffacexmine-hidden-face'((t(:background"blue")))"The face used for hidden tiles on ttys":group'xmine)(deffacexmine-flagged-face'((t(:background"red")))"The face used for flagged tiles on ttys":group'xmine)(deffacexmine-number-face'((t(:background"green")))"The face used for unhidden, numbered tiles on ttys":group'xmine)(defvarxmine-pad-glyph(make-glyph(if(and(console-on-window-system-p)(featurep'xpm))(concatxmine-glyph-dir"pad.xpm")" ")))(defvarxmine-title-glyph(make-glyph(if(and(console-on-window-system-p)(featurep'xpm))(concatxmine-glyph-dir"splash.xpm")"------------------ XEmacs XMine ------------------")))(defvarxmine-glyph-production-list'(("xmine-new-up""new_up.png""new"nil)("xmine-new-down""new_down.png""NEW"nil)("xmine-quit-up""quit_up.png""quit"nil)("xmine-quit-down""quit_down.png""QUIT"nil)("xmine-up-glyph""empty_16_up.png""@ "xmine-hidden-face)("xmine-up-sel-glyph""empty_16_up_sel.png""@<"xmine-hidden-face)("xmine-down-glyph""empty_16_down.png""? "nil)("xmine-flagged-glyph""flagged_16_up.png""! "xmine-flagged-face)("xmine-flagged-sel-glyph""flagged_16_up_sel.png""!<"xmine-flagged-face)("xmine-mine-glyph""bomb_16_flat.png""* "nil)("xmine-mine-sel-glyph""bomb_16_flat.png""*<"nil)("xmine-trapped-glyph""bomb_trapped_16_flat.png""X "nil)("xmine-0-glyph""empty_16_flat.png"". "nil)("xmine-0-sel-glyph""empty_16_flat_sel.png"".<"nil)("xmine-1-glyph""1_16_flat.png""1 "xmine-number-face)("xmine-1-sel-glyph""1_16_flat_sel.png""1<"xmine-number-face)("xmine-2-glyph""2_16_flat.png""2 "xmine-number-face)("xmine-2-sel-glyph""2_16_flat_sel.png""2<"xmine-number-face)("xmine-3-glyph""3_16_flat.png""3 "xmine-number-face)("xmine-3-sel-glyph""3_16_flat_sel.png""3<"xmine-number-face)("xmine-4-glyph""4_16_flat.png""4 "xmine-number-face)("xmine-4-sel-glyph""4_16_flat_sel.png""4<"xmine-number-face)("xmine-5-glyph""5_16_flat.png""5 "xmine-number-face)("xmine-5-sel-glyph""5_16_flat_sel.png""5<"xmine-number-face)("xmine-6-glyph""6_16_flat.png""6 "xmine-number-face)("xmine-6-sel-glyph""6_16_flat_sel.png""6<"xmine-number-face)("xmine-7-glyph""7_16_flat.png""7 "xmine-number-face)("xmine-7-sel-glyph""7_16_flat_sel.png""7<"xmine-number-face)("xmine-8-glyph""8_16_flat.png""8 "xmine-number-face)("xmine-8-sel-glyph""8_16_flat_sel.png""8<"xmine-number-face)))(defvarxmine-force-textualnil"This is for debugging purposes only. No need to set it. Really.")(defunxmine-generate-glyphs()(let((listxmine-glyph-production-list)elemvargiftextface)(while(setqelem(poplist))(setqvar(carelem)gif(cadrelem)text(caddrelem)face(cadddrelem))(set(internvar)(make-glyph(if(and(notxmine-force-textual)(console-on-window-system-p))(concatxmine-glyph-dirgif)text)))(ifface(set-glyph-face(eval(intern-softvar))face)))))(xmine-generate-glyphs)(defvarxmine-key-sel-buttonnil)(defunxmine-up-glyph(ext)(if(equalextxmine-key-sel-button)(progn(set-extent-propertyext'xmine-non-selected-glyphxmine-up-glyph)xmine-up-sel-glyph)xmine-up-glyph))(defunxmine-flagged-glyph(ext)(if(equalextxmine-key-sel-button)(progn(set-extent-propertyext'xmine-non-selected-glyphxmine-flagged-glyph)xmine-flagged-sel-glyph)xmine-flagged-glyph))(defcustomxmine-%-of-mines12"The percentage of tiles that should be mines.":group'xmine:type'integer)(defcustomxmine-balloon-list(list"Push Me!""Don't Sleep.""Are you sure?""I'm getting bored.")"(Random) texts for the balloon-help property of the tiles. This can be a list of strings or the symbol 'yow, in which case a random quote fromZippy The Pinhead will be used...":group'xmine:type'(choice(repeatstring)(constyow)))(defcustomxmine-background"white""The background color of XMine's buffer.Many colors will not blend nicely with the logo. Shades of light grey arepreferred if you don't want to use white.":group'xmine:type'color)(defvarxmine-keymapnil)(ifxmine-keymap()(setqxmine-keymap(make-sparse-keymap))(suppress-keymapxmine-keymap)(define-keyxmine-keymap[up]'xmine-key-up)(define-keyxmine-keymap[down]'xmine-key-down)(define-keyxmine-keymap[right]'xmine-key-right)(define-keyxmine-keymap[left]'xmine-key-left)(define-keyxmine-keymap"e"'xmine-key-up)(define-keyxmine-keymap"c"'xmine-key-down)(define-keyxmine-keymap"f"'xmine-key-right)(define-keyxmine-keymap"s"'xmine-key-left)(define-keyxmine-keymap"w"'xmine-key-up-left)(define-keyxmine-keymap"x"'xmine-key-down-left)(define-keyxmine-keymap"r"'xmine-key-up-right)(define-keyxmine-keymap"v"'xmine-key-down-right)(define-keyxmine-keymap[return]'xmine-key-action3)(define-keyxmine-keymap"d"'xmine-key-action3)(define-keyxmine-keymap[(shiftspace)]'xmine-key-action2)(define-keyxmine-keymap"a"'xmine-key-action2)(define-keyxmine-keymap[space]'xmine-key-action1)(define-keyxmine-keymap[Q]'xmine-key-quit)(define-keyxmine-keymap[N]'xmine-key-new))(defvarxmine-number-of-flagged0)(defvarxmine-number-of-opened0)(defvarxmine-number-of-mines0)(defvarxmine-fieldnil)(defvarxmine-buffernil)(defvarxmine-quit-annnil)(defvarxmine-new-annnil)(defvarxmine-count-annnil)(defvarxmine-count-glyph(make-glyph"Mines: 00"))(defvarxmine-mode-hooknil"*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(defunxmine-activate-function-button(event)(interactive"e")(let*((extent(event-glyph-extentevent))(button(number-to-string(event-buttonevent)))(action(intern(concat"action"button)))(down-action(intern(concat"down-action"button)))(restore-down-action(intern(concat"restore-down-action"button)))(mouse-downt)(action-do-itt)up-glyph);; make the glyph look pressed(cond((annotation-down-glyphextent)(setqup-glyph(annotation-glyphextent))(set-annotation-glyphextent(annotation-down-glyphextent))))(if(extent-propertyextentdown-action)(setqaction-do-it(funcall(extent-propertyextentdown-action)extent)))(whilemouse-down(setqevent(next-eventevent))(if(button-release-event-pevent)(setqmouse-downnil)));; make the glyph look released(cond((annotation-down-glyphextent)(set-annotation-glyphextentup-glyph)))(if(eqextent(event-glyph-extentevent))(if(and(extent-propertyextentaction)action-do-it)(funcall(extent-propertyextentaction)extent)(if(extent-propertyextentrestore-down-action)(funcall(extent-propertyextentrestore-down-action)extent)))(if(extent-propertyextentrestore-down-action)(funcall(extent-propertyextentrestore-down-action)extent)))));;; Here we define the button object's constructor function(defunxmine-button-create(xytype)(let((ext(make-annotationxmine-up-glyphnil'textnilnilxmine-down-glyphnil)))(set-extent-propertyext'action1'xmine-action1)(set-extent-propertyext'action2'xmine-beep)(set-extent-propertyext'action3'xmine-action3)(set-extent-propertyext'down-action2'xmine-down-action2)(set-extent-propertyext'restore-down-action2'xmine-restore-down-action2)(set-extent-propertyext'xmine-glyph(xmine-type-to-glyphtype))(set-extent-propertyext'xmine-sel-glyph(xmine-type-to-sel-glyphtype))(set-extent-propertyext'xmine-typetype)(set-extent-propertyext'xmine-xx)(set-extent-propertyext'xmine-yy)(set-extent-propertyext'xmine-flaggednil)(set-extent-propertyext'xmine-hiddent)(set-extent-propertyext'end-opent)(set-extent-propertyext'balloon-help(xmine-balloon-text))(asetxmine-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.(defunxmine-button-change-type(exttype)(set-extent-propertyext'xmine-glyph(xmine-type-to-glyphtype))(set-extent-propertyext'xmine-sel-glyph(xmine-type-to-sel-glyphtype))(set-extent-propertyext'xmine-typetype));;; some needed predicates.(defunxmine-flat-button-p(ext)(andext(not(extent-propertyext'xmine-hidden))(equal"0"(extent-propertyext'xmine-type))))(defunxmine-enough-flagged-p(ext)(let((list(xmine-get-neighboursext))(number(extent-propertyext'xmine-type))(flagged0)elemres)(if(not(or(equalnumber"mine")(equalnumber"0")))(progn(setqnumber(string-to-numbernumber))(while(setqelem(poplist))(if(extent-propertyelem'xmine-flagged)(setqflagged(1+flagged))))(setqres(>=flaggednumber))))res))(defunxmine-mine-button-p(ext)(andext(equal"mine"(extent-propertyext'xmine-type))));;; the next three functions are helper functions used inside a button;;; object.(defunxmine-balloon-text()(if(eqxmine-balloon-list'yow)(progn(or(featurep'yow)(require'yow)(error"You need to have YOW installed!"))(yow))(nth(random(lengthxmine-balloon-list))xmine-balloon-list)))(defunxmine-beep(&restforget)(beep))(defunxmine-type-to-glyph(type)(eval(intern-soft(concat"xmine-"type"-glyph"))))(defunxmine-type-to-sel-glyph(type)(eval(intern-soft(concat"xmine-"type"-sel-glyph"))));;; the next 3 functions are the main functions that are used;;; inside the button objects and which are bound to the 'action1,;;; 'action2 and 'action3 slots respectively(defunxmine-action1(ext&optionalno-repaintforce)"This unhides a hidden button"(if(orforce(not(extent-propertyext'xmine-flagged)))(progn(if(and(notforce)(extent-propertyext'xmine-hidden))(setqxmine-number-of-opened(1+xmine-number-of-opened)))(set-extent-propertyext'xmine-hiddennil)(set-annotation-glyphext(if(equalextxmine-key-sel-button)(progn(set-extent-propertyext'xmine-non-selected-glyph(extent-propertyext'xmine-glyph))(extent-propertyext'xmine-sel-glyph))(extent-propertyext'xmine-glyph)))(set-extent-propertyext'action3nil)(set-extent-propertyext'action1nil)(set-extent-propertyext'balloon-helpnil)(set-extent-propertyext'action2'xmine-action2)(if(notno-repaint)(progn(xmine-unhide-sound)(xmine-field-repaintext)(if(and(xmine-game-solved-p)(not(xmine-mine-button-pext)))(xmine-end-game)))))))(defunxmine-action2(ext)"This unhides all hidden neighbours of a button.It is meant as convenience function you can use if you're sure thatyou've marked all mines around the button correctly (or you're surethere isn't one)"(let((list(xmine-get-neighboursext))(xmine-no-unhide-soundt)next);; (xmine-restore-down-action2 ext)(iflist(xmine-unhide-many-sound))(while(setqnext(poplist))(if(not(xmine-flat-button-pnext))(xmine-action1next)))))(defunxmine-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-propertyext'xmine-flagged)(progn(set-annotation-glyphext(xmine-up-glyphext))(set-extent-propertyext'action1'xmine-action1)(set-extent-propertyext'xmine-flaggednil)(setqxmine-number-of-flagged(1-xmine-number-of-flagged))(xmine-flag-sound)(set-annotation-glyphxmine-count-ann(make-glyph(format"Mines: %2d"(-xmine-number-of-minesxmine-number-of-flagged)))))(if(=xmine-number-of-flaggedxmine-number-of-mines)(progn(beep)(message"Impossible. You seem to have marked too many tiles as mines?"))(set-annotation-glyphext(xmine-flagged-glyphext))(set-extent-propertyext'action1nil)(set-extent-propertyext'xmine-flaggedt)(setqxmine-number-of-flagged(1+xmine-number-of-flagged))(xmine-flag-sound)(if(xmine-game-solved-p)(xmine-end-game)(set-annotation-glyphxmine-count-ann(make-glyph(format"Mines: %2d"(-xmine-number-of-minesxmine-number-of-flagged))))))))(defunxmine-down-action2(ext)(let((list(xmine-get-neighboursext))(do-it(xmine-enough-flagged-pext))elem)(if(notdo-it)(while(setqelem(poplist))(set-extent-propertyelem'xmine-temp-glyph(annotation-glyphelem))(set-annotation-glyphelem(annotation-down-glyphelem))))do-it))(defunxmine-restore-down-action2(ext)(let((list(xmine-get-neighboursext))elem)(while(setqelem(poplist))(set-annotation-glyphelem(extent-propertyelem'xmine-temp-glyph)))));;; the sounds...(defcustomxmine-play-soundsnil"If XMine should play some sounds for various events to happen.":group'xmine:type'boolean)(defunxmine-play-sounds-p()(andxmine-play-sounds(or(featurep'native-sound)(featurep'nas-sound))(or(device-sound-enabled-p)(and(featurep'native-sound)(notnative-sound-only-on-console)(eq(device-type)'x)))))(defcustomxmine-flag-sound(locate-data-file(concat(file-name-as-directory"sounds")"click.au"))"The sound played when flagging/un-flagging a tile":group'xmine:type'file)(defcustomxmine-unhide-sound(locate-data-file(concat(file-name-as-directory"sounds")"drip.au"))"The sound played when unhiding a tile":group'xmine:type'file)(defcustomxmine-unhide-many-sound(locate-data-file(concat(file-name-as-directory"sounds")"boing.au"))"The sound played when unhiding all neighbours of a tile":group'xmine:type'file)(defcustomxmine-explode-sound(concatxmine-glyph-dir"explosion3.au")"The sound played when you unhide a mine":group'xmine:type'file)(defcustomxmine-solved-sound(locate-data-file(concat(file-name-as-directory"sounds")"im_so_happy.au"))"The sound played if you managed to win the game.":group'xmine:type'file)(defunxmine-flag-sound()(if(xmine-play-sounds-p)(play-sound-filexmine-flag-sound)))(defvarxmine-no-unhide-soundnil)(defunxmine-unhide-sound()(if(and(xmine-play-sounds-p)(notxmine-no-unhide-sound))(play-sound-filexmine-unhide-sound)))(defunxmine-unhide-many-sound()(if(xmine-play-sounds-p)(play-sound-filexmine-unhide-many-sound)))(defunxmine-explode-sound()(if(xmine-play-sounds-p)(play-sound-filexmine-explode-sound)(beep)))(defunxmine-solved-sound()(if(xmine-play-sounds-p)(play-sound-filexmine-solved-sound)(beep)));;; what to do after a button is unhidden: We (maybe) have to repaint;;; parts of the board. This is done here recursively.(defunxmine-field-repaint(ext)(let*((flatp(xmine-flat-button-pext))(minep(xmine-mine-button-pext))(neighbours(xmine-get-neighboursext))(max-lisp-eval-depth(*8xmine-widthxmine-height))next-extext-list)(cond(flatp(while(setqnext-ext(popneighbours))(if(extent-propertynext-ext'xmine-hidden)(progn(xmine-action1next-ext'no-repaint)(and(equal"0"(extent-propertynext-ext'xmine-type))(pushnext-extext-list)))))(whileext-list(setqnext-ext(popext-list))(xmine-field-repaintnext-ext)))(minep(set-extent-propertyext'xmine-glyphxmine-trapped-glyph)(set-extent-propertyext'xmine-sel-glyphxmine-trapped-glyph)(xmine-show-all)(xmine-end-game-trapped)))))(defunxmine-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-propertyext'xmine-x))(y(extent-propertyext'xmine-y))next-coordnextlist(neighbours(list(list(1-x)(1+y))(listx(1+y))(list(1+x)(1+y))(list(1-x)(1-y))(listx(1-y))(list(1+x)(1-y))(list(1+x)y)(list(1-x)y))))(while(setqnext-coord(popneighbours))(if(setqnext(xmine-field-button-at(carnext-coord)(cadrnext-coord)))(pushnextlist)))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(defunxmine-game-solved-p()"You have solved the game successfully if the number of flaggedmines plus the number of unhidden buttons equals width*height of the field"(equal(+xmine-number-of-flaggedxmine-number-of-opened)(*xmine-widthxmine-height)))(defunxmine-end-game()(set-annotation-glyphxmine-count-ann(make-glyph" Solved. "))(sit-for0)(xmine-solved-sound))(defunxmine-end-game-trapped()(xmine-explode-sound)(set-annotation-glyphxmine-count-ann(make-glyph"++ RIP ++")))(defunxmine-show-all()(let((list(appendxmine-fieldnil))next)(while(setqnext(poplist))(xmine-action1next'no-repaint'force))))(defunxmine-field-button-at(xy)"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 theneighbours of a button (and for keyboard handling...), as we don'twant to use coordinates in the main loop, only the button objectitself should be referenced. Of course the use of this function couldbe avoided in xmine-get-neighbours by storing the neighbour buttonsdirectly in the button, but this seems to be a bit oversized for thislittle game."(if(or(>xxmine-width)(<x1)(>yxmine-height)(<y1))nil(arefxmine-field(+(*(1-y)xmine-width)(1-x)))));;;###autoload(defunxmine-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 unhiddentile showing a number tells you something about the number of mines in it'sneighborhood, where the neighborhood are all 8 tiles (or less if it'sat a border) around the tile.E.g. a \"1\" shows you that there is only one mine in the neighborhood ofthis tile. Empty tiles have no mines around them, and empty tiles inthe neighborhood of another empty tile are all automatically unhiddenif you unhide one of them. You need to find a strategy to use theinformation you have from the numbers to \"flag\" the tiles with minesunder them and unhide all other tiles. If you correctly made thiswithout 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'sneighbors. But beware: If you made a mistake by flagging the wrong mines,you'll blow up! Have Fun."(interactive)(xmine-field-create));;;###autoload(fset'xmine'xmine-mode)(defunxmine-field-create()"We create the playing board here."(let((width1)(height1)(pop-up-windowsnil)total)(xmine-buffer-init)(pop-to-bufferxmine-buffer)(setqtotal(*xmine-heightxmine-width))(setqxmine-field(make-vectortotalnil))(xmine-init-mines(setqxmine-number-of-mines(min99(round(*(/(floatxmine-%-of-mines)100)total)))))(insert"\n ")(set-extent-end-glyph(make-extent(point)(point))xmine-title-glyph)(insert"\n\n")(while(<=heightxmine-height)(insert" ")(while(<=widthxmine-width)(if(xmine-field-button-atwidthheight)(xmine-button-createwidthheight"mine")(xmine-button-createwidthheight"0"))(setqwidth(+width1)))(insert" \n")(setqwidth1)(setqheight(+height1)))(insert"\n ")(set-extent-begin-glyph(make-extent(point)(point))xmine-pad-glyph)(setqxmine-new-ann(make-annotationxmine-new-upnil'textnilnilxmine-new-downnil))(set-extent-propertyxmine-new-ann'action1'(lambda(&restegal)(xmine-field-create)))(set-extent-propertyxmine-new-ann'action2nil)(set-extent-propertyxmine-new-ann'action3nil)(set-extent-propertyxmine-new-ann'end-opent)(set-extent-begin-glyph(make-extent(point)(point))xmine-pad-glyph)(setqxmine-count-ann(make-annotationxmine-count-glyphnil'textnilnilnilnil))(set-extent-begin-glyph(make-extent(point)(point))xmine-pad-glyph)(setqxmine-quit-ann(make-annotationxmine-quit-upnil'textnilnilxmine-quit-downnil))(set-extent-propertyxmine-quit-ann'action1'(lambda(&restegal)(kill-buffer(current-buffer))))(set-extent-propertyxmine-quit-ann'action2nil)(set-extent-propertyxmine-quit-ann'action3nil)(set-extent-propertyxmine-quit-ann'end-opent)(xmine-attach-numbers)(setqxmine-number-of-flagged0)(setqxmine-number-of-opened0)(set-annotation-glyphxmine-count-ann(make-glyph(format"Mines: %2d"xmine-number-of-mines)))(goto-char(point-min))(setqbuffer-read-only't)(if(console-on-window-system-p)(set-specifier(face-background'default)xmine-backgroundxmine-buffer))(set-specifier(face-background'text-cursor)xmine-backgroundxmine-buffer)(setqxmine-key-sel-buttonnil)(xmine-select-button(xmine-field-button-at(/xmine-width2)(/xmine-height2)))))(defunxmine-init-mines(num)"A subroutine for xmine-field create.We randomly set a part of the nil-filled board vector with t toindicate the places where mines should reside."(let(xyelem)(randomt)(while(>num0)(setqx(1+(randomxmine-width)))(setqy(1+(randomxmine-height)))(setqelem(xmine-field-button-atxy))(if(notelem)(progn(asetxmine-field(+(*(1-y)xmine-width)(1-x))t)(setqnum(1-num)))))))(defunxmine-attach-numbers()"A subroutine for xmine-field-create.The board is populated by now with empty buttons and mines. Here wechange the correct empty buttons to \"numbered\" buttons"(let((buttons(appendxmine-fieldnil))ext)(while(setqext(popbuttons))(let((num0)(minep(xmine-mine-button-pext))(neighbours(xmine-get-neighboursext))next)(if(notminep)(progn(while(setqnext(popneighbours))(if(xmine-mine-button-pnext)(setqnum(1+num))))(if(>num0)(xmine-button-change-typeext(number-to-stringnum)))))))))(defunxmine-buffer-init()"A subroutine for xmine-create-field.We set up the XMine buffer, set up the keymap and so on."(ifxmine-buffer(kill-bufferxmine-buffer))(setqxmine-buffer(get-buffer-create"XEmacs Mine"))(save-excursion(set-bufferxmine-buffer)(kill-all-local-variables)(make-local-variable'annotation-local-map-default)(setqtruncate-lines't)(setqmajor-mode'xmine-mode)(setqmode-name"XMine")(put'xmine-mode'mode-class'special)(use-local-mapxmine-keymap)(buffer-disable-undo(current-buffer))(setqannotation-local-map-default(let((map(make-sparse-keymap)))(set-keymap-namemap'annotation-local-map)(define-keymap'button1'xmine-activate-function-button)(define-keymap'button2'xmine-activate-function-button)(define-keymap'button3'xmine-activate-function-button)map))(run-hooks'xmine-mode-hook)));;; The keyboard navigation.(defunxmine-select-button(ext)(let((flagged(extent-propertyext'xmine-flagged))(hidden(extent-propertyext'xmine-hidden))sel-glyph)(setqsel-glyph(ifhidden(ifflaggedxmine-flagged-sel-glyphxmine-up-sel-glyph)(extent-propertyext'xmine-sel-glyph)))(ifxmine-key-sel-button(set-annotation-glyphxmine-key-sel-button(extent-propertyxmine-key-sel-button'xmine-non-selected-glyph)))(set-extent-propertyext'xmine-non-selected-glyph(annotation-glyphext))(set-annotation-glyphextsel-glyph)(setqxmine-key-sel-buttonext)))(defunxmine-key-action1()(interactive)(let((action(extent-propertyxmine-key-sel-button'action1)))(ifaction(funcallactionxmine-key-sel-button))))(defunxmine-key-action2()(interactive)(let((action(extent-propertyxmine-key-sel-button'action2)))(if(andaction(xmine-enough-flagged-pxmine-key-sel-button))(funcallactionxmine-key-sel-button)(beep))))(defunxmine-key-action3()(interactive)(let((action(extent-propertyxmine-key-sel-button'action3)))(ifaction(funcallactionxmine-key-sel-button))))(defunxmine-key-quit()(interactive)(kill-buffer(current-buffer)))(defunxmine-key-new()(interactive)(xmine-field-create))(defunxmine-key-down-right()(interactive)(xmine-key-down)(xmine-key-right))(defunxmine-key-down-left()(interactive)(xmine-key-down)(xmine-key-left))(defunxmine-key-up-right()(interactive)(xmine-key-up)(xmine-key-right))(defunxmine-key-up-left()(interactive)(xmine-key-up)(xmine-key-left))(defunxmine-key-down()(interactive)(let*((x(extent-propertyxmine-key-sel-button'xmine-x))(y(extent-propertyxmine-key-sel-button'xmine-y))(ext(xmine-field-button-atx(1+y))))(ifext(xmine-select-buttonext)(xmine-select-button(xmine-field-button-atx1)))))(defunxmine-key-up()(interactive)(let*((x(extent-propertyxmine-key-sel-button'xmine-x))(y(extent-propertyxmine-key-sel-button'xmine-y))(ext(xmine-field-button-atx(1-y))))(ifext(xmine-select-buttonext)(xmine-select-button(xmine-field-button-atxxmine-height)))))(defunxmine-key-right()(interactive)(let*((x(extent-propertyxmine-key-sel-button'xmine-x))(y(extent-propertyxmine-key-sel-button'xmine-y))(ext(xmine-field-button-at(1+x)y)))(ifext(xmine-select-buttonext)(xmine-select-button(xmine-field-button-at1y)))))(defunxmine-key-left()(interactive)(let*((x(extent-propertyxmine-key-sel-button'xmine-x))(y(extent-propertyxmine-key-sel-button'xmine-y))(ext(xmine-field-button-at(1-x)y)))(ifext(xmine-select-buttonext)(xmine-select-button(xmine-field-button-atxmine-widthy)))))(provide'xmine)