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