(FILE_SYSTEM_CASE): Call Fmsdos_downcase_filename instead of Fdowncase.
[bpt/emacs.git] / lisp / play / solitaire.el
CommitLineData
be010748 1;;; solitaire.el --- game of solitaire in Emacs Lisp
8ad6fb8d
RS
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
e451cded
EN
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.
8ad6fb8d
RS
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.
88To learn how to play solitaire, see the documentation for function
89`solitaire'.
90\\<solitaire-mode-map>
91The 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.
121This takes a while, so switch this on if you like to be informed when
122the 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
131To play Solitaire, type \\[solitaire].
132\\<solitaire-mode-map>
133Move around the board using the cursor keys.
134Move stones using \\[solitaire-move] followed by a direction key.
135Undo moves using \\[solitaire-undo].
136Check for possible moves using \\[solitaire-do-check].
f5a90940 137\(The variable solitaire-auto-eval controls whether to automatically
8ad6fb8d
RS
138check after each move or undo)
139
140What is Solitaire?
141
142I don't know who invented this game, but it seems to be rather old and
143it's origin seems be northern Africa. Here's how to play:
144Initially, 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
163Let's call the o's stones and the .'s holes. One stone fits into one
164hole. As you can see, all holes but one are occupied by stones. The
165aim of the game is to get rid of all but one stone, leaving that last
166one in the middle of the board if you're cool.
167
168A stone can be moved if there is another stone next to it, and a hole
169after that one. Thus there must be three fields in a row, either
170horizontally or vertically, up, down, left or right, which look like
171this: o o .
172
173Then the first stone is moved to the hole, jumping over the second,
174which therefore is taken away. The above thus `evaluates' to: . . o
175
176That'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
192Pick 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.
318MOVESYMBOL specifies the direction.
319Returns either a string, indicating cause of contraindication, or a
320list containing three numbers: starting field, skipped field (from
321which 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.
414Seen 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