Commits

Anonymous committed d653ae1

Tetris updates from David Costanzo
Update email address, remove URL

Comments (0)

Files changed (5)

+2003-06-09  David Costanzo  <david_costanzo@yahoo.com>
+
+	* tetris.el:
+	* tetris.el (tetris-clear-wait-tick-period): New.  The time taken to clear a full row.  Independent of current drop speed.
+	* tetris.el (tetris-clearing-options): New.  Glyph properties for a row that is being cleared.
+	* tetris.el (tetris-shapes): Make 'T' shape pivot around center.  Make 'I' shape pivot closer to center.
+	* tetris.el (tetris-clearing): New.  Glyph index for a row that is being cleared.
+	* tetris.el (tetris-state-playing): New.  Game FSM state for when shapes are dropping.
+	* tetris.el (tetris-state-clearing): New.  Game FSM state for dropping rows down after a lower row is cleared.
+	* tetris.el (tetris-state-clearing-wait): New.  Game FSM state for showing a row that is being cleared.
+	* tetris.el (tetris-state): New.  The state machine's current state.
+	* tetris.el (tetris-shapes-bounding-boxes): New.  Multi-dimensional array that holds the bounding box for each of the seven shapes.
+	* tetris.el (tetris-display-options): Add tetris-clearing-options.
+	* tetris.el (tetris-shape-rotation-get-cell): New.  Get glyph index for shape,rotation,x,y tuple.
+	* tetris.el (tetris-get-shape-cell):  Reimplement in terms of tetris-shape-rotation-get-cell.
+	* tetris.el (tetris-shape-min-x): New.  Get left-side of bounding-box for shape,rotation tuple.
+	* tetris.el (tetris-shape-max-x): New.  Get right-side of bounding-box for shape,rotation tuple.
+	* tetris.el (tetris-shape-min-y): New.  Get top of bounding-box for shape,rotation tuple.
+	* tetris.el (tetris-shape-max-y): New. Get bottom of bounding-box for shape,rotation tuple.
+	* tetris.el ((tetris-bounding-box)): New. Struct for a bounding-box.
+	* tetris.el (tetris-bounding-box-add-point): New.  Recompute bounding box extrema to include point.
+	* tetris.el (tetris-bounding-box-compute-all): New.  Compute bounding boxes for all shape,rotation tuples.
+	* tetris.el (tetris-new-shape): Add new shape and rotation parameters to tetris-shape-width.
+	* tetris.el (tetris-draw-shape):  Make loop from bounding box extrema, instead of shape width and height.
+	* tetris.el (tetris-erase-shape): Ditto.
+	* tetris.el (tetris-test-shape): Ditto.
+	* tetris.el (tetris-full-rows): New.  Return all rows that are full.
+	* tetris.el (tetris-mark-rows-for-clearing): New.  Change glyph index for all full rows to tetris-clearing.
+	* tetris.el (tetris-reset-game):  Set FSM state to playing.
+	* tetris.el (tetris-shape-done): Add FSM transition to tetris-state-clearing and tetris-state-clearing-wait.
+	* tetris.el (tetris-update-game): Ditto.
+	* tetris.el (tetris-move-bottom): Only execute if FSM state is playing.
+	* tetris.el (tetris-move-left): Ditto.
+	* tetris.el (tetris-move-right): Ditto.
+	* tetris.el (tetris-rotate-prev): Ditto.
+	* tetris.el (tetris-rotate-next): Ditto.
+	* tetris.el (tetris-mode):  Precompute shape bounding boxes.
+
 2003-03-30  Steve Youngs  <youngs@xemacs.org>
 
 	* Makefile (EARLY_GENERATED_LISP): Revert previous change.
 
 ;; Copyright (C) 1998 Free Software Foundation, Inc.
 
-;; Author: Glynn Clements <glynn@sensei.co.uk>
+;; Author: Glynn Clements <glynn.clements@virgin.net>
 ;; Version: 1.03
 ;; Created: 1997-08-13
 ;; Keywords: games
 ;; Modified: 1998-01-27, added gamegrid-event-x/y
 ;; Modified: 1998-05-28, enclose body of gamegrid-add-score in save-excursion
 ;; Modified: 1998-06-23, copyright assigned to FSF
+;; Modified: 2003-06-14, update email address, remove URL
 
-;; URL: ftp://sensei.co.uk/misc/elisp-games.tar.gz
 ;; Tested with XEmacs 20.3/4/5 and Emacs 19.34
 
 (eval-when-compile
 
 ;; Copyright (C) 1998 Free Software Foundation, Inc.
 
-;; Author: Glynn Clements <glynn@sensei.co.uk>
+;; Author: Glynn Clements <glynn.clements@virgin.net>
 ;; Version: 1.02
 ;; Created: 1997-09-10
 ;; Keywords: games
 ;; Modified: 1998-05-28
 ;;	Added popup menu
 ;; Modified: 1998-06-23, copyright assigned to FSF
+;; Modified: 2003-06-14, update email address, remove URL
 
-;; URL: ftp://sensei.co.uk/misc/elisp-games.tar.gz
 ;; Tested with XEmacs 20.3/4/5 and Emacs 19.34
 
 (eval-when-compile
 
 ;; Copyright (C) 1998 Free Software Foundation, Inc.
 
-;; Author: Glynn Clements <glynn@sensei.co.uk>
+;; Author: Glynn Clements <glynn.clements@virgin.net>
 ;; Version: 1.04
 ;; Created: 1997-09-11
 ;; Keywords: games
 ;; Modified: 1998-06-04, added `undo' feature
 ;;   added number of blocks done/total to score and modeline
 ;; Modified: 1998-06-23, copyright assigned to FSF
+;; Modified: 2003-06-14, update email address, remove URL
 
-;; 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
 
 ;; Copyright (C) 1998 Free Software Foundation, Inc.
 
-;; Author: Glynn Clements <glynn@sensei.co.uk>
-;; Version: 2.02
+;; Author: Glynn Clements <glynn.clements@virgin.net>
+;; Version: 2.03
 ;; Created: 1997-08-13
 ;; Keywords: games
 
 ;;	Make tetris-shift-down clear the top row
 ;;	Added popup menu
 ;; Modified: 1998-06-23, copyright assigned to FSF
+;; Modified: 2003-06-14
+;;	update email address, remove URL
+;;	various changes from David Costanzo <david_costanzo@yahoo.com>
 
-;; URL: ftp://sensei.co.uk/misc/elisp-games.tar.gz
-;; Tested with XEmacs 20.3/4/5 and Emacs 19.34
+;; Tested with XEmacs 20.3/4/5, 21.4.13 and Emacs 19.34
 
 (eval-when-compile
   (require 'cl))
 (defvar tetris-default-tick-period 0.3
   "The default time taken for a shape to drop one row")
 
+(defvar tetris-clear-wait-tick-period 0.5
+  "The default time taken to clear a full row.")
+
 (defvar tetris-update-speed-function
   'tetris-default-update-speed-function
   "Function run whenever the Tetris score changes
      (color-tty "black")
      (t nil))))
 
+(defvar tetris-clearing-options
+  '(((glyph colorize)
+     (t ?\*))
+    ((color-x color-x)
+     (mono-x grid-x)
+     (t nil))
+    (((glyph color-x) [1.0 1.0 1.0])
+     (color-tty "white")
+     (t nil))))
+
 (defvar tetris-cell-options
   '(((glyph colorize)
      (emacs-tty ?O)
     [[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 6 0 0] [0 6 0 0] [0 0 0 0] [0 6 0 0]]
+    [[6 6 6 0] [0 6 6 0] [6 6 6 0] [6 6 0 0]]
+    [[0 0 0 0] [0 6 0 0] [0 6 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]]]])
+   [[[7 7 7 7] [0 7 0 0] [7 7 7 7] [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 0] [0 7 0 0]]]])
 
 ;;the scoring rules were taken from "xtetris".  Blocks score differently 
 ;;depending on their rotation
 
 (defconst tetris-space 9)
 
+(defconst tetris-clearing 11)
+
+;;; Game states
+(defconst tetris-state-playing       0)
+(defconst tetris-state-clearing      1)
+(defconst tetris-state-clearing-wait 2)
+
 (defun tetris-default-update-speed-function (shapes rows)
   (/ 20.0 (+ 50.0 rows)))
 
 (defvar tetris-pos-x 0)
 (defvar tetris-pos-y 0)
 (defvar tetris-paused nil)
+(defvar tetris-state  tetris-state-playing)
+(defvar tetris-shapes-bounding-boxes nil)
 
 (make-variable-buffer-local 'tetris-shape)
 (make-variable-buffer-local 'tetris-rot)
 		    tetris-border-options)
 		   ((= c tetris-space)
 		    tetris-space-options)
+		   ((= c tetris-clearing)
+		    tetris-clearing-options)
                   (t
                    '(nil nil nil)))))
     options))
 			   tetris-n-rows nil)))
 	(and (numberp period) period))))
 
-(defun tetris-get-shape-cell (x y)
+(defun tetris-shape-rotation-get-cell (shape rotation x y)
   (aref (aref (aref (aref tetris-shapes
-			  tetris-shape)
+			  shape)
 		    y)
-	      tetris-rot)
+	      rotation)
 	x))
 
-(defun tetris-shape-width ()
-  (aref (aref tetris-shape-dimensions tetris-shape)
-	(% tetris-rot 2)))
+(defun tetris-get-shape-cell (x y)
+  (tetris-shape-rotation-get-cell tetris-shape tetris-rot x y))
 
-(defun tetris-shape-height ()
-  (aref (aref tetris-shape-dimensions tetris-shape)
-	(- 1 (% tetris-rot 2))))
+(defun tetris-shape-width (shape rotation)
+  (aref (aref tetris-shape-dimensions shape)
+	(% rotation 2)))
+
+(defun tetris-shape-height (shape rotation)
+  (aref (aref tetris-shape-dimensions shape)
+	(- 1 (% rotation 2))))
 
 (defun tetris-draw-score ()
   (let ((strings (vector (format "Shapes: %05d" tetris-n-shapes)
 				     (+ tetris-score-y y)
 				     (aref string x)))))))
 
+
+(defun tetris-shape-min-x (shape rotation)
+  (tetris-bounding-box-min-x (aref (aref tetris-shapes-bounding-boxes 
+					    shape) 
+				      rotation)))
+
+(defun tetris-shape-max-x (shape rotation)
+  (tetris-bounding-box-max-x (aref (aref tetris-shapes-bounding-boxes 
+					    shape) 
+				      rotation)))
+
+(defun tetris-shape-min-y (shape rotation)
+  (tetris-bounding-box-min-y (aref (aref tetris-shapes-bounding-boxes 
+					    shape) 
+				      rotation)))
+
+(defun tetris-shape-max-y (shape rotation)
+  (tetris-bounding-box-max-y (aref (aref tetris-shapes-bounding-boxes 
+					    shape) 
+				      rotation)))
+
+;; Structure for a 2D bounding box.
+(defstruct (tetris-bounding-box)
+  (min-x 0)
+  (min-y 0)
+  (max-x 3)
+  (max-y 3))
+
+
+;; Adds a new point to the bounding box, adjusting the min and max values 
+;; for x and y necessary
+(defun tetris-bounding-box-add-point (box x y)
+  (if (< x (tetris-bounding-box-min-x box)) ; new min-x
+      (setf (tetris-bounding-box-min-x box) x))
+  (if (< y (tetris-bounding-box-min-y box)) ; new min-y
+      (setf (tetris-bounding-box-min-y box) y))
+  (if (> x (tetris-bounding-box-max-x box)) ; new max-x
+      (setf (tetris-bounding-box-max-x box) x))
+  (if (> y (tetris-bounding-box-max-y box)) ; new min-y
+      (setf (tetris-bounding-box-max-y box) y)))
+
+;; Computes the bounding boxes for all shape/rotation pairs.
+;; Returns the result as a 2D array of tetris-bounding-box structures, 
+;; where the first dimensions is keyed by the shape index and the second
+;; dimension is keyed by the rotation.
+(defun tetris-bounding-box-compute-all () 
+  
+  ; create the structure
+  (let ((bounding-boxes (make-vector 7 nil)))
+    (loop for current-shape from 0 to 6 do
+      (setf (aref bounding-boxes current-shape) (make-vector 4 nil))
+      (loop for current-rotation from 0 to 3 do
+	(let ((bounding-box (make-tetris-bounding-box :min-x 3 
+							 :min-y 3 
+							 :max-x 0 
+							 :max-y 0)))
+
+	  (setf (aref (aref bounding-boxes 
+			    current-shape) 
+		      current-rotation)
+		bounding-box))))
+
+  ; fill in the values
+  (loop for current-shape from 0 to 6 do
+      (loop for current-rotation from 0 to 3 do
+	(loop for x from 0 to 3 do
+	    (loop for y from 0 to 3 do
+	      (if (/= tetris-space
+		      (tetris-shape-rotation-get-cell current-shape current-rotation x y))
+		  (tetris-bounding-box-add-point (aref (aref bounding-boxes 
+								current-shape) 
+							  current-rotation)
+						    x 
+						    y))))))
+  bounding-boxes))
+
+
 (defun tetris-update-score ()
   (tetris-draw-score)
   (let ((period (tetris-get-tick-period)))
   (setq tetris-shape tetris-next-shape)
   (setq tetris-rot 0)
   (setq tetris-next-shape (random 7))
-  (setq tetris-pos-x (/ (- tetris-width (tetris-shape-width)) 2))
+  (setq tetris-pos-x (/ (- tetris-width (tetris-shape-width tetris-shape 
+                                                            tetris-rot)) 
+                        2))
   (setq tetris-pos-y 0)
   (if (tetris-test-shape)
       (tetris-end-game)
 				   (tetris-get-shape-cell x y))))))
 
 (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 ((min-x (tetris-shape-min-x tetris-shape tetris-rot))
+	(min-y (tetris-shape-min-y tetris-shape tetris-rot))
+	(max-x (tetris-shape-max-x tetris-shape tetris-rot))
+	(max-y (tetris-shape-max-y tetris-shape tetris-rot)))
+    (loop for y from min-y to max-y do
+	(loop for x from min-x to max-x do
 	      (let ((c (tetris-get-shape-cell x y)))
 		(if (/= c tetris-blank)
 		    (gamegrid-set-cell (+ tetris-top-left-x
 				       (+ tetris-top-left-y
 					  tetris-pos-y
 					  y)
-				       c))))))
+				       c)))))))
 
 (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 ((min-x (tetris-shape-min-x tetris-shape tetris-rot))
+	(min-y (tetris-shape-min-y tetris-shape tetris-rot))
+	(max-x (tetris-shape-max-x tetris-shape tetris-rot))
+	(max-y (tetris-shape-max-y tetris-shape tetris-rot)))
+    (loop for y from min-y to max-y do
+	(loop for x from min-x to max-x 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))))))
+		    (gamegrid-set-cell px py 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
+  (let ((min-x (tetris-shape-min-x tetris-shape tetris-rot))
+	(min-y (tetris-shape-min-y tetris-shape tetris-rot))
+	(max-x (tetris-shape-max-x tetris-shape tetris-rot))
+	(max-y (tetris-shape-max-y tetris-shape tetris-rot)))
+    (let ((hit nil))
+       (loop for y from min-y to max-y do
+	    (loop for x from min-x to max-x do
 		(unless hit
 		  (setq hit
 			(let* ((c (tetris-get-shape-cell x y))
 				   (>= yy tetris-height)
 				   (/= (gamegrid-get-cell px py)
 				       tetris-blank))))))))
-    hit))
+    hit)))
+
+(defun tetris-full-rows () 
+  (let (full-rows)
+    (loop for y from 0 to (1- tetris-height) do
+      (if (tetris-full-row y)
+	  (push y full-rows)))
+    full-rows))
+
+(defun tetris-mark-rows-for-clearing (rows) 
+  (dolist (row-y rows)
+    (loop for x from 0 to (1- tetris-width) do
+      (gamegrid-set-cell (+ tetris-top-left-x x)
+			 (+ tetris-top-left-y row-y)
+			 tetris-clearing)
+      )))
 
 (defun tetris-full-row (y)
   (let ((full t))
 	tetris-n-shapes	0
 	tetris-n-rows	0
 	tetris-score	0
-	tetris-paused	nil)
+	tetris-paused	nil
+        tetris-state    tetris-state-playing)
   (tetris-new-shape))
 
 (defun tetris-shape-done ()
-  (tetris-shift-down)
-  (setq tetris-n-shapes (1+ tetris-n-shapes))
-  (setq tetris-score
-	(+ tetris-score 
-	   (aref (aref tetris-shape-scores tetris-shape) tetris-rot)))
-  (tetris-update-score)
-  (tetris-new-shape))
+  (let ((full-rows (tetris-full-rows)))
+
+    ;; Update the score while the shape still exists
+    (setq tetris-n-shapes (1+ tetris-n-shapes))
+    (setq tetris-score
+	  (+ tetris-score 
+	     (aref (aref tetris-shape-scores tetris-shape) tetris-rot)))
+    (tetris-update-score)
+
+    (if full-rows
+	;; state transition to clear the full rows
+	(progn
+	  (setq tetris-state tetris-state-clearing-wait)
+	  (setq tetris-shape nil))
+      ;; drop the next shape
+      (tetris-new-shape))))
 
 (defun tetris-update-game (tetris-buffer)
   "Called on each clock tick.
 Drops the shape one square, testing for collision."
-  (if (and (not tetris-paused)
-	   (eq (current-buffer) tetris-buffer))
-      (let (hit)
-	(tetris-erase-shape)
-	(setq tetris-pos-y (1+ tetris-pos-y))
-	(setq hit (tetris-test-shape))
-	(if hit
-	    (setq tetris-pos-y (1- tetris-pos-y)))
-	(tetris-draw-shape)
-	(if hit
-	    (tetris-shape-done)))))
+  (if (eq (current-buffer) tetris-buffer)
+      (cond 
+	((eq tetris-state tetris-state-playing) ; The game is playing
+	 (if (not tetris-paused)
+	     (let (hit)
+	       (tetris-erase-shape)
+	       (setq tetris-pos-y (1+ tetris-pos-y))
+	       (setq hit (tetris-test-shape))
+	       (if hit
+		   (setq tetris-pos-y (1- tetris-pos-y)))
+	       (tetris-draw-shape)
+	       (if hit
+		   (tetris-shape-done)))))
+	
+
+	 ((eq tetris-state tetris-state-clearing-wait) ; The game is marking a full row
+	  (tetris-mark-rows-for-clearing (tetris-full-rows))
+	  (setq tetris-state tetris-state-clearing)
+
+	  ; Show the marked rows for a peroid of time that is
+          ; independent of current game speed.
+	  (gamegrid-set-timer tetris-clear-wait-tick-period))
+	
+
+	 ((eq tetris-state tetris-state-clearing) ; The game is clearing a full row
+
+	  (tetris-shift-down) 
+	  (tetris-new-shape)
+
+	  ; Restore the tick period back to game-speed
+	  (let ((period (tetris-get-tick-period)))
+	    (if period 
+		(gamegrid-set-timer period)
+		(gamegrid-set-timer tetris-default-tick-period)))
+	      
+	  (setq tetris-state tetris-state-playing)))))
+
 
 (defun tetris-move-bottom ()
   "Drops the shape to the bottom of the playing area"
   (interactive)
-  (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)))
+  (if (eq tetris-state tetris-state-playing)
+      (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 ()
   "Moves the shape one square to the left"
   (interactive)
-  (unless (= tetris-pos-x 0)
-    (tetris-erase-shape)
-    (setq tetris-pos-x (1- tetris-pos-x))
-    (if (tetris-test-shape)
-	(setq tetris-pos-x (1+ tetris-pos-x)))
-    (tetris-draw-shape)))
+  (if (eq tetris-state tetris-state-playing)
+      (progn
+        (tetris-erase-shape)
+        (setq tetris-pos-x (1- tetris-pos-x))
+        (if (tetris-test-shape)
+            (setq tetris-pos-x (1+ tetris-pos-x)))
+        (tetris-draw-shape))))
 
 (defun tetris-move-right ()
   "Moves the shape one square to the right"
   (interactive)
-  (unless (= (+ tetris-pos-x (tetris-shape-width))
-	     tetris-width)
-    (tetris-erase-shape)
-    (setq tetris-pos-x (1+ tetris-pos-x))
-    (if (tetris-test-shape)
-	(setq tetris-pos-x (1- tetris-pos-x)))
-    (tetris-draw-shape)))
+  (if (eq tetris-state tetris-state-playing)
+      (progn
+        (tetris-erase-shape)
+        (setq tetris-pos-x (1+ tetris-pos-x))
+        (if (tetris-test-shape)
+            (setq tetris-pos-x (1- tetris-pos-x)))
+        (tetris-draw-shape))))
 
 (defun tetris-rotate-prev ()
   "Rotates the shape clockwise"
   (interactive)
-  (tetris-erase-shape)
-  (setq tetris-rot (% (+ 1 tetris-rot) 4))
-  (if (tetris-test-shape)
-      (setq tetris-rot (% (+ 3 tetris-rot) 4)))
-  (tetris-draw-shape))
+  (if (eq tetris-state tetris-state-playing)
+      (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))))
 
 (defun tetris-rotate-next ()
   "Rotates the shape anticlockwise"
   (interactive)
-  (tetris-erase-shape)
-  (setq tetris-rot (% (+ 3 tetris-rot) 4))
-  (if (tetris-test-shape)
-      (setq tetris-rot (% (+ 1 tetris-rot) 4)))
-  (tetris-draw-shape))
+  (if (eq tetris-state tetris-state-playing)
+      (progn
+        (tetris-erase-shape)
+        (setq tetris-rot (% (+ 3 tetris-rot) 4))
+        (if (tetris-test-shape)
+            (setq tetris-rot (% (+ 1 tetris-rot) 4)))
+        (tetris-draw-shape))))
 
 (defun tetris-end-game ()
   "Terminates the current game"
   (setq gamegrid-use-glyphs tetris-use-glyphs)
   (setq gamegrid-use-color tetris-use-color)
 
+  ;; Precompute the bounding boxes for known each shape/rotation pair
+  (setq tetris-shapes-bounding-boxes (tetris-bounding-box-compute-all))
+
   (gamegrid-init (tetris-display-options))
 
   (run-hooks 'tetris-mode-hook))