diff options
| author | Joseph Arceneaux | 1990-03-06 16:45:37 +0000 |
|---|---|---|
| committer | Joseph Arceneaux | 1990-03-06 16:45:37 +0000 |
| commit | 80a677d9852bb4eb26c2c0bb2c119fdae0770c43 (patch) | |
| tree | 4173bbe88910bbdda7edea70cffca42670f1c7cf | |
| parent | 4a07a2afa9b1bf52028cd6e5c876905f0dc2bf3c (diff) | |
| download | emacs-80a677d9852bb4eb26c2c0bb2c119fdae0770c43.tar.gz emacs-80a677d9852bb4eb26c2c0bb2c119fdae0770c43.zip | |
Initial revision
| -rw-r--r-- | lisp/mail/mailalias.el | 157 | ||||
| -rw-r--r-- | lisp/play/hanoi.el | 192 | ||||
| -rw-r--r-- | lisp/play/life.el | 276 |
3 files changed, 625 insertions, 0 deletions
diff --git a/lisp/mail/mailalias.el b/lisp/mail/mailalias.el new file mode 100644 index 00000000000..7fc41289a15 --- /dev/null +++ b/lisp/mail/mailalias.el | |||
| @@ -0,0 +1,157 @@ | |||
| 1 | ;; Expand mailing address aliases defined in ~/.mailrc. | ||
| 2 | ;; Copyright (C) 1985, 1987 Free Software Foundation, Inc. | ||
| 3 | |||
| 4 | ;; This file is part of GNU Emacs. | ||
| 5 | |||
| 6 | ;; GNU Emacs is free software; you can redistribute it and/or modify | ||
| 7 | ;; it under the terms of the GNU General Public License as published by | ||
| 8 | ;; the Free Software Foundation; either version 1, or (at your option) | ||
| 9 | ;; any later version. | ||
| 10 | |||
| 11 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 12 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | ;; GNU General Public License for more details. | ||
| 15 | |||
| 16 | ;; You should have received a copy of the GNU General Public License | ||
| 17 | ;; along with GNU Emacs; see the file COPYING. If not, write to | ||
| 18 | ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 19 | |||
| 20 | |||
| 21 | ;; Called from sendmail-send-it, or similar functions, | ||
| 22 | ;; only if some mail aliases are defined. | ||
| 23 | (defun expand-mail-aliases (beg end &optional exclude) | ||
| 24 | "Expand all mail aliases in suitable header fields found between BEG and END. | ||
| 25 | Suitable header fields are To, Cc and Bcc. Optional 2nd arg EXCLUDE may be a | ||
| 26 | regular expression defining text to be removed from alias expansions." | ||
| 27 | (if (eq mail-aliases t) | ||
| 28 | (progn (setq mail-aliases nil) (build-mail-aliases))) | ||
| 29 | (goto-char beg) | ||
| 30 | (setq end (set-marker (make-marker) end)) | ||
| 31 | (let ((case-fold-search nil)) | ||
| 32 | (while (let ((case-fold-search t)) | ||
| 33 | (re-search-forward "^\\(to\\|cc\\|bcc\\):" end t)) | ||
| 34 | (skip-chars-forward " \t") | ||
| 35 | (let ((beg1 (point)) | ||
| 36 | end1 pos epos seplen | ||
| 37 | ;; DISABLED-ALIASES records aliases temporarily disabled | ||
| 38 | ;; while we scan text that resulted from expanding those aliases. | ||
| 39 | ;; Each element is (ALIAS . TILL-WHEN), where TILL-WHEN | ||
| 40 | ;; is where to reenable the alias (expressed as number of chars | ||
| 41 | ;; counting from END1). | ||
| 42 | (disabled-aliases nil)) | ||
| 43 | (re-search-forward "^[^ \t]" end 'move) | ||
| 44 | (beginning-of-line) | ||
| 45 | (skip-chars-backward " \t\n") | ||
| 46 | (setq end1 (point-marker)) | ||
| 47 | (goto-char beg1) | ||
| 48 | (while (< (point) end1) | ||
| 49 | (setq pos (point)) | ||
| 50 | ;; Reenable any aliases which were disabled for ranges | ||
| 51 | ;; that we have passed out of. | ||
| 52 | (while (and disabled-aliases (> pos (- end1 (cdr (car disabled-aliases))))) | ||
| 53 | (setq disabled-aliases (cdr disabled-aliases))) | ||
| 54 | ;; EPOS gets position of end of next name; | ||
| 55 | ;; SEPLEN gets length of whitespace&separator that follows it. | ||
| 56 | (if (re-search-forward "[ \t]*[\n,][ \t]*" end1 t) | ||
| 57 | (setq epos (match-beginning 0) | ||
| 58 | seplen (- (point) epos)) | ||
| 59 | (setq epos (marker-position end1) seplen 0)) | ||
| 60 | (let (translation | ||
| 61 | (string (buffer-substring pos epos))) | ||
| 62 | (if (and (not (assoc string disabled-aliases)) | ||
| 63 | (setq translation | ||
| 64 | (cdr (assoc string mail-aliases)))) | ||
| 65 | (progn | ||
| 66 | ;; This name is an alias. Disable it. | ||
| 67 | (setq disabled-aliases (cons (cons string (- end1 epos)) | ||
| 68 | disabled-aliases)) | ||
| 69 | ;; Replace the alias with its expansion | ||
| 70 | ;; then rescan the expansion for more aliases. | ||
| 71 | (goto-char pos) | ||
| 72 | (insert translation) | ||
| 73 | (if exclude | ||
| 74 | (let ((regexp | ||
| 75 | (concat "\\b\\(" exclude "\\)\\b")) | ||
| 76 | (end (point-marker))) | ||
| 77 | (goto-char pos) | ||
| 78 | (while (re-search-forward regexp end t) | ||
| 79 | (replace-match "")) | ||
| 80 | (goto-char end))) | ||
| 81 | (delete-region (point) (+ (point) (- epos pos))) | ||
| 82 | (goto-char pos)) | ||
| 83 | ;; Name is not an alias. Skip to start of next name. | ||
| 84 | (goto-char epos) | ||
| 85 | (forward-char seplen)))) | ||
| 86 | (set-marker end1 nil))) | ||
| 87 | (set-marker end nil))) | ||
| 88 | |||
| 89 | ;; Called by mail-setup, or similar functions, only if ~/.mailrc exists. | ||
| 90 | (defun build-mail-aliases (&optional file) | ||
| 91 | "Read mail aliases from ~/.mailrc and set mail-aliases." | ||
| 92 | (setq file (expand-file-name (or file "~/.mailrc"))) | ||
| 93 | (let ((buffer nil) | ||
| 94 | (obuf (current-buffer))) | ||
| 95 | (unwind-protect | ||
| 96 | (progn | ||
| 97 | (setq buffer (generate-new-buffer "mailrc")) | ||
| 98 | (buffer-disable-undo buffer) | ||
| 99 | (set-buffer buffer) | ||
| 100 | (cond ((get-file-buffer file) | ||
| 101 | (insert (save-excursion | ||
| 102 | (set-buffer (get-file-buffer file)) | ||
| 103 | (buffer-substring (point-min) (point-max))))) | ||
| 104 | ((not (file-exists-p file))) | ||
| 105 | (t (insert-file-contents file))) | ||
| 106 | ;; Don't lose if no final newline. | ||
| 107 | (goto-char (point-max)) | ||
| 108 | (or (eq (preceding-char) ?\n) (newline)) | ||
| 109 | (goto-char (point-min)) | ||
| 110 | ;; handle "\\\n" continuation lines | ||
| 111 | (while (not (eobp)) | ||
| 112 | (end-of-line) | ||
| 113 | (if (= (preceding-char) ?\\) | ||
| 114 | (progn (delete-char -1) (delete-char 1) (insert ?\ )) | ||
| 115 | (forward-char 1))) | ||
| 116 | (goto-char (point-min)) | ||
| 117 | (while (or (re-search-forward "^a\\(lias\\|\\)[ \t]+" nil t) | ||
| 118 | (re-search-forward "^g\\(roup\\|\\)[ \t]+" nil t)) | ||
| 119 | (re-search-forward "[^ \t]+") | ||
| 120 | (let* ((name (buffer-substring (match-beginning 0) (match-end 0))) | ||
| 121 | (start (progn (skip-chars-forward " \t") (point)))) | ||
| 122 | (end-of-line) | ||
| 123 | (define-mail-alias | ||
| 124 | name | ||
| 125 | (buffer-substring start (point))))) | ||
| 126 | mail-aliases) | ||
| 127 | (if buffer (kill-buffer buffer)) | ||
| 128 | (set-buffer obuf)))) | ||
| 129 | |||
| 130 | ;; Always autoloadable in case the user wants to define aliases | ||
| 131 | ;; interactively or in .emacs. | ||
| 132 | (defun define-mail-alias (name definition) | ||
| 133 | "Define NAME as a mail-alias that translates to DEFINITION. | ||
| 134 | This means that sending a message to NAME will actually send to DEFINITION. | ||
| 135 | DEFINITION can be one or more mail addresses separated by commas." | ||
| 136 | (interactive "sDefine mail alias: \nsDefine %s as mail alias for: ") | ||
| 137 | ;; Read the defaults first, if we have not done so. | ||
| 138 | (if (eq mail-aliases t) | ||
| 139 | (progn | ||
| 140 | (setq mail-aliases nil) | ||
| 141 | (if (file-exists-p "~/.mailrc") | ||
| 142 | (build-mail-aliases)))) | ||
| 143 | (let (tem) | ||
| 144 | ;; ~/.mailrc contains addresses separated by spaces. | ||
| 145 | ;; mailers should expect addresses separated by commas. | ||
| 146 | (while (setq tem (string-match "[^ \t,][ \t,]+" definition tem)) | ||
| 147 | (if (= (match-end 0) (length definition)) | ||
| 148 | (setq definition (substring definition 0 (1+ tem))) | ||
| 149 | (setq definition (concat (substring definition | ||
| 150 | 0 (1+ tem)) | ||
| 151 | ", " | ||
| 152 | (substring definition (match-end 0)))) | ||
| 153 | (setq tem (+ 3 tem)))) | ||
| 154 | (setq tem (assoc name mail-aliases)) | ||
| 155 | (if tem | ||
| 156 | (rplacd tem definition) | ||
| 157 | (setq mail-aliases (cons (cons name definition) mail-aliases))))) | ||
diff --git a/lisp/play/hanoi.el b/lisp/play/hanoi.el new file mode 100644 index 00000000000..8884f6cb37b --- /dev/null +++ b/lisp/play/hanoi.el | |||
| @@ -0,0 +1,192 @@ | |||
| 1 | ; | ||
| 2 | ; hanoi - towers of hanoi in GNUmacs | ||
| 3 | ; | ||
| 4 | ; Author (a) 1985, Damon Anton Permezel | ||
| 5 | ; | ||
| 6 | |||
| 7 | ;;; | ||
| 8 | ;;; hanoi-topos - direct cursor addressing | ||
| 9 | ;;; | ||
| 10 | (defun hanoi-topos (row col) | ||
| 11 | (goto-line row) | ||
| 12 | (beginning-of-line) | ||
| 13 | (forward-char col)) | ||
| 14 | |||
| 15 | ;;; | ||
| 16 | ;;; hanoi - user callable Towers of Hanoi | ||
| 17 | ;;; | ||
| 18 | (defun hanoi (nrings) | ||
| 19 | "Towers of Hanoi diversion. Argument is number of rings." | ||
| 20 | (interactive | ||
| 21 | (list (if (null current-prefix-arg) | ||
| 22 | 3 | ||
| 23 | (prefix-numeric-value current-prefix-arg)))) | ||
| 24 | (if (<= nrings 0) (error "Negative number of rings")) | ||
| 25 | (let (pole-spacing | ||
| 26 | floor-row | ||
| 27 | fly-row | ||
| 28 | (window-height (window-height (selected-window))) | ||
| 29 | (window-width (window-width (selected-window)))) | ||
| 30 | (let ((h (+ nrings 2)) | ||
| 31 | (w (+ (* (1- nrings) 6) 2 5))) | ||
| 32 | (if (not (and (>= window-width h) | ||
| 33 | (> window-width w))) | ||
| 34 | (progn | ||
| 35 | (delete-other-windows) | ||
| 36 | (if (not (and (>= (setq window-height | ||
| 37 | (window-height (selected-window))) h) | ||
| 38 | (> (setq window-width | ||
| 39 | (window-width (selected-window))) w))) | ||
| 40 | (error "Screen is too small (need at least %dx%d)" w h)))) | ||
| 41 | (setq pole-spacing (/ window-width 6)) | ||
| 42 | (if (not (zerop (logand pole-spacing 1))) | ||
| 43 | ;; must be even | ||
| 44 | (setq pole-spacing (1+ pole-spacing))) | ||
| 45 | (setq floor-row (if (> (- window-height 3) h) | ||
| 46 | (- window-height 3) window-height))) | ||
| 47 | (let ((fly-row (- floor-row nrings 1)) | ||
| 48 | ;; pole: column . fill height | ||
| 49 | (pole-1 (cons pole-spacing floor-row)) | ||
| 50 | (pole-2 (cons (* 3 pole-spacing) floor-row)) | ||
| 51 | (pole-3 (cons (* 5 pole-spacing) floor-row)) | ||
| 52 | (rings (make-vector nrings nil))) | ||
| 53 | ;; construct the ring list | ||
| 54 | (let ((i 0)) | ||
| 55 | (while (< i nrings) | ||
| 56 | ;; ring: [pole-number string empty-string] | ||
| 57 | (aset rings i (vector nil | ||
| 58 | (make-string (+ i i 3) (+ ?0 i)) | ||
| 59 | (make-string (+ i i 3) ?\ ))) | ||
| 60 | (setq i (1+ i)))) | ||
| 61 | ;; | ||
| 62 | ;; init the screen | ||
| 63 | ;; | ||
| 64 | (switch-to-buffer "*Hanoi*") | ||
| 65 | (setq buffer-read-only nil) | ||
| 66 | (buffer-disable-undo (current-buffer)) | ||
| 67 | (erase-buffer) | ||
| 68 | (let ((i 0)) | ||
| 69 | (while (< i floor-row) | ||
| 70 | (setq i (1+ i)) | ||
| 71 | (insert-char ?\ (1- window-width)) | ||
| 72 | (insert ?\n))) | ||
| 73 | (insert-char ?= (1- window-width)) | ||
| 74 | |||
| 75 | (let ((n 1)) | ||
| 76 | (while (< n 6) | ||
| 77 | (hanoi-topos fly-row (* n pole-spacing)) | ||
| 78 | (setq n (+ n 2)) | ||
| 79 | (let ((i fly-row)) | ||
| 80 | (while (< i floor-row) | ||
| 81 | (setq i (1+ i)) | ||
| 82 | (next-line 1) | ||
| 83 | (insert ?\|) | ||
| 84 | (delete-char 1) | ||
| 85 | (backward-char 1))))) | ||
| 86 | ;(sit-for 0) | ||
| 87 | ;; | ||
| 88 | ;; now draw the rings in their initial positions | ||
| 89 | ;; | ||
| 90 | (let ((i 0) | ||
| 91 | ring) | ||
| 92 | (while (< i nrings) | ||
| 93 | (setq ring (aref rings (- nrings 1 i))) | ||
| 94 | (aset ring 0 (- floor-row i)) | ||
| 95 | (hanoi-topos (cdr pole-1) | ||
| 96 | (- (car pole-1) (- nrings i))) | ||
| 97 | (hanoi-draw-ring ring t nil) | ||
| 98 | (setcdr pole-1 (1- (cdr pole-1))) | ||
| 99 | (setq i (1+ i)))) | ||
| 100 | (setq buffer-read-only t) | ||
| 101 | (sit-for 0) | ||
| 102 | ;; | ||
| 103 | ;; do it! | ||
| 104 | ;; | ||
| 105 | (hanoi0 (1- nrings) pole-1 pole-2 pole-3) | ||
| 106 | (goto-char (point-min)) | ||
| 107 | (message "Done") | ||
| 108 | (setq buffer-read-only t) | ||
| 109 | (set-buffer-modified-p (buffer-modified-p)) | ||
| 110 | (sit-for 0)))) | ||
| 111 | |||
| 112 | ;;; | ||
| 113 | ;;; hanoi0 - work horse of hanoi | ||
| 114 | ;;; | ||
| 115 | (defun hanoi0 (n from to work) | ||
| 116 | (cond ((input-pending-p) | ||
| 117 | (signal 'quit (list "I can tell you've had enough"))) | ||
| 118 | ((< n 0)) | ||
| 119 | (t | ||
| 120 | (hanoi0 (1- n) from work to) | ||
| 121 | (hanoi-move-ring n from to) | ||
| 122 | (hanoi0 (1- n) work to from)))) | ||
| 123 | |||
| 124 | ;;; | ||
| 125 | ;;; hanoi-move-ring - move ring 'n' from 'from' to 'to' | ||
| 126 | ;;; | ||
| 127 | ;;; | ||
| 128 | (defun hanoi-move-ring (n from to) | ||
| 129 | (let ((ring (aref rings n)) ; ring <- ring: (ring# . row) | ||
| 130 | (buffer-read-only nil)) | ||
| 131 | (let ((row (aref ring 0)) ; row <- row ring is on | ||
| 132 | (col (- (car from) n 1)) ; col <- left edge of ring | ||
| 133 | (dst-col (- (car to) n 1)) ; dst-col <- dest col for left edge | ||
| 134 | (dst-row (cdr to))) ; dst-row <- dest row for ring | ||
| 135 | (hanoi-topos row col) | ||
| 136 | (while (> row fly-row) ; move up to the fly row | ||
| 137 | (hanoi-draw-ring ring nil t) ; blank out ring | ||
| 138 | (previous-line 1) ; move up a line | ||
| 139 | (hanoi-draw-ring ring t nil) ; redraw | ||
| 140 | (sit-for 0) | ||
| 141 | (setq row (1- row))) | ||
| 142 | (setcdr from (1+ (cdr from))) ; adjust top row | ||
| 143 | ;; | ||
| 144 | ;; fly the ring over to the right pole | ||
| 145 | ;; | ||
| 146 | (while (not (equal dst-col col)) | ||
| 147 | (cond ((> dst-col col) ; dst-col > col: right shift | ||
| 148 | (end-of-line 1) | ||
| 149 | (delete-backward-char 2) | ||
| 150 | (beginning-of-line 1) | ||
| 151 | (insert ?\ ?\ ) | ||
| 152 | (sit-for 0) | ||
| 153 | (setq col (1+ (1+ col)))) | ||
| 154 | ((< dst-col col) ; dst-col < col: left shift | ||
| 155 | (beginning-of-line 1) | ||
| 156 | (delete-char 2) | ||
| 157 | (end-of-line 1) | ||
| 158 | (insert ?\ ?\ ) | ||
| 159 | (sit-for 0) | ||
| 160 | (setq col (1- (1- col)))))) | ||
| 161 | ;; | ||
| 162 | ;; let the ring float down | ||
| 163 | ;; | ||
| 164 | (hanoi-topos fly-row dst-col) | ||
| 165 | (while (< row dst-row) ; move down to the dest row | ||
| 166 | (hanoi-draw-ring ring nil (> row fly-row)) ; blank out ring | ||
| 167 | (next-line 1) ; move down a line | ||
| 168 | (hanoi-draw-ring ring t nil) ; redraw ring | ||
| 169 | (sit-for 0) | ||
| 170 | (setq row (1+ row))) | ||
| 171 | (aset ring 0 dst-row) | ||
| 172 | (setcdr to (1- (cdr to)))))) ; adjust top row | ||
| 173 | |||
| 174 | ;;; | ||
| 175 | ;;; draw-ring - draw the ring at point, leave point unchanged | ||
| 176 | ;;; | ||
| 177 | ;;; Input: | ||
| 178 | ;;; ring | ||
| 179 | ;;; f1 - flag: t -> draw, nil -> erase | ||
| 180 | ;;; f2 - flag: t -> erasing and need to draw ?\| | ||
| 181 | ;;; | ||
| 182 | (defun hanoi-draw-ring (ring f1 f2) | ||
| 183 | (save-excursion | ||
| 184 | (let* ((string (if f1 (aref ring 1) (aref ring 2))) | ||
| 185 | (len (length string))) | ||
| 186 | (delete-char len) | ||
| 187 | (insert string) | ||
| 188 | (if f2 | ||
| 189 | (progn | ||
| 190 | (backward-char (/ (+ len 1) 2)) | ||
| 191 | (delete-char 1) (insert ?\|)))))) | ||
| 192 | |||
diff --git a/lisp/play/life.el b/lisp/play/life.el new file mode 100644 index 00000000000..e216dbf0b4e --- /dev/null +++ b/lisp/play/life.el | |||
| @@ -0,0 +1,276 @@ | |||
| 1 | ;; Conway's `Life' for GNU Emacs | ||
| 2 | ;; Copyright (C) 1988 Free Software Foundation, Inc. | ||
| 3 | ;; Contributed by Kyle Jones, talos!kjones@uunet.uu.net | ||
| 4 | |||
| 5 | ;; This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | ;; GNU Emacs is free software; you can redistribute it and/or modify | ||
| 8 | ;; it under the terms of the GNU General Public License as published by | ||
| 9 | ;; the Free Software Foundation; either version 1, or (at your option) | ||
| 10 | ;; any later version. | ||
| 11 | |||
| 12 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | ;; GNU General Public License for more details. | ||
| 16 | |||
| 17 | ;; You should have received a copy of the GNU General Public License | ||
| 18 | ;; along with GNU Emacs; see the file COPYING. If not, write to | ||
| 19 | ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | |||
| 21 | (provide 'life) | ||
| 22 | |||
| 23 | (defconst life-patterns | ||
| 24 | [("@@@" " @@" "@@@") | ||
| 25 | ("@@@ @@@" "@@ @@ " "@@@ @@@") | ||
| 26 | ("@@@ @@@" "@@ @@" "@@@ @@@") | ||
| 27 | ("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") | ||
| 28 | ("@@@@@@@@@@") | ||
| 29 | (" @@@@@@@@@@ " | ||
| 30 | " @@@@@@@@@@ " | ||
| 31 | " @@@@@@@@@@ " | ||
| 32 | "@@@@@@@@@@ " | ||
| 33 | "@@@@@@@@@@ ") | ||
| 34 | ("@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@") | ||
| 35 | ("@ @" "@ @" "@ @" | ||
| 36 | "@ @" "@ @" "@ @" | ||
| 37 | "@ @" "@ @" "@ @" | ||
| 38 | "@ @" "@ @" "@ @" | ||
| 39 | "@ @" "@ @" "@ @") | ||
| 40 | ("@@ " " @@ " " @@ " | ||
| 41 | " @@ " " @@ " " @@ " | ||
| 42 | " @@ " " @@ " " @@ " | ||
| 43 | " @@ " " @@ " " @@ " | ||
| 44 | " @@ " " @@ " " @@ " | ||
| 45 | " @@") | ||
| 46 | ("@@@@@@@@@" "@ @ @" "@ @@@@@ @" "@ @ @ @" "@@@ @@@" | ||
| 47 | "@ @ @ @" "@ @@@@@ @" "@ @ @" "@@@@@@@@@")] | ||
| 48 | "Vector of rectangles containing some Life startup patterns.") | ||
| 49 | |||
| 50 | ;; Macros are used macros for manifest constants instead of variables | ||
| 51 | ;; because the compiler will convert them to constants, which should | ||
| 52 | ;; eval faster than symbols. | ||
| 53 | ;; | ||
| 54 | ;; The (require) wrapping forces the compiler to eval these macros at | ||
| 55 | ;; compile time. This would not be necessary if we did not use macros | ||
| 56 | ;; inside of macros, which the compiler doesn't seem to check for. | ||
| 57 | ;; | ||
| 58 | ;; Don't change any of the life-* macro constants unless you thoroughly | ||
| 59 | ;; understand the `life-grim-reaper' function. | ||
| 60 | (require | ||
| 61 | (progn | ||
| 62 | (defmacro life-life-char () ?@) | ||
| 63 | (defmacro life-death-char () (1+ (life-life-char))) | ||
| 64 | (defmacro life-birth-char () 3) | ||
| 65 | (defmacro life-void-char () ?\ ) | ||
| 66 | |||
| 67 | (defmacro life-life-string () (char-to-string (life-life-char))) | ||
| 68 | (defmacro life-death-string () (char-to-string (life-death-char))) | ||
| 69 | (defmacro life-birth-string () (char-to-string (life-birth-char))) | ||
| 70 | (defmacro life-void-string () (char-to-string (life-void-char))) | ||
| 71 | (defmacro life-not-void-regexp () (concat "[^" (life-void-string) "\n]")) | ||
| 72 | |||
| 73 | ;; try to optimize the (goto-char (point-min)) & (goto-char (point-max)) | ||
| 74 | ;; idioms. This depends on goto-char's not griping if we underrshoot | ||
| 75 | ;; or overshoot beginning or end of buffer. | ||
| 76 | (defmacro goto-beginning-of-buffer () '(goto-char 1)) | ||
| 77 | (defmacro maxint () (lsh (lsh (lognot 0) 1) -1)) | ||
| 78 | (defmacro goto-end-of-buffer () '(goto-char (maxint))) | ||
| 79 | |||
| 80 | (defmacro increment (variable) (list 'setq variable (list '1+ variable))) | ||
| 81 | 'life)) | ||
| 82 | |||
| 83 | ;; list of numbers that tell how many characters to move to get to | ||
| 84 | ;; each of a cell's eight neighbors. | ||
| 85 | (defconst life-neighbor-deltas nil) | ||
| 86 | |||
| 87 | ;; window display always starts here. Easier to deal with than | ||
| 88 | ;; (scroll-up) and (scroll-down) when trying to center the display. | ||
| 89 | (defconst life-window-start nil) | ||
| 90 | |||
| 91 | ;; For mode line | ||
| 92 | (defconst life-current-generation nil) | ||
| 93 | ;; Sadly, mode-line-format won't display numbers. | ||
| 94 | (defconst life-generation-string nil) | ||
| 95 | |||
| 96 | (defun abs (n) (if (< n 0) (- n) n)) | ||
| 97 | |||
| 98 | (defun life (&optional sleeptime) | ||
| 99 | "Run Conway's Life simulation. | ||
| 100 | The starting pattern is randomly selected. Prefix arg (optional first arg | ||
| 101 | non-nil from a program) is the number of seconds to sleep between | ||
| 102 | generations (this defaults to 1)." | ||
| 103 | (interactive "p") | ||
| 104 | (or sleeptime (setq sleeptime 1)) | ||
| 105 | (life-setup) | ||
| 106 | (life-display-generation sleeptime) | ||
| 107 | (while t | ||
| 108 | (let ((inhibit-quit t)) | ||
| 109 | (life-grim-reaper) | ||
| 110 | (life-expand-plane-if-needed) | ||
| 111 | (life-increment-generation) | ||
| 112 | (life-display-generation sleeptime)))) | ||
| 113 | |||
| 114 | (fset 'life-mode 'life) | ||
| 115 | (put 'life-mode 'mode-class 'special) | ||
| 116 | |||
| 117 | (random t) | ||
| 118 | |||
| 119 | (defun life-setup () | ||
| 120 | (let (n) | ||
| 121 | (switch-to-buffer (get-buffer-create "*Life*") t) | ||
| 122 | (erase-buffer) | ||
| 123 | (kill-all-local-variables) | ||
| 124 | (setq case-fold-search nil | ||
| 125 | mode-name "Life" | ||
| 126 | major-mode 'life-mode | ||
| 127 | truncate-lines t | ||
| 128 | life-current-generation 0 | ||
| 129 | life-generation-string "0" | ||
| 130 | mode-line-buffer-identification '("Life: generation " | ||
| 131 | life-generation-string) | ||
| 132 | fill-column (1- (window-width)) | ||
| 133 | life-window-start 1) | ||
| 134 | (buffer-disable-undo (current-buffer)) | ||
| 135 | ;; stuff in the random pattern | ||
| 136 | (life-insert-random-pattern) | ||
| 137 | ;; make sure (life-life-char) is used throughout | ||
| 138 | (goto-beginning-of-buffer) | ||
| 139 | (while (re-search-forward (life-not-void-regexp) nil t) | ||
| 140 | (replace-match (life-life-string) t t)) | ||
| 141 | ;; center the pattern horizontally | ||
| 142 | (goto-beginning-of-buffer) | ||
| 143 | (setq n (/ (- fill-column (save-excursion (end-of-line) (point))) 2)) | ||
| 144 | (while (not (eobp)) | ||
| 145 | (indent-to n) | ||
| 146 | (forward-line)) | ||
| 147 | ;; center the pattern vertically | ||
| 148 | (setq n (/ (- (1- (window-height)) | ||
| 149 | (count-lines (point-min) (point-max))) | ||
| 150 | 2)) | ||
| 151 | (goto-beginning-of-buffer) | ||
| 152 | (newline n) | ||
| 153 | (goto-end-of-buffer) | ||
| 154 | (newline n) | ||
| 155 | ;; pad lines out to fill-column | ||
| 156 | (goto-beginning-of-buffer) | ||
| 157 | (while (not (eobp)) | ||
| 158 | (end-of-line) | ||
| 159 | (indent-to fill-column) | ||
| 160 | (move-to-column fill-column) | ||
| 161 | (delete-region (point) (progn (end-of-line) (point))) | ||
| 162 | (forward-line)) | ||
| 163 | ;; expand tabs to spaces | ||
| 164 | (untabify (point-min) (point-max)) | ||
| 165 | ;; before starting be sure the automaton has room to grow | ||
| 166 | (life-expand-plane-if-needed) | ||
| 167 | ;; compute initial neighbor deltas | ||
| 168 | (life-compute-neighbor-deltas))) | ||
| 169 | |||
| 170 | (defun life-compute-neighbor-deltas () | ||
| 171 | (setq life-neighbor-deltas | ||
| 172 | (list -1 (- fill-column) | ||
| 173 | (- (1+ fill-column)) (- (+ 2 fill-column)) | ||
| 174 | 1 fill-column (1+ fill-column) | ||
| 175 | (+ 2 fill-column)))) | ||
| 176 | |||
| 177 | (defun life-insert-random-pattern () | ||
| 178 | (insert-rectangle | ||
| 179 | (elt life-patterns (% (abs (random)) (length life-patterns)))) | ||
| 180 | (insert ?\n)) | ||
| 181 | |||
| 182 | (defun life-increment-generation () | ||
| 183 | (increment life-current-generation) | ||
| 184 | (setq life-generation-string (int-to-string life-current-generation))) | ||
| 185 | |||
| 186 | (defun life-grim-reaper () | ||
| 187 | ;; Clear the match information. Later we check to see if it | ||
| 188 | ;; is still clear, if so then all the cells have died. | ||
| 189 | (store-match-data nil) | ||
| 190 | (goto-beginning-of-buffer) | ||
| 191 | ;; For speed declare all local variable outside the loop. | ||
| 192 | (let (point char pivot living-neighbors list) | ||
| 193 | (while (search-forward (life-life-string) nil t) | ||
| 194 | (setq list life-neighbor-deltas | ||
| 195 | living-neighbors 0 | ||
| 196 | pivot (1- (point))) | ||
| 197 | (while list | ||
| 198 | (setq point (+ pivot (car list)) | ||
| 199 | char (char-after point)) | ||
| 200 | (cond ((eq char (life-void-char)) | ||
| 201 | (subst-char-in-region point (1+ point) | ||
| 202 | (life-void-char) 1 t)) | ||
| 203 | ((< char 3) | ||
| 204 | (subst-char-in-region point (1+ point) char (1+ char) t)) | ||
| 205 | ((< char 9) | ||
| 206 | (subst-char-in-region point (1+ point) char 9 t)) | ||
| 207 | ((>= char (life-life-char)) | ||
| 208 | (increment living-neighbors))) | ||
| 209 | (setq list (cdr list))) | ||
| 210 | (if (memq living-neighbors '(2 3)) | ||
| 211 | () | ||
| 212 | (subst-char-in-region pivot (1+ pivot) | ||
| 213 | (life-life-char) (life-death-char) t)))) | ||
| 214 | (if (null (match-beginning 0)) | ||
| 215 | (life-extinct-quit)) | ||
| 216 | (subst-char-in-region 1 (point-max) 9 (life-void-char) t) | ||
| 217 | (subst-char-in-region 1 (point-max) 1 (life-void-char) t) | ||
| 218 | (subst-char-in-region 1 (point-max) 2 (life-void-char) t) | ||
| 219 | (subst-char-in-region 1 (point-max) (life-birth-char) (life-life-char) t) | ||
| 220 | (subst-char-in-region 1 (point-max) (life-death-char) (life-void-char) t)) | ||
| 221 | |||
| 222 | (defun life-expand-plane-if-needed () | ||
| 223 | (catch 'done | ||
| 224 | (goto-beginning-of-buffer) | ||
| 225 | (while (not (eobp)) | ||
| 226 | ;; check for life at beginning or end of line. If found at | ||
| 227 | ;; either end, expand at both ends, | ||
| 228 | (cond ((or (eq (following-char) (life-life-char)) | ||
| 229 | (eq (progn (end-of-line) (preceding-char)) (life-life-char))) | ||
| 230 | (goto-beginning-of-buffer) | ||
| 231 | (while (not (eobp)) | ||
| 232 | (insert (life-void-char)) | ||
| 233 | (end-of-line) | ||
| 234 | (insert (life-void-char)) | ||
| 235 | (forward-char)) | ||
| 236 | (setq fill-column (+ 2 fill-column)) | ||
| 237 | (scroll-left 1) | ||
| 238 | (life-compute-neighbor-deltas) | ||
| 239 | (throw 'done t))) | ||
| 240 | (forward-line))) | ||
| 241 | (goto-beginning-of-buffer) | ||
| 242 | ;; check for life within the first two lines of the buffer. | ||
| 243 | ;; If present insert two lifeless lines at the beginning.. | ||
| 244 | (cond ((search-forward (life-life-string) | ||
| 245 | (+ (point) fill-column fill-column 2) t) | ||
| 246 | (goto-beginning-of-buffer) | ||
| 247 | (insert-char (life-void-char) fill-column) | ||
| 248 | (insert ?\n) | ||
| 249 | (insert-char (life-void-char) fill-column) | ||
| 250 | (insert ?\n) | ||
| 251 | (setq life-window-start (+ life-window-start fill-column 1)))) | ||
| 252 | (goto-end-of-buffer) | ||
| 253 | ;; check for life within the last two lines of the buffer. | ||
| 254 | ;; If present insert two lifeless lines at the end. | ||
| 255 | (cond ((search-backward (life-life-string) | ||
| 256 | (- (point) fill-column fill-column 2) t) | ||
| 257 | (goto-end-of-buffer) | ||
| 258 | (insert-char (life-void-char) fill-column) | ||
| 259 | (insert ?\n) | ||
| 260 | (insert-char (life-void-char) fill-column) | ||
| 261 | (insert ?\n) | ||
| 262 | (setq life-window-start (+ life-window-start fill-column 1))))) | ||
| 263 | |||
| 264 | (defun life-display-generation (sleeptime) | ||
| 265 | (goto-char life-window-start) | ||
| 266 | (recenter 0) | ||
| 267 | (sit-for sleeptime)) | ||
| 268 | |||
| 269 | (defun life-extinct-quit () | ||
| 270 | (life-display-generation 0) | ||
| 271 | (signal 'life-extinct nil)) | ||
| 272 | |||
| 273 | (put 'life-extinct 'error-conditions '(life-extinct quit)) | ||
| 274 | (put 'life-extinct 'error-message "All life has perished") | ||
| 275 | |||
| 276 | |||