]> git.eshelyaron.com Git - emacs.git/commitdiff
Initial revision
authorRichard M. Stallman <rms@gnu.org>
Sun, 14 Jun 1998 21:24:54 +0000 (21:24 +0000)
committerRichard M. Stallman <rms@gnu.org>
Sun, 14 Jun 1998 21:24:54 +0000 (21:24 +0000)
lisp/play/snake.el [new file with mode: 0644]

diff --git a/lisp/play/snake.el b/lisp/play/snake.el
new file mode 100644 (file)
index 0000000..17dcb3a
--- /dev/null
@@ -0,0 +1,379 @@
+;;; snake.el -- Implementation of Snake for Emacs.
+
+;; Copyright (C) 1997 Free Software Foundation, Inc.
+
+;; Author: Glynn Clements <glynn@sensei.co.uk>
+;; Created: 1997-09-10
+;; 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 GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+(eval-when-compile
+  (require 'cl))
+
+(require 'gamegrid)
+
+;; ;;;;;;;;;;;;; customization variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar snake-use-glyphs t
+  "Non-nil means use glyphs when available.")
+
+(defvar snake-use-color t
+  "Non-nil means use color when available.")
+
+(defvar snake-buffer-name "*Snake*"
+  "Name used for Snake buffer.")
+
+(defvar snake-buffer-width 30
+  "Width of used portion of buffer.")
+
+(defvar snake-buffer-height 22
+  "Height of used portion of buffer.")
+
+(defvar snake-width 30
+  "Width of playing area.")
+
+(defvar snake-height 20
+  "Height of playing area.")
+
+(defvar snake-initial-length 5
+  "Initial length of snake.")
+
+(defvar snake-initial-x 10
+  "Initial X position of snake.")
+
+(defvar snake-initial-y 10
+  "Initial Y position of snake.")
+
+(defvar snake-initial-velocity-x 1
+  "Initial X velocity of snake.")
+
+(defvar snake-initial-velocity-y 0
+  "Initial Y velocity of snake.")
+
+(defvar snake-tick-period 0.2
+  "The default time taken for the snake to advance one square.")
+
+(defvar snake-mode-hook nil
+  "Hook run upon starting Snake.")
+
+(defvar snake-score-x 0
+  "X position of score.")
+
+(defvar snake-score-y snake-height
+  "Y position of score.")
+
+(defvar snake-score-file "/tmp/snake-scores"
+  "File for holding high scores.")
+
+;; ;;;;;;;;;;;;; display options ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar snake-blank-options
+  '(((glyph colorize)
+     (t ?\040))
+    ((color-x color-x)
+     (mono-x grid-x)
+     (color-tty color-tty))
+    (((glyph color-x) [0 0 0])
+     (color-tty "black"))))
+
+(defvar snake-snake-options
+  '(((glyph colorize)
+     (emacs-tty ?O)
+     (t ?\040))
+    ((color-x color-x)
+     (mono-x mono-x)
+     (color-tty color-tty)
+     (mono-tty mono-tty))
+    (((glyph color-x) [1 1 0])
+     (color-tty "yellow"))))
+
+(defvar snake-dot-options
+  '(((glyph colorize)
+     (t ?\*))
+    ((color-x color-x)
+     (mono-x grid-x)
+     (color-tty color-tty))
+    (((glyph color-x) [1 0 0])
+     (color-tty "red"))))
+
+(defvar snake-border-options
+  '(((glyph colorize)
+     (t ?\+))
+    ((color-x color-x)
+     (mono-x grid-x))
+    (((glyph color-x) [0.5 0.5 0.5])
+     (color-tty "white"))))
+
+(defvar snake-space-options
+  '(((t ?\040))
+    nil
+    nil))
+
+;; ;;;;;;;;;;;;; constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst snake-blank  0)
+(defconst snake-snake  1)
+(defconst snake-dot    2)
+(defconst snake-border 3)
+(defconst snake-space  4)
+
+;; ;;;;;;;;;;;;; variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar snake-length 0)
+(defvar snake-velocity-x 1)
+(defvar snake-velocity-y 0)
+(defvar snake-positions nil)
+(defvar snake-cycle 0)
+(defvar snake-score 0)
+(defvar snake-paused nil)
+
+(make-variable-buffer-local 'snake-length)
+(make-variable-buffer-local 'snake-velocity-x)
+(make-variable-buffer-local 'snake-velocity-y)
+(make-variable-buffer-local 'snake-positions)
+(make-variable-buffer-local 'snake-cycle)
+(make-variable-buffer-local 'snake-score)
+(make-variable-buffer-local 'snake-paused)
+
+;; ;;;;;;;;;;;;; keymaps ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar snake-mode-map
+  (make-sparse-keymap 'snake-mode-map))
+
+(define-key snake-mode-map "n"         'snake-start-game)
+(define-key snake-mode-map "q"         'snake-end-game)
+(define-key snake-mode-map "p"         'snake-pause-game)
+
+(define-key snake-mode-map [left]      'snake-move-left)
+(define-key snake-mode-map [right]     'snake-move-right)
+(define-key snake-mode-map [up]                'snake-move-up)
+(define-key snake-mode-map [down]      'snake-move-down)
+
+(defvar snake-null-map
+  (make-sparse-keymap 'snake-null-map))
+
+(define-key snake-null-map "n"         'snake-start-game)
+
+;; ;;;;;;;;;;;;;;;; game functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun snake-display-options ()
+  (let ((options (make-vector 256 nil)))
+    (loop for c from 0 to 255 do
+      (aset options c
+           (cond ((= c snake-blank)
+                  snake-blank-options)
+                  ((= c snake-snake)
+                  snake-snake-options)
+                  ((= c snake-dot)
+                  snake-dot-options)
+                  ((= c snake-border)
+                  snake-border-options)
+                  ((= c snake-space)
+                  snake-space-options)
+                  (t
+                  '(nil nil nil)))))
+    options))
+
+(defun snake-update-score ()
+  (let* ((string (format "Score:  %05d" snake-score))
+        (len (length string)))
+    (loop for x from 0 to (1- len) do
+      (gamegrid-set-cell (+ snake-score-x x)
+                        snake-score-y
+                        (aref string x)))))
+
+(defun snake-init-buffer ()
+  (gamegrid-init-buffer snake-buffer-width
+                       snake-buffer-height
+                       snake-space)
+  (let ((buffer-read-only nil))
+    (loop for y from 0 to (1- snake-height) do
+         (loop for x from 0 to (1- snake-width) do
+               (gamegrid-set-cell x y snake-border)))
+    (loop for y from 1 to (- snake-height 2) do
+         (loop for x from 1 to (- snake-width 2) do
+               (gamegrid-set-cell x y snake-blank)))))
+
+(defun snake-reset-game ()
+  (gamegrid-kill-timer)
+  (snake-init-buffer)
+  (setq snake-length           snake-initial-length
+       snake-velocity-x        snake-initial-velocity-x
+       snake-velocity-y        snake-initial-velocity-y
+       snake-positions         nil
+       snake-cycle             1
+       snake-score             0
+       snake-paused            nil)
+  (let ((x snake-initial-x)
+       (y snake-initial-y))
+    (dotimes (i snake-length)
+      (gamegrid-set-cell x y snake-snake)
+      (setq snake-positions (cons (vector x y) snake-positions))
+      (incf x snake-velocity-x)
+      (incf y snake-velocity-y)))
+  (snake-update-score))
+
+(defun snake-update-game (snake-buffer)
+  "Called on each clock tick.
+Advances the snake one square, testing for collision."
+  (if (and (not snake-paused)
+          (eq (current-buffer) snake-buffer))
+      (let* ((pos (car snake-positions))
+            (x (+ (aref pos 0) snake-velocity-x))
+            (y (+ (aref pos 1) snake-velocity-y))
+            (c (gamegrid-get-cell x y)))
+       (if (or (= c snake-border)
+               (= c snake-snake))
+           (snake-end-game)
+         (cond ((= c snake-dot)
+                (incf snake-length)
+                (incf snake-score)
+                (snake-update-score))
+               (t
+                (let* ((last-cons (nthcdr (- snake-length 2)
+                                          snake-positions))
+                       (tail-pos (cadr last-cons))
+                       (x0 (aref tail-pos 0))
+                       (y0 (aref tail-pos 1)))
+                  (gamegrid-set-cell x0 y0
+                                     (if (= (% snake-cycle 5) 0)
+                                         snake-dot
+                                       snake-blank))
+                  (incf snake-cycle)
+                  (setcdr last-cons nil))))
+         (gamegrid-set-cell x y snake-snake)
+         (setq snake-positions
+               (cons (vector x y) snake-positions))))))
+
+(defun snake-move-left ()
+  "Makes the snake move left"
+  (interactive)
+  (unless (= snake-velocity-x 1)
+    (setq snake-velocity-x -1
+         snake-velocity-y 0)))
+
+(defun snake-move-right ()
+  "Makes the snake move right"
+  (interactive)
+  (unless (= snake-velocity-x -1)
+    (setq snake-velocity-x 1
+         snake-velocity-y 0)))
+
+(defun snake-move-up ()
+  "Makes the snake move up"
+  (interactive)
+  (unless (= snake-velocity-y 1)
+    (setq snake-velocity-x 0
+         snake-velocity-y -1)))
+
+(defun snake-move-down ()
+  "Makes the snake move down"
+  (interactive)
+  (unless (= snake-velocity-y -1)
+    (setq snake-velocity-x 0
+         snake-velocity-y 1)))
+
+(defun snake-end-game ()
+  "Terminates the current game"
+  (interactive)
+  (gamegrid-kill-timer)
+  (use-local-map snake-null-map)
+  (gamegrid-add-score snake-score-file snake-score))
+
+(defun snake-start-game ()
+  "Starts a new game of Snake"
+  (interactive)
+  (snake-reset-game)
+  (use-local-map snake-mode-map)
+  (gamegrid-start-timer snake-tick-period 'snake-update-game))
+
+(defun snake-pause-game ()
+  "Pauses (or resumes) the current game"
+  (interactive)
+  (setq snake-paused (not snake-paused))
+  (message (and snake-paused "Game paused (press p to resume)")))
+
+(defun snake-active-p ()
+  (eq (current-local-map) snake-mode-map))
+
+(put 'snake-mode 'mode-class 'special)
+
+(defun snake-mode ()
+  "A mode for playing Snake.
+
+snake-mode keybindings:
+   \\{snake-mode-map}
+"
+  (kill-all-local-variables)
+
+  (make-local-hook 'kill-buffer-hook)
+  (add-hook 'kill-buffer-hook 'gamegrid-kill-timer nil t)
+
+  (use-local-map snake-null-map)
+
+  (setq major-mode 'snake-mode)
+  (setq mode-name "Snake")
+
+  (setq mode-popup-menu
+       '("Snake Commands"
+         ["Start new game"     snake-start-game]
+         ["End game"           snake-end-game
+          (snake-active-p)]
+         ["Pause"              snake-pause-game
+          (and (snake-active-p) (not snake-paused))]
+         ["Resume"             snake-pause-game
+          (and (snake-active-p) snake-paused)]))
+
+  (setq gamegrid-use-glyphs snake-use-glyphs)
+  (setq gamegrid-use-color snake-use-color)
+
+  (gamegrid-init (snake-display-options))
+
+  (run-hooks 'snake-mode-hook))
+
+;;;###autoload
+(defun snake ()
+  "Play the Snake game.
+Move the snake around without colliding with its tail or with the border.
+
+Eating dots causes the snake to get longer.
+
+snake-mode keybindings:
+   \\<snake-mode-map>
+\\[snake-start-game]   Starts a new game of Snake
+\\[snake-end-game]     Terminates the current game
+\\[snake-pause-game]   Pauses (or resumes) the current game
+\\[snake-move-left]    Makes the snake move left
+\\[snake-move-right]   Makes the snake move right
+\\[snake-move-up]      Makes the snake move up
+\\[snake-move-down]    Makes the snake move down
+
+"
+  (interactive)
+
+  (switch-to-buffer snake-buffer-name)
+  (gamegrid-kill-timer)
+  (snake-mode)
+  (snake-start-game))
+
+(provide 'snake)
+
+;;; snake.el ends here