1 ;;; 5x5.el --- simple little puzzle game
3 ;; Copyright (C) 1999-2011 Free Software Foundation, Inc.
5 ;; Author: Dave Pearson <davep@davep.org>
6 ;; Maintainer: Dave Pearson <davep@davep.org>
8 ;; Keywords: games puzzles
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/>.
27 ;; The aim of 5x5 is to fill in all the squares. If you need any more of an
28 ;; explanation you probably shouldn't play the game.
32 ;; o The code for updating the grid needs to be re-done. At the moment it
33 ;; simply re-draws the grid every time a move is made.
35 ;; o Look into tarting up the display with color. gamegrid.el looks
36 ;; interesting, perhaps that is the way to go?
40 ;; Ralf Fassel <ralf@akutech.de> for his help and introduction to writing an
43 ;; Pascal Q. Porcupine <joshagam@cs.nmsu.edu> for inspiring the animated
56 "5x5 - Silly little puzzle game."
60 (defcustom 5x5-grid-size
5
61 "Size of the playing area."
65 (defcustom 5x5-x-scale
4
66 "X scaling factor for drawing the grid."
70 (defcustom 5x5-y-scale
3
71 "Y scaling factor for drawing the grid."
75 (defcustom 5x5-animate-delay
.01
76 "Delay in seconds when animating a solution crack."
80 (defcustom 5x5-hassle-me t
81 "Should 5x5 ask you when you want to do a destructive operation?"
85 (defcustom 5x5-mode-hook nil
86 "Hook run on starting 5x5."
90 ;; Non-customize variables.
96 "X position of cursor.")
99 "Y position of cursor.")
104 (defvar 5x5-cracking nil
105 "Are we in cracking mode?")
107 (defvar 5x5-buffer-name
"*5x5*"
108 "Name of the 5x5 play buffer.")
111 (let ((map (make-sparse-keymap)))
112 (suppress-keymap map t
)
113 (define-key map
"?" #'describe-mode
)
114 (define-key map
"\r" #'5x5-flip-current
)
115 (define-key map
" " #'5x5-flip-current
)
116 (define-key map
[up] #'5x5-up)
117 (define-key map [down] #'5x5-down)
118 (define-key map [left] #'5x5-left)
119 (define-key map [tab] #'5x5-right)
120 (define-key map [right] #'5x5-right)
121 (define-key map [(control a)] #'5x5-bol)
122 (define-key map [(control e)] #'5x5-eol)
123 (define-key map [(control p)] #'5x5-up)
124 (define-key map [(control n)] #'5x5-down)
125 (define-key map [(control b)] #'5x5-left)
126 (define-key map [(control f)] #'5x5-right)
127 (define-key map [home] #'5x5-bol)
128 (define-key map [end] #'5x5-eol)
129 (define-key map [prior] #'5x5-first)
130 (define-key map [next] #'5x5-last)
131 (define-key map "r" #'5x5-randomize)
132 (define-key map [(control c) (control r)] #'5x5-crack-randomly)
133 (define-key map [(control c) (control c)] #'5x5-crack-mutating-current)
134 (define-key map [(control c) (control b)] #'5x5-crack-mutating-best)
135 (define-key map [(control c) (control x)] #'5x5-crack-xor-mutate)
136 (define-key map "n" #'5x5-new-game)
137 (define-key map "q" #'5x5-quit-game)
139 "Local keymap for the 5x5 game.")
143 (easy-menu-define 5x5-mode-menu 5x5-mode-map "5x5 menu."
145 ["New game" 5x5-new-game t]
146 ["Random game" 5x5-randomize t]
147 ["Quit game" 5x5-quit-game t]
149 ["Crack randomly" 5x5-crack-randomly t]
150 ["Crack mutating current" 5x5-crack-mutating-current t]
151 ["Crack mutating best" 5x5-crack-mutating-best t]
152 ["Crack with xor mutate" 5x5-crack-xor-mutate t]))
154 ;; Gameplay functions.
156 (put '5x5-mode 'mode-class 'special)
159 "A mode for playing `5x5'.
161 The key bindings for 5x5-mode are:
164 (kill-all-local-variables)
165 (use-local-map 5x5-mode-map)
166 (setq major-mode '5x5-mode
168 (run-mode-hooks '5x5-mode-hook)
169 (setq buffer-read-only t
171 (buffer-disable-undo))
174 (defun 5x5 (&optional size)
177 The object of 5x5 is very simple, by moving around the grid and flipping
178 squares you must fill the grid.
180 5x5 keyboard bindings are:
182 Flip \\[5x5-flip-current]
184 Move down \\[5x5-down]
185 Move left \\[5x5-left]
186 Move right \\[5x5-right]
187 Start new game \\[5x5-new-game]
188 New game with random grid \\[5x5-randomize]
189 Random cracker \\[5x5-crack-randomly]
190 Mutate current cracker \\[5x5-crack-mutating-current]
191 Mutate best cracker \\[5x5-crack-mutating-best]
192 Mutate xor cracker \\[5x5-crack-xor-mutate]
193 Quit current game \\[5x5-quit-game]"
196 (setq 5x5-cracking nil)
198 (setq 5x5-grid-size size))
199 (switch-to-buffer 5x5-buffer-name)
200 (if (or (not 5x5-grid) (not (= 5x5-grid-size (length (aref 5x5-grid 0)))))
202 (5x5-draw-grid (list 5x5-grid))
203 (5x5-position-cursor)
206 (defun 5x5-new-game ()
207 "Start a new game of `5x5'."
209 (when (if (called-interactively-p 'interactive)
210 (5x5-y-or-n-p "Start a new game? ") t)
211 (setq 5x5-x-pos (/ 5x5-grid-size 2)
212 5x5-y-pos (/ 5x5-grid-size 2)
214 5x5-grid (5x5-make-move (5x5-make-new-grid) 5x5-y-pos 5x5-x-pos))
215 (5x5-draw-grid (list 5x5-grid))
216 (5x5-position-cursor)))
218 (defun 5x5-quit-game ()
219 "Quit the current game of `5x5'."
221 (kill-buffer 5x5-buffer-name))
223 (defun 5x5-make-new-grid ()
224 "Create and return a new `5x5' grid structure."
225 (let ((grid (make-vector 5x5-grid-size nil)))
226 (loop for y from 0 to (1- 5x5-grid-size) do
227 (aset grid y (make-vector 5x5-grid-size nil)))
230 (defun 5x5-cell (grid y x)
231 "Return the value of the cell in GRID at location X,Y."
232 (aref (aref grid y) x))
234 (defun 5x5-set-cell (grid y x value)
235 "Set the value of cell X,Y in GRID to VALUE."
236 (aset (aref grid y) x value))
238 (defun 5x5-flip-cell (grid y x)
239 "Flip the value of cell X,Y in GRID."
240 (5x5-set-cell grid y x (not (5x5-cell grid y x))))
242 (defun 5x5-copy-grid (grid)
243 "Make a new copy of GRID."
244 (let ((copy (5x5-make-new-grid)))
245 (loop for y from 0 to (1- 5x5-grid-size) do
246 (loop for x from 0 to (1- 5x5-grid-size) do
247 (5x5-set-cell copy y x (5x5-cell grid y x))))
250 (defun 5x5-make-move (grid row col)
251 "Make a move on GRID at row ROW and column COL."
252 (5x5-flip-cell grid row col)
254 (5x5-flip-cell grid (1- row) col))
255 (if (< row (- 5x5-grid-size 1))
256 (5x5-flip-cell grid (1+ row) col))
258 (5x5-flip-cell grid row (1- col)))
259 (if (< col (- 5x5-grid-size 1))
260 (5x5-flip-cell grid row (1+ col)))
263 (defun 5x5-row-value (row)
264 "Get the \"on-value\" for grid row ROW."
265 (loop for y from 0 to (1- 5x5-grid-size) sum (if (aref row y) 1 0)))
267 (defun 5x5-grid-value (grid)
268 "Get the \"on-value\" for grid GRID."
269 (loop for y from 0 to (1- 5x5-grid-size) sum (5x5-row-value (aref grid y))))
271 (defun 5x5-draw-grid-end ()
272 "Draw the top/bottom of the grid."
274 (loop for x from 0 to (1- 5x5-grid-size) do
275 (insert "-" (make-string 5x5-x-scale ?-)))
278 (defun 5x5-draw-grid (grids)
279 "Draw the grids GRIDS into the current buffer."
280 (let ((buffer-read-only nil))
282 (loop for grid in grids do (5x5-draw-grid-end))
284 (loop for y from 0 to (1- 5x5-grid-size) do
285 (loop for lines from 0 to (1- 5x5-y-scale) do
286 (loop for grid in grids do
287 (loop for x from 0 to (1- 5x5-grid-size) do
288 (insert (if (zerop x) "| " " ")
289 (make-string 5x5-x-scale
290 (if (5x5-cell grid y x) ?# ?.))))
293 (loop for grid in grids do (5x5-draw-grid-end))
295 (insert (format "On: %d Moves: %d" (5x5-grid-value (car grids)) 5x5-moves))))
297 (defun 5x5-position-cursor ()
298 "Position the cursor on the grid."
299 (goto-char (point-min))
300 (forward-line (1+ (* 5x5-y-pos 5x5-y-scale)))
301 (goto-char (+ (point) (* 5x5-x-pos 5x5-x-scale) (+ 5x5-x-pos 1) 1)))
303 (defun 5x5-made-move ()
304 "Keep track of how many moves have been made."
307 (defun 5x5-make-random-grid ()
308 "Make a random grid."
309 (let ((grid (5x5-make-new-grid)))
310 (loop for y from 0 to (1- 5x5-grid-size) do
311 (loop for x from 0 to (1- 5x5-grid-size) do
312 (if (zerop (random 2))
313 (5x5-flip-cell grid y x))))
316 ;; Cracker functions.
319 (defun 5x5-crack-randomly ()
320 "Attempt to crack 5x5 using random solutions."
322 (5x5-crack #'5x5-make-random-solution))
325 (defun 5x5-crack-mutating-current ()
326 "Attempt to crack 5x5 by mutating the current solution."
328 (5x5-crack #'5x5-make-mutate-current))
331 (defun 5x5-crack-mutating-best ()
332 "Attempt to crack 5x5 by mutating the best solution."
334 (5x5-crack #'5x5-make-mutate-best))
337 (defun 5x5-crack-xor-mutate ()
338 "Attempt to crack 5x5 by xoring the current and best solution.
341 (5x5-crack #'5x5-make-xor-with-mutation))
344 (defun 5x5-crack (breeder)
345 "Attempt to find a solution for 5x5.
347 5x5-crack takes the argument BREEDER which should be a function that takes
348 two parameters, the first will be a grid vector array that is the current
349 solution and the second will be the best solution so far. The function
350 should return a grid vector array that is the new solution."
352 (interactive "aBreeder function: ")
354 (setq 5x5-cracking t)
355 (let* ((best-solution (5x5-make-random-grid))
356 (current-solution best-solution)
357 (best-result (5x5-make-new-grid))
358 (current-result (5x5-make-new-grid))
359 (target (* 5x5-grid-size 5x5-grid-size)))
360 (while (and (< (5x5-grid-value best-result) target)
361 (not (input-pending-p)))
362 (setq current-result (5x5-play-solution current-solution best-solution))
363 (if (> (5x5-grid-value current-result) (5x5-grid-value best-result))
364 (setq best-solution current-solution
365 best-result current-result))
366 (setq current-solution (funcall breeder
367 (5x5-copy-grid current-solution)
368 (5x5-copy-grid best-solution)))))
369 (setq 5x5-cracking nil))
371 (defun 5x5-make-random-solution (&rest _ignore)
372 "Make a random solution."
373 (5x5-make-random-grid))
375 (defun 5x5-make-mutate-current (current _best)
376 "Mutate the current solution."
377 (5x5-mutate-solution current))
379 (defun 5x5-make-mutate-best (_current best)
380 "Mutate the best solution."
381 (5x5-mutate-solution best))
383 (defun 5x5-make-xor-with-mutation (current best)
384 "Xor current and best solution then mutate the result."
385 (let ((xored (5x5-make-new-grid)))
386 (loop for y from 0 to (1- 5x5-grid-size) do
387 (loop for x from 0 to (1- 5x5-grid-size) do
388 (5x5-set-cell xored y x
389 (5x5-xor (5x5-cell current y x)
390 (5x5-cell best y x)))))
391 (5x5-mutate-solution xored)))
393 (defun 5x5-mutate-solution (solution)
394 "Randomly flip bits in the solution."
395 (loop for y from 0 to (1- 5x5-grid-size) do
396 (loop for x from 0 to (1- 5x5-grid-size) do
397 (if (= (random (/ (* 5x5-grid-size 5x5-grid-size) 2))
398 (/ (/ (* 5x5-grid-size 5x5-grid-size) 2) 2))
399 (5x5-flip-cell solution y x))))
402 (defun 5x5-play-solution (solution best)
403 "Play a solution on an empty grid. This destroys the current game
404 in progress because it is an animated attempt."
406 (let ((inhibit-quit t))
407 (loop for y from 0 to (1- 5x5-grid-size) do
408 (loop for x from 0 to (1- 5x5-grid-size) do
411 (if (5x5-cell solution y x)
413 (5x5-draw-grid (list 5x5-grid solution best))
414 (5x5-position-cursor)
415 (sit-for 5x5-animate-delay))))
418 ;; Keyboard response functions.
420 (defun 5x5-flip-current ()
421 "Make a move on the current cursor location."
423 (setq 5x5-grid (5x5-make-move 5x5-grid 5x5-y-pos 5x5-x-pos))
426 (5x5-draw-grid (list 5x5-grid)))
427 (5x5-position-cursor)
428 (when (= (5x5-grid-value 5x5-grid) (* 5x5-grid-size 5x5-grid-size))
430 (message "You win!")))
435 (unless (zerop 5x5-y-pos)
437 (5x5-position-cursor)))
442 (unless (= 5x5-y-pos (1- 5x5-grid-size))
444 (5x5-position-cursor)))
449 (unless (zerop 5x5-x-pos)
451 (5x5-position-cursor)))
456 (unless (= 5x5-x-pos (1- 5x5-grid-size))
458 (5x5-position-cursor)))
461 "Move to beginning of line."
464 (5x5-position-cursor))
467 "Move to end of line."
469 (setq 5x5-x-pos (1- 5x5-grid-size))
470 (5x5-position-cursor))
473 "Move to the first cell."
477 (5x5-position-cursor))
480 "Move to the last cell."
482 (setq 5x5-x-pos (1- 5x5-grid-size)
483 5x5-y-pos (1- 5x5-grid-size))
484 (5x5-position-cursor))
486 (defun 5x5-randomize ()
487 "Randomize the grid."
489 (when (5x5-y-or-n-p "Start a new game with a random grid? ")
490 (setq 5x5-x-pos (/ 5x5-grid-size 2)
491 5x5-y-pos (/ 5x5-grid-size 2)
493 5x5-grid (5x5-make-random-grid))
495 (5x5-draw-grid (list 5x5-grid)))
496 (5x5-position-cursor)))
501 "Boolean exclusive-or of X and Y."
502 (and (or x y) (not (and x y))))
504 (defun 5x5-y-or-n-p (prompt)
505 "5x5 wrapper for `y-or-n-p' which respects the `5x5-hassle-me' setting."