]> code.delx.au - gnu-emacs/blobdiff - lisp/play/tetris.el
Update copyright year to 2015
[gnu-emacs] / lisp / play / tetris.el
index 00ebbae2814359d28194b03332f433e83663000a..586d1d5d4623627d83cc25dfa748392b0ab26fb4 100644 (file)
@@ -1,7 +1,6 @@
 ;;; tetris.el --- implementation of Tetris for Emacs
 
-;; Copyright (C) 1997, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
-;;   2009, 2010  Free Software Foundation, Inc.
+;; Copyright (C) 1997, 2001-2015 Free Software Foundation, Inc.
 
 ;; Author: Glynn Clements <glynn@sensei.co.uk>
 ;; Version: 2.01
@@ -27,8 +26,7 @@
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl))
+(eval-when-compile (require 'cl-lib))
 
 (require 'gamegrid)
 
@@ -76,29 +74,20 @@ If the return value is a number, it is used as the timer period."
   :type 'hook)
 
 (defcustom tetris-tty-colors
-  [nil "blue" "white" "yellow" "magenta" "cyan" "green" "red"]
-  "Vector of colors of the various shapes in text mode.
-Element 0 is ignored."
+  ["blue" "white" "yellow" "magenta" "cyan" "green" "red"]
+  "Vector of colors of the various shapes in text mode."
   :group 'tetris
-  :type (let ((names `("Shape 1" "Shape 2" "Shape 3"
-                      "Shape 4" "Shape 5" "Shape 6" "Shape 7"))
-             (result `(vector (const nil))))
-         (while names
-           (add-to-list 'result
-                        (cons 'choice
-                              (cons :tag
-                                    (cons (car names)
-                                          (mapcar (lambda (color)
-                                                    (list 'const color))
-                                                  (defined-colors)))))
-                        t)
-           (setq names (cdr names)))
-         result))
+  :type '(vector (color :tag "Shape 1")
+                (color :tag "Shape 2")
+                (color :tag "Shape 3")
+                (color :tag "Shape 4")
+                (color :tag "Shape 5")
+                (color :tag "Shape 6")
+                (color :tag "Shape 7")))
 
 (defcustom tetris-x-colors
-  [nil [0 0 1] [0.7 0 1] [1 1 0] [1 0 1] [0 1 1] [0 1 0] [1 0 0]]
-  "Vector of colors of the various shapes.
-Element 0 is ignored."
+  [[0 0 1] [0.7 0 1] [1 1 0] [1 0 1] [0 1 1] [0 1 0] [1 0 0]]
+  "Vector of colors of the various shapes."
   :group 'tetris
   :type 'sexp)
 
@@ -196,57 +185,50 @@ Element 0 is ignored."
 ;; ;;;;;;;;;;;;; constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (defconst tetris-shapes
-  [[[[1 1 0 0] [1 1 0 0] [1 1 0 0] [1 1 0 0]]
-    [[1 1 0 0] [1 1 0 0] [1 1 0 0] [1 1 0 0]]
-    [[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]
-    [[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]]
-
-   [[[2 2 2 0] [0 2 0 0] [2 0 0 0] [2 2 0 0]]
-    [[0 0 2 0] [0 2 0 0] [2 2 2 0] [2 0 0 0]]
-    [[0 0 0 0] [2 2 0 0] [0 0 0 0] [2 0 0 0]]
-    [[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]]
-
-   [[[3 3 3 0] [3 3 0 0] [0 0 3 0] [3 0 0 0]]
-    [[3 0 0 0] [0 3 0 0] [3 3 3 0] [3 0 0 0]]
-    [[0 0 0 0] [0 3 0 0] [0 0 0 0] [3 3 0 0]]
-    [[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]]
-
-   [[[4 4 0 0] [0 4 0 0] [4 4 0 0] [0 4 0 0]]
-    [[0 4 4 0] [4 4 0 0] [0 4 4 0] [4 4 0 0]]
-    [[0 0 0 0] [4 0 0 0] [0 0 0 0] [4 0 0 0]]
-    [[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]]
-
-   [[[0 5 5 0] [5 0 0 0] [0 5 5 0] [5 0 0 0]]
-    [[5 5 0 0] [5 5 0 0] [5 5 0 0] [5 5 0 0]]
-    [[0 0 0 0] [0 5 0 0] [0 0 0 0] [0 5 0 0]]
-    [[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]]
-
-   [[[0 6 0 0] [6 0 0 0] [6 6 6 0] [0 6 0 0]]
-    [[6 6 6 0] [6 6 0 0] [0 6 0 0] [6 6 0 0]]
-    [[0 0 0 0] [6 0 0 0] [0 0 0 0] [0 6 0 0]]
-    [[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]]
-
-   [[[7 7 7 7] [7 0 0 0] [7 7 7 7] [7 0 0 0]]
-    [[0 0 0 0] [7 0 0 0] [0 0 0 0] [7 0 0 0]]
-    [[0 0 0 0] [7 0 0 0] [0 0 0 0] [7 0 0 0]]
-    [[0 0 0 0] [7 0 0 0] [0 0 0 0] [7 0 0 0]]]])
+  [[[[0  0] [1  0] [0  1] [1  1]]]
+
+   [[[0  0] [1  0] [2  0] [2  1]]
+    [[1 -1] [1  0] [1  1] [0  1]]
+    [[0 -1] [0  0] [1  0] [2  0]]
+    [[1 -1] [2 -1] [1  0] [1  1]]]
+
+   [[[0  0] [1  0] [2  0] [0  1]]
+    [[0 -1] [1 -1] [1  0] [1  1]]
+    [[2 -1] [0  0] [1  0] [2  0]]
+    [[1 -1] [1  0] [1  1] [2  1]]]
+
+   [[[0  0] [1  0] [1  1] [2  1]]
+    [[1  0] [0  1] [1  1] [0  2]]]
+
+   [[[1  0] [2  0] [0  1] [1  1]]
+    [[0  0] [0  1] [1  1] [1  2]]]
+
+   [[[1  0] [0  1] [1  1] [2  1]]
+    [[1  0] [1  1] [2  1] [1  2]]
+    [[0  1] [1  1] [2  1] [1  2]]
+    [[1  0] [0  1] [1  1] [1  2]]]
+
+   [[[0  0] [1  0] [2  0] [3  0]]
+    [[1 -1] [1  0] [1  1] [1  2]]]]
+  "Each shape is described by a vector that contains the coordinates of
+each one of its four blocks.")
 
 ;;the scoring rules were taken from "xtetris".  Blocks score differently
 ;;depending on their rotation
 
 (defconst tetris-shape-scores
-  [ [6 6 6 6] [6 7 6 7] [6 7 6 7] [6 7 6 7] [6 7 6 7] [5 5 6 5] [5 8 5 8]] )
+  [[6] [6 7 6 7] [6 7 6 7] [6 7] [6 7] [5 5 6 5] [5 8]] )
 
 (defconst tetris-shape-dimensions
   [[2 2] [3 2] [3 2] [3 2] [3 2] [3 2] [4 1]])
 
-(defconst tetris-blank 0)
+(defconst tetris-blank 7)
 
 (defconst tetris-border 8)
 
 (defconst tetris-space 9)
 
-(defun tetris-default-update-speed-function (shapes rows)
+(defun tetris-default-update-speed-function (_shapes rows)
   (/ 20.0 (+ 50.0 rows)))
 
 ;; ;;;;;;;;;;;;; variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -295,20 +277,20 @@ Element 0 is ignored."
 
 (defun tetris-display-options ()
   (let ((options (make-vector 256 nil)))
-    (loop for c from 0 to 255 do
+    (dotimes (c 256)
       (aset options c
            (cond ((= c tetris-blank)
-                   tetris-blank-options)
-                  ((and (>= c 1) (<= c 7))
+                   tetris-blank-options)
+                  ((and (>= c 0) (<= c 6))
                   (append
                    tetris-cell-options
                    `((((glyph color-x) ,(aref tetris-x-colors c))
                       (color-tty ,(aref tetris-tty-colors c))
                       (t nil)))))
-                  ((= c tetris-border)
-                   tetris-border-options)
-                  ((= c tetris-space)
-                   tetris-space-options)
+                  ((= c tetris-border)
+                   tetris-border-options)
+                  ((= c tetris-space)
+                   tetris-space-options)
                   (t
                    '(nil nil nil)))))
     options))
@@ -320,32 +302,28 @@ Element 0 is ignored."
                           tetris-n-rows nil)))
        (and (numberp period) period))))
 
-(defun tetris-get-shape-cell (x y)
-  (aref (aref (aref (aref tetris-shapes
-                         tetris-shape)
-                   y)
-             tetris-rot)
-       x))
+(defun tetris-get-shape-cell (block)
+  (aref (aref  (aref tetris-shapes
+                     tetris-shape) tetris-rot)
+        block))
 
 (defun tetris-shape-width ()
-  (aref (aref tetris-shape-dimensions tetris-shape)
-       (% tetris-rot 2)))
+  (aref (aref tetris-shape-dimensions tetris-shape) 0))
 
-(defun tetris-shape-height ()
-  (aref (aref tetris-shape-dimensions tetris-shape)
-       (- 1 (% tetris-rot 2))))
+(defun tetris-shape-rotations ()
+  (length (aref tetris-shapes tetris-shape)))
 
 (defun tetris-draw-score ()
   (let ((strings (vector (format "Shapes: %05d" tetris-n-shapes)
                         (format "Rows:   %05d" tetris-n-rows)
                         (format "Score:  %05d" tetris-score))))
-    (loop for y from 0 to 2 do
-         (let* ((string (aref strings y))
-                (len (length string)))
-           (loop for x from 0 to (1- len) do
-                 (gamegrid-set-cell (+ tetris-score-x x)
-                                    (+ tetris-score-y y)
-                                    (aref string x)))))))
+    (dotimes (y 3)
+      (let* ((string (aref strings y))
+             (len (length string)))
+        (dotimes (x len)
+          (gamegrid-set-cell (+ tetris-score-x x)
+                             (+ tetris-score-y y)
+                             (aref string x)))))))
 
 (defun tetris-update-score ()
   (tetris-draw-score)
@@ -365,82 +343,88 @@ Element 0 is ignored."
     (tetris-update-score)))
 
 (defun tetris-draw-next-shape ()
-  (loop for y from 0 to 3 do
-       (loop for x from 0 to 3 do
-             (gamegrid-set-cell (+ tetris-next-x x)
-                                (+ tetris-next-y y)
-                                (let ((tetris-shape tetris-next-shape)
-                                      (tetris-rot 0))
-                                  (tetris-get-shape-cell x y))))))
+  (dotimes (x 4)
+    (dotimes (y 4)
+      (gamegrid-set-cell (+ tetris-next-x x)
+                         (+ tetris-next-y y)
+                         tetris-blank)))
+  (dotimes (i 4)
+    (let ((tetris-shape tetris-next-shape)
+          (tetris-rot 0))
+      (gamegrid-set-cell (+ tetris-next-x
+                            (aref (tetris-get-shape-cell i) 0))
+                         (+ tetris-next-y
+                            (aref (tetris-get-shape-cell i) 1))
+                         tetris-shape))))
 
 (defun tetris-draw-shape ()
-  (loop for y from 0 to (1- (tetris-shape-height)) do
-       (loop for x from 0 to (1- (tetris-shape-width)) do
-             (let ((c (tetris-get-shape-cell x y)))
-               (if (/= c tetris-blank)
-                   (gamegrid-set-cell (+ tetris-top-left-x
-                                         tetris-pos-x
-                                         x)
-                                      (+ tetris-top-left-y
-                                         tetris-pos-y
-                                         y)
-                                      c))))))
+  (dotimes (i 4)
+    (let ((c (tetris-get-shape-cell i)))
+      (gamegrid-set-cell (+ tetris-top-left-x
+                            tetris-pos-x
+                            (aref c 0))
+                         (+ tetris-top-left-y
+                            tetris-pos-y
+                            (aref c 1))
+                         tetris-shape))))
 
 (defun tetris-erase-shape ()
-  (loop for y from 0 to (1- (tetris-shape-height)) do
-       (loop for x from 0 to (1- (tetris-shape-width)) do
-             (let ((c (tetris-get-shape-cell x y))
-                   (px (+ tetris-top-left-x tetris-pos-x x))
-                   (py (+ tetris-top-left-y tetris-pos-y y)))
-               (if (/= c tetris-blank)
-                   (gamegrid-set-cell px py tetris-blank))))))
+  (dotimes (i 4)
+    (let ((c (tetris-get-shape-cell i)))
+      (gamegrid-set-cell (+ tetris-top-left-x
+                            tetris-pos-x
+                            (aref c 0))
+                         (+ tetris-top-left-y
+                            tetris-pos-y
+                            (aref c 1))
+                         tetris-blank))))
 
 (defun tetris-test-shape ()
   (let ((hit nil))
-    (loop for y from 0 to (1- (tetris-shape-height)) do
-         (loop for x from 0 to (1- (tetris-shape-width)) do
-               (unless hit
-                 (setq hit
-                       (let* ((c (tetris-get-shape-cell x y))
-                             (xx (+ tetris-pos-x x))
-                             (yy (+ tetris-pos-y y))
-                             (px (+ tetris-top-left-x xx))
-                             (py (+ tetris-top-left-y yy)))
-                         (and (/= c tetris-blank)
-                              (or (>= xx tetris-width)
-                                  (>= yy tetris-height)
-                                  (/= (gamegrid-get-cell px py)
-                                      tetris-blank))))))))
+    (dotimes (i 4)
+      (unless hit
+        (setq hit
+              (let* ((c (tetris-get-shape-cell i))
+                     (xx (+ tetris-pos-x
+                            (aref c 0)))
+                     (yy (+ tetris-pos-y
+                            (aref c 1))))
+                (or (>= xx tetris-width)
+                    (>= yy tetris-height)
+                    (/= (gamegrid-get-cell
+                         (+ xx tetris-top-left-x)
+                         (+ yy tetris-top-left-y))
+                        tetris-blank))))))
     hit))
 
 (defun tetris-full-row (y)
   (let ((full t))
-    (loop for x from 0 to (1- tetris-width) do
-         (if (= (gamegrid-get-cell (+ tetris-top-left-x x)
-                                   (+ tetris-top-left-y y))
-                tetris-blank)
-             (setq full nil)))
+    (dotimes (x tetris-width)
+      (if (= (gamegrid-get-cell (+ tetris-top-left-x x)
+                                (+ tetris-top-left-y y))
+             tetris-blank)
+          (setq full nil)))
     full))
 
 (defun tetris-shift-row (y)
   (if (= y 0)
-      (loop for x from 0 to (1- tetris-width) do
+      (dotimes (x tetris-width)
        (gamegrid-set-cell (+ tetris-top-left-x x)
                           (+ tetris-top-left-y y)
                           tetris-blank))
-  (loop for x from 0 to (1- tetris-width) do
-       (let ((c (gamegrid-get-cell (+ tetris-top-left-x x)
-                                   (+ tetris-top-left-y y -1))))
-         (gamegrid-set-cell (+ tetris-top-left-x x)
-                            (+ tetris-top-left-y y)
+    (dotimes (x tetris-width)
+      (let ((c (gamegrid-get-cell (+ tetris-top-left-x x)
+                                  (+ tetris-top-left-y y -1))))
+        (gamegrid-set-cell (+ tetris-top-left-x x)
+                           (+ tetris-top-left-y y)
                           c)))))
 
 (defun tetris-shift-down ()
-  (loop for y0 from 0 to (1- tetris-height) do
-       (if (tetris-full-row y0)
-           (progn (setq tetris-n-rows (1+ tetris-n-rows))
-                  (loop for y from y0 downto 0 do
-                        (tetris-shift-row y))))))
+  (dotimes (y0 tetris-height)
+    (when (tetris-full-row y0)
+      (setq tetris-n-rows (1+ tetris-n-rows))
+      (cl-loop for y from y0 downto 0 do
+               (tetris-shift-row y)))))
 
 (defun tetris-draw-border-p ()
   (or (not (eq gamegrid-display-mode 'glyph))
@@ -452,22 +436,22 @@ Element 0 is ignored."
                        tetris-space)
   (let ((buffer-read-only nil))
     (if (tetris-draw-border-p)
-       (loop for y from -1 to tetris-height do
-             (loop for x from -1 to tetris-width do
-                   (gamegrid-set-cell (+ tetris-top-left-x x)
-                                      (+ tetris-top-left-y y)
-                                      tetris-border))))
-    (loop for y from 0 to (1- tetris-height) do
-         (loop for x from 0 to (1- tetris-width) do
-               (gamegrid-set-cell (+ tetris-top-left-x x)
-                                  (+ tetris-top-left-y y)
-                                  tetris-blank)))
+       (cl-loop for y from -1 to tetris-height do
+                 (cl-loop for x from -1 to tetris-width do
+                          (gamegrid-set-cell (+ tetris-top-left-x x)
+                                             (+ tetris-top-left-y y)
+                                             tetris-border))))
+    (dotimes (y tetris-height)
+      (dotimes (x tetris-width)
+        (gamegrid-set-cell (+ tetris-top-left-x x)
+                           (+ tetris-top-left-y y)
+                           tetris-blank)))
     (if (tetris-draw-border-p)
-       (loop for y from -1 to 4 do
-             (loop for x from -1 to 4 do
-                   (gamegrid-set-cell (+ tetris-next-x x)
-                                      (+ tetris-next-y y)
-                                      tetris-border))))))
+       (cl-loop for y from -1 to 4 do
+                 (cl-loop for x from -1 to 4 do
+                          (gamegrid-set-cell (+ tetris-next-x x)
+                                             (+ tetris-next-y y)
+                                             tetris-border))))))
 
 (defun tetris-reset-game ()
   (gamegrid-kill-timer)
@@ -510,33 +494,30 @@ Drops the shape one square, testing for collision."
 (defun tetris-move-bottom ()
   "Drop the shape to the bottom of the playing area."
   (interactive)
-  (if (not tetris-paused)
-      (let ((hit nil))
-        (tetris-erase-shape)
-        (while (not hit)
-          (setq tetris-pos-y (1+ tetris-pos-y))
-          (setq hit (tetris-test-shape)))
-        (setq tetris-pos-y (1- tetris-pos-y))
-        (tetris-draw-shape)
-        (tetris-shape-done))))
+  (unless tetris-paused
+    (let ((hit nil))
+      (tetris-erase-shape)
+      (while (not hit)
+        (setq tetris-pos-y (1+ tetris-pos-y))
+        (setq hit (tetris-test-shape)))
+      (setq tetris-pos-y (1- tetris-pos-y))
+      (tetris-draw-shape)
+      (tetris-shape-done))))
 
 (defun tetris-move-left ()
   "Move the shape one square to the left."
   (interactive)
-  (unless (or (= tetris-pos-x 0)
-              tetris-paused)
+  (unless tetris-paused
     (tetris-erase-shape)
     (setq tetris-pos-x (1- tetris-pos-x))
     (if (tetris-test-shape)
-       (setq tetris-pos-x (1+ tetris-pos-x)))
+        (setq tetris-pos-x (1+ tetris-pos-x)))
     (tetris-draw-shape)))
 
 (defun tetris-move-right ()
   "Move the shape one square to the right."
   (interactive)
-  (unless (or (= (+ tetris-pos-x (tetris-shape-width))
-                 tetris-width)
-              tetris-paused)
+  (unless tetris-paused
     (tetris-erase-shape)
     (setq tetris-pos-x (1+ tetris-pos-x))
     (if (tetris-test-shape)
@@ -546,23 +527,26 @@ Drops the shape one square, testing for collision."
 (defun tetris-rotate-prev ()
   "Rotate the shape clockwise."
   (interactive)
-  (if (not tetris-paused)
-      (progn (tetris-erase-shape)
-             (setq tetris-rot (% (+ 1 tetris-rot) 4))
-             (if (tetris-test-shape)
-                 (setq tetris-rot (% (+ 3 tetris-rot) 4)))
-             (tetris-draw-shape))))
+  (unless tetris-paused
+      (tetris-erase-shape)
+      (setq tetris-rot (% (+ 1 tetris-rot)
+                          (tetris-shape-rotations)))
+      (if (tetris-test-shape)
+          (setq tetris-rot (% (+ 3 tetris-rot)
+                              (tetris-shape-rotations))))
+      (tetris-draw-shape)))
 
 (defun tetris-rotate-next ()
   "Rotate the shape anticlockwise."
   (interactive)
-  (if (not tetris-paused)
-      (progn
+  (unless tetris-paused
         (tetris-erase-shape)
-        (setq tetris-rot (% (+ 3 tetris-rot) 4))
+        (setq tetris-rot (% (+ 3 tetris-rot)
+                            (tetris-shape-rotations)))
         (if (tetris-test-shape)
-            (setq tetris-rot (% (+ 1 tetris-rot) 4)))
-        (tetris-draw-shape))))
+            (setq tetris-rot (% (+ 1 tetris-rot)
+                                (tetris-shape-rotations))))
+        (tetris-draw-shape)))
 
 (defun tetris-end-game ()
   "Terminate the current game."
@@ -644,9 +628,6 @@ tetris-mode keybindings:
   (tetris-mode)
   (tetris-start-game))
 
-(random t)
-
 (provide 'tetris)
 
-;; arch-tag: fb780d53-3ff0-49f0-8e19-f7f13cf2d49e
 ;;; tetris.el ends here