Commits

Anonymous committed 3b074e9

Created

Comments (0)

Files changed (8)

+1998-01-11  SL Baur  <steve@altair.xemacs.org>
+
+	* Makefile: Update to newer package interface.
+
+1997-01-06  SL Baur  <steve@altair.xemacs.org>
+
+	* Makefile: Created.
+# Makefile for Glynn Clements Games lisp code
+
+# This file is part of XEmacs.
+
+# XEmacs 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.
+
+# XEmacs 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 XEmacs; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# This XEmacs package contains independent single file lisp packages
+
+VERSION = 1.01
+PACKAGE = games
+PKG_TYPE = regular
+REQUIRES = xemacs-base
+CATEGORY = games
+
+ELCS = gamegrid.elc snake.elc tetris.elc sokoban.elc
+
+include ../../XEmacs.rules
+
+all:: $(ELCS) auto-autoloads.elc
+
+srckit: srckit-std
+
+binkit: all
+	-rm -rf $(STAGING)/lisp/$(PACKAGE)
+	-rm -f $(STAGING)/etc/sokoban.levels
+	-mkdir -p $(STAGING)/lisp/$(PACKAGE)
+	-mkdir -p $(STAGING)/etc
+	cp -a ChangeLog *.el* $(STAGING)/lisp/$(PACKAGE)
+	cp -a sokoban.levels $(STAGING)/etc
+	(cd $(STAGING); \
+	rm -f $(PACKAGE)-$(VERSION)-pkg.tar*; \
+	tar cf $(PACKAGE)-$(VERSION)-pkg.tar lisp/$(PACKAGE) \
+		etc/sokoban.levels; \
+	gzip -v9 $(PACKAGE)-$(VERSION)-pkg.tar)
+;;; gamegrid.el -- Library for implementing grid-based games on Emacs.
+
+;; Copyright (C) 1997 Glynn Clements <glynn@sensei.co.uk>
+
+;; Author: Glynn Clements <glynn@sensei.co.uk>
+;; Version: 1.0
+;; Created: 1997-08-13
+;; Keywords: games
+
+;; This file is part of XEmacs.
+
+;; XEmacs 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 of the License, or
+;; (at your option) any later version.
+
+;; XEmacs 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 XEmacs; see the file COPYING.  If not, write to the Free
+;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+;; 02111-1307, USA.
+
+;;; Synched up with: Not synched.
+
+;;; Commentary:
+
+;; URL: ftp://sensei.co.uk/misc/elisp-games.tar.gz
+;; Tested with XEmacs 20.3/4/5 and Emacs 19.34
+
+(eval-when-compile
+  (require 'cl))
+
+;; ;;;;;;;;;;;;; buffer-local variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar gamegrid-use-glyphs t
+  "Non-nil means use glyphs when available")
+
+(defvar gamegrid-use-color t
+  "Non-nil means use color when available")
+
+(defvar gamegrid-font "-*-courier-medium-r-*-*-*-140-100-75-*-*-iso8859-*"
+  "Name of the font used in X mode")
+
+(defvar gamegrid-display-options nil)
+
+(defvar gamegrid-buffer-width 0)
+(defvar gamegrid-buffer-height 0)
+(defvar gamegrid-blank 0)
+
+(defvar gamegrid-timer nil)
+
+(defvar gamegrid-display-mode nil)
+
+(defvar gamegrid-display-table)
+
+(defvar gamegrid-face-table nil)
+
+(defvar gamegrid-buffer-start 1)
+
+(defvar gamegrid-score-file-length 50
+  "Number of high scores to keep")
+
+(make-variable-buffer-local 'gamegrid-use-glyphs)
+(make-variable-buffer-local 'gamegrid-use-color)
+(make-variable-buffer-local 'gamegrid-font)
+(make-variable-buffer-local 'gamegrid-display-options)
+(make-variable-buffer-local 'gamegrid-buffer-width)
+(make-variable-buffer-local 'gamegrid-buffer-height)
+(make-variable-buffer-local 'gamegrid-blank)
+(make-variable-buffer-local 'gamegrid-timer)
+(make-variable-buffer-local 'gamegrid-display-mode)
+(make-variable-buffer-local 'gamegrid-display-table)
+(make-variable-buffer-local 'gamegrid-face-table)
+(make-variable-buffer-local 'gamegrid-buffer-start)
+(make-variable-buffer-local 'gamegrid-score-file-length)
+
+;; ;;;;;;;;;;;;; global variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar gamegrid-grid-x-face nil)
+(defvar gamegrid-mono-x-face nil)
+(defvar gamegrid-mono-tty-face nil)
+
+;; ;;;;;;;;;;;;; constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst gamegrid-glyph-height 16)
+
+(defconst gamegrid-xpm "\
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+\"16 16 3 1\",
+/* colors */
+\"+ s col1\",
+\". s col2\",
+\"- s col3\",
+/* pixels */
+\"---------------+\",
+\"--------------++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"--............++\",
+\"-+++++++++++++++\",
+\"++++++++++++++++\"
+};
+"
+  "XPM format image used for each square")
+
+;; ;;;;;;;;;;;;;;;; miscellaneous functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defsubst gamegrid-characterp (arg)
+  (if (fboundp 'characterp)
+      (characterp arg)
+    (integerp arg)))
+
+;; ;;;;;;;;;;;;; display functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun gamegrid-color (color shade)
+  (let* ((v (floor (* shade 255)))
+	 (r (* v (aref color 0)))
+	 (g (* v (aref color 1)))
+	 (b (* v (aref color 2))))
+    (format "#%02x%02x%02x" r g b)))
+
+(defun gamegrid-set-font (face)
+  (if gamegrid-font
+      (condition-case nil
+	  (set-face-font face gamegrid-font)
+	('error nil))))
+
+(defun gamegrid-setup-face (face color)
+  (set-face-foreground face color)
+  (set-face-background face color)
+  (gamegrid-set-font face)
+  (condition-case nil
+      (set-face-background-pixmap face [nothing]);; XEmacs
+    ('error nil))
+  (condition-case nil
+      (set-face-background-pixmap face nil);; Emacs
+    ('error nil)))
+
+(defun gamegrid-make-mono-tty-face ()
+  (let ((face (make-face 'gamegrid-mono-tty-face)))
+    (condition-case nil
+	(set-face-property face 'reverse t)
+      ('error nil))
+    face))
+
+(defun gamegrid-make-color-tty-face (color)
+  (let* ((hex (gamegrid-color color 1.0))
+	 (name (intern (format "gamegrid-color-tty-face-%s" hex)))
+	 (face (make-face name)))
+    (gamegrid-setup-face face color)
+    face))
+
+(defun gamegrid-make-grid-x-face ()
+  (let ((face (make-face 'gamegrid-x-border-face)))
+    (gamegrid-set-font face)
+    face))
+
+(defun gamegrid-make-mono-x-face ()
+  (let ((face (make-face 'gamegrid-mono-x-face))
+	(color (face-foreground 'default)))
+    (if (null color)
+	(setq color
+	      (cdr-safe (assq 'foreground-color (frame-parameters)))))
+    (gamegrid-setup-face face color)
+    face))
+
+(defun gamegrid-make-color-x-face (color)
+  (let* ((hex (gamegrid-color color 1.0))
+	 (name (intern (format "gamegrid-color-x-face-%s" hex)))
+	 (face (make-face name)))
+    (gamegrid-setup-face face (gamegrid-color color 1.0))
+    face))
+
+(defun gamegrid-make-face (data-spec-list color-spec-list)
+  (let ((data (gamegrid-match-spec-list data-spec-list))
+	(color (gamegrid-match-spec-list color-spec-list)))
+    (case data
+      ('color-x
+       (gamegrid-make-color-x-face color))
+      ('grid-x
+       (unless gamegrid-grid-x-face
+	 (setq gamegrid-grid-x-face (gamegrid-make-grid-x-face)))
+       gamegrid-grid-x-face)
+      ('mono-x
+       (unless gamegrid-mono-x-face
+	 (setq gamegrid-mono-x-face (gamegrid-make-mono-x-face)))
+       gamegrid-mono-x-face)
+      ('color-tty
+       (gamegrid-make-color-tty-face color))
+      ('mono-tty
+       (unless gamegrid-mono-tty-face
+	 (setq gamegrid-mono-tty-face (gamegrid-make-mono-tty-face)))
+       gamegrid-mono-tty-face))))
+
+(defun gamegrid-colorize-glyph (color)
+  (make-glyph
+   (vector
+    'xpm
+    :data gamegrid-xpm
+    :color-symbols (list (cons "col1" (gamegrid-color color 0.6))
+			 (cons "col2" (gamegrid-color color 0.8))
+			 (cons "col3" (gamegrid-color color 1.0))))))
+
+(defun gamegrid-match-spec (spec)
+  (let ((locale (car spec))
+	(value (cadr spec)))
+    (and (or (eq locale t)
+	     (and (listp locale)
+		  (memq gamegrid-display-mode locale))
+	     (and (symbolp locale)
+		  (eq gamegrid-display-mode locale)))
+	 value)))
+
+(defun gamegrid-match-spec-list (spec-list)
+  (and spec-list
+       (or (gamegrid-match-spec (car spec-list))
+	   (gamegrid-match-spec-list (cdr spec-list)))))
+
+(defun gamegrid-make-glyph (data-spec-list color-spec-list)
+  (let ((data (gamegrid-match-spec-list data-spec-list))
+	(color (gamegrid-match-spec-list color-spec-list)))
+    (cond ((gamegrid-characterp data)
+	   (vector data))
+	  ((eq data 'colorize)
+	   (gamegrid-colorize-glyph color))
+	  ((vectorp data)
+	   (make-glyph data)))))
+
+(defun gamegrid-color-display-p ()
+  (if (fboundp 'device-class)
+      (eq (device-class (selected-device)) 'color)
+    (eq (cdr-safe (assq 'display-type (frame-parameters))) 'color)))
+
+(defun gamegrid-display-type ()
+  (cond ((and gamegrid-use-glyphs
+	      (eq window-system 'x)
+	      (featurep 'xpm))
+	 'glyph)
+	((and gamegrid-use-color
+	      (eq window-system 'x)
+	      (gamegrid-color-display-p))
+	 'color-x)
+	((eq window-system 'x)
+	 'mono-x)
+	((and gamegrid-use-color
+	      (gamegrid-color-display-p))
+	 'color-tty)
+	((fboundp 'set-face-property)
+	 'mono-tty)
+	(t
+	 'emacs-tty)))
+
+(defun gamegrid-set-display-table ()
+  (if (fboundp 'specifierp)
+      (add-spec-to-specifier current-display-table
+			     gamegrid-display-table
+			     (current-buffer)
+			     nil
+			     'remove-locale)
+    (setq buffer-display-table gamegrid-display-table)))
+
+(defun gamegrid-hide-cursor ()
+  (if (fboundp 'specifierp)
+      (set-specifier text-cursor-visible-p nil (current-buffer))))
+
+(defun gamegrid-setup-default-font ()
+  (cond ((eq gamegrid-display-mode 'glyph)
+	 (let* ((font-spec (face-property 'default 'font))
+		(name (font-name font-spec))
+		(max-height nil))
+	   (loop for c from 0 to 255 do
+	     (let ((glyph (aref gamegrid-display-table c)))
+	       (cond ((glyphp glyph)
+		      (let ((height (glyph-height glyph)))
+			(if (or (null max-height)
+				(< max-height height))
+			    (setq max-height height)))))))
+	   (if max-height
+	       (while (> (font-height font-spec) max-height)
+		 (setq name (x-find-smaller-font name))
+		 (add-spec-to-specifier font-spec name (current-buffer))))))))
+
+(defun gamegrid-initialize-display ()
+  (setq gamegrid-display-mode (gamegrid-display-type))
+  (setq gamegrid-display-table (make-display-table))
+  (setq gamegrid-face-table (make-vector 256 nil))
+  (loop for c from 0 to 255 do
+    (let* ((spec (aref gamegrid-display-options c))
+	   (glyph (gamegrid-make-glyph (car spec) (caddr spec)))
+	   (face (gamegrid-make-face (cadr spec) (caddr spec))))
+      (aset gamegrid-face-table c face)
+      (aset gamegrid-display-table c glyph)))
+  (gamegrid-setup-default-font)
+  (gamegrid-set-display-table)
+  (gamegrid-hide-cursor))
+
+
+(defun gamegrid-set-face (c)
+  (unless (eq gamegrid-display-mode 'glyph)
+    (put-text-property (1- (point))
+		       (point)
+		       'face
+		       (aref gamegrid-face-table c))))
+
+(defun gamegrid-cell-offset (x y)
+  (+ gamegrid-buffer-start
+     (* (1+ gamegrid-buffer-width) y)
+     x))
+
+;; ;;;;;;;;;;;;;;;; grid functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun gamegrid-get-cell (x y)
+  (char-after (gamegrid-cell-offset x y)))
+
+(defun gamegrid-set-cell (x y c)
+  (save-excursion
+    (let ((buffer-read-only nil))
+      (goto-char (gamegrid-cell-offset x y))
+      (delete-char 1)
+      (insert-char c 1)
+      (gamegrid-set-face c))))
+
+(defun gamegrid-init-buffer (width height blank)
+  (setq gamegrid-buffer-width width
+	gamegrid-buffer-height height)
+  (let ((line (concat
+	       (make-string width blank)
+	       "\n"))
+	(buffer-read-only nil))
+    (erase-buffer)
+    (setq gamegrid-buffer-start (point))
+    (dotimes (i height)
+      (insert-string line))
+    (goto-char (point-min))))
+
+(defun gamegrid-init (options)
+  (setq buffer-read-only t
+	truncate-lines t
+	gamegrid-display-options options)
+  (buffer-disable-undo (current-buffer))
+  (gamegrid-initialize-display))
+
+;; ;;;;;;;;;;;;;;;; timer functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun gamegrid-start-timer (period func)
+  (setq gamegrid-timer
+	(if (featurep 'itimer)
+	    (start-itimer "Gamegrid"
+			  func
+			  period
+			  period
+			  nil
+			  t
+			  (current-buffer))
+	  (run-with-timer period
+			  period
+			  func
+			  (current-buffer)))))
+
+(defun gamegrid-set-timer (delay)
+  (if gamegrid-timer
+      (if (featurep 'itimer)
+	  (set-itimer-restart gamegrid-timer delay)
+	(timer-set-time gamegrid-timer
+			(list (aref gamegrid-timer 1)
+			      (aref gamegrid-timer 2)
+			      (aref gamegrid-timer 3))
+			delay))))
+
+(defun gamegrid-kill-timer ()
+  (if gamegrid-timer
+      (if (featurep 'itimer)
+          (delete-itimer gamegrid-timer)
+        (timer-set-time gamegrid-timer '(0 0 0) nil)))
+  (setq gamegrid-timer nil))
+
+;; ;;;;;;;;;;;;;;; high score functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun gamegrid-add-score (file score)
+  "Adds the current score to the high score file"
+  (find-file-other-window file)
+  (setq buffer-read-only nil)
+  (goto-char (point-max))
+  (insert (format "%05d\t%s\t%s <%s>\n"
+		  score
+		  (current-time-string)
+		  (user-full-name)
+		  (cond ((fboundp 'user-mail-address)
+			 (user-mail-address))
+			((boundp 'user-mail-address)
+			 user-mail-address)
+			(t ""))))
+  (sort-numeric-fields 1 (point-min) (point-max))
+  (reverse-region (point-min)(point-max))
+  (goto-line (1+ gamegrid-score-file-length))
+  (delete-region (point) (point-max))
+  (setq buffer-read-only t)
+  (save-buffer))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(provide 'gamegrid)
+(games
+  (version VERSION
+   description "Tetris, Sokoban, and Snake."
+   filename FILENAME
+   md5sum MD5SUM
+   size SIZE
+   provides (gamegrid snake tetris sokoban)
+   requires (REQUIRES)
+   type regular
+))
+;;; snake.el -- Implementation of Snake for Emacs.
+
+;; Copyright (C) 1997 Glynn Clements <glynn@sensei.co.uk>
+
+;; Author: Glynn Clements <glynn@sensei.co.uk>
+;; Version: 1.0
+;; Created: 1997-09-10
+;; Keywords: games
+
+;; This file is part of XEmacs.
+
+;; XEmacs 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 of the License, or
+;; (at your option) any later version.
+
+;; XEmacs 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 XEmacs; see the file COPYING.  If not, write to the Free
+;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+;; 02111-1307, USA.
+
+;;; Synched up with: Not synched.
+
+;;; Commentary:
+
+;; URL: ftp://sensei.co.uk/misc/elisp-games.tar.gz
+;; Tested with XEmacs 20.3/4/5 and Emacs 19.34
+
+(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)")))
+
+(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 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 ()
+  "Snake
+
+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
+
+;;; sokoban.el -- Implementation of Sokoban for Emacs.
+
+;; Copyright (C) 1997 Glynn Clements <glynn@sensei.co.uk>
+
+;; Author: Glynn Clements <glynn@sensei.co.uk>
+;; Version: 1.0
+;; Created: 1997-09-11
+;; Keywords: games
+
+;; This file is part of XEmacs.
+
+;; XEmacs 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 of the License, or
+;; (at your option) any later version.
+
+;; XEmacs 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 XEmacs; see the file COPYING.  If not, write to the Free
+;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+;; 02111-1307, USA.
+
+;;; Synched up with: Not synched.
+
+;;; Commentary:
+
+;; URL: ftp://sensei.co.uk/misc/elisp-games.tar.gz
+;; Tested with XEmacs 20.3/4/5 and Emacs 19.34
+
+;; The game is based upon XSokoban, by
+;; Michael Bischoff <mbi@mo.math.nat.tu-bs.de>
+
+;; The levels and some of the pixmaps were
+;; taken directly from XSokoban
+
+(eval-when-compile
+  (require 'cl))
+
+(require 'gamegrid)
+
+;; ;;;;;;;;;;;;; customization variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar sokoban-use-glyphs t
+  "Non-nil means use glyphs when available")
+
+(defvar sokoban-use-color t
+  "Non-nil means use color when available")
+
+(defvar sokoban-font "-*-courier-medium-r-*-*-*-200-100-75-*-*-iso8859-*"
+  "Name of the font used in X mode")
+
+(defvar sokoban-buffer-name "*Sokoban*")
+
+(defvar sokoban-temp-buffer-name " Sokoban-tmp")
+
+(defvar sokoban-level-file
+  (locate-data-file "sokoban.levels"))
+
+(defvar sokoban-width 20)
+(defvar sokoban-height 16)
+
+(defvar sokoban-buffer-width 20)
+(defvar sokoban-buffer-height 20)
+
+(defvar sokoban-score-x 0)
+(defvar sokoban-score-y 17)
+
+(defvar sokoban-level-data nil)
+
+;; ;;;;;;;;;;;;; constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst sokoban-floor-xpm "\
+/* XPM */
+static char * floor_xpm[] = {
+\"32 32 1 1\",
+\"  c None\",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+};
+")
+
+(defconst sokoban-target-xpm "\
+/* XPM */
+static char * target_xpm[] = {
+\"32 32 3 1\",
+\"  c None\",
+\". c black\",
+\"X c yellow\",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"          ............          \",
+\"          .XXXXXXXXXX.          \",
+\"           .XXXXXXXX.           \",
+\"            .XXXXXX.            \",
+\"      ..     .XXXX.     ..      \",
+\"      .X.     .XX.     .X.      \",
+\"      .XX.     ..     .XX.      \",
+\"      .XXX.          .XXX.      \",
+\"      .XXXX.        .XXXX.      \",
+\"      .XXXXX.      .XXXXX.      \",
+\"      .XXXXX.      .XXXXX.      \",
+\"      .XXXX.        .XXXX.      \",
+\"      .XXX.          .XXX.      \",
+\"      .XX.     ..     .XX.      \",
+\"      .X.     .XX.     .X.      \",
+\"      ..     .XXXX.     ..      \",
+\"            .XXXXXX.            \",
+\"           .XXXXXXXX.           \",
+\"          .XXXXXXXXXX.          \",
+\"          ............          \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+\"                                \",
+};
+")
+
+(defconst sokoban-wall-xpm "\
+/* XPM */
+static char * wall_xpm[] = {
+\"32 32 2 1\",
+\"  c white\",
+\". c SteelBlue\",
+\" .............................. \",
+\". ............................ .\",
+\".. .......................... . \",
+\"... ........................ . .\",
+\"....                        . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\".... ......................  . .\",
+\".... ...................... . . \",
+\"....                         . .\",
+\"... . . . . . . . . . . . .   . \",
+\".. . . . . . . . . . . . . .   .\",
+\". . . . . . . . . . . . . . .   \",
+\" . . . . . . . . . . . . . . .  \",
+};
+")
+
+(defconst sokoban-block-xpm "\
+/* XPM */
+static char * block_xpm[] = {
+\"32 32 3 1\",
+\"  c None\",
+\". c black\",
+\"X c yellow\",
+\".............................   \",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.   \",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX..  \",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX..  \",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.X. \",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.X. \",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\".............................XX.\",
+\".XXXXXXXXXXXXXXXXXXXXXXXXXXX.XX.\",
+\" .XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.\",
+\" .XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.\",
+\"  .XXXXXXXXXXXXXXXXXXXXXXXXXXX..\",
+\"  .XXXXXXXXXXXXXXXXXXXXXXXXXXX..\",
+\"   .XXXXXXXXXXXXXXXXXXXXXXXXXXX.\",
+\"   .............................\",
+};
+")
+
+(defconst sokoban-player-xpm "\
+/* XPM */
+static char * player_xpm[] = {
+\"32 32 3 1\",
+\"  c None\",
+\"o c white\",
+\". c black\",
+\"                                \",
+\"                                \",
+\"                                \",
+\"            oooooooo            \",
+\"            o......o            \",
+\"           o.oooooo.o           \",
+\"           o.oooooo.o           \",
+\"          o.oooooooo.o          \",
+\"          o.o..oo..o.o          \",
+\"          o.oooooooo.o          \",
+\"          oo.o....o.oo          \",
+\"         oo..oo..oo..oo         \",
+\"         o....o..o....o         \",
+\"         o.o..o..o..o.o         \",
+\"         o.o...oo...o.o         \",
+\"        o.oo........oo.o        \",
+\"        o.oo........oo.o        \",
+\"       o.ooo........ooo.o       \",
+\"       o.ooo........ooo.o       \",
+\"       o.ooo........ooo.o       \",
+\"        o.oo........oo.o        \",
+\"        o.oo........oo.o        \",
+\"        o.o..........o.o        \",
+\"         o............o         \",
+\"          o..........o          \",
+\"           o........o           \",
+\"          o.o.oooo.o.o          \",
+\"         o.....oo.....o         \",
+\"        o......oo......o        \",
+\"       o.......oo.......o       \",
+\"      o..o..o..oo.oo..o..o      \",
+\"      oooooooooooooooooooo      \",
+};
+")
+
+(defconst sokoban-floor ?\+)
+(defconst sokoban-target ?\.)
+(defconst sokoban-wall ?\#)
+(defconst sokoban-block ?\$)
+(defconst sokoban-player ?\@)
+
+;; ;;;;;;;;;;;;; display options ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar sokoban-floor-options
+  `(((glyph
+      [xpm :data ,sokoban-floor-xpm])
+     (t ?\040))
+    ((color-x color-x)
+     (mono-x grid-x)
+     (color-tty color-tty))
+    (((glyph color-x) [0 0 0])
+     (color-tty "black"))))
+
+(defvar sokoban-target-options
+  `(((glyph
+      [xpm :data ,sokoban-target-xpm])
+     ((mono-x mono-tty emacs-tty) ?\.)
+     (t ?\040))
+    ((color-x color-x)
+     (mono-x grid-x)
+     (color-tty color-tty))
+    (((glyph color-x) [1 1 0.5])
+     (color-tty "yellow"))))
+
+(defvar sokoban-wall-options
+  `(((glyph
+      [xpm :data ,sokoban-wall-xpm])
+     (emacs-tty ?\X)
+     (t ?\040))
+    ((color-x color-x)
+     (mono-x mono-x)
+     (color-tty color-tty)
+     (mono-tty mono-tty))
+    (((glyph color-x) [0 0 1])
+     (color-tty "blue"))))
+
+(defvar sokoban-block-options
+  `(((glyph
+      [xpm :data ,sokoban-block-xpm])
+     ((mono-x mono-tty emacs-tty) ?\O)
+     (t ?\040))
+    ((color-x color-x)
+     (mono-x grid-x)
+     (color-tty color-tty))
+    (((glyph color-x) [1 0 0])
+     (color-tty "red"))))
+
+(defvar sokoban-player-options
+  `(((glyph
+      [xpm :data ,sokoban-player-xpm])
+     (t ?\*))
+    ((color-x color-x)
+     (mono-x grid-x)
+     (color-tty color-tty))
+    (((glyph color-x) [0 1 0])
+     (color-tty "green"))))
+
+;; ;;;;;;;;;;;;; variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar sokoban-level 0)
+(defvar sokoban-level-map nil)
+(defvar sokoban-targets 0)
+(defvar sokoban-x 0)
+(defvar sokoban-y 0)
+(defvar sokoban-moves 0)
+(defvar sokoban-pushes 0)
+(defvar sokoban-done 0)
+
+(make-variable-buffer-local 'sokoban-level)
+(make-variable-buffer-local 'sokoban-level-map)
+(make-variable-buffer-local 'sokoban-targets)
+(make-variable-buffer-local 'sokoban-x)
+(make-variable-buffer-local 'sokoban-y)
+(make-variable-buffer-local 'sokoban-moves)
+(make-variable-buffer-local 'sokoban-pushes)
+(make-variable-buffer-local 'sokoban-done)
+
+;; ;;;;;;;;;;;;; keymaps ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar sokoban-mode-map
+  (make-sparse-keymap 'sokoban-mode-map))
+
+(define-key sokoban-mode-map "n"	'sokoban-start-game)
+(define-key sokoban-mode-map "r"	'sokoban-restart-level)
+(define-key sokoban-mode-map "g"	'sokoban-goto-level)
+
+(define-key sokoban-mode-map [left]	'sokoban-move-left)
+(define-key sokoban-mode-map [right]	'sokoban-move-right)
+(define-key sokoban-mode-map [up]	'sokoban-move-up)
+(define-key sokoban-mode-map [down]	'sokoban-move-down)
+
+(defvar sokoban-null-map
+  (make-sparse-keymap 'sokoban-null-map))
+
+(define-key sokoban-null-map "n"	'sokoban-start-game)
+
+;; ;;;;;;;;;;;;;;;; level file parsing functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst sokoban-level-regexp "^;LEVEL [0-9]+$")
+
+(defconst sokoban-comment-regexp "^;")
+
+(defun sokoban-init-level-data ()
+  (setq sokoban-level-data nil)
+  (save-excursion
+    (find-file-read-only sokoban-level-file)
+    (goto-char (point-min))
+    (re-search-forward sokoban-level-regexp nil t)
+    (forward-char)
+    (while (not (eq (point) (point-max)))
+      (while (looking-at sokoban-comment-regexp)
+	(forward-line))
+      (let ((data (make-vector sokoban-height nil))
+	    (fmt (format "%%-%ds" sokoban-width))
+	    start end)
+	(loop for y from 0 to (1- sokoban-height) do
+	      (cond ((or (eq (point) (point-max))
+			 (looking-at sokoban-comment-regexp))
+		     (aset data y (format fmt "")))
+		    (t
+		     (setq start (point))
+		     (end-of-line)
+		     (setq end (point))
+		     (aset data
+			   y
+			   (format fmt (buffer-substring start end)))
+		     (forward-char))))
+	(setq sokoban-level-data
+	      (cons data sokoban-level-data))))
+    (kill-buffer (current-buffer))
+    (setq sokoban-level-data (reverse sokoban-level-data))))
+
+;; ;;;;;;;;;;;;;;;; game functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun sokoban-display-options ()
+  (let ((options (make-vector 256 nil)))
+    (loop for c from 0 to 255 do
+      (aset options c
+	    (cond ((= c sokoban-floor)
+		   sokoban-floor-options)
+                  ((= c sokoban-target)
+		   sokoban-target-options)
+                  ((= c sokoban-wall)
+		   sokoban-wall-options)
+                  ((= c sokoban-block)
+		   sokoban-block-options)
+                  ((= c sokoban-player)
+		   sokoban-player-options)
+                  (t
+		   '(nil nil nil)))))
+    options))
+
+(defun sokoban-get-level-data ()
+  (setq sokoban-level-map (nth (1- sokoban-level) sokoban-level-data)
+	sokoban-targets 0)
+  (loop for y from 0 to (1- sokoban-height) do
+	(loop for x from 0 to (1- sokoban-width) do
+	      (let ((c (aref (aref sokoban-level-map y) x)))
+		(cond
+		 ((= c sokoban-target)
+		  (incf sokoban-targets))
+		 ((= c ?\040)
+		  (aset (aref sokoban-level-map y) x sokoban-floor)))))))
+
+(defun sokoban-get-floor (x y)
+  (let ((c (aref (aref sokoban-level-map y) x)))
+    (if (eq c sokoban-target)
+	sokoban-target
+      sokoban-floor)))
+
+(defun sokoban-init-buffer ()
+  (gamegrid-init-buffer sokoban-buffer-width
+			sokoban-buffer-height
+			?\040)
+  (loop for y from 0 to (1- sokoban-height) do
+	(loop for x from 0 to (1- sokoban-width) do
+	      (let ((c (aref (aref sokoban-level-map y) x)))
+		(if (= c sokoban-player)
+		    (setq sokoban-x x
+			  sokoban-y y))
+		(gamegrid-set-cell x y c)))))
+
+(defun sokoban-draw-score ()
+  (let ((strings (vector (format "Moves:  %05d" sokoban-moves)
+			 (format "Pushes: %05d" sokoban-pushes))))
+    (loop for y from 0 to 1 do
+	  (let* ((string (aref strings y))
+		 (len (length string)))
+	    (loop for x from 0 to (1- len) do
+		  (gamegrid-set-cell (+ sokoban-score-x x)
+				     (+ sokoban-score-y y)
+				     (aref string x)))))))
+
+(defun sokoban-move (dx dy)
+  (let* ((x (+ sokoban-x dx))
+	 (y (+ sokoban-y dy))
+	 (c (gamegrid-get-cell x y)))
+    (cond ((or (eq c sokoban-floor)
+	       (eq c sokoban-target))
+	   (gamegrid-set-cell sokoban-x
+			      sokoban-y
+			      (sokoban-get-floor sokoban-x
+						 sokoban-y))
+	   (setq sokoban-x x
+		 sokoban-y y)
+	   (gamegrid-set-cell sokoban-x
+			      sokoban-y
+			      sokoban-player)
+	   (incf sokoban-moves)
+	   (sokoban-draw-score))
+	  ((eq c sokoban-block)
+	   (let* ((xx (+ x dx))
+		  (yy (+ y dy))
+		  (cc (gamegrid-get-cell xx yy)))
+	     (cond ((or (eq cc sokoban-floor)
+			(eq cc sokoban-target))
+		    (if (eq (sokoban-get-floor x y) sokoban-target)
+			(decf sokoban-done))
+		    (gamegrid-set-cell xx yy sokoban-block)
+		    (gamegrid-set-cell x y sokoban-player)
+		    (gamegrid-set-cell sokoban-x
+				       sokoban-y
+				       (sokoban-get-floor sokoban-x
+							  sokoban-y))
+		    (setq sokoban-x x
+			  sokoban-y y)
+		    (incf sokoban-moves)
+		    (incf sokoban-pushes)
+		    (cond ((eq cc sokoban-target)
+			   (incf sokoban-done)
+			   (cond ((= sokoban-done sokoban-targets)
+				  (sit-for 3)
+				  (sokoban-next-level)))))
+		    (sokoban-draw-score))))))))
+
+(defun sokoban-move-left ()
+  "Move one square left"
+  (interactive)
+  (sokoban-move -1 0))
+
+(defun sokoban-move-right ()
+  "Move one square right"
+  (interactive)
+  (sokoban-move 1 0))
+
+(defun sokoban-move-up ()
+  "Move one square up"
+  (interactive)
+  (sokoban-move 0 -1))
+
+(defun sokoban-move-down ()
+  "Move one square down"
+  (interactive)
+  (sokoban-move 0 1))
+
+(defun sokoban-restart-level ()
+  "Restarts the current level"
+  (interactive)
+  (setq sokoban-moves 0
+	sokoban-pushes 0)
+  (sokoban-get-level-data)
+  (sokoban-init-buffer)
+  (sokoban-draw-score))
+
+(defun sokoban-next-level ()
+  (incf sokoban-level)
+  (sokoban-restart-level))
+
+(defun sokoban-goto-level (level)
+  "Jumps to a specified level"
+  (interactive "nLevel: ")
+  (setq sokoban-level level)
+  (sokoban-restart-level))
+
+(defun sokoban-start-game ()
+  "Starts a new game of Sokoban"
+  (interactive)
+  (setq sokoban-level 0)
+  (sokoban-next-level))
+
+(put 'sokoban-mode 'mode-class 'special)
+
+(defun sokoban-mode ()
+  "A mode for playing Sokoban.
+
+sokoban-mode keybindings:
+   \\{sokoban-mode-map}
+"
+  (kill-all-local-variables)
+
+  (use-local-map sokoban-mode-map)
+
+  (setq major-mode 'sokoban-mode)
+  (setq mode-name "Sokoban")
+
+  (setq gamegrid-use-glyphs sokoban-use-glyphs)
+  (setq gamegrid-use-color sokoban-use-color)
+  (setq gamegrid-font sokoban-font)
+
+  (gamegrid-init (sokoban-display-options))
+
+  (if (null sokoban-level-data)
+      (sokoban-init-level-data))
+
+  (run-hooks 'sokoban-mode-hook))
+
+;;;###autoload
+(defun sokoban ()
+  "Sokoban
+
+Push the blocks onto the target squares.
+
+sokoban-mode keybindings:
+   \\<sokoban-mode-map>
+\\[sokoban-start-game]	Starts a new game of Sokoban
+\\[sokoban-restart-level]	Restarts the current level
+\\[sokoban-goto-level]	Jumps to a specified level
+\\[sokoban-move-left]	Move one square to the left
+\\[sokoban-move-right]	Move one square to the right
+\\[sokoban-move-up]	Move one square up
+\\[sokoban-move-down]	Move one square down
+
+"
+  (interactive)
+
+  (switch-to-buffer sokoban-buffer-name)
+  (gamegrid-kill-timer)
+  (sokoban-mode)
+  (sokoban-start-game))
+
+(provide 'sokoban)
+
+;;; sokoban.el ends here
+
+;WALLS
+            12     f     f   ff      0   standard floor
+.           13     f     f   ff      4   target field
+#            0     0     0    0      0   walls
+
+;OBJECTS
+@            0       f       0    101   201    1     0 player
+$            1       f       0    100     0    2  1000 heavy box
+
+;MAXLEVEL 88
+;ATOP *$.
+;LEVEL 1
+    #####
+    #   #
+    #$  #
+  ###  $##
+  #  $ $ #
+### # ## #   ######
+#   # ## #####  ..#
+# $  $          ..#
+##### ### #@##  ..#
+    #     #########
+    #######
+;LEVEL 2
+############
+#..  #     ###
+#..  # $  $  #
+#..  #$####  #
+#..    @ ##  #
+#..  # #  $ ##
+###### ##$ $ #
+  # $  $ $ $ #
+  #    #     #
+  ############
+;LEVEL 3
+        ########
+        #     @#
+        # $#$ ##
+        # $  $#
+        ##$ $ #
+######### $ # ###
+#....  ## $  $  #
+##...    $  $   #
+#....  ##########
+########
+;LEVEL 4
+           ########
+           #  ....#
+############  ....#
+#    #  $ $   ....#
+# $$$#$  $ #  ....#
+#  $     $ #  ....#
+# $$ #$ $ $########
+#  $ #     #
+## #########
+#    #    ##
+#     $   ##
+#  $$#$$  @#
+#    #    ##
+###########
+;LEVEL 5
+        #####
+        #   #####
+        # #$##  #
+        #     $ #
+######### ###   #
+#....  ## $  $###
+#....    $ $$ ##
+#....  ##$  $ @#
+#########  $  ##
+        # $ $  #
+        ### ## #
+          #    #
+          ######
+;LEVEL 6
+######  ###
+#..  # ##@##
+#..  ###   #
+#..     $$ #
+#..  # # $ #
+#..### # $ #
+#### $ #$  #
+   #  $# $ #
+   # $  $  #
+   #  ##   #
+   #########
+;LEVEL 7
+       #####
+ #######   ##
+## # @## $$ #
+#    $      #
+#  $  ###   #
+### #####$###
+# $  ### ..#
+# $ $ $ ...#
+#    ###...#
+# $$ # #...#
+#  ### #####
+####
+;LEVEL 8
+  ####
+  #  ###########
+  #    $   $ $ #
+  # $# $ #  $  #
+  #  $ $  #    #
+### $# #  #### #
+#@#$ $ $  ##   #
+#    $ #$#   # #
+#   $    $ $ $ #
+#####  #########
+  #      #
+  #      #
+  #......#
+  #......#
+  #......#
+  ########
+;LEVEL 9
+          #######
+          #  ...#
+      #####  ...#
+      #      . .#
+      #  ##  ...#
+      ## ##  ...#
+     ### ########
+     # $$$ ##
+ #####  $ $ #####
+##   #$ $   #   #
+#@ $  $    $  $ #
+###### $$ $ #####
+     #      #
+     ########
+;LEVEL 10
+ ###  #############
+##@####       #   #
+# $$   $$  $ $ ...#
+#  $$$#    $  #...#
+# $   # $$ $$ #...#
+###   #  $    #...#
+#     # $ $ $ #...#
+#    ###### ###...#
+## #  #  $ $  #...#
+#  ## # $$ $ $##..#
+# ..# #  $      #.#
+# ..# # $$$ $$$ #.#
+##### #       # #.#
+    # ######### #.#
+    #           #.#
+    ###############
+;LEVEL 11
+          ####
+     #### #  #
+   ### @###$ #
+  ##      $  #
+ ##  $ $$## ##
+ #  #$##     #
+ # # $ $$ # ###
+ #   $ #  # $ #####
+####    #  $$ #   #
+#### ## $         #
+#.    ###  ########
+#.. ..# ####
+#...#.#
+#.....#
+#######
+;LEVEL 12
+################
+#              #
+# # ######     #
+# #  $ $ $ $#  #
+# #   $@$   ## ##
+# #  $ $ $###...#
+# #   $ $  ##...#
+# ###$$$ $ ##...#
+#     # ## ##...#
+#####   ## ##...#
+    #####     ###
+        #     #
+        #######
+;LEVEL 13
+   #########
+  ##   ##  #####
+###     #  #    ###
+#  $ #$ #  #  ... #
+# # $#@$## # #.#. #
+#  # #$  #    . . #
+# $    $ # # #.#. #
+#   ##  ##$ $ . . #
+# $ #   #  #$#.#. #
+## $  $   $  $... #
+ #$ ######    ##  #
+ #  #    ##########
+ ####
+;LEVEL 14
+       #######
+ #######     #
+ #     # $@$ #
+ #$$ #   #########
+ # ###......##   #
+ #   $......## # #
+ # ###......     #
+##   #### ### #$##
+#  #$   #  $  # #
+#  $ $$$  # $## #
+#   $ $ ###$$ # #
+#####     $   # #
+    ### ###   # #
+      #     #   #
+      ########  #
+             ####
+;LEVEL 15
+   ########
+   #   #  #
+   #  $   #
+ ### #$   ####
+ #  $  ##$   #
+ #  # @ $ # $#
+ #  #      $ ####
+ ## ####$##     #
+ # $#.....# #   #
+ #  $..**. $# ###
+##  #.....#   #
+#   ### #######
+# $$  #  #
+#  #     #
+######   #
+     #####
+;LEVEL 16
+#####
+#   ##
+#    #  ####
+# $  ####  #
+#  $$ $   $#
+###@ #$    ##
+ #  ##  $ $ ##
+ # $  ## ## .#
+ #  #$##$  #.#
+ ###   $..##.#
+  #    #.*...#
+  # $$ #.....#
+  #  #########
+  #  #
+  ####
+;LEVEL 17
+   ##########
+   #..  #   #
+   #..      #
+   #..  #  ####
+  #######  #  ##
+  #            #
+  #  #  ##  #  #
+#### ##  #### ##
+#  $  ##### #  #
+# # $  $  # $  #
+# @$  $   #   ##
+#### ## #######
+   #    #
+   ######
+;LEVEL 18
+     ###########
+     #  .  #   #
+     # #.    @ #
+ ##### ##..# ####
+##  # ..###     ###
+# $ #...   $ #  $ #
+#    .. ##  ## ## #
+####$##$# $ #   # #
+  ## #    #$ $$ # #
+  #  $ # #  # $## #
+  #               #
+  #  ###########  #
+  ####         ####
+;LEVEL 19
+  ######
+  #   @####
+##### $   #
+#   ##    ####
+# $ #  ##    #
+# $ #  ##### #
+## $  $    # #
+## $ $ ### # #
+## #  $  # # #
+## # #$#   # #
+## ###   # # ######
+#  $  #### # #....#
+#    $    $   ..#.#
+####$  $# $   ....#
+#       #  ## ....#
+###################
+;LEVEL 20
+    ##########
+#####        ####
+#     #   $  #@ #
+# #######$####  ###
+# #    ## #  #$ ..#
+# # $     #  #  #.#
+# # $  #     #$ ..#
+# #  ### ##     #.#
+# ###  #  #  #$ ..#
+# #    #  ####  #.#
+# #$   $  $  #$ ..#
+#    $ # $ $ #  #.#
+#### $###    #$ ..#
+   #    $$ ###....#
+   #      ## ######
+   ########
+;LEVEL 21
+#########
+#       #
+#       ####
+## #### #  #
+## #@##    #
+# $$$ $  $$#
+#  # ## $  #
+#  # ##  $ ####
+####  $$$ $#  #
+ #   ##   ....#
+ # #   # #.. .#
+ #   # # ##...#
+ ##### $  #...#
+     ##   #####
+      #####
+;LEVEL 22
+######     ####
+#    #######  #####
+#   $#  #  $  #   #
+#  $  $  $ # $ $  #
+##$ $   # @# $    #
+#  $ ########### ##
+# #   #.......# $#
+# ##  # ......#  #
+# #   $........$ #
+# # $ #.... ..#  #
+#  $ $####$#### $#
+# $   ### $   $  ##
+# $     $ $  $    #
+## ###### $ ##### #
+#         #       #
+###################
+;LEVEL 23
+    #######
+    #  #  ####
+##### $#$ #  ##
+#.. #  #  #   #
+#.. # $#$ #  $####
+#.  #     #$  #  #
+#..   $#  # $    #
+#..@#  #$ #$  #  #
+#.. # $#     $#  #
+#.. #  #$$#$  #  ##
+#.. # $#  #  $#$  #
+#.. #  #  #   #   #
+##. ####  #####   #
+ ####  ####   #####
+;LEVEL 24
+###############
+#..........  .####
+#..........$$.#  #
+###########$ #   ##
+#      $  $     $ #
+## ####   #  $ #  #
+#      #   ##  # ##
+#  $#  # ##  ### ##
+# $ #$###    ### ##
+###  $ #  #  ### ##
+###    $ ## #  # ##
+ # $  #  $  $ $   #
+ #  $  $#$$$  #   #
+ #  #  $      #####
+ # @##  #  #  #
+ ##############
+;LEVEL 25
+####
+#  ##############
+#  #   ..#......#
+#  # # ##### ...#
+##$#    ........#
+#   ##$######  ####
+# $ #     ######@ #
+##$ # $   ######  #
+#  $ #$$$##       #
+#      #    #$#$###
+# #### #$$$$$    #
+# #    $     #   #
+# #   ##        ###
+# ######$###### $ #
+#        #    #   #
+##########    #####
+;LEVEL 26
+ #######
+ #  #  #####
+##  #  #...###
+#  $#  #...  #
+# $ #$$ ...  #
+#  $#  #... .#
+#   # $########
+##$       $ $ #
+##  #  $$ #   #
+ ######  ##$$@#
+      #      ##
+      ########
+;LEVEL 27
+ #################
+ #...   #    #   ##
+##.....  $## # #$ #
+#......#  $  #    #
+#......#  #  # #  #
+######### $  $ $  #
+  #     #$##$ ##$##
+ ##   $    # $    #
+ #  ## ### #  ##$ #
+ # $ $$     $  $  #
+ # $    $##$ ######
+ #######  @ ##
+       ######
+;LEVEL 28
+         #####
+     #####   #
+    ## $  $  ####
+##### $  $ $ ##.#
+#       $$  ##..#
+#  ###### ###.. #
+## #  #    #... #
+# $   #    #... #
+#@ #$ ## ####...#
+####  $ $$  ##..#
+   ##  $ $  $...#
+    # $$  $ #  .#
+    #   $ $  ####
+    ######   #
+         #####
+;LEVEL 29
+#####
+#   ##
+# $  #########
+## # #       ######
+## #   $#$#@  #   #
+#  #      $ #   $ #
+#  ### ######### ##
+#  ## ..*..... # ##
+## ## *.*..*.* # ##
+# $########## ##$ #
+#  $   $  $    $  #
+#  #   #   #   #  #
+###################
+;LEVEL 30
+       ###########
+       #   #     #
+#####  #     $ $ #
+#   ##### $## # ##
+# $ ##   # ## $  #
+# $  @$$ # ##$$$ #
+## ###   # ##    #
+## #   ### #####$#
+## #     $  #....#
+#  ### ## $ #....##
+# $   $ #   #..$. #
+#  ## $ #  ##.... #
+#####   ######...##
+    #####    #####
+;LEVEL 31
+  ####
+  #  #########
+ ##  ##  #   #
+ #  $# $@$   ####
+ #$  $  # $ $#  ##
+##  $## #$ $     #
+#  #  # #   $$$  #
+# $    $  $## ####
+# $ $ #$#  #  #
+##  ###  ###$ #
+ #  #....     #
+ ####......####
+   #....####
+   #...##
+   #...#
+   #####
+;LEVEL 32
+      ####
+  #####  #
+ ##     $#
+## $  ## ###
+#@$ $ # $  #
+#### ##   $#
+ #....#$ $ #
+ #....#   $#
+ #....  $$ ##
+ #... # $   #
+ ######$ $  #
+      #   ###
+      #$ ###
+      #  #
+      ####
+;LEVEL 33
+############
+##     ##  #
+##   $   $ #
+#### ## $$ #
+#   $ #    #
+# $$$ # ####
+#   # # $ ##
+#  #  #  $ #
+# $# $#    #
+#   ..# ####
+####.. $ #@#
+#.....# $# #
+##....#  $ #
+###..##    #
+############
+;LEVEL 34
+ #########
+ #....   ##
+ #.#.#  $ ##
+##....# # @##
+# ....#  #  ##
+#     #$ ##$ #
+## ###  $    #
+ #$  $ $ $#  #
+ # #  $ $ ## #
+ #  ###  ##  #
+ #    ## ## ##
+ #  $ #  $  #
+ ###$ $   ###
+   #  #####
+   ####
+;LEVEL 35
+############ ######
+#   #    # ###....#
+#   $$#   @  .....#
+#   # ###   # ....#
+## ## ###  #  ....#
+ # $ $     # # ####
+ #  $ $##  #      #
+#### #  #### # ## #
+#  # #$   ## #    #
+# $  $  # ## #   ##
+# # $ $    # #   #
+#  $ ## ## # #####
+# $$     $$  #
+## ## ### $  #
+ #    # #    #
+ ###### ######
+;LEVEL 36
+            #####
+#####  ######   #
+#   ####  $ $ $ #
+# $   ## ## ##  ##
+#   $ $     $  $ #
+### $  ## ##     ##
+  # ##### #####$$ #
+ ##$##### @##     #
+ # $  ###$### $  ##
+ # $  #   ###  ###
+ # $$ $ #   $$ #
+ #     #   ##  #
+ #######.. .###
+    #.........#
+    #.........#
+    ###########
+;LEVEL 37
+###########
+#......   #########
+#......   #  ##   #
+#..### $    $     #
+#... $ $ #   ##   #
+#...#$#####    #  #
+###    #   #$  #$ #
+  #  $$ $ $  $##  #
+  #  $   #$#$ ##$ #
+  ### ## #    ##  #
+   #  $ $ ## ######
+   #    $  $  #
+   ##   # #   #
+    #####@#####
+        ###
+;LEVEL 38
+      ####
+####### @#
+#     $  #
+#   $## $#
+##$#...# #
+ # $...  #
+ # #. .# ##
+ #   # #$ #
+ #$  $    #
+ #  #######
+ ####
+;LEVEL 39
+             ######
+ #############....#
+##   ##     ##....#
+#  $$##  $ @##....#
+#      $$ $#  ....#
+#  $ ## $$ # # ...#
+#  $ ## $  #  ....#
+## ##### ### ##.###
+##   $  $ ##   .  #
+# $###  # ##### ###
+#   $   #       #
+#  $ #$ $ $###  #
+# $$$# $   # ####
+#    #  $$ #
+######   ###
+     #####
+;LEVEL 40
+    ############
+    #          ##
+    #  # #$$ $  #
+    #$ #$#  ## @#
+   ## ## # $ # ##
+   #   $ #$  # #
+   #   # $   # #
+   ## $ $   ## #
+   #  #  ##  $ #
+   #    ## $$# #
+######$$   #   #
+#....#  ########
+#.#... ##
+#....   #
+#....   #
+#########
+;LEVEL 41
+           #####
+          ##   ##
+         ##     #
+        ##  $$  #
+       ## $$  $ #
+       # $    $ #
+####   #   $$ #####
+#  ######## ##    #
+#.            $$$@#
+#.# ####### ##   ##
+#.# #######. #$ $##
+#........... #    #
+##############  $ #
+             ##  ##
+              ####
+;LEVEL 42
+     ########
+  ####      ######
+  #    ## $ $   @#
+  # ## ##$#$ $ $##
+### ......#  $$ ##
+#   ......#  #   #
+# # ......#$  $  #
+# #$...... $$# $ #
+#   ### ###$  $ ##
+###  $  $  $  $ #
+  #  $  $  $  $ #
+  ######   ######
+       #####
+;LEVEL 43
+        #######
+    #####  #  ####
+    #   #   $    #
+ #### #$$ ## ##  #
+##      # #  ## ###
+#  ### $#$  $  $  #
+#...    # ##  #   #
+#...#    @ # ### ##
+#...#  ###  $  $  #
+######## ##   #   #
+          #########
+;LEVEL 44
+ #####
+ #   #
+ # # #######
+ #      $@######
+ # $ ##$ ###   #
+ # #### $    $ #
+ # ##### #  #$ ####
+##  #### ##$      #
+#  $#  $  # ## ## #
+#         # #...# #
+######  ###  ...  #
+     #### # #...# #
+          # ### # #
+          #       #
+          #########
+;LEVEL 45
+##### ####
+#...# #  ####
+#...###  $  #
+#....## $  $###
+##....##   $  #
+###... ## $ $ #
+# ##    #  $  #
+#  ## # ### ####
+# $ # #$  $    #
+#  $ @ $    $  #
+#   # $ $$ $ ###
+#  ######  ###
+# ##    ####
+###
+;LEVEL 46
+##########
+#        ####
+# ###### #  ##
+# # $ $ $  $ #
+#       #$   #
+###$  $$#  ###
+  #  ## # $##
+  ##$#   $ @#
+   #  $ $ ###
+   # #   $  #
+   # ##   # #
+  ##  ##### #
+  #         #
+  #.......###
+  #.......#
+  #########
+;LEVEL 47
+         ####
+ #########  ##
+##  $      $ #####
+#   ## ##   ##...#
+# #$$ $ $$#$##...#
+# #   @   #   ...#
+#  $# ###$$   ...#
+# $  $$  $ ##....#
+###$       #######
+  #  #######
+  ####
+;LEVEL 48
+  #########
+  #*.*#*.*#
+  #.*.*.*.#
+  #*.*.*.*#
+  #.*.*.*.#
+  #*.*.*.*#
+  ###   ###
+    #   #
+###### ######
+#           #
+# $ $ $ $ $ #
+## $ $ $ $ ##
+ #$ $ $ $ $#
+ #   $@$   #
+ #  #####  #
+ ####   ####
+;LEVEL 49
+       ####
+       #  ##
+       #   ##
+       # $$ ##
+     ###$  $ ##
+  ####    $   #
+###  # #####  #
+#    # #....$ #
+# #   $ ....# #
+#  $ # #.*..# #
+###  #### ### #
+  #### @$  ##$##
+     ### $     #
+       #  ##   #
+       #########
+;LEVEL 50
+      ############
+     ##..    #   #
+    ##..* $    $ #
+   ##..*.# # # $##
+   #..*.# # # $  #
+####...#  #    # #
+#  ## #          #
+# @$ $ ###  #   ##
+# $   $   # #   #
+###$$   # # # # #
+  #   $   # # #####
+  # $# #####      #
+  #$   #   #    # #
+  #  ###   ##     #
+  #  #      #    ##
+  ####      ######
+;LEVEL 51
+#########
+#       #
+#  $   $#
+####    #
+   # $  ##
+####   $ #
+#.. $ ## ####
+#..  $##    #
+#..    $    #
+#.###$### #@#
+#.# #     ###
+### #######
+;LEVEL 52
+####################
+#  ##########     @#
+# $#    #     ######
+#      ####   #  ###
+#####         #  ###
+#   $         #  ###
+#  $####  #   #    #
+# # #  #..#$###  # #
+# # #$ #..#  $  $$ #
+#      #..#  #   # #
+#   #  #..#  #   # #
+####################
+;LEVEL 53
+####################
+#                ###
+# $#   $ ##  $    ##
+#    $###    # $$ ##
+#.###     $ $ ##  ##
+#...#  #  #    #$  #
+#..##$$#### $  #   #
+#...#      $ ##  ###
+#...$  ###  #    # #
+##..  $#  ##   ##@ #
+###.#              #
+####################
+;LEVEL 54
+####################
+#   #    #   #   #@#
+# $      $   $   # #
+## ###..## ###     #
+#   #....#$#  $### #
+# $ #....#  $  $ $ #
+#   #....# # # $ $ #
+#   ##..##   #$#   #
+##$##    ##  #  #$##
+#   $  $     #  #  #
+#   #    #   #     #
+####################
+;LEVEL 55
+####################
+#    @##      #   ##
+#    ##    $    $ ##