1 ;;; snake.el --- implementation of Snake for Emacs
3 ;; Copyright (C) 1997 Free Software Foundation, Inc.
5 ;; Author: Glynn Clements <glynn@sensei.co.uk>
9 ;; This file is part of GNU Emacs.
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)
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.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
35 ;; ;;;;;;;;;;;;; customization variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
37 (defvar snake-use-glyphs t
38 "Non-nil means use glyphs when available.")
40 (defvar snake-use-color t
41 "Non-nil means use color when available.")
43 (defvar snake-buffer-name "*Snake*"
44 "Name used for Snake buffer.")
46 (defvar snake-buffer-width 30
47 "Width of used portion of buffer.")
49 (defvar snake-buffer-height 22
50 "Height of used portion of buffer.")
52 (defvar snake-width 30
53 "Width of playing area.")
55 (defvar snake-height 20
56 "Height of playing area.")
58 (defvar snake-initial-length 5
59 "Initial length of snake.")
61 (defvar snake-initial-x 10
62 "Initial X position of snake.")
64 (defvar snake-initial-y 10
65 "Initial Y position of snake.")
67 (defvar snake-initial-velocity-x 1
68 "Initial X velocity of snake.")
70 (defvar snake-initial-velocity-y 0
71 "Initial Y velocity of snake.")
73 (defvar snake-tick-period 0.2
74 "The default time taken for the snake to advance one square.")
76 (defvar snake-mode-hook nil
77 "Hook run upon starting Snake.")
79 (defvar snake-score-x 0
80 "X position of score.")
82 (defvar snake-score-y snake-height
83 "Y position of score.")
85 (defvar snake-score-file "/tmp/snake-scores"
86 "File for holding high scores.")
88 ;; ;;;;;;;;;;;;; display options ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
90 (defvar snake-blank-options
95 (color-tty color-tty))
96 (((glyph color-x) [0 0 0])
97 (color-tty "black"))))
99 (defvar snake-snake-options
105 (color-tty color-tty)
107 (((glyph color-x) [1 1 0])
108 (color-tty "yellow"))))
110 (defvar snake-dot-options
115 (color-tty color-tty))
116 (((glyph color-x) [1 0 0])
119 (defvar snake-border-options
124 (((glyph color-x) [0.5 0.5 0.5])
125 (color-tty "white"))))
127 (defvar snake-space-options
132 ;; ;;;;;;;;;;;;; constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
134 (defconst snake-blank 0)
135 (defconst snake-snake 1)
136 (defconst snake-dot 2)
137 (defconst snake-border 3)
138 (defconst snake-space 4)
140 ;; ;;;;;;;;;;;;; variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
142 (defvar snake-length 0)
143 (defvar snake-velocity-x 1)
144 (defvar snake-velocity-y 0)
145 (defvar snake-positions nil)
146 (defvar snake-cycle 0)
147 (defvar snake-score 0)
148 (defvar snake-paused nil)
150 (make-variable-buffer-local 'snake-length)
151 (make-variable-buffer-local 'snake-velocity-x)
152 (make-variable-buffer-local 'snake-velocity-y)
153 (make-variable-buffer-local 'snake-positions)
154 (make-variable-buffer-local 'snake-cycle)
155 (make-variable-buffer-local 'snake-score)
156 (make-variable-buffer-local 'snake-paused)
158 ;; ;;;;;;;;;;;;; keymaps ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
160 (defvar snake-mode-map
161 (make-sparse-keymap 'snake-mode-map))
163 (define-key snake-mode-map "n" 'snake-start-game)
164 (define-key snake-mode-map "q" 'snake-end-game)
165 (define-key snake-mode-map "p" 'snake-pause-game)
167 (define-key snake-mode-map [left] 'snake-move-left)
168 (define-key snake-mode-map [right] 'snake-move-right)
169 (define-key snake-mode-map [up] 'snake-move-up)
170 (define-key snake-mode-map [down] 'snake-move-down)
172 (defvar snake-null-map
173 (make-sparse-keymap 'snake-null-map))
175 (define-key snake-null-map "n" 'snake-start-game)
177 ;; ;;;;;;;;;;;;;;;; game functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
179 (defun snake-display-options ()
180 (let ((options (make-vector 256 nil)))
181 (loop for c from 0 to 255 do
183 (cond ((= c snake-blank)
190 snake-border-options)
197 (defun snake-update-score ()
198 (let* ((string (format "Score: %05d" snake-score))
199 (len (length string)))
200 (loop for x from 0 to (1- len) do
201 (gamegrid-set-cell (+ snake-score-x x)
205 (defun snake-init-buffer ()
206 (gamegrid-init-buffer snake-buffer-width
209 (let ((buffer-read-only nil))
210 (loop for y from 0 to (1- snake-height) do
211 (loop for x from 0 to (1- snake-width) do
212 (gamegrid-set-cell x y snake-border)))
213 (loop for y from 1 to (- snake-height 2) do
214 (loop for x from 1 to (- snake-width 2) do
215 (gamegrid-set-cell x y snake-blank)))))
217 (defun snake-reset-game ()
218 (gamegrid-kill-timer)
220 (setq snake-length snake-initial-length
221 snake-velocity-x snake-initial-velocity-x
222 snake-velocity-y snake-initial-velocity-y
227 (let ((x snake-initial-x)
229 (dotimes (i snake-length)
230 (gamegrid-set-cell x y snake-snake)
231 (setq snake-positions (cons (vector x y) snake-positions))
232 (incf x snake-velocity-x)
233 (incf y snake-velocity-y)))
234 (snake-update-score))
236 (defun snake-update-game (snake-buffer)
237 "Called on each clock tick.
238 Advances the snake one square, testing for collision."
239 (if (and (not snake-paused)
240 (eq (current-buffer) snake-buffer))
241 (let* ((pos (car snake-positions))
242 (x (+ (aref pos 0) snake-velocity-x))
243 (y (+ (aref pos 1) snake-velocity-y))
244 (c (gamegrid-get-cell x y)))
245 (if (or (= c snake-border)
248 (cond ((= c snake-dot)
251 (snake-update-score))
253 (let* ((last-cons (nthcdr (- snake-length 2)
255 (tail-pos (cadr last-cons))
256 (x0 (aref tail-pos 0))
257 (y0 (aref tail-pos 1)))
258 (gamegrid-set-cell x0 y0
259 (if (= (% snake-cycle 5) 0)
263 (setcdr last-cons nil))))
264 (gamegrid-set-cell x y snake-snake)
265 (setq snake-positions
266 (cons (vector x y) snake-positions))))))
268 (defun snake-move-left ()
269 "Makes the snake move left"
271 (unless (= snake-velocity-x 1)
272 (setq snake-velocity-x -1
273 snake-velocity-y 0)))
275 (defun snake-move-right ()
276 "Makes the snake move right"
278 (unless (= snake-velocity-x -1)
279 (setq snake-velocity-x 1
280 snake-velocity-y 0)))
282 (defun snake-move-up ()
283 "Makes the snake move up"
285 (unless (= snake-velocity-y 1)
286 (setq snake-velocity-x 0
287 snake-velocity-y -1)))
289 (defun snake-move-down ()
290 "Makes the snake move down"
292 (unless (= snake-velocity-y -1)
293 (setq snake-velocity-x 0
294 snake-velocity-y 1)))
296 (defun snake-end-game ()
297 "Terminates the current game"
299 (gamegrid-kill-timer)
300 (use-local-map snake-null-map)
301 (gamegrid-add-score snake-score-file snake-score))
303 (defun snake-start-game ()
304 "Starts a new game of Snake"
307 (use-local-map snake-mode-map)
308 (gamegrid-start-timer snake-tick-period 'snake-update-game))
310 (defun snake-pause-game ()
311 "Pauses (or resumes) the current game"
313 (setq snake-paused (not snake-paused))
314 (message (and snake-paused "Game paused (press p to resume)")))
316 (defun snake-active-p ()
317 (eq (current-local-map) snake-mode-map))
319 (put 'snake-mode 'mode-class 'special)
322 "A mode for playing Snake.
324 snake-mode keybindings:
327 (kill-all-local-variables)
329 (make-local-hook 'kill-buffer-hook)
330 (add-hook 'kill-buffer-hook 'gamegrid-kill-timer nil t)
332 (use-local-map snake-null-map)
334 (setq major-mode 'snake-mode)
335 (setq mode-name "Snake")
337 (setq mode-popup-menu
339 ["Start new game" snake-start-game]
340 ["End game" snake-end-game
342 ["Pause" snake-pause-game
343 (and (snake-active-p) (not snake-paused))]
344 ["Resume" snake-pause-game
345 (and (snake-active-p) snake-paused)]))
347 (setq gamegrid-use-glyphs snake-use-glyphs)
348 (setq gamegrid-use-color snake-use-color)
350 (gamegrid-init (snake-display-options))
352 (run-hooks 'snake-mode-hook))
356 "Play the Snake game.
357 Move the snake around without colliding with its tail or with the border.
359 Eating dots causes the snake to get longer.
361 snake-mode keybindings:
363 \\[snake-start-game] Starts a new game of Snake
364 \\[snake-end-game] Terminates the current game
365 \\[snake-pause-game] Pauses (or resumes) the current game
366 \\[snake-move-left] Makes the snake move left
367 \\[snake-move-right] Makes the snake move right
368 \\[snake-move-up] Makes the snake move up
369 \\[snake-move-down] Makes the snake move down
374 (switch-to-buffer snake-buffer-name)
375 (gamegrid-kill-timer)
381 ;;; snake.el ends here