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