Mercurial > emacs
changeset 12766:eed43624bc00
Initial revision
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Fri, 04 Aug 1995 03:03:01 +0000 |
parents | 27b568b455b1 |
children | 287cc74602fa |
files | lisp/play/solitaire.el |
diffstat | 1 files changed, 454 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/play/solitaire.el Fri Aug 04 03:03:01 1995 +0000 @@ -0,0 +1,454 @@ +;; solitaire.el --- game of solitaire in emacs lisp + +;; Copyright (C) 1994 Free Software Foundation, Inc. + +;; Author: Jan Schormann <Jan.Schormann@informatik.uni-oldenburg.de> +;; Created: Fri afternoon, Jun 3, 1994 +;; Keywords: games + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Commentary: + +;; This mode is for playing a well-known game of solitaire +;; in which you jump pegs across other pegs. + +;; The game itself is somehow self-explanatory. Read the help text to +;; solitaire, and try it. + +;;; Code: + +(defvar solitaire-mode-map nil + "Keymap for playing solitaire.") + +(if solitaire-mode-map + () + (setq solitaire-mode-map (make-sparse-keymap)) + (suppress-keymap solitaire-mode-map t) + (define-key solitaire-mode-map "\C-f" 'solitaire-right) + (define-key solitaire-mode-map "\C-b" 'solitaire-left) + (define-key solitaire-mode-map "\C-p" 'solitaire-up) + (define-key solitaire-mode-map "\C-n" 'solitaire-down) + (define-key solitaire-mode-map [return] 'solitaire-move) + (substitute-key-definition 'undo 'solitaire-undo + solitaire-mode-map global-map) + (define-key solitaire-mode-map " " 'solitaire-do-check) + (define-key solitaire-mode-map "q" 'solitaire-quit) + + (define-key solitaire-mode-map [right] 'solitaire-right) + (define-key solitaire-mode-map [left] 'solitaire-left) + (define-key solitaire-mode-map [up] 'solitaire-up) + (define-key solitaire-mode-map [down] 'solitaire-down) + + (define-key solitaire-mode-map [S-right] 'solitaire-move-right) + (define-key solitaire-mode-map [S-left] 'solitaire-move-left) + (define-key solitaire-mode-map [S-up] 'solitaire-move-up) + (define-key solitaire-mode-map [S-down] 'solitaire-move-down) + + (define-key solitaire-mode-map [kp-6] 'solitaire-right) + (define-key solitaire-mode-map [kp-4] 'solitaire-left) + (define-key solitaire-mode-map [kp-8] 'solitaire-up) + (define-key solitaire-mode-map [kp-2] 'solitaire-down) + (define-key solitaire-mode-map [kp-5] 'solitaire-center-point) + + (define-key solitaire-mode-map [S-kp-6] 'solitaire-move-right) + (define-key solitaire-mode-map [S-kp-4] 'solitaire-move-left) + (define-key solitaire-mode-map [S-kp-8] 'solitaire-move-up) + (define-key solitaire-mode-map [S-kp-2] 'solitaire-move-down) + + (define-key solitaire-mode-map [kp-enter] 'solitaire-move) + (define-key solitaire-mode-map [kp-0] 'solitaire-undo) + + ;; spoil it with s ;) + (define-key solitaire-mode-map [?s] 'solitaire-solve) + + ;; (define-key solitaire-mode-map [kp-0] 'solitaire-hint) - Not yet provided ;) + ) + +;; Solitaire mode is suitable only for specially formatted data. +(put 'solitaire-mode 'mode-class 'special) + +(defun solitaire-mode () + "Major mode for playing solitaire. +To learn how to play solitaire, see the documentation for function +`solitaire'. +\\<solitaire-mode-map> +The usual mnemonic keys move the cursor around the board; in addition, +\\[solitaire-move] is a prefix character for actually moving a stone on the board." + (interactive) + (kill-all-local-variables) + (use-local-map solitaire-mode-map) + (setq truncate-lines t) + (setq major-mode 'solitaire-mode) + (setq mode-name "Solitaire") + (run-hooks 'solitaire-mode-hook)) + +(defvar solitaire-stones 0 + "Counter for the stones that are still there.") + +(defvar solitaire-center nil + "Center of the board.") + +(defvar solitaire-start nil + "Upper left corner of the board.") + +(defvar solitaire-start-x nil) +(defvar solitaire-start-y nil) + +(defvar solitaire-end nil + "Lower right corner of the board.") + +(defvar solitaire-end-x nil) +(defvar solitaire-end-y nil) + +(defvar solitaire-auto-eval t + "*Non-nil means check for possible moves after each major change. +This takes a while, so switch this on if you like to be informed when +the game is over, or off, if you are working on a slow machine.") + +(defconst solitaire-valid-directions + '(solitaire-left solitaire-right solitaire-up solitaire-down)) + +;;;###autoload +(defun solitaire (arg) + "Play Solitaire. + +To play Solitaire, type \\[solitaire]. +\\<solitaire-mode-map> +Move around the board using the cursor keys. +Move stones using \\[solitaire-move] followed by a direction key. +Undo moves using \\[solitaire-undo]. +Check for possible moves using \\[solitaire-do-check]. +(The variable solitaire-auto-eval controls whether to automatically +check after each move or undo) + +What is Solitaire? + +I don't know who invented this game, but it seems to be rather old and +it's origin seems be northern Africa. Here's how to play: +Initially, the board will look similar to this: + + Le Solitaire + ============ + + o o o + + o o o + + o o o o o o o + + o o o . o o o + + o o o o o o o + + o o o + + o o o + +Let's call the o's stones and the .'s holes. One stone fits into one +hole. As you can see, all holes but one are occupied by stones. The +aim of the game is to get rid of all but one stone, leaving that last +one in the middle of the board if you're cool. + +A stone can be moved if there is another stone next to it, and a hole +after that one. Thus there must be three fields in a row, either +horizontally or vertically, up, down, left or right, which look like +this: o o . + +Then the first stone is moved to the hole, jumping over the second, +which therefore is taken away. The above thus `evaluates' to: . . o + +That's all. Here's the board after two moves: + + o o o + + . o o + + o o . o o o o + + o . o o o o o + + o o o o o o o + + o o o + + o o o + +Pick your favourite shortcuts: + +\\{solitaire-mode-map}" + + (interactive "P") + (switch-to-buffer "*Solitaire*") + (solitaire-mode) + (setq buffer-read-only t) + (setq solitaire-stones 32) + (solitaire-insert-board) + (solitaire-build-modeline) + (goto-char (point-max)) + (setq solitaire-center (search-backward ".")) + (setq buffer-undo-list (list (point))) + (set-buffer-modified-p nil)) + +(defun solitaire-build-modeline () + (setq mode-line-format + (list "" "---" 'mode-line-buffer-identification + (if (< 1 solitaire-stones) + (format "--> There are %d stones left <--" solitaire-stones) + "------") + 'global-mode-string " %[(" 'mode-name 'minor-mode-alist "%n" + ")%]-%-")) + (force-mode-line-update)) + +(defun solitaire-insert-board () + (let* ((buffer-read-only nil) + (w (window-width)) + (h (window-height)) + (hsep (cond ((> w 26) " ") + ((> w 20) " ") + (t ""))) + (vsep (cond ((> h 17) "\n\n") + (t "\n"))) + (indent (make-string (/ (- w 7 (* 6 (length hsep))) 2) ?\ ))) + (erase-buffer) + (insert (make-string (/ (- h 7 (if (> h 12) 3 0) + (* 6 (1- (length vsep)))) 2) ?\n)) + (if (or (string= vsep "\n\n") (> h 12)) + (progn + (insert (format "%sLe Solitaire\n" indent)) + (insert (format "%s============\n\n" indent)))) + (insert indent) + (setq solitaire-start (point)) + (setq solitaire-start-x (current-column)) + (setq solitaire-start-y (solitaire-current-line)) + (insert (format " %s %so%so%so%s" hsep hsep hsep hsep vsep)) + (insert (format "%s %s %so%so%so%s" indent hsep hsep hsep hsep vsep)) + (insert (format "%so%so%so%so%so%so%so%s" indent hsep hsep hsep hsep hsep hsep vsep)) + (insert (format "%so%so%so%s" indent hsep hsep hsep)) + (setq solitaire-center (point)) + (insert (format ".%so%so%so%s" hsep hsep hsep vsep)) + (insert (format "%so%so%so%so%so%so%so%s" indent hsep hsep hsep hsep hsep hsep vsep)) + (insert (format "%s %s %so%so%so%s" indent hsep hsep hsep hsep vsep)) + (insert (format "%s %s %so%so%so%s %s " indent hsep hsep hsep hsep hsep hsep)) + (setq solitaire-end (point)) + (setq solitaire-end-x (current-column)) + (setq solitaire-end-y (solitaire-current-line)) + )) + +(defun solitaire-right () + (interactive) + (let ((start (point))) + (forward-char) + (while (= ?\ (following-char)) + (forward-char)) + (if (or (= 0 (following-char)) + (= ?\ (following-char)) + (= ?\n (following-char))) + (goto-char start)))) + +(defun solitaire-left () + (interactive) + (let ((start (point))) + (backward-char) + (while (= ?\ (following-char)) + (backward-char)) + (if (or (= 0 (preceding-char)) + (= ?\ (following-char)) + (= ?\n (following-char))) + (goto-char start)))) + +(defun solitaire-up () + (interactive) + (let ((start (point)) + (c (current-column))) + (forward-line -1) + (move-to-column c) + (while (and (= ?\n (following-char)) + (forward-line -1) + (move-to-column c) + (not (bolp)))) + (if (or (= 0 (preceding-char)) + (= ?\ (following-char)) + (= ?\= (following-char)) + (= ?\n (following-char))) + (goto-char start) + ))) + +(defun solitaire-down () + (interactive) + (let ((start (point)) + (c (current-column))) + (forward-line 1) + (move-to-column c) + (while (and (= ?\n (following-char)) + (forward-line 1) + (move-to-column c) + (not (eolp)))) + (if (or (= 0 (following-char)) + (= ?\ (following-char)) + (= ?\n (following-char))) + (goto-char start)))) + +(defun solitaire-center-point () + (interactive) + (goto-char solitaire-center)) + +(defun solitaire-move-right () (interactive) (solitaire-move '[right])) +(defun solitaire-move-left () (interactive) (solitaire-move '[left])) +(defun solitaire-move-up () (interactive) (solitaire-move '[up])) +(defun solitaire-move-down () (interactive) (solitaire-move '[down])) + +(defun solitaire-possible-move (movesymbol) + "Check if a move is possible from current point in the specified direction. +MOVESYMBOL specifies the direction. +Returns either a string, indicating cause of contraindication, or a +list containing three numbers: starting field, skipped field (from +which a stone will be taken away) and target." + + (save-excursion + (let (move) + (fset 'move movesymbol) + (if (memq movesymbol solitaire-valid-directions) + (let ((start (point)) + (skip (progn (move) (point))) + (target (progn (move) (point)))) + (if (= skip target) + "Off Board!" + (if (or (/= ?o (char-after start)) + (/= ?o (char-after skip)) + (/= ?. (char-after target))) + "Wrong move!" + (list start skip target)))) + "Not a valid direction")))) + +(defun solitaire-move (dir) + "Pseudo-prefix command to move a stone in Solitaire." + (interactive "kMove where? ") + (let* ((class (solitaire-possible-move (lookup-key solitaire-mode-map dir))) + (buffer-read-only nil)) + (if (stringp class) + (error class) + (let ((start (car class)) + (skip (car (cdr class))) + (target (car (cdr (cdr class))))) + (goto-char start) + (delete-char 1) + (insert ?.) + (goto-char skip) + (delete-char 1) + (insert ?.) + (goto-char target) + (delete-char 1) + (insert ?o) + (goto-char target) + (setq solitaire-stones (1- solitaire-stones)) + (solitaire-build-modeline) + (if solitaire-auto-eval (solitaire-do-check)))))) + +(defun solitaire-undo (arg) + "Undo a move in Solitaire." + (interactive "P") + (let ((buffer-read-only nil)) + (undo arg)) + (save-excursion + (setq solitaire-stones + (let ((count 0)) + (goto-char solitaire-end) + (while (search-backward "o" solitaire-start 'done) + (and (>= (current-column) solitaire-start-x) + (<= (current-column) solitaire-end-x) + (>= (solitaire-current-line) solitaire-start-y) + (<= (solitaire-current-line) solitaire-end-y) + (setq count (1+ count)))) + count))) + (solitaire-build-modeline) + (if solitaire-auto-eval (solitaire-do-check))) + +(defun solitaire-check () + (save-excursion + (if (= 1 solitaire-stones) + 0 + (goto-char solitaire-end) + (let ((count 0)) + (while (search-backward "o" solitaire-start 'done) + (and (>= (current-column) solitaire-start-x) + (<= (current-column) solitaire-end-x) + (>= (solitaire-current-line) solitaire-start-y) + (<= (solitaire-current-line) solitaire-end-y) + (mapcar + (lambda (movesymbol) + (if (listp (solitaire-possible-move movesymbol)) + (setq count (1+ count)))) + solitaire-valid-directions))) + count)))) + +(defun solitaire-do-check (&optional arg) + "Check for any possible moves in Solitaire." + (interactive "P") + (let ((moves (solitaire-check))) + (cond ((= 1 solitaire-stones) + (message "Yeah! You made it! Only the King is left!")) + ((zerop moves) + (message "Sorry, no more possible moves.")) + ((= 1 moves) + (message "There is one possible move.")) + (t (message "There are %d possible moves." moves))))) + +(defun solitaire-current-line () + "Return the vertical position of point. +Seen in info on text lines." + (+ (count-lines (point-min) (point)) + (if (= (current-column) 0) 1 0) + -1)) + +(defun solitaire-quit () + "Quit playing Solitaire." + (interactive) + (kill-buffer "*Solitaire*")) + +;; And here's the spoiler:) +(defun solitaire-solve () + "Spoil solitaire by solving the game for you - nearly ... +... stops with five stones left ;)" + (interactive) + (let ((allmoves [up up S-down up left left S-right up up left S-down + up up right right S-left down down down S-up up + S-down down down down S-up left left down + S-right left left up up S-down right right right + S-left left S-right right right right S-left + right down down S-up down down left left S-right + up up up S-down down S-up up up up S-down up + right right S-left down right right down S-up + left left left S-right right S-left down down + left S-right S-up S-left S-left S-down S-right + up S-right left left]) + ;; down down S-up left S-right + ;; right S-left + (solitaire-auto-eval nil)) + (solitaire-center-point) + (mapcar (lambda (op) + (if (memq op '(S-left S-right S-up S-down)) + (sit-for 0.2)) + (execute-kbd-macro (vector op)) + (if (memq op '(S-left S-right S-up S-down)) + (sit-for 0.4))) + allmoves)) + (solitaire-do-check)) + +(provide 'solitaire) + +;;; solitaire.el ends here