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