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