13337
|
1 ;;; solitaire.el --- game of solitaire in Emacs Lisp
|
12766
|
2
|
|
3 ;; Copyright (C) 1994 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; Author: Jan Schormann <Jan.Schormann@informatik.uni-oldenburg.de>
|
|
6 ;; Created: Fri afternoon, Jun 3, 1994
|
|
7 ;; Keywords: games
|
|
8
|
|
9 ;; This file is part of GNU Emacs.
|
|
10
|
|
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
12 ;; it under the terms of the GNU General Public License as published by
|
|
13 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
14 ;; any later version.
|
|
15
|
|
16 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19 ;; GNU General Public License for more details.
|
|
20
|
|
21 ;; You should have received a copy of the GNU General Public License
|
14183
|
22 ;; along with GNU Emacs; see the file COPYING. If not, write to
|
|
23 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
24 ;; Boston, MA 02111-1307, USA.
|
12766
|
25
|
|
26 ;;; Commentary:
|
|
27
|
|
28 ;; This mode is for playing a well-known game of solitaire
|
|
29 ;; in which you jump pegs across other pegs.
|
|
30
|
|
31 ;; The game itself is somehow self-explanatory. Read the help text to
|
|
32 ;; solitaire, and try it.
|
|
33
|
|
34 ;;; Code:
|
|
35
|
|
36 (defvar solitaire-mode-map nil
|
|
37 "Keymap for playing solitaire.")
|
|
38
|
|
39 (if solitaire-mode-map
|
|
40 ()
|
|
41 (setq solitaire-mode-map (make-sparse-keymap))
|
|
42 (suppress-keymap solitaire-mode-map t)
|
|
43 (define-key solitaire-mode-map "\C-f" 'solitaire-right)
|
|
44 (define-key solitaire-mode-map "\C-b" 'solitaire-left)
|
|
45 (define-key solitaire-mode-map "\C-p" 'solitaire-up)
|
|
46 (define-key solitaire-mode-map "\C-n" 'solitaire-down)
|
|
47 (define-key solitaire-mode-map [return] 'solitaire-move)
|
|
48 (substitute-key-definition 'undo 'solitaire-undo
|
|
49 solitaire-mode-map global-map)
|
|
50 (define-key solitaire-mode-map " " 'solitaire-do-check)
|
|
51 (define-key solitaire-mode-map "q" 'solitaire-quit)
|
|
52
|
|
53 (define-key solitaire-mode-map [right] 'solitaire-right)
|
|
54 (define-key solitaire-mode-map [left] 'solitaire-left)
|
|
55 (define-key solitaire-mode-map [up] 'solitaire-up)
|
|
56 (define-key solitaire-mode-map [down] 'solitaire-down)
|
|
57
|
|
58 (define-key solitaire-mode-map [S-right] 'solitaire-move-right)
|
|
59 (define-key solitaire-mode-map [S-left] 'solitaire-move-left)
|
|
60 (define-key solitaire-mode-map [S-up] 'solitaire-move-up)
|
|
61 (define-key solitaire-mode-map [S-down] 'solitaire-move-down)
|
|
62
|
|
63 (define-key solitaire-mode-map [kp-6] 'solitaire-right)
|
|
64 (define-key solitaire-mode-map [kp-4] 'solitaire-left)
|
|
65 (define-key solitaire-mode-map [kp-8] 'solitaire-up)
|
|
66 (define-key solitaire-mode-map [kp-2] 'solitaire-down)
|
|
67 (define-key solitaire-mode-map [kp-5] 'solitaire-center-point)
|
|
68
|
|
69 (define-key solitaire-mode-map [S-kp-6] 'solitaire-move-right)
|
|
70 (define-key solitaire-mode-map [S-kp-4] 'solitaire-move-left)
|
|
71 (define-key solitaire-mode-map [S-kp-8] 'solitaire-move-up)
|
|
72 (define-key solitaire-mode-map [S-kp-2] 'solitaire-move-down)
|
|
73
|
|
74 (define-key solitaire-mode-map [kp-enter] 'solitaire-move)
|
|
75 (define-key solitaire-mode-map [kp-0] 'solitaire-undo)
|
|
76
|
|
77 ;; spoil it with s ;)
|
|
78 (define-key solitaire-mode-map [?s] 'solitaire-solve)
|
|
79
|
|
80 ;; (define-key solitaire-mode-map [kp-0] 'solitaire-hint) - Not yet provided ;)
|
|
81 )
|
|
82
|
|
83 ;; Solitaire mode is suitable only for specially formatted data.
|
|
84 (put 'solitaire-mode 'mode-class 'special)
|
|
85
|
|
86 (defun solitaire-mode ()
|
|
87 "Major mode for playing solitaire.
|
|
88 To learn how to play solitaire, see the documentation for function
|
|
89 `solitaire'.
|
|
90 \\<solitaire-mode-map>
|
|
91 The usual mnemonic keys move the cursor around the board; in addition,
|
|
92 \\[solitaire-move] is a prefix character for actually moving a stone on the board."
|
|
93 (interactive)
|
|
94 (kill-all-local-variables)
|
|
95 (use-local-map solitaire-mode-map)
|
|
96 (setq truncate-lines t)
|
|
97 (setq major-mode 'solitaire-mode)
|
|
98 (setq mode-name "Solitaire")
|
|
99 (run-hooks 'solitaire-mode-hook))
|
|
100
|
|
101 (defvar solitaire-stones 0
|
|
102 "Counter for the stones that are still there.")
|
|
103
|
|
104 (defvar solitaire-center nil
|
|
105 "Center of the board.")
|
|
106
|
|
107 (defvar solitaire-start nil
|
|
108 "Upper left corner of the board.")
|
|
109
|
|
110 (defvar solitaire-start-x nil)
|
|
111 (defvar solitaire-start-y nil)
|
|
112
|
|
113 (defvar solitaire-end nil
|
|
114 "Lower right corner of the board.")
|
|
115
|
|
116 (defvar solitaire-end-x nil)
|
|
117 (defvar solitaire-end-y nil)
|
|
118
|
|
119 (defvar solitaire-auto-eval t
|
|
120 "*Non-nil means check for possible moves after each major change.
|
|
121 This takes a while, so switch this on if you like to be informed when
|
|
122 the game is over, or off, if you are working on a slow machine.")
|
|
123
|
|
124 (defconst solitaire-valid-directions
|
|
125 '(solitaire-left solitaire-right solitaire-up solitaire-down))
|
|
126
|
|
127 ;;;###autoload
|
|
128 (defun solitaire (arg)
|
|
129 "Play Solitaire.
|
|
130
|
|
131 To play Solitaire, type \\[solitaire].
|
|
132 \\<solitaire-mode-map>
|
|
133 Move around the board using the cursor keys.
|
|
134 Move stones using \\[solitaire-move] followed by a direction key.
|
|
135 Undo moves using \\[solitaire-undo].
|
|
136 Check for possible moves using \\[solitaire-do-check].
|
14274
|
137 \(The variable solitaire-auto-eval controls whether to automatically
|
12766
|
138 check after each move or undo)
|
|
139
|
|
140 What is Solitaire?
|
|
141
|
|
142 I don't know who invented this game, but it seems to be rather old and
|
16419
|
143 its origin seems be northern Africa. Here's how to play:
|
12766
|
144 Initially, the board will look similar to this:
|
|
145
|
|
146 Le Solitaire
|
|
147 ============
|
|
148
|
|
149 o o o
|
|
150
|
|
151 o o o
|
|
152
|
|
153 o o o o o o o
|
|
154
|
|
155 o o o . o o o
|
|
156
|
|
157 o o o o o o o
|
|
158
|
|
159 o o o
|
|
160
|
|
161 o o o
|
|
162
|
|
163 Let's call the o's stones and the .'s holes. One stone fits into one
|
|
164 hole. As you can see, all holes but one are occupied by stones. The
|
|
165 aim of the game is to get rid of all but one stone, leaving that last
|
|
166 one in the middle of the board if you're cool.
|
|
167
|
|
168 A stone can be moved if there is another stone next to it, and a hole
|
|
169 after that one. Thus there must be three fields in a row, either
|
|
170 horizontally or vertically, up, down, left or right, which look like
|
|
171 this: o o .
|
|
172
|
|
173 Then the first stone is moved to the hole, jumping over the second,
|
|
174 which therefore is taken away. The above thus `evaluates' to: . . o
|
|
175
|
|
176 That's all. Here's the board after two moves:
|
|
177
|
|
178 o o o
|
|
179
|
|
180 . o o
|
|
181
|
|
182 o o . o o o o
|
|
183
|
|
184 o . o o o o o
|
|
185
|
|
186 o o o o o o o
|
|
187
|
|
188 o o o
|
|
189
|
|
190 o o o
|
|
191
|
|
192 Pick your favourite shortcuts:
|
|
193
|
|
194 \\{solitaire-mode-map}"
|
|
195
|
|
196 (interactive "P")
|
|
197 (switch-to-buffer "*Solitaire*")
|
|
198 (solitaire-mode)
|
|
199 (setq buffer-read-only t)
|
|
200 (setq solitaire-stones 32)
|
|
201 (solitaire-insert-board)
|
|
202 (solitaire-build-modeline)
|
|
203 (goto-char (point-max))
|
|
204 (setq solitaire-center (search-backward "."))
|
|
205 (setq buffer-undo-list (list (point)))
|
|
206 (set-buffer-modified-p nil))
|
|
207
|
|
208 (defun solitaire-build-modeline ()
|
|
209 (setq mode-line-format
|
|
210 (list "" "---" 'mode-line-buffer-identification
|
|
211 (if (< 1 solitaire-stones)
|
|
212 (format "--> There are %d stones left <--" solitaire-stones)
|
|
213 "------")
|
|
214 'global-mode-string " %[(" 'mode-name 'minor-mode-alist "%n"
|
|
215 ")%]-%-"))
|
|
216 (force-mode-line-update))
|
|
217
|
|
218 (defun solitaire-insert-board ()
|
|
219 (let* ((buffer-read-only nil)
|
|
220 (w (window-width))
|
|
221 (h (window-height))
|
|
222 (hsep (cond ((> w 26) " ")
|
|
223 ((> w 20) " ")
|
|
224 (t "")))
|
|
225 (vsep (cond ((> h 17) "\n\n")
|
|
226 (t "\n")))
|
|
227 (indent (make-string (/ (- w 7 (* 6 (length hsep))) 2) ?\ )))
|
|
228 (erase-buffer)
|
|
229 (insert (make-string (/ (- h 7 (if (> h 12) 3 0)
|
|
230 (* 6 (1- (length vsep)))) 2) ?\n))
|
|
231 (if (or (string= vsep "\n\n") (> h 12))
|
|
232 (progn
|
|
233 (insert (format "%sLe Solitaire\n" indent))
|
|
234 (insert (format "%s============\n\n" indent))))
|
|
235 (insert indent)
|
|
236 (setq solitaire-start (point))
|
|
237 (setq solitaire-start-x (current-column))
|
|
238 (setq solitaire-start-y (solitaire-current-line))
|
|
239 (insert (format " %s %so%so%so%s" hsep hsep hsep hsep vsep))
|
|
240 (insert (format "%s %s %so%so%so%s" indent hsep hsep hsep hsep vsep))
|
|
241 (insert (format "%so%so%so%so%so%so%so%s" indent hsep hsep hsep hsep hsep hsep vsep))
|
|
242 (insert (format "%so%so%so%s" indent hsep hsep hsep))
|
|
243 (setq solitaire-center (point))
|
|
244 (insert (format ".%so%so%so%s" hsep hsep hsep vsep))
|
|
245 (insert (format "%so%so%so%so%so%so%so%s" indent hsep hsep hsep hsep hsep hsep vsep))
|
|
246 (insert (format "%s %s %so%so%so%s" indent hsep hsep hsep hsep vsep))
|
|
247 (insert (format "%s %s %so%so%so%s %s " indent hsep hsep hsep hsep hsep hsep))
|
|
248 (setq solitaire-end (point))
|
|
249 (setq solitaire-end-x (current-column))
|
|
250 (setq solitaire-end-y (solitaire-current-line))
|
|
251 ))
|
|
252
|
|
253 (defun solitaire-right ()
|
|
254 (interactive)
|
|
255 (let ((start (point)))
|
|
256 (forward-char)
|
|
257 (while (= ?\ (following-char))
|
|
258 (forward-char))
|
|
259 (if (or (= 0 (following-char))
|
|
260 (= ?\ (following-char))
|
|
261 (= ?\n (following-char)))
|
|
262 (goto-char start))))
|
|
263
|
|
264 (defun solitaire-left ()
|
|
265 (interactive)
|
|
266 (let ((start (point)))
|
|
267 (backward-char)
|
|
268 (while (= ?\ (following-char))
|
|
269 (backward-char))
|
|
270 (if (or (= 0 (preceding-char))
|
|
271 (= ?\ (following-char))
|
|
272 (= ?\n (following-char)))
|
|
273 (goto-char start))))
|
|
274
|
|
275 (defun solitaire-up ()
|
|
276 (interactive)
|
|
277 (let ((start (point))
|
|
278 (c (current-column)))
|
|
279 (forward-line -1)
|
|
280 (move-to-column c)
|
|
281 (while (and (= ?\n (following-char))
|
|
282 (forward-line -1)
|
|
283 (move-to-column c)
|
|
284 (not (bolp))))
|
|
285 (if (or (= 0 (preceding-char))
|
|
286 (= ?\ (following-char))
|
|
287 (= ?\= (following-char))
|
|
288 (= ?\n (following-char)))
|
|
289 (goto-char start)
|
|
290 )))
|
|
291
|
|
292 (defun solitaire-down ()
|
|
293 (interactive)
|
|
294 (let ((start (point))
|
|
295 (c (current-column)))
|
|
296 (forward-line 1)
|
|
297 (move-to-column c)
|
|
298 (while (and (= ?\n (following-char))
|
|
299 (forward-line 1)
|
|
300 (move-to-column c)
|
|
301 (not (eolp))))
|
|
302 (if (or (= 0 (following-char))
|
|
303 (= ?\ (following-char))
|
|
304 (= ?\n (following-char)))
|
|
305 (goto-char start))))
|
|
306
|
|
307 (defun solitaire-center-point ()
|
|
308 (interactive)
|
|
309 (goto-char solitaire-center))
|
|
310
|
|
311 (defun solitaire-move-right () (interactive) (solitaire-move '[right]))
|
|
312 (defun solitaire-move-left () (interactive) (solitaire-move '[left]))
|
|
313 (defun solitaire-move-up () (interactive) (solitaire-move '[up]))
|
|
314 (defun solitaire-move-down () (interactive) (solitaire-move '[down]))
|
|
315
|
|
316 (defun solitaire-possible-move (movesymbol)
|
|
317 "Check if a move is possible from current point in the specified direction.
|
|
318 MOVESYMBOL specifies the direction.
|
|
319 Returns either a string, indicating cause of contraindication, or a
|
|
320 list containing three numbers: starting field, skipped field (from
|
|
321 which a stone will be taken away) and target."
|
|
322
|
|
323 (save-excursion
|
|
324 (let (move)
|
|
325 (fset 'move movesymbol)
|
|
326 (if (memq movesymbol solitaire-valid-directions)
|
|
327 (let ((start (point))
|
|
328 (skip (progn (move) (point)))
|
|
329 (target (progn (move) (point))))
|
|
330 (if (= skip target)
|
|
331 "Off Board!"
|
|
332 (if (or (/= ?o (char-after start))
|
|
333 (/= ?o (char-after skip))
|
|
334 (/= ?. (char-after target)))
|
|
335 "Wrong move!"
|
|
336 (list start skip target))))
|
|
337 "Not a valid direction"))))
|
|
338
|
|
339 (defun solitaire-move (dir)
|
|
340 "Pseudo-prefix command to move a stone in Solitaire."
|
|
341 (interactive "kMove where? ")
|
|
342 (let* ((class (solitaire-possible-move (lookup-key solitaire-mode-map dir)))
|
|
343 (buffer-read-only nil))
|
|
344 (if (stringp class)
|
|
345 (error class)
|
|
346 (let ((start (car class))
|
|
347 (skip (car (cdr class)))
|
|
348 (target (car (cdr (cdr class)))))
|
|
349 (goto-char start)
|
|
350 (delete-char 1)
|
|
351 (insert ?.)
|
|
352 (goto-char skip)
|
|
353 (delete-char 1)
|
|
354 (insert ?.)
|
|
355 (goto-char target)
|
|
356 (delete-char 1)
|
|
357 (insert ?o)
|
|
358 (goto-char target)
|
|
359 (setq solitaire-stones (1- solitaire-stones))
|
|
360 (solitaire-build-modeline)
|
|
361 (if solitaire-auto-eval (solitaire-do-check))))))
|
|
362
|
|
363 (defun solitaire-undo (arg)
|
|
364 "Undo a move in Solitaire."
|
|
365 (interactive "P")
|
|
366 (let ((buffer-read-only nil))
|
|
367 (undo arg))
|
|
368 (save-excursion
|
|
369 (setq solitaire-stones
|
|
370 (let ((count 0))
|
|
371 (goto-char solitaire-end)
|
|
372 (while (search-backward "o" solitaire-start 'done)
|
|
373 (and (>= (current-column) solitaire-start-x)
|
|
374 (<= (current-column) solitaire-end-x)
|
|
375 (>= (solitaire-current-line) solitaire-start-y)
|
|
376 (<= (solitaire-current-line) solitaire-end-y)
|
|
377 (setq count (1+ count))))
|
|
378 count)))
|
|
379 (solitaire-build-modeline)
|
|
380 (if solitaire-auto-eval (solitaire-do-check)))
|
|
381
|
|
382 (defun solitaire-check ()
|
|
383 (save-excursion
|
|
384 (if (= 1 solitaire-stones)
|
|
385 0
|
|
386 (goto-char solitaire-end)
|
|
387 (let ((count 0))
|
|
388 (while (search-backward "o" solitaire-start 'done)
|
|
389 (and (>= (current-column) solitaire-start-x)
|
|
390 (<= (current-column) solitaire-end-x)
|
|
391 (>= (solitaire-current-line) solitaire-start-y)
|
|
392 (<= (solitaire-current-line) solitaire-end-y)
|
|
393 (mapcar
|
|
394 (lambda (movesymbol)
|
|
395 (if (listp (solitaire-possible-move movesymbol))
|
|
396 (setq count (1+ count))))
|
|
397 solitaire-valid-directions)))
|
|
398 count))))
|
|
399
|
|
400 (defun solitaire-do-check (&optional arg)
|
|
401 "Check for any possible moves in Solitaire."
|
|
402 (interactive "P")
|
|
403 (let ((moves (solitaire-check)))
|
|
404 (cond ((= 1 solitaire-stones)
|
|
405 (message "Yeah! You made it! Only the King is left!"))
|
|
406 ((zerop moves)
|
|
407 (message "Sorry, no more possible moves."))
|
|
408 ((= 1 moves)
|
|
409 (message "There is one possible move."))
|
|
410 (t (message "There are %d possible moves." moves)))))
|
|
411
|
|
412 (defun solitaire-current-line ()
|
|
413 "Return the vertical position of point.
|
|
414 Seen in info on text lines."
|
|
415 (+ (count-lines (point-min) (point))
|
|
416 (if (= (current-column) 0) 1 0)
|
|
417 -1))
|
|
418
|
|
419 (defun solitaire-quit ()
|
|
420 "Quit playing Solitaire."
|
|
421 (interactive)
|
|
422 (kill-buffer "*Solitaire*"))
|
|
423
|
|
424 ;; And here's the spoiler:)
|
|
425 (defun solitaire-solve ()
|
|
426 "Spoil solitaire by solving the game for you - nearly ...
|
|
427 ... stops with five stones left ;)"
|
|
428 (interactive)
|
|
429 (let ((allmoves [up up S-down up left left S-right up up left S-down
|
|
430 up up right right S-left down down down S-up up
|
|
431 S-down down down down S-up left left down
|
|
432 S-right left left up up S-down right right right
|
|
433 S-left left S-right right right right S-left
|
|
434 right down down S-up down down left left S-right
|
|
435 up up up S-down down S-up up up up S-down up
|
|
436 right right S-left down right right down S-up
|
|
437 left left left S-right right S-left down down
|
|
438 left S-right S-up S-left S-left S-down S-right
|
|
439 up S-right left left])
|
|
440 ;; down down S-up left S-right
|
|
441 ;; right S-left
|
|
442 (solitaire-auto-eval nil))
|
|
443 (solitaire-center-point)
|
|
444 (mapcar (lambda (op)
|
|
445 (if (memq op '(S-left S-right S-up S-down))
|
|
446 (sit-for 0.2))
|
|
447 (execute-kbd-macro (vector op))
|
|
448 (if (memq op '(S-left S-right S-up S-down))
|
|
449 (sit-for 0.4)))
|
|
450 allmoves))
|
|
451 (solitaire-do-check))
|
|
452
|
|
453 (provide 'solitaire)
|
|
454
|
|
455 ;;; solitaire.el ends here
|