aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThien-Thi Nguyen2007-08-27 18:49:42 +0000
committerThien-Thi Nguyen2007-08-27 18:49:42 +0000
commita79b55e56e8261ff2c9a49af5328285d0239c5e4 (patch)
treee7e746069327f0487d511cad4fc3a6c68cf02ae1
parent2503f22288aa87e0ce77af4d45f7ef6e7b6eeab7 (diff)
downloademacs-a79b55e56e8261ff2c9a49af5328285d0239c5e4.tar.gz
emacs-a79b55e56e8261ff2c9a49af5328285d0239c5e4.zip
Initial revision
-rw-r--r--lisp/play/bubbles.el1438
1 files changed, 1438 insertions, 0 deletions
diff --git a/lisp/play/bubbles.el b/lisp/play/bubbles.el
new file mode 100644
index 00000000000..a3faecb54ab
--- /dev/null
+++ b/lisp/play/bubbles.el
@@ -0,0 +1,1438 @@
1;;; bubbles.el --- Puzzle game for Emacs.
2
3;; Copyright (C) 2007 Free Software Foundation, Inc.
4
5;; This file is NOT part of GNU Emacs.
6
7;; Author: Ulf Jasper <ulf.jasper@web.de>
8;; Filename: bubbles.el
9;; URL: http://ulf.epplejasper.de/
10;; Created: 5. Feb. 2007
11;; Keywords: Games
12;; Time-stamp: "27. August 2007, 19:51:08 (ulf)"
13;; CVS-Version: $Id: bubbles.el,v 1.16 2007-08-27 17:51:29 ulf Exp $
14
15;; ======================================================================
16
17;; This program is free software; you can redistribute it and/or modify
18;; it under the terms of the GNU General Public License as published by
19;; the Free Software Foundation; either version 2 of the License, or (at
20;; your option) any later version.
21
22;; This program is distributed in the hope that it will be useful, but
23;; WITHOUT ANY WARRANTY; without even the implied warranty of
24;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25;; General Public License for more details.
26
27;; You should have received a copy of the GNU General Public License
28;; along with this program; if not, write to the Free Software Foundation,
29;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30
31(defconst bubbles-version "0.4" "Version number of bubbles.el.")
32
33;; ======================================================================
34
35;;; Commentary:
36
37;; Bubbles is a puzzle game. Its goal is to remove as many bubbles as
38;; possible in as few moves as possible.
39
40;; Bubbles is an implementation of the "Same Game", similar to "Same
41;; GNOME" and many others, see http://en.wikipedia.org/wiki/SameGame.
42
43;; Installation
44;; ------------
45
46;; Add the following lines to your Emacs startup file (`~/.emacs').
47;; (add-to-list 'load-path "/path/to/bubbles/")
48;; (autoload 'bubbles "bubbles" "Play Bubbles" t)
49
50;; ======================================================================
51
52;;; History:
53
54;; 0.4 (2007-08-27)
55;; - Allow for undoing last move.
56;; - Bonus for removing all bubbles.
57;; - Speed improvements.
58;; - Animation enhancements.
59;; - Added `bubbles-mode-hook'.
60;; - Fixes: Don't move point.
61;; - New URL.
62
63;; 0.3 (2007-03-11)
64;; - Renamed shift modes and thus names of score files. All
65;; highscores are lost, unless you rename the score files from
66;; bubbles-shift-... to bubbles-...!
67;; - Bugfixes: Check for successful image creation.
68;; Disable menus and counter when game is over.
69;; Tested with GNU Emacs 22.0.93
70
71;; 0.2 (2007-02-24)
72;; - Introduced game themes.
73;; - Introduced graphics themes (changeable while playing).
74;; - Added menu.
75;; - Customization: grid size, colors, chars, shift mode.
76;; - More keybindings.
77;; - Changed shift direction from to-right to to-left.
78;; - Bugfixes: Don't remove single-bubble regions;
79;; Animation glitches fixed.
80;; Tested with GNU Emacs 22.0.93 and 21.4.1.
81
82;; 0.1 (2007-02-11)
83;; Initial release. Tested with GNU Emacs 22.0.93 and 21.4.1.
84
85;; ======================================================================
86
87;;; Code:
88
89(require 'gamegrid)
90(require 'cl)
91
92;; User options
93
94;; Careful with that axe, Eugene! Order does matter in the custom
95;; section below.
96
97(defcustom bubbles-game-theme
98 'easy
99 "Overall game theme.
100The overall game theme specifies a grid size, a set of colors,
101and a shift mode."
102 :type '(radio (const :tag "Easy" easy)
103 (const :tag "Medium" medium)
104 (const :tag "Difficult" difficult)
105 (const :tag "Hard" hard)
106 (const :tag "User defined" user-defined))
107 :group 'bubbles)
108
109(defun bubbles-set-game-easy ()
110 "Set game theme to 'easy'."
111 (interactive)
112 (setq bubbles-game-theme 'easy)
113 (bubbles))
114
115(defun bubbles-set-game-medium ()
116 "Set game theme to 'medium'."
117 (interactive)
118 (setq bubbles-game-theme 'medium)
119 (bubbles))
120
121(defun bubbles-set-game-difficult ()
122 "Set game theme to 'difficult'."
123 (interactive)
124 (setq bubbles-game-theme 'difficult)
125 (bubbles))
126
127(defun bubbles-set-game-hard ()
128 "Set game theme to 'hard'."
129 (interactive)
130 (setq bubbles-game-theme 'hard)
131 (bubbles))
132
133(defun bubbles-set-game-userdefined ()
134 "Set game theme to 'user-defined'."
135 (interactive)
136 (setq bubbles-game-theme 'user-defined)
137 (bubbles))
138
139(defgroup bubbles nil
140 "Bubbles, a puzzle game."
141 :group 'games)
142
143(defcustom bubbles-graphics-theme
144 'circles
145 "Graphics theme.
146It is safe to choose a graphical theme. If Emacs cannot display
147images the `ascii' theme will be used."
148 :type '(radio (const :tag "Circles" circles)
149 (const :tag "Squares" squares)
150 (const :tag "Diamonds" diamonds)
151 (const :tag "Balls" balls)
152 (const :tag "Emacs" emacs)
153 (const :tag "ASCII (no images)" ascii))
154 :group 'bubbles)
155
156(defconst bubbles--grid-small '(10 . 10)
157 "Predefined small bubbles grid.")
158
159(defconst bubbles--grid-medium '(15 . 10)
160 "Predefined medium bubbles grid.")
161
162(defconst bubbles--grid-large '(20 . 15)
163 "Predefined large bubbles grid.")
164
165(defconst bubbles--grid-huge '(30 . 20)
166 "Predefined huge bubbles grid.")
167
168(defcustom bubbles-grid-size
169 bubbles--grid-medium
170 "Size of bubbles grid."
171 :type `(radio (const :tag "Small" ,bubbles--grid-small)
172 (const :tag "Medium" ,bubbles--grid-medium)
173 (const :tag "Large" ,bubbles--grid-large)
174 (const :tag "Huge" ,bubbles--grid-huge)
175 (cons :tag "User defined"
176 (integer :tag "Width")
177 (integer :tag "Height")))
178 :group 'bubbles)
179
180(defconst bubbles--colors-2 '("orange" "violet")
181 "Predefined bubbles color list with two colors.")
182
183(defconst bubbles--colors-3 '("lightblue" "palegreen" "pink")
184 "Predefined bubbles color list with three colors.")
185
186(defconst bubbles--colors-4 '("firebrick" "sea green" "steel blue" "chocolate")
187 "Predefined bubbles color list with four colors.")
188
189(defconst bubbles--colors-5 '("firebrick" "sea green" "steel blue"
190 "sandy brown" "bisque3")
191 "Predefined bubbles color list with five colors.")
192
193(defcustom bubbles-colors
194 bubbles--colors-3
195 "List of bubble colors.
196The length of this list determines how many different bubble
197types are present."
198 :type `(radio (const :tag "Red, darkgreen" ,bubbles--colors-2)
199 (const :tag "Red, darkgreen, blue" ,bubbles--colors-3)
200 (const :tag "Red, darkgreen, blue, orange" ,bubbles--colors-4)
201 (const :tag "Red, darkgreen, blue, orange, violet"
202 ,bubbles--colors-5)
203 (repeat :tag "User defined" color))
204 :group 'bubbles)
205
206(defcustom bubbles-chars
207 '(?+ ?O ?# ?X ?. ?* ?& ?§)
208 "Characters used for bubbles.
209Note that the actual number of different bubbles is determined by
210the number of colors, see `bubbles-colors'."
211 :type '(repeat character)
212 :group 'bubbles)
213
214(defcustom bubbles-shift-mode
215 'default
216 "Shift mode.
217Available modes are `shift-default' and`shift-always'."
218 :type '(radio (const :tag "Default" default)
219 (const :tag "Shifter" always)
220 ;;(const :tag "Mega Shifter" 'mega)
221 )
222 :group 'bubbles)
223
224(defcustom bubbles-mode-hook nil
225 "Hook run by Bubbles mode."
226 :group 'bubbles
227 :type 'hook)
228
229(defun bubbles-customize ()
230 "Open customization buffer for bubbles."
231 (interactive)
232 (customize-group 'bubbles))
233
234;; ======================================================================
235;; internal variables
236
237(defvar bubbles--score 0
238 "Current Bubbles score.")
239
240(defvar bubbles--neighbourhood-score 0
241 "Score of active bubbles neighbourhood.")
242
243(defvar bubbles--faces nil
244 "List of currently used faces.")
245
246(defvar bubbles--playing nil
247 "Play status indicator.")
248
249(defvar bubbles--empty-image nil
250 "Image used for removed bubbles (empty grid cells).")
251
252(defvar bubbles--images nil
253 "List of images for bubbles.")
254
255(defvar bubbles--images-ok nil
256 "Indicate whether images have been created successfully.")
257
258(defvar bubbles--col-offset 0
259 "Horizontal offset for centering the bubbles grid.")
260
261(defvar bubbles--row-offset 0
262 "Vertical offset for centering the bubbles grid.")
263
264(defvar bubbles--save-data nil
265 "List containing bubbles save data (SCORE BUFFERCONTENTS).")
266
267(defconst bubbles--image-template-circle
268 "/* XPM */
269static char * dot_xpm[] = {
270\"20 20 2 1\",
271\" c None\",
272\". c #FFFFFF\",
273\" ...... \",
274\" .......... \",
275\" .............. \",
276\" ................ \",
277\" ................ \",
278\" .................. \",
279\" .................. \",
280\"....................\",
281\"....................\",
282\"....................\",
283\"....................\",
284\"....................\",
285\"....................\",
286\" .................. \",
287\" .................. \",
288\" ................ \",
289\" ................ \",
290\" .............. \",
291\" .......... \",
292\" ...... \"};")
293
294(defconst bubbles--image-template-square
295 "/* XPM */
296static char * dot_xpm[] = {
297\"20 20 2 1\",
298\"0 c None\",
299\"1 c #FFFFFF\",
300\"00000000000000000000\",
301\"01111111111111111110\",
302\"01111111111111111110\",
303\"01111111111111111110\",
304\"01111111111111111110\",
305\"01111111111111111110\",
306\"01111111111111111110\",
307\"01111111111111111110\",
308\"01111111111111111110\",
309\"01111111111111111110\",
310\"01111111111111111110\",
311\"01111111111111111110\",
312\"01111111111111111110\",
313\"01111111111111111110\",
314\"01111111111111111110\",
315\"01111111111111111110\",
316\"01111111111111111110\",
317\"01111111111111111110\",
318\"01111111111111111110\",
319\"00000000000000000000\"};")
320
321(defconst bubbles--image-template-diamond
322 "/* XPM */
323static char * dot_xpm[] = {
324\"20 20 2 1\",
325\"0 c None\",
326\"1 c #FFFFFF\",
327\"00000000011000000000\",
328\"00000000111100000000\",
329\"00000001111110000000\",
330\"00000011111111000000\",
331\"00000111111111100000\",
332\"00001111111111110000\",
333\"00011111111111111000\",
334\"00111111111111111100\",
335\"01111111111111111110\",
336\"11111111111111111111\",
337\"01111111111111111110\",
338\"00111111111111111100\",
339\"00011111111111111000\",
340\"00001111111111110000\",
341\"00000111111111100000\",
342\"00000011111111000000\",
343\"00000001111110000000\",
344\"00000000111100000000\",
345\"00000000011000000000\",
346\"00000000000000000000\"};")
347
348(defconst bubbles--image-template-emacs
349 "/* XPM */
350static char * emacs_24_xpm[] = {
351\"24 24 129 2\",
352\" c None\",
353\". c #837DA4\",
354\"+ c #807AA0\",
355\"@ c #9894B2\",
356\"# c #CCCAD9\",
357\"$ c #C2C0D2\",
358\"% c #B6B3C9\",
359\"& c #A19DB9\",
360\"* c #8681A5\",
361\"= c #7D779B\",
362\"- c #B6B3C7\",
363\"; c #ABA7BE\",
364\"> c #9792AF\",
365\", c #AAA6BD\",
366\"' c #CBC9D7\",
367\") c #AAA7BE\",
368\"! c #908BAA\",
369\"~ c #797397\",
370\"{ c #948FAC\",
371\"] c #9A95B1\",
372\"^ c #EBEAEF\",
373\"/ c #F1F1F5\",
374\"( c #BCB9CB\",
375\"_ c #A9A5BD\",
376\": c #757093\",
377\"< c #918DA9\",
378\"[ c #DDDBE4\",
379\"} c #FFFFFF\",
380\"| c #EAE9EF\",
381\"1 c #A7A4BA\",
382\"2 c #716C8F\",
383\"3 c #8D89A5\",
384\"4 c #9C98B1\",
385\"5 c #DBDAE3\",
386\"6 c #A4A1B7\",
387\"7 c #6E698A\",
388\"8 c #8B87A1\",
389\"9 c #928EA7\",
390\"0 c #C5C3D1\",
391\"a c #F8F8F9\",
392\"b c #CCCAD6\",
393\"c c #A29FB4\",
394\"d c #6A6585\",
395\"e c #88849D\",
396\"f c #B5B2C2\",
397\"g c #F0F0F3\",
398\"h c #E1E0E6\",
399\"i c #A5A2B5\",
400\"j c #A09DB1\",
401\"k c #676281\",
402\"l c #85819A\",
403\"m c #9591A7\",
404\"n c #E1E0E5\",
405\"o c #F0EFF2\",
406\"p c #B3B0C0\",
407\"q c #9D9AAE\",
408\"r c #635F7C\",
409\"s c #827F96\",
410\"t c #9997AA\",
411\"u c #F7F7F9\",
412\"v c #C8C7D1\",
413\"w c #89869D\",
414\"x c #9B99AB\",
415\"y c #5F5B78\",
416\"z c #7F7C93\",
417\"A c #CFCDD6\",
418\"B c #B7B5C2\",
419\"C c #9996A9\",
420\"D c #5C5873\",
421\"E c #7A778D\",
422\"F c #F5F5F6\",
423\"G c #8E8C9E\",
424\"H c #7D798F\",
425\"I c #58546F\",
426\"J c #6C6981\",
427\"K c #D5D4DB\",
428\"L c #F5F4F6\",
429\"M c #9794A5\",
430\"N c #625F78\",
431\"O c #79768C\",
432\"P c #55516A\",
433\"Q c #605C73\",
434\"R c #CAC9D1\",
435\"S c #EAE9EC\",
436\"T c #B4B3BE\",
437\"U c #777488\",
438\"V c #514E66\",
439\"W c #DEDEE2\",
440\"X c #F4F4F5\",
441\"Y c #9D9BA9\",
442\"Z c #747185\",
443\"` c #4E4B62\",
444\" . c #DEDDE1\",
445\".. c #A6A5B0\",
446\"+. c #716F81\",
447\"@. c #4A475D\",
448\"#. c #A4A3AE\",
449\"$. c #F4F3F5\",
450\"%. c #777586\",
451\"&. c #6E6C7D\",
452\"*. c #464358\",
453\"=. c #514E62\",
454\"-. c #B9B8C0\",
455\";. c #D1D0D5\",
456\">. c #747282\",
457\",. c #6B6979\",
458\"'. c #434054\",
459\"). c #5A5769\",
460\"!. c #D0CFD4\",
461\"~. c #5B5869\",
462\"{. c #696676\",
463\"]. c #403D50\",
464\"^. c #DBDADE\",
465\"/. c #F3F3F4\",
466\"(. c #646271\",
467\"_. c #666473\",
468\":. c #3D3A4C\",
469\"<. c #555362\",
470\"[. c #9E9DA6\",
471\"}. c #9E9CA5\",
472\"|. c #646170\",
473\"1. c #393647\",
474\"2. c #514E5D\",
475\"3. c #83818C\",
476\"4. c #A8A7AE\",
477\"5. c #E6E6E8\",
478\"6. c #DAD9DC\",
479\"7. c #353343\",
480\"8. c #32303E\",
481\" . . . . . . . . . . . . . . . . . . \",
482\" + @ # $ % % % % % % % % % % % % % % & * + + \",
483\" = - ; > > > > > > > > , ' ) > > > > > > ! = \",
484\"~ ~ { { { { { { { { { { { ] ^ / ( { { { { _ ~ ~ \",
485\": : < < < < < < < < < < < < [ } } | < < < 1 : : \",
486\"2 2 3 3 3 3 3 3 3 3 3 3 4 5 } } } 5 3 3 3 6 2 2 \",
487\"7 7 8 8 8 8 8 8 8 8 9 0 a } } } b 8 8 8 8 c 7 7 \",
488\"d d e e e e e e e f g } } } h i e e e e e j d d \",
489\"k k l l l l l m n } } } o p l l l l l l l q k k \",
490\"r r s s s s t u } } } v w s s s s s s s s x r r \",
491\"y y z z z z A } } } B z z z z z z z z z z C y y \",
492\"D D D D D D E F } } G D D D D D D D D D D H D D \",
493\"I I I I I I I J K } L M N I I I I I I I I O I I \",
494\"P P P P P P Q R } } } S T P P P P P P P P U P P \",
495\"V V V V V V W } } X Y V V V V V V V V V V Z V V \",
496\"` ` ` ` ` ` .} } ..` ` ` ` ` ` ` ` ` ` ` +.` ` \",
497\"@.@.@.@.@.@.@.#.$.$.%.@.@.@.@.@.@.@.@.@.@.&.@.@.\",
498\"*.*.*.*.*.*.*.*.=.-.} ;.>.*.*.*.*.*.*.*.*.,.*.*.\",
499\"'.'.'.'.'.'.'.'.'.'.).!.} !.~.'.'.'.'.'.'.{.'.'.\",
500\"].].].].].].].].].].].].^.} /.(.].].].].]._.].].\",
501\":.:.:.:.:.:.:.:.:.:.<.[./.} } }.:.:.:.:.:.|.:.:.\",
502\" 1.1.1.1.1.1.1.1.2.3.4.5.6.3.1.1.1.1.1.1.1.1. \",
503\" 7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7. \",
504\" 8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8. \"};")
505
506(defconst bubbles--image-template-ball
507 "/* XPM */
508static char * dot3d_xpm[] = {
509\"20 20 190 2\",
510\" c None\",
511\". c #F9F6F6\",
512\"+ c #D6D0D0\",
513\"@ c #BFBBBB\",
514\"# c #AAA4A4\",
515\"$ c #ABAAAB\",
516\"% c #A8A8A8\",
517\"& c #A29D9D\",
518\"* c #B5B2B2\",
519\"= c #CDC9C9\",
520\"- c #D7D0D0\",
521\"; c #B3AFAF\",
522\"> c #B5B5B5\",
523\", c #B7B7B7\",
524\"' c #B8B8B8\",
525\") c #B6B6B6\",
526\"! c #B3B3B3\",
527\"~ c #AFAFAF\",
528\"{ c #A9A9A9\",
529\"] c #A2A2A2\",
530\"^ c #9C9A9A\",
531\"/ c #C9C5C5\",
532\"( c #FDFBFB\",
533\"_ c #C3BCBC\",
534\": c #BBBBBB\",
535\"< c #C0C0C0\",
536\"[ c #C3C2C2\",
537\"} c #C3C3C3\",
538\"| c #C2C2C2\",
539\"1 c #BEBEBE\",
540\"2 c #B9B9B9\",
541\"3 c #B2B2B2\",
542\"4 c #ABAAAA\",
543\"5 c #999999\",
544\"6 c #ACA7A7\",
545\"7 c #C2BBBB\",
546\"8 c #C5C5C5\",
547\"9 c #CACBCB\",
548\"0 c #CECECE\",
549\"a c #CFCFCF\",
550\"b c #CDCDCD\",
551\"c c #C8C9C9\",
552\"d c #9F9F9F\",
553\"e c #959595\",
554\"f c #A9A5A5\",
555\"g c #D5CFCE\",
556\"h c #BDBDBD\",
557\"i c #C6C6C6\",
558\"j c #D5D5D5\",
559\"k c #D9D9D9\",
560\"l c #DADADA\",
561\"m c #D8D8D8\",
562\"n c #D2D2D2\",
563\"o c #CBCBCB\",
564\"p c #A4A4A5\",
565\"q c #9A9A9A\",
566\"r c #8F8F8F\",
567\"s c #C3BFBF\",
568\"t c #AFACAB\",
569\"u c #CCCCCC\",
570\"v c #D6D6D6\",
571\"w c #DEDEDE\",
572\"x c #E4E4E4\",
573\"y c #E5E5E5\",
574\"z c #E2E2E2\",
575\"A c #DBDBDB\",
576\"B c #C9C8C8\",
577\"C c #A8A9A8\",
578\"D c #9D9E9D\",
579\"E c #929292\",
580\"F c #8A8888\",
581\"G c #D3CECE\",
582\"H c #B0B0B0\",
583\"I c #D1D1D1\",
584\"J c #DCDCDC\",
585\"K c #E6E6E6\",
586\"L c #EEEEEE\",
587\"M c #F1F1F0\",
588\"N c #EBEBEB\",
589\"O c #D7D7D8\",
590\"P c #ABABAB\",
591\"Q c #A0A0A0\",
592\"R c #949494\",
593\"S c #898989\",
594\"T c #C0BDBD\",
595\"U c #B9B6B6\",
596\"V c #B1B1B1\",
597\"W c #BCBCBC\",
598\"X c #C8C8C8\",
599\"Y c #D3D3D3\",
600\"Z c #DFDFDE\",
601\"` c #EAEAEA\",
602\" . c #F5F5F5\",
603\".. c #FAFAFA\",
604\"+. c #F1F1F1\",
605\"@. c #CECFCF\",
606\"#. c #ACACAC\",
607\"$. c #A1A1A1\",
608\"%. c #8A8A8A\",
609\"&. c #9B9999\",
610\"*. c #C7C7C7\",
611\"=. c #DDDDDD\",
612\"-. c #E8E8E8\",
613\";. c #F2F2F2\",
614\">. c #898A89\",
615\",. c #7A7878\",
616\"'. c #AEAEAE\",
617\"). c #C4C4C4\",
618\"!. c #CBCBCA\",
619\"~. c #AAAAAA\",
620\"{. c #939393\",
621\"]. c #888888\",
622\"^. c #7C7C7C\",
623\"/. c #AAAAAB\",
624\"(. c #BFBFBF\",
625\"_. c #C9C9C9\",
626\":. c #DFDEDF\",
627\"<. c #A6A6A6\",
628\"[. c #9B9B9B\",
629\"}. c #909191\",
630\"|. c #858586\",
631\"1. c #797979\",
632\"2. c #989494\",
633\"3. c #A5A6A5\",
634\"4. c #B9B9B8\",
635\"5. c #C1C1C1\",
636\"6. c #CFCFCE\",
637\"7. c #979797\",
638\"8. c #8D8D8D\",
639\"9. c #828282\",
640\"0. c #747171\",
641\"a. c #ADAAAA\",
642\"b. c #A9A8A9\",
643\"c. c #B8B9B9\",
644\"d. c #A5A5A5\",
645\"e. c #9C9C9C\",
646\"f. c #7E7E7D\",
647\"g. c #929191\",
648\"h. c #C9C4C4\",
649\"i. c #989898\",
650\"j. c #ADADAD\",
651\"k. c #9D9D9D\",
652\"l. c #8C8C8C\",
653\"m. c #787878\",
654\"n. c #B8B6B6\",
655\"o. c #939191\",
656\"p. c #A5A5A6\",
657\"q. c #ABABAA\",
658\"r. c #A8A8A9\",
659\"s. c #A3A3A3\",
660\"t. c #858585\",
661\"u. c #757474\",
662\"v. c #C5C1C1\",
663\"w. c #969696\",
664\"x. c #9B9B9C\",
665\"y. c #A4A4A4\",
666\"z. c #9E9E9E\",
667\"A. c #939394\",
668\"B. c #7D7D7D\",
669\"C. c #747474\",
670\"D. c #B7B5B5\",
671\"E. c #A5A1A1\",
672\"F. c #919191\",
673\"G. c #9A9999\",
674\"H. c #838383\",
675\"I. c #757575\",
676\"J. c #939090\",
677\"K. c #A29E9E\",
678\"L. c #868686\",
679\"M. c #8D8D8C\",
680\"N. c #8E8E8E\",
681\"O. c #8D8D8E\",
682\"P. c #8B8C8C\",
683\"Q. c #848485\",
684\"R. c #7F7F80\",
685\"S. c #7A7A7A\",
686\"T. c #737373\",
687\"U. c #929090\",
688\"V. c #828080\",
689\"W. c #818181\",
690\"X. c #808080\",
691\"Y. c #7E7E7E\",
692\"Z. c #737272\",
693\"`. c #B7B4B4\",
694\" + c #BCBABA\",
695\".+ c #959494\",
696\"++ c #747172\",
697\"@+ c #767676\",
698\"#+ c #6F6D6D\",
699\"$+ c #8F8E8E\",
700\" . + @ # $ % & * = . \",
701\" - ; > , ' ) ! ~ { ] ^ / \",
702\" ( _ > : < [ } | 1 2 3 4 ] 5 6 ( \",
703\" 7 ) 1 8 9 0 a b c | : 3 { d e f \",
704\" g ! h i 0 j k l m n o | 2 ~ p q r s \",
705\". t ' | u v w x y z A n B 1 ! C D E F . \",
706\"G H : i I J K L M N z O b | ) P Q R S T \",
707\"U V W X Y Z ` ...+.y l @.} ' #.$.e %.&.\",
708\"& H W *.n =.-.;. .L x k 0 [ , #.Q e >.,.\",
709\"] '.2 ).a k z -.` K w j !.< > ~.d {.].^.\",
710\"d /.> (._.I k =.:.J v 0 8 : V <.[.}.|.1.\",
711\"2.3.~ 4.5._.6.n Y I u i 1 > P $.7.8.9.0.\",
712\"a.d b.V c.(.).*.X i | h ) '.d.e.E ].f.g.\",
713\"h.i.$.C ~ > 2 W W : ' ! j.d.k.e l.9.m.n.\",
714\". o.i.d p.q.'.H V H j.r.s.k.e 8.t.^.u.. \",
715\" v.r w.x.Q s.d.d.y.] z.5 A.8.t.B.C.D. \",
716\" E.l.F.e i.G.q 5 7.{.r %.H.^.I.J. \",
717\" ( K.L.%.M.N.N.O.P.S Q.R.S.T.U.( \",
718\" @ V.W.H.H.9.X.Y.S.I.Z.`. \",
719\" . +.+++@+C.#+$+D.. \"};")
720
721;; ======================================================================
722;; Functions
723
724(defsubst bubbles--grid-width ()
725 "Return the grid width for the current game theme."
726 (car (case bubbles-game-theme
727 ('easy
728 bubbles--grid-small)
729 ('medium
730 bubbles--grid-medium)
731 ('difficult
732 bubbles--grid-large)
733 ('hard
734 bubbles--grid-huge)
735 ('user-defined
736 bubbles-grid-size))))
737
738(defsubst bubbles--grid-height ()
739 "Return the grid height for the current game theme."
740 (cdr (case bubbles-game-theme
741 ('easy
742 bubbles--grid-small)
743 ('medium
744 bubbles--grid-medium)
745 ('difficult
746 bubbles--grid-large)
747 ('hard
748 bubbles--grid-huge)
749 ('user-defined
750 bubbles-grid-size))))
751
752(defsubst bubbles--colors ()
753 "Return the color list for the current game theme."
754 (case bubbles-game-theme
755 ('easy
756 bubbles--colors-2)
757 ('medium
758 bubbles--colors-3)
759 ('difficult
760 bubbles--colors-4)
761 ('hard
762 bubbles--colors-5)
763 ('user-defined
764 bubbles-colors)))
765
766(defsubst bubbles--shift-mode ()
767 "Return the shift mode for the current game theme."
768 (case bubbles-game-theme
769 ('easy
770 'default)
771 ('medium
772 'default)
773 ('difficult
774 'always)
775 ('hard
776 'always)
777 ('user-defined
778 bubbles-shift-mode)))
779
780(defun bubbles-save-settings ()
781 "Save current customization settings."
782 (interactive)
783 (custom-set-variables
784 (list 'bubbles-game-theme `(quote ,bubbles-game-theme) t)
785 (list 'bubbles-graphics-theme `(quote ,bubbles-graphics-theme) t))
786 (customize-save-customized))
787
788(defsubst bubbles--empty-char ()
789 "The character used for removed bubbles (empty grid cells)."
790 ? )
791
792(defun bubbles-set-graphics-theme-ascii ()
793 "Set graphics theme to `ascii'."
794 (interactive)
795 (setq bubbles-graphics-theme 'ascii)
796 (bubbles--update-faces-or-images))
797
798(defun bubbles-set-graphics-theme-circles ()
799 "Set graphics theme to `circles'."
800 (interactive)
801 (setq bubbles-graphics-theme 'circles)
802 (bubbles--initialize-images)
803 (bubbles--update-faces-or-images))
804
805(defun bubbles-set-graphics-theme-squares ()
806 "Set graphics theme to `squares'."
807 (interactive)
808 (setq bubbles-graphics-theme 'squares)
809 (bubbles--initialize-images)
810 (bubbles--update-faces-or-images))
811
812(defun bubbles-set-graphics-theme-diamonds ()
813 "Set graphics theme to `diamonds'."
814 (interactive)
815 (setq bubbles-graphics-theme 'diamonds)
816 (bubbles--initialize-images)
817 (bubbles--update-faces-or-images))
818
819(defun bubbles-set-graphics-theme-balls ()
820 "Set graphics theme to `balls'."
821 (interactive)
822 (setq bubbles-graphics-theme 'balls)
823 (bubbles--initialize-images)
824 (bubbles--update-faces-or-images))
825
826(defun bubbles-set-graphics-theme-emacs ()
827 "Set graphics theme to `emacs'."
828 (interactive)
829 (setq bubbles-graphics-theme 'emacs)
830 (bubbles--initialize-images)
831 (bubbles--update-faces-or-images))
832
833;; bubbles mode map
834(defvar bubbles-mode-map
835 (make-keymap 'bubbles-mode-map))
836(define-key bubbles-mode-map "q" 'bubbles-quit)
837(define-key bubbles-mode-map "\n" 'bubbles-plop)
838(define-key bubbles-mode-map " " 'bubbles-plop)
839(define-key bubbles-mode-map [double-down-mouse-1] 'bubbles-plop)
840(define-key bubbles-mode-map [mouse-2] 'bubbles-plop)
841(define-key bubbles-mode-map "\C-m" 'bubbles-plop)
842(define-key bubbles-mode-map "u" 'bubbles-undo)
843(define-key bubbles-mode-map "p" 'previous-line)
844(define-key bubbles-mode-map "n" 'next-line)
845(define-key bubbles-mode-map "f" 'forward-char)
846(define-key bubbles-mode-map "b" 'backward-char)
847
848
849;; game theme menu
850(defvar bubbles-game-theme-menu (make-sparse-keymap "Game Theme"))
851(define-key bubbles-game-theme-menu [bubbles-set-game-userdefined]
852 (list 'menu-item "User defined" 'bubbles-set-game-userdefined
853 :button '(:radio . (eq bubbles-game-theme 'user-defined))))
854(define-key bubbles-game-theme-menu [bubbles-set-game-hard]
855 (list 'menu-item "Hard" 'bubbles-set-game-hard
856 :button '(:radio . (eq bubbles-game-theme 'hard))))
857(define-key bubbles-game-theme-menu [bubbles-set-game-difficult]
858 (list 'menu-item "Difficult" 'bubbles-set-game-difficult
859 :button '(:radio . (eq bubbles-game-theme 'difficult))))
860(define-key bubbles-game-theme-menu [bubbles-set-game-medium]
861 (list 'menu-item "Medium" 'bubbles-set-game-medium
862 :button '(:radio . (eq bubbles-game-theme 'medium))))
863(define-key bubbles-game-theme-menu [bubbles-set-game-easy]
864 (list 'menu-item "Easy" 'bubbles-set-game-easy
865 :button '(:radio . (eq bubbles-game-theme 'easy))))
866
867;; graphics theme menu
868(defvar bubbles-graphics-theme-menu (make-sparse-keymap "Graphics Theme"))
869(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-ascii]
870 (list 'menu-item "ASCII" 'bubbles-set-graphics-theme-ascii
871 :button '(:radio . (eq bubbles-graphics-theme 'ascii))))
872(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-emacs]
873 (list 'menu-item "Emacs" 'bubbles-set-graphics-theme-emacs
874 :button '(:radio . (eq bubbles-graphics-theme 'emacs))))
875(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-balls]
876 (list 'menu-item "Balls" 'bubbles-set-graphics-theme-balls
877 :button '(:radio . (eq bubbles-graphics-theme 'balls))))
878(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-diamonds]
879 (list 'menu-item "Diamonds" 'bubbles-set-graphics-theme-diamonds
880 :button '(:radio . (eq bubbles-graphics-theme 'diamonds))))
881(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-squares]
882 (list 'menu-item "Squares" 'bubbles-set-graphics-theme-squares
883 :button '(:radio . (eq bubbles-graphics-theme 'squares))))
884(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-circles]
885 (list 'menu-item "Circles" 'bubbles-set-graphics-theme-circles
886 :button '(:radio . (eq bubbles-graphics-theme 'circles))))
887
888;; menu
889(defvar bubbles-menu (make-sparse-keymap "Bubbles"))
890(define-key bubbles-menu [bubbles-quit]
891 (list 'menu-item "Quit" 'bubbles-quit))
892(define-key bubbles-menu [bubbles]
893 (list 'menu-item "New game" 'bubbles))
894(define-key bubbles-menu [bubbles-separator-1]
895 '("--"))
896(define-key bubbles-menu [bubbles-save-settings]
897 (list 'menu-item "Save all settings" 'bubbles-save-settings))
898(define-key bubbles-menu [bubbles-customize]
899 (list 'menu-item "Edit all settings" 'bubbles-customize))
900(define-key bubbles-menu [bubbles-game-theme-menu]
901 (list 'menu-item "Game Theme" bubbles-game-theme-menu))
902(define-key bubbles-menu [bubbles-graphics-theme-menu]
903 (list 'menu-item "Graphics Theme" bubbles-graphics-theme-menu
904 :enable 'bubbles--playing))
905(define-key bubbles-menu [bubbles-separator-2]
906 '("--"))
907(define-key bubbles-menu [bubbles-undo]
908 (list 'menu-item "Undo last move" 'bubbles-undo
909 :enable '(and bubbles--playing bubbles--save-data)))
910
911;; bind menu to mouse
912(define-key bubbles-mode-map [down-mouse-3] bubbles-menu)
913;; Put menu in menu-bar
914(define-key bubbles-mode-map [menu-bar Bubbles]
915 (cons "Bubbles" bubbles-menu))
916
917(defun bubbles-mode ()
918 "Major mode for playing bubbles.
919\\{bubbles-mode-map}"
920 (kill-all-local-variables)
921 (use-local-map bubbles-mode-map)
922 (setq major-mode 'bubbles-mode)
923 (setq mode-name "Bubbles")
924 (setq buffer-read-only t)
925 (buffer-enable-undo)
926 (add-hook 'post-command-hook 'bubbles--mark-neighbourhood t t)
927 (run-hooks 'bubbles-mode-hook))
928
929;;;###autoload
930(defun bubbles ()
931 "Play Bubbles game."
932 (interactive)
933 (switch-to-buffer (get-buffer-create "*bubbles*"))
934 (when (or (not bubbles--playing)
935 (y-or-n-p "Start new game? "))
936 (setq bubbles--save-data nil)
937 (setq bubbles--playing t)
938 (bubbles--initialize)))
939
940(defun bubbles-quit ()
941 "Quit Bubbles."
942 (interactive)
943 (message "bubbles-quit")
944 (bury-buffer))
945
946(defun bubbles--compute-offsets ()
947 "Update horizontal and vertical offsets for centering the bubbles grid.
948Set `bubbles--col-offset' and `bubbles--row-offset'."
949 (cond ((and (display-images-p)
950 bubbles--images-ok
951 (not (eq bubbles-graphics-theme 'ascii))
952 (fboundp 'window-inside-pixel-edges))
953 ;; compute offset in units of pixels
954 (let ((bubbles--image-size
955 (car (image-size (car bubbles--images) t))))
956 (setq bubbles--col-offset
957 (list
958 (max 0 (/ (- (nth 2 (window-inside-pixel-edges))
959 (nth 0 (window-inside-pixel-edges))
960 (* ( + bubbles--image-size 2) ;; margin
961 (bubbles--grid-width))) 2))))
962 (setq bubbles--row-offset
963 (list
964 (max 0 (/ (- (nth 3 (window-inside-pixel-edges))
965 (nth 1 (window-inside-pixel-edges))
966 (* (+ bubbles--image-size 1) ;; margin
967 (bubbles--grid-height))) 2))))))
968 (t
969 ;; compute offset in units of chars
970 (setq bubbles--col-offset
971 (max 0 (/ (- (window-width)
972 (bubbles--grid-width)) 2)))
973 (setq bubbles--row-offset
974 (max 0 (/ (- (window-height)
975 (bubbles--grid-height) 2) 2))))))
976
977(defun bubbles--remove-overlays ()
978 "Remove all overlays."
979 (if (fboundp 'remove-overlays)
980 (remove-overlays)))
981
982(defun bubbles--initialize ()
983 "Initialize Bubbles game."
984 (bubbles--initialize-faces)
985 (bubbles--initialize-images)
986 (bubbles--remove-overlays)
987
988 (switch-to-buffer (get-buffer-create "*bubbles*"))
989 (bubbles--compute-offsets)
990 (let ((inhibit-read-only t))
991 (set-buffer-modified-p nil)
992 (erase-buffer)
993 (insert " ")
994 (add-text-properties
995 (point-min) (point) (list 'intangible t 'display
996 (cons 'space
997 (list :height bubbles--row-offset))))
998 (insert "\n")
999 (let ((max-char (length (bubbles--colors))))
1000 (dotimes (i (bubbles--grid-height))
1001 (let ((p (point)))
1002 (insert " ")
1003 (add-text-properties
1004 p (point) (list 'intangible t
1005 'display (cons 'space
1006 (list :width
1007 bubbles--col-offset)))))
1008 (dotimes (j (bubbles--grid-width))
1009 (let* ((index (random max-char))
1010 (char (nth index bubbles-chars)))
1011 (insert char)
1012 (add-text-properties (1- (point)) (point) (list 'index index))))
1013 (insert "\n"))
1014 (insert "\n ")
1015 (add-text-properties
1016 (1- (point)) (point) (list 'intangible t 'display
1017 (cons 'space
1018 (list :width bubbles--col-offset)))))
1019 (put-text-property (point-min) (point-max) 'pointer 'arrow))
1020 (bubbles-mode)
1021 (bubbles--reset-score)
1022 (bubbles--update-faces-or-images)
1023 (bubbles--goto 0 0))
1024
1025(defun bubbles--initialize-faces ()
1026 "Prepare faces for playing `bubbles'."
1027 (copy-face 'default 'bubbles--highlight-face)
1028 (set-face-background 'bubbles--highlight-face "#8080f4")
1029 (when (display-color-p)
1030 (setq bubbles--faces
1031 (mapcar (lambda (color)
1032 (let ((fname (intern (format "bubbles--face-%s" color))))
1033 (unless (facep fname)
1034 (copy-face 'default fname)
1035 (set-face-foreground fname color))
1036 fname))
1037 (bubbles--colors)))))
1038
1039(defsubst bubbles--row (pos)
1040 "Return row of point POS."
1041 (save-excursion
1042 (goto-char pos)
1043 (beginning-of-line)
1044 (1- (count-lines (point-min) (point)))))
1045
1046(defsubst bubbles--col (pos)
1047 "Return column of point POS."
1048 (save-excursion
1049 (goto-char pos)
1050 (1- (current-column))))
1051
1052(defun bubbles--goto (row col)
1053 "Move point to bubble at coordinates ROW and COL."
1054 (if (or (< row 0)
1055 (< col 0)
1056 (>= row (bubbles--grid-height))
1057 (>= col (bubbles--grid-width)))
1058 ;; Error! return nil
1059 nil
1060 ;; go
1061 (goto-char (point-min))
1062 (forward-line (1+ row))
1063 (forward-char (1+ col))
1064 (point)))
1065
1066(defun bubbles--char-at (row col)
1067 "Return character at bubble ROW and COL."
1068 (save-excursion
1069 (if (bubbles--goto row col)
1070 (char-after (point))
1071 nil)))
1072
1073(defun bubbles--mark-direct-neighbours (row col char)
1074 "Mark direct neighbours of bubble at ROW COL with same CHAR."
1075 (save-excursion
1076 (let ((count 0))
1077 (when (and (bubbles--goto row col)
1078 (eq char (char-after (point)))
1079 (not (get-text-property (point) 'active)))
1080 (add-text-properties (point) (1+ (point))
1081 '(active t face 'bubbles--highlight-face))
1082 (setq count (+ 1
1083 (bubbles--mark-direct-neighbours row (1+ col) char)
1084 (bubbles--mark-direct-neighbours row (1- col) char)
1085 (bubbles--mark-direct-neighbours (1+ row) col char)
1086 (bubbles--mark-direct-neighbours (1- row) col char))))
1087 count)))
1088
1089(defun bubbles--mark-neighbourhood (&optional pos)
1090 "Mark neighbourhood of point.
1091Use optional parameter POS instead of point if given."
1092 (when bubbles--playing
1093 (unless pos (setq pos (point)))
1094 (condition-case err
1095 (let ((char (char-after pos))
1096 (inhibit-read-only t)
1097 (row (bubbles--row (point)))
1098 (col (bubbles--col (point))))
1099 (add-text-properties (point-min) (point-max)
1100 '(face default active nil))
1101 (let ((count 0))
1102 (when (and row col (not (eq char (bubbles--empty-char))))
1103 (setq count (bubbles--mark-direct-neighbours row col char))
1104 (unless (> count 1)
1105 (add-text-properties (point-min) (point-max)
1106 '(face default active nil))
1107 (setq count 0)))
1108 (bubbles--update-neighbourhood-score count))
1109 (put-text-property (point-min) (point-max) 'pointer 'arrow)
1110 (bubbles--update-faces-or-images)
1111 (sit-for 0))
1112 (error (message "Bubbles: Internal error %s" err)))))
1113
1114(defun bubbles--neighbourhood-available ()
1115 "Return t if another valid neighbourhood is available."
1116 (catch 'found
1117 (save-excursion
1118 (dotimes (i (bubbles--grid-height))
1119 (dotimes (j (bubbles--grid-width))
1120 (let ((c (bubbles--char-at i j)))
1121 (if (and (not (eq c (bubbles--empty-char)))
1122 (or (eq c (bubbles--char-at (1+ i) j))
1123 (eq c (bubbles--char-at i (1+ j)))))
1124 (throw 'found t)))))
1125 nil)))
1126
1127(defun bubbles--count ()
1128 "Count remaining bubbles."
1129 (let ((count 0))
1130 (save-excursion
1131 (dotimes (i (bubbles--grid-height))
1132 (dotimes (j (bubbles--grid-width))
1133 (let ((c (bubbles--char-at i j)))
1134 (if (not (eq c (bubbles--empty-char)))
1135 (setq count (1+ count)))))))
1136 count))
1137
1138(defun bubbles--reset-score ()
1139 "Reset bubbles score."
1140 (setq bubbles--neighbourhood-score 0
1141 bubbles--score 0)
1142 (bubbles--update-score))
1143
1144(defun bubbles--update-score ()
1145 "Calculate and display new bubble score."
1146 (setq bubbles--score (+ bubbles--score bubbles--neighbourhood-score))
1147 (bubbles--show-scores))
1148
1149(defun bubbles--update-neighbourhood-score (size)
1150 "Calculate and display score of active neighbourhood from its SIZE."
1151 (if (> size 1)
1152 (setq bubbles--neighbourhood-score (expt (- size 1) 2))
1153 (setq bubbles--neighbourhood-score 0))
1154 (bubbles--show-scores))
1155
1156(defun bubbles--show-scores ()
1157 "Display current scores."
1158 (save-excursion
1159 (goto-char (or (next-single-property-change (point-min) 'status)
1160 (point-max)))
1161 (let ((inhibit-read-only t)
1162 (pos (point)))
1163 (delete-region (point) (point-max))
1164 (insert (format "Selected: %4d\n" bubbles--neighbourhood-score))
1165 (insert " ")
1166 (add-text-properties (1- (point)) (point)
1167 (list 'intangible t 'display
1168 (cons 'space
1169 (list :width bubbles--col-offset))))
1170 (insert (format "Score: %4d" bubbles--score))
1171 (put-text-property pos (point) 'status t))))
1172
1173(defun bubbles--game-over ()
1174 "Finish bubbles game."
1175 (bubbles--update-faces-or-images)
1176 (setq bubbles--playing nil
1177 bubbles--save-data nil)
1178 ;; add bonus if all bubbles were removed
1179 (when (= 0 (bubbles--count))
1180 (setq bubbles--score (+ bubbles--score (* (bubbles--grid-height)
1181 (bubbles--grid-width))))
1182 (bubbles--show-scores))
1183 ;; Game over message
1184 (goto-char (point-max))
1185 (let* ((inhibit-read-only t))
1186 (insert "\n ")
1187 (add-text-properties (1- (point)) (point)
1188 (list 'intangible t 'display
1189 (cons 'space
1190 (list :width bubbles--col-offset))))
1191 (insert "Game Over!"))
1192 ;; save score
1193 (gamegrid-add-score (format "bubbles-%s-%d-%d-%d-scores"
1194 (symbol-name (bubbles--shift-mode))
1195 (length (bubbles--colors))
1196 (bubbles--grid-width) (bubbles--grid-height))
1197 bubbles--score))
1198
1199(defun bubbles-plop ()
1200 "Remove active bubbles region."
1201 (interactive)
1202 (when (and bubbles--playing
1203 (> bubbles--neighbourhood-score 0))
1204 (setq bubbles--save-data (list bubbles--score (buffer-string)))
1205 (setq buffer-undo-list '(apply bubbles-undo . nil))
1206 (let ((inhibit-read-only t))
1207 ;; blank out current neighbourhood
1208 (let ((row (bubbles--row (point)))
1209 (col (bubbles--col (point))))
1210 (goto-char (point-max))
1211 (while (not (bobp))
1212 (backward-char)
1213 (while (get-text-property (point) 'active)
1214 (delete-char 1)
1215 (insert (bubbles--empty-char))
1216 (add-text-properties (1- (point)) (point) (list 'removed t
1217 'index -1))))
1218 (bubbles--goto row col))
1219 ;; show new score
1220 (bubbles--update-score)
1221 ;; update display and wait
1222 (bubbles--update-faces-or-images)
1223 (sit-for 0)
1224 (sleep-for 0.2)
1225 (discard-input)
1226 ;; drop down
1227 (let ((something-dropped nil))
1228 (save-excursion
1229 (dotimes (i (bubbles--grid-height))
1230 (dotimes (j (bubbles--grid-width))
1231 (bubbles--goto i j)
1232 (while (get-text-property (point) 'removed)
1233 (setq something-dropped (or (bubbles--shift 'top i j)
1234 something-dropped))))))
1235 ;; update display and wait
1236 (bubbles--update-faces-or-images)
1237 (when something-dropped
1238 (sit-for 0)))
1239 (discard-input)
1240 ;; shift to left
1241 (put-text-property (point-min) (point-max) 'removed nil)
1242 (save-excursion
1243 (goto-char (point-min))
1244 (let ((removed-string (format "%c" (bubbles--empty-char))))
1245 (while (search-forward removed-string nil t)
1246 (put-text-property (1- (point)) (point) 'removed t))))
1247 (let ((shifted nil))
1248 (cond ((eq (bubbles--shift-mode) 'always)
1249 (save-excursion
1250 (dotimes (i (bubbles--grid-height))
1251 (dotimes (j (bubbles--grid-width))
1252 (bubbles--goto i j)
1253 (while (get-text-property (point) 'removed)
1254 (setq shifted (or (bubbles--shift 'right i j) shifted))))))
1255 (bubbles--update-faces-or-images)
1256 (sleep-for 0.5))
1257 (t ;; default shift-mode
1258 (save-excursion
1259 (dotimes (j (bubbles--grid-width))
1260 (bubbles--goto (1- (bubbles--grid-height)) j)
1261 (let ((shifted-cols 0))
1262 (while (get-text-property (point) 'removed)
1263 (setq shifted-cols (1+ shifted-cols))
1264 (bubbles--shift 'right (1- (bubbles--grid-height)) j))
1265 (dotimes (k shifted-cols)
1266 (let ((i (- (bubbles--grid-height) 2)))
1267 (while (>= i 0)
1268 (setq shifted (or (bubbles--shift 'right i j) shifted))
1269 (setq i (1- i))))))))))
1270 (when shifted
1271 ;;(sleep-for 0.5)
1272 (bubbles--update-faces-or-images)
1273 (sit-for 0)))
1274 (put-text-property (point-min) (point-max) 'removed nil)
1275 (unless (bubbles--neighbourhood-available)
1276 (bubbles--game-over)))))
1277
1278(defun bubbles-undo ()
1279 "Undo last move."
1280 (interactive)
1281 (when bubbles--save-data
1282 (let ((inhibit-read-only t)
1283 (pos (point)))
1284 (erase-buffer)
1285 (insert (cadr bubbles--save-data))
1286 (bubbles--update-faces-or-images)
1287 (setq bubbles--score (car bubbles--save-data))
1288 (goto-char pos))))
1289
1290(defun bubbles--shift (from row col)
1291 "Move bubbles FROM one side to position ROW COL.
1292Return t if new char is non-empty."
1293 (save-excursion
1294 (when (bubbles--goto row col)
1295 (let ((char-org (char-after (point)))
1296 (char-new (bubbles--empty-char))
1297 (removed nil)
1298 (trow row)
1299 (tcol col)
1300 (index -1))
1301 (cond ((eq from 'top)
1302 (setq trow (1- row)))
1303 ((eq from 'left)
1304 (setq tcol (1- col)))
1305 ((eq from 'right)
1306 (setq tcol (1+ col))))
1307 (save-excursion
1308 (when (bubbles--goto trow tcol)
1309 (setq char-new (char-after (point)))
1310 (setq removed (get-text-property (point) 'removed))
1311 (setq index (get-text-property (point) 'index))
1312 (bubbles--shift from trow tcol)))
1313 (insert char-new)
1314 (delete-char 1)
1315 (add-text-properties (1- (point)) (point) (list 'index index
1316 'removed removed))
1317 (not (eq char-new (bubbles--empty-char)))))))
1318
1319(defun bubbles--initialize-images ()
1320 "Prepare images for playing `bubbles'."
1321 (when (and (display-images-p)
1322 (not (eq bubbles-graphics-theme 'ascii)))
1323 (let ((template (case bubbles-graphics-theme
1324 ('circles bubbles--image-template-circle)
1325 ('balls bubbles--image-template-ball)
1326 ('squares bubbles--image-template-square)
1327 ('diamonds bubbles--image-template-diamond)
1328 ('emacs bubbles--image-template-emacs))))
1329 (setq bubbles--empty-image
1330 (create-image (replace-regexp-in-string
1331 "^\"\\(.*\\)\t.*c .*\",$"
1332 "\"\\1\tc #FFFFFF\"," template)
1333 'xpm t
1334 ;;:mask 'heuristic
1335 :margin '(2 . 1)))
1336 (setq bubbles--images
1337 (mapcar (lambda (color)
1338 (let* ((rgb (color-values color))
1339 (red (nth 0 rgb))
1340 (green (nth 1 rgb))
1341 (blue (nth 2 rgb)))
1342 (with-temp-buffer
1343 (insert template)
1344 (goto-char (point-min))
1345 (re-search-forward
1346 "^\"[0-9]+ [0-9]+ \\(.*?\\) .*\",$" nil t)
1347 (goto-char (point-min))
1348 (while (re-search-forward
1349 "^\"\\(.*\\)\t.*c \\(#.*\\)\",$" nil t)
1350 (let* ((crgb (color-values (match-string 2)))
1351 (r (nth 0 crgb))
1352 (g (nth 1 crgb))
1353 (b (nth 2 crgb))
1354 (brightness (/ (+ r g b) 3.0 256 256))
1355 (val (sin (* brightness (/ pi 2))))
1356 (rr (* red val))
1357 (gg (* green val))
1358 (bb (* blue val))
1359 ;;(rr (/ (+ red r) 2))
1360 ;;(gg (/ (+ green g) 2))
1361 ;;(bb (/ (+ blue b) 2))
1362 (color (format "#%02x%02x%02x"
1363 (/ rr 256) (/ gg 256)
1364 (/ bb 256))))
1365 (replace-match (format "\"\\1\tc %s\","
1366 (upcase color)))))
1367 (create-image (buffer-string) 'xpm t
1368 :margin '(2 . 1)
1369 ;;:mask 'heuristic
1370 ))))
1371 (bubbles--colors))))
1372 ;; check images
1373 (setq bubbles--images-ok bubbles--empty-image)
1374 (mapc (lambda (elt)
1375 (setq bubbles--images-ok (and bubbles--images-ok elt)))
1376 bubbles--images)))
1377
1378(defun bubbles--update-faces-or-images ()
1379 "Update faces and/or images, depending on graphics mode."
1380 (bubbles--set-faces)
1381 (bubbles--show-images))
1382
1383(defun bubbles--set-faces ()
1384 "Update faces in the bubbles buffer."
1385 (unless (and (display-images-p)
1386 bubbles--images-ok
1387 (not (eq bubbles-graphics-theme 'ascii)))
1388 (when (display-color-p)
1389 (save-excursion
1390 (let ((inhibit-read-only t))
1391 (dotimes (i (bubbles--grid-height))
1392 (dotimes (j (bubbles--grid-width))
1393 (bubbles--goto i j)
1394 (let* ((index (get-text-property (point) 'index))
1395 (face (nth index bubbles--faces))
1396 (fg-col (face-foreground face)))
1397 (when (get-text-property (point) 'active)
1398 (set-face-foreground 'bubbles--highlight-face "#ff0000")
1399 (setq face 'bubbles--highlight-face))
1400 (put-text-property (point) (1+ (point))
1401 'face face)))))))))
1402
1403(defun bubbles--show-images ()
1404 "Update images in the bubbles buffer."
1405 (bubbles--remove-overlays)
1406 (if (and (display-images-p)
1407 bubbles--images-ok
1408 (not (eq bubbles-graphics-theme 'ascii)))
1409 (save-excursion
1410 (goto-char (point-min))
1411 (forward-line 1)
1412 (let ((inhibit-read-only t)
1413 char)
1414 (dotimes (i (bubbles--grid-height))
1415 (dotimes (j (bubbles--grid-width))
1416 (forward-char 1)
1417 (let ((index (get-text-property (point) 'index)))
1418 (let ((img bubbles--empty-image))
1419 (if (>= index 0)
1420 (setq img (nth index bubbles--images)))
1421 (put-text-property (point) (1+ (point))
1422 'display (cons img nil)))))
1423 (forward-line 1))))
1424 (save-excursion
1425 (let ((inhibit-read-only t))
1426 (goto-char (point-min))
1427 (while (not (eobp))
1428 (let ((disp-prop (get-text-property (point) 'display)))
1429 (if (and (listp disp-prop)
1430 (listp (car disp-prop))
1431 (eq (caar disp-prop) 'image))
1432 (put-text-property (point) (1+ (point)) 'display nil))
1433 (forward-char 1)))
1434 (put-text-property (point-min) (point-max) 'pointer 'arrow)))))
1435
1436(provide 'bubbles)
1437
1438;;; bubbles.el ends here