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 of the License, or
15 ;; (at your option) 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. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26
27 ;; Bubbles is a puzzle game. Its goal is to remove as many bubbles as
28 ;; possible in as few moves as possible.
29
30 ;; Bubbles is an implementation of the "Same Game", similar to "Same
31 ;; GNOME" and many others, see <http://en.wikipedia.org/wiki/SameGame>.
32
33 ;; Installation
34 ;; ------------
35
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)
39
40 ;; ======================================================================
41
42 ;;; History:
43
44 ;; 0.5 (2007-09-14)
45 ;; - Minor bugfixes.
46
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.
55
56 ;; 0.3 (2007-03-11)
57 ;; - Renamed shift modes and thus names of score files. All
58 ;; highscores 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
63
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.
74
75 ;; 0.1 (2007-02-11)
76 ;; Initial release. Tested with GNU Emacs 22.0.93 and 21.4.1.
77
78 ;; ======================================================================
79
80 ;;; Code:
81
82 (defconst bubbles-version "0.5" "Version number of bubbles.el.")
83
84 (require 'gamegrid)
85 (require 'cl)
86
87 ;; User options
88
89 ;; Careful with that axe, Eugene! Order does matter in the custom
90 ;; section below.
91
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)
103
104 (defun bubbles-set-game-easy ()
105 "Set game theme to 'easy'."
106 (interactive)
107 (setq bubbles-game-theme 'easy)
108 (bubbles))
109
110 (defun bubbles-set-game-medium ()
111 "Set game theme to 'medium'."
112 (interactive)
113 (setq bubbles-game-theme 'medium)
114 (bubbles))
115
116 (defun bubbles-set-game-difficult ()
117 "Set game theme to 'difficult'."
118 (interactive)
119 (setq bubbles-game-theme 'difficult)
120 (bubbles))
121
122 (defun bubbles-set-game-hard ()
123 "Set game theme to 'hard'."
124 (interactive)
125 (setq bubbles-game-theme 'hard)
126 (bubbles))
127
128 (defun bubbles-set-game-userdefined ()
129 "Set game theme to 'user-defined'."
130 (interactive)
131 (setq bubbles-game-theme 'user-defined)
132 (bubbles))
133
134 (defgroup bubbles nil
135 "Bubbles, a puzzle game."
136 :group 'games)
137
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)
150
151 (defconst bubbles--grid-small '(10 . 10)
152 "Predefined small bubbles grid.")
153
154 (defconst bubbles--grid-medium '(15 . 10)
155 "Predefined medium bubbles grid.")
156
157 (defconst bubbles--grid-large '(20 . 15)
158 "Predefined large bubbles grid.")
159
160 (defconst bubbles--grid-huge '(30 . 20)
161 "Predefined huge bubbles grid.")
162
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)
174
175 (defconst bubbles--colors-2 '("orange" "violet")
176 "Predefined bubbles color list with two colors.")
177
178 (defconst bubbles--colors-3 '("lightblue" "palegreen" "pink")
179 "Predefined bubbles color list with three colors.")
180
181 (defconst bubbles--colors-4 '("firebrick" "sea green" "steel blue" "chocolate")
182 "Predefined bubbles color list with four colors.")
183
184 (defconst bubbles--colors-5 '("firebrick" "sea green" "steel blue"
185 "sandy brown" "bisque3")
186 "Predefined bubbles color list with five colors.")
187
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)
200
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)
208
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)
216 )
217 :group 'bubbles)
218
219 (defcustom bubbles-mode-hook nil
220 "Hook run by Bubbles mode."
221 :group 'bubbles
222 :type 'hook)
223
224 (defun bubbles-customize ()
225 "Open customization buffer for bubbles."
226 (interactive)
227 (customize-group 'bubbles))
228
229 ;; ======================================================================
230 ;; internal variables
231
232 (defvar bubbles--score 0
233 "Current Bubbles score.")
234
235 (defvar bubbles--neighbourhood-score 0
236 "Score of active bubbles neighbourhood.")
237
238 (defvar bubbles--faces nil
239 "List of currently used faces.")
240
241 (defvar bubbles--playing nil
242 "Play status indicator.")
243
244 (defvar bubbles--empty-image nil
245 "Image used for removed bubbles (empty grid cells).")
246
247 (defvar bubbles--images nil
248 "List of images for bubbles.")
249
250 (defvar bubbles--images-ok nil
251 "Indicate whether images have been created successfully.")
252
253 (defvar bubbles--col-offset 0
254 "Horizontal offset for centering the bubbles grid.")
255
256 (defvar bubbles--row-offset 0
257 "Vertical offset for centering the bubbles grid.")
258
259 (defvar bubbles--save-data nil
260 "List containing bubbles save data (SCORE BUFFERCONTENTS).")
261
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 \" ...... \"};")
288
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\"};")
315
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\"};")
342
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. \"};")
500
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.. \"};")
715
716 ;; ======================================================================
717 ;; Functions
718
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))))
732
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))))
746
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)))
760
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)))
774
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))
782
783 (defsubst bubbles--empty-char ()
784 "The character used for removed bubbles (empty grid cells)."
785 ? )
786
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))
792
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))
799
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))
806
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))
813
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))
820
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))
827
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.")
848
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.")
872
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.")
898
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.")
920
921 (defun bubbles-mode ()
922 "Major mode for playing bubbles.
923 \\{bubbles-mode-map}"
924 (kill-all-local-variables)
925 (use-local-map bubbles-mode-map)
926 (setq major-mode 'bubbles-mode)
927 (setq mode-name "Bubbles")
928 (setq buffer-read-only t)
929 (buffer-disable-undo)
930 (setq buffer-undo-list t)
931 (force-mode-line-update)
932 (redisplay)
933 (add-hook 'post-command-hook 'bubbles--mark-neighbourhood t t)
934 (run-hooks 'bubbles-mode-hook))
935
936 ;;;###autoload
937 (defun bubbles ()
938 "Play Bubbles game."
939 (interactive)
940 (switch-to-buffer (get-buffer-create "*bubbles*"))
941 (when (or (not bubbles--playing)
942 (y-or-n-p "Start new game? "))
943 (setq bubbles--save-data nil)
944 (setq bubbles--playing t)
945 (bubbles--initialize)))
946
947 (defun bubbles-quit ()
948 "Quit Bubbles."
949 (interactive)
950 (message "bubbles-quit")
951 (bury-buffer))
952
953 (defun bubbles--compute-offsets ()
954 "Update horizontal and vertical offsets for centering the bubbles grid.
955 Set `bubbles--col-offset' and `bubbles--row-offset'."
956 (cond ((and (display-images-p)
957 bubbles--images-ok
958 (not (eq bubbles-graphics-theme 'ascii))
959 (fboundp 'window-inside-pixel-edges))
960 ;; compute offset in units of pixels
961 (let ((bubbles--image-size
962 (car (image-size (car bubbles--images) t))))
963 (setq bubbles--col-offset
964 (list
965 (max 0 (/ (- (nth 2 (window-inside-pixel-edges))
966 (nth 0 (window-inside-pixel-edges))
967 (* ( + bubbles--image-size 2) ;; margin
968 (bubbles--grid-width))) 2))))
969 (setq bubbles--row-offset
970 (list
971 (max 0 (/ (- (nth 3 (window-inside-pixel-edges))
972 (nth 1 (window-inside-pixel-edges))
973 (* (+ bubbles--image-size 1) ;; margin
974 (bubbles--grid-height))) 2))))))
975 (t
976 ;; compute offset in units of chars
977 (setq bubbles--col-offset
978 (max 0 (/ (- (window-width)
979 (bubbles--grid-width)) 2)))
980 (setq bubbles--row-offset
981 (max 0 (/ (- (window-height)
982 (bubbles--grid-height) 2) 2))))))
983
984 (defun bubbles--remove-overlays ()
985 "Remove all overlays."
986 (if (fboundp 'remove-overlays)
987 (remove-overlays)))
988
989 (defun bubbles--initialize ()
990 "Initialize Bubbles game."
991 (bubbles--initialize-faces)
992 (bubbles--initialize-images)
993 (bubbles--remove-overlays)
994
995 (switch-to-buffer (get-buffer-create "*bubbles*"))
996 (bubbles--compute-offsets)
997 (let ((inhibit-read-only t))
998 (set-buffer-modified-p nil)
999 (erase-buffer)
1000 (insert " ")
1001 (add-text-properties
1002 (point-min) (point) (list 'intangible t 'display
1003 (cons 'space
1004 (list :height bubbles--row-offset))))
1005 (insert "\n")
1006 (let ((max-char (length (bubbles--colors))))
1007 (dotimes (i (bubbles--grid-height))
1008 (let ((p (point)))
1009 (insert " ")
1010 (add-text-properties
1011 p (point) (list 'intangible t
1012 'display (cons 'space
1013 (list :width
1014 bubbles--col-offset)))))
1015 (dotimes (j (bubbles--grid-width))
1016 (let* ((index (random max-char))
1017 (char (nth index bubbles-chars)))
1018 (insert char)
1019 (add-text-properties (1- (point)) (point) (list 'index index))))
1020 (insert "\n"))
1021 (insert "\n ")
1022 (add-text-properties
1023 (1- (point)) (point) (list 'intangible t 'display
1024 (cons 'space
1025 (list :width bubbles--col-offset)))))
1026 (put-text-property (point-min) (point-max) 'pointer 'arrow))
1027 (bubbles-mode)
1028 (bubbles--reset-score)
1029 (bubbles--update-faces-or-images)
1030 (bubbles--goto 0 0)
1031 (setq buffer-undo-list t)
1032 (force-mode-line-update)
1033 (redisplay))
1034
1035 (defun bubbles--initialize-faces ()
1036 "Prepare faces for playing `bubbles'."
1037 (copy-face 'default 'bubbles--highlight-face)
1038 (set-face-background 'bubbles--highlight-face "#8080f4")
1039 (when (display-color-p)
1040 (setq bubbles--faces
1041 (mapcar (lambda (color)
1042 (let ((fname (intern (format "bubbles--face-%s" color))))
1043 (unless (facep fname)
1044 (copy-face 'default fname)
1045 (set-face-foreground fname color))
1046 fname))
1047 (bubbles--colors)))))
1048
1049 (defsubst bubbles--row (pos)
1050 "Return row of point POS."
1051 (save-excursion
1052 (goto-char pos)
1053 (beginning-of-line)
1054 (1- (count-lines (point-min) (point)))))
1055
1056 (defsubst bubbles--col (pos)
1057 "Return column of point POS."
1058 (save-excursion
1059 (goto-char pos)
1060 (1- (current-column))))
1061
1062 (defun bubbles--goto (row col)
1063 "Move point to bubble at coordinates ROW and COL."
1064 (if (or (< row 0)
1065 (< col 0)
1066 (>= row (bubbles--grid-height))
1067 (>= col (bubbles--grid-width)))
1068 ;; Error! return nil
1069 nil
1070 ;; go
1071 (goto-char (point-min))
1072 (forward-line (1+ row))
1073 (forward-char (1+ col))
1074 (point)))
1075
1076 (defun bubbles--char-at (row col)
1077 "Return character at bubble ROW and COL."
1078 (save-excursion
1079 (if (bubbles--goto row col)
1080 (char-after (point))
1081 nil)))
1082
1083 (defun bubbles--mark-direct-neighbours (row col char)
1084 "Mark direct neighbours of bubble at ROW COL with same CHAR."
1085 (save-excursion
1086 (let ((count 0))
1087 (when (and (bubbles--goto row col)
1088 (eq char (char-after (point)))
1089 (not (get-text-property (point) 'active)))
1090 (add-text-properties (point) (1+ (point))
1091 '(active t face 'bubbles--highlight-face))
1092 (setq count (+ 1
1093 (bubbles--mark-direct-neighbours row (1+ col) char)
1094 (bubbles--mark-direct-neighbours row (1- col) char)
1095 (bubbles--mark-direct-neighbours (1+ row) col char)
1096 (bubbles--mark-direct-neighbours (1- row) col char))))
1097 count)))
1098
1099 (defun bubbles--mark-neighbourhood (&optional pos)
1100 "Mark neighbourhood of point.
1101 Use optional parameter POS instead of point if given."
1102 (when bubbles--playing
1103 (unless pos (setq pos (point)))
1104 (condition-case err
1105 (let ((char (char-after pos))
1106 (inhibit-read-only t)
1107 (row (bubbles--row (point)))
1108 (col (bubbles--col (point))))
1109 (add-text-properties (point-min) (point-max)
1110 '(face default active nil))
1111 (let ((count 0))
1112 (when (and row col (not (eq char (bubbles--empty-char))))
1113 (setq count (bubbles--mark-direct-neighbours row col char))
1114 (unless (> count 1)
1115 (add-text-properties (point-min) (point-max)
1116 '(face default active nil))
1117 (setq count 0)))
1118 (bubbles--update-neighbourhood-score count))
1119 (put-text-property (point-min) (point-max) 'pointer 'arrow)
1120 (bubbles--update-faces-or-images)
1121 (sit-for 0))
1122 (error (message "Bubbles: Internal error %s" err)))))
1123
1124 (defun bubbles--neighbourhood-available ()
1125 "Return t if another valid neighbourhood is available."
1126 (catch 'found
1127 (save-excursion
1128 (dotimes (i (bubbles--grid-height))
1129 (dotimes (j (bubbles--grid-width))
1130 (let ((c (bubbles--char-at i j)))
1131 (if (and (not (eq c (bubbles--empty-char)))
1132 (or (eq c (bubbles--char-at (1+ i) j))
1133 (eq c (bubbles--char-at i (1+ j)))))
1134 (throw 'found t)))))
1135 nil)))
1136
1137 (defun bubbles--count ()
1138 "Count remaining bubbles."
1139 (let ((count 0))
1140 (save-excursion
1141 (dotimes (i (bubbles--grid-height))
1142 (dotimes (j (bubbles--grid-width))
1143 (let ((c (bubbles--char-at i j)))
1144 (if (not (eq c (bubbles--empty-char)))
1145 (setq count (1+ count)))))))
1146 count))
1147
1148 (defun bubbles--reset-score ()
1149 "Reset bubbles score."
1150 (setq bubbles--neighbourhood-score 0
1151 bubbles--score 0)
1152 (bubbles--update-score))
1153
1154 (defun bubbles--update-score ()
1155 "Calculate and display new bubble score."
1156 (setq bubbles--score (+ bubbles--score bubbles--neighbourhood-score))
1157 (bubbles--show-scores))
1158
1159 (defun bubbles--update-neighbourhood-score (size)
1160 "Calculate and display score of active neighbourhood from its SIZE."
1161 (if (> size 1)
1162 (setq bubbles--neighbourhood-score (expt (- size 1) 2))
1163 (setq bubbles--neighbourhood-score 0))
1164 (bubbles--show-scores))
1165
1166 (defun bubbles--show-scores ()
1167 "Display current scores."
1168 (save-excursion
1169 (goto-char (or (next-single-property-change (point-min) 'status)
1170 (point-max)))
1171 (let ((inhibit-read-only t)
1172 (pos (point)))
1173 (delete-region (point) (point-max))
1174 (insert (format "Selected: %4d\n" bubbles--neighbourhood-score))
1175 (insert " ")
1176 (add-text-properties (1- (point)) (point)
1177 (list 'intangible t 'display
1178 (cons 'space
1179 (list :width bubbles--col-offset))))
1180 (insert (format "Score: %4d" bubbles--score))
1181 (put-text-property pos (point) 'status t))))
1182
1183 (defun bubbles--game-over ()
1184 "Finish bubbles game."
1185 (bubbles--update-faces-or-images)
1186 (setq bubbles--playing nil
1187 bubbles--save-data nil)
1188 ;; add bonus if all bubbles were removed
1189 (when (= 0 (bubbles--count))
1190 (setq bubbles--score (+ bubbles--score (* (bubbles--grid-height)
1191 (bubbles--grid-width))))
1192 (bubbles--show-scores))
1193 ;; Game over message
1194 (goto-char (point-max))
1195 (let* ((inhibit-read-only t))
1196 (insert "\n ")
1197 (add-text-properties (1- (point)) (point)
1198 (list 'intangible t 'display
1199 (cons 'space
1200 (list :width bubbles--col-offset))))
1201 (insert "Game Over!"))
1202 ;; save score
1203 (gamegrid-add-score (format "bubbles-%s-%d-%d-%d-scores"
1204 (symbol-name (bubbles--shift-mode))
1205 (length (bubbles--colors))
1206 (bubbles--grid-width) (bubbles--grid-height))
1207 bubbles--score))
1208
1209 (defun bubbles-plop ()
1210 "Remove active bubbles region."
1211 (interactive)
1212 (when (and bubbles--playing
1213 (> bubbles--neighbourhood-score 0))
1214 (setq bubbles--save-data (list bubbles--score (buffer-string)))
1215 (let ((inhibit-read-only t))
1216 ;; blank out current neighbourhood
1217 (let ((row (bubbles--row (point)))
1218 (col (bubbles--col (point))))
1219 (goto-char (point-max))
1220 (while (not (bobp))
1221 (backward-char)
1222 (while (get-text-property (point) 'active)
1223 (delete-char 1)
1224 (insert (bubbles--empty-char))
1225 (add-text-properties (1- (point)) (point) (list 'removed t
1226 'index -1))))
1227 (bubbles--goto row col))
1228 ;; show new score
1229 (bubbles--update-score)
1230 ;; update display and wait
1231 (bubbles--update-faces-or-images)
1232 (sit-for 0)
1233 (sleep-for 0.2)
1234 (discard-input)
1235 ;; drop down
1236 (let ((something-dropped nil))
1237 (save-excursion
1238 (dotimes (i (bubbles--grid-height))
1239 (dotimes (j (bubbles--grid-width))
1240 (bubbles--goto i j)
1241 (while (get-text-property (point) 'removed)
1242 (setq something-dropped (or (bubbles--shift 'top i j)
1243 something-dropped))))))
1244 ;; update display and wait
1245 (bubbles--update-faces-or-images)
1246 (when something-dropped
1247 (sit-for 0)))
1248 (discard-input)
1249 ;; shift to left
1250 (put-text-property (point-min) (point-max) 'removed nil)
1251 (save-excursion
1252 (goto-char (point-min))
1253 (let ((removed-string (format "%c" (bubbles--empty-char))))
1254 (while (search-forward removed-string nil t)
1255 (put-text-property (1- (point)) (point) 'removed t))))
1256 (let ((shifted nil))
1257 (cond ((eq (bubbles--shift-mode) 'always)
1258 (save-excursion
1259 (dotimes (i (bubbles--grid-height))
1260 (dotimes (j (bubbles--grid-width))
1261 (bubbles--goto i j)
1262 (while (get-text-property (point) 'removed)
1263 (setq shifted (or (bubbles--shift 'right i j)
1264 shifted))))))
1265 (bubbles--update-faces-or-images)
1266 (sleep-for 0.5))
1267 (t ;; default shift-mode
1268 (save-excursion
1269 (dotimes (j (bubbles--grid-width))
1270 (bubbles--goto (1- (bubbles--grid-height)) j)
1271 (let ((shifted-cols 0))
1272 (while (get-text-property (point) 'removed)
1273 (setq shifted-cols (1+ shifted-cols))
1274 (bubbles--shift 'right (1- (bubbles--grid-height)) j))
1275 (dotimes (k shifted-cols)
1276 (let ((i (- (bubbles--grid-height) 2)))
1277 (while (>= i 0)
1278 (setq shifted (or (bubbles--shift 'right i j)
1279 shifted))
1280 (setq i (1- i))))))))))
1281 (when shifted
1282 ;;(sleep-for 0.5)
1283 (bubbles--update-faces-or-images)
1284 (sit-for 0)))
1285 (put-text-property (point-min) (point-max) 'removed nil)
1286 (unless (bubbles--neighbourhood-available)
1287 (bubbles--game-over)))
1288 ;; undo
1289 (setq buffer-undo-list '((apply bubbles-undo . nil)))
1290 (force-mode-line-update)
1291 (redisplay)))
1292
1293 (defun bubbles-undo ()
1294 "Undo last move."
1295 (interactive)
1296 (when bubbles--save-data
1297 (let ((inhibit-read-only t)
1298 (pos (point)))
1299 (erase-buffer)
1300 (insert (cadr bubbles--save-data))
1301 (bubbles--update-faces-or-images)
1302 (setq bubbles--score (car bubbles--save-data))
1303 (goto-char pos))
1304 (setq buffer-undo-list t)
1305 (force-mode-line-update)
1306 (redisplay)))
1307
1308 (defun bubbles--shift (from row col)
1309 "Move bubbles FROM one side to position ROW COL.
1310 Return t if new char is non-empty."
1311 (save-excursion
1312 (when (bubbles--goto row col)
1313 (let ((char-org (char-after (point)))
1314 (char-new (bubbles--empty-char))
1315 (removed nil)
1316 (trow row)
1317 (tcol col)
1318 (index -1))
1319 (cond ((eq from 'top)
1320 (setq trow (1- row)))
1321 ((eq from 'left)
1322 (setq tcol (1- col)))
1323 ((eq from 'right)
1324 (setq tcol (1+ col))))
1325 (save-excursion
1326 (when (bubbles--goto trow tcol)
1327 (setq char-new (char-after (point)))
1328 (setq removed (get-text-property (point) 'removed))
1329 (setq index (get-text-property (point) 'index))
1330 (bubbles--shift from trow tcol)))
1331 (insert char-new)
1332 (delete-char 1)
1333 (add-text-properties (1- (point)) (point) (list 'index index
1334 'removed removed))
1335 (not (eq char-new (bubbles--empty-char)))))))
1336
1337 (defun bubbles--initialize-images ()
1338 "Prepare images for playing `bubbles'."
1339 (when (and (display-images-p)
1340 (not (eq bubbles-graphics-theme 'ascii)))
1341 (let ((template (case bubbles-graphics-theme
1342 ('circles bubbles--image-template-circle)
1343 ('balls bubbles--image-template-ball)
1344 ('squares bubbles--image-template-square)
1345 ('diamonds bubbles--image-template-diamond)
1346 ('emacs bubbles--image-template-emacs))))
1347 (setq bubbles--empty-image
1348 (create-image (replace-regexp-in-string
1349 "^\"\\(.*\\)\t.*c .*\",$"
1350 "\"\\1\tc None\"," template)
1351 'xpm t
1352 ;;:mask 'heuristic
1353 :margin '(2 . 1)))
1354 (setq bubbles--images
1355 (mapcar (lambda (color)
1356 (let* ((rgb (color-values color))
1357 (red (nth 0 rgb))
1358 (green (nth 1 rgb))
1359 (blue (nth 2 rgb)))
1360 (with-temp-buffer
1361 (insert template)
1362 (goto-char (point-min))
1363 (re-search-forward
1364 "^\"[0-9]+ [0-9]+ \\(.*?\\) .*\",$" nil t)
1365 (goto-char (point-min))
1366 (while (re-search-forward
1367 "^\"\\(.*\\)\t.*c \\(#.*\\)\",$" nil t)
1368 (let* ((crgb (color-values (match-string 2)))
1369 (r (nth 0 crgb))
1370 (g (nth 1 crgb))
1371 (b (nth 2 crgb))
1372 (brightness (/ (+ r g b) 3.0 256 256))
1373 (val (sin (* brightness (/ pi 2))))
1374 (rr (* red val))
1375 (gg (* green val))
1376 (bb (* blue val))
1377 ;;(rr (/ (+ red r) 2))
1378 ;;(gg (/ (+ green g) 2))
1379 ;;(bb (/ (+ blue b) 2))
1380 (color (format "#%02x%02x%02x"
1381 (/ rr 256) (/ gg 256)
1382 (/ bb 256))))
1383 (replace-match (format "\"\\1\tc %s\","
1384 (upcase color)))))
1385 (create-image (buffer-string) 'xpm t
1386 :margin '(2 . 1)
1387 ;;:mask 'heuristic
1388 ))))
1389 (bubbles--colors))))
1390 ;; check images
1391 (setq bubbles--images-ok bubbles--empty-image)
1392 (mapc (lambda (elt)
1393 (setq bubbles--images-ok (and bubbles--images-ok elt)))
1394 bubbles--images)))
1395
1396 (defun bubbles--update-faces-or-images ()
1397 "Update faces and/or images, depending on graphics mode."
1398 (bubbles--set-faces)
1399 (bubbles--show-images))
1400
1401 (defun bubbles--set-faces ()
1402 "Update faces in the bubbles buffer."
1403 (unless (and (display-images-p)
1404 bubbles--images-ok
1405 (not (eq bubbles-graphics-theme 'ascii)))
1406 (when (display-color-p)
1407 (save-excursion
1408 (let ((inhibit-read-only t))
1409 (dotimes (i (bubbles--grid-height))
1410 (dotimes (j (bubbles--grid-width))
1411 (bubbles--goto i j)
1412 (let* ((index (get-text-property (point) 'index))
1413 (face (nth index bubbles--faces))
1414 (fg-col (face-foreground face)))
1415 (when (get-text-property (point) 'active)
1416 (set-face-foreground 'bubbles--highlight-face "#ff0000")
1417 (setq face 'bubbles--highlight-face))
1418 (put-text-property (point) (1+ (point))
1419 'face face)))))))))
1420
1421 (defun bubbles--show-images ()
1422 "Update images in the bubbles buffer."
1423 (bubbles--remove-overlays)
1424 (if (and (display-images-p)
1425 bubbles--images-ok
1426 (not (eq bubbles-graphics-theme 'ascii)))
1427 (save-excursion
1428 (goto-char (point-min))
1429 (forward-line 1)
1430 (let ((inhibit-read-only t)
1431 char)
1432 (dotimes (i (bubbles--grid-height))
1433 (dotimes (j (bubbles--grid-width))
1434 (forward-char 1)
1435 (let ((index (or (get-text-property (point) 'index) -1)))
1436 (let ((img bubbles--empty-image))
1437 (if (>= index 0)
1438 (setq img (nth index bubbles--images)))
1439 (put-text-property (point) (1+ (point))
1440 'display (cons img nil)))))
1441 (forward-line 1))))
1442 (save-excursion
1443 (let ((inhibit-read-only t))
1444 (goto-char (point-min))
1445 (while (not (eobp))
1446 (let ((disp-prop (get-text-property (point) 'display)))
1447 (if (and (listp disp-prop)
1448 (listp (car disp-prop))
1449 (eq (caar disp-prop) 'image))
1450 (put-text-property (point) (1+ (point)) 'display nil))
1451 (forward-char 1)))
1452 (put-text-property (point-min) (point-max) 'pointer 'arrow)))))
1453
1454 (provide 'bubbles)
1455
1456 ;; arch-tag: 2cd7237a-b0ad-400d-a7fd-75f676dceb70
1457 ;;; bubbles.el ends here