hetris-0.1: src/Pieces.lhs
% vim: set tw=72:
% Part of Hetris
\section{Pieces: Concrete implementation}
The header is, as it always is for a concrete implementation of an
abstract module, the same as that of the abstract specification:
\begin{code}
module Pieces (Piece, blocks, extent_down, extent_left, extent_right,
rot_left, rot_right, pieces) where
import Data
\end{code}
Tetris is not a particularly challenging task for today's CPUs, and
the amount of RAM required is not likely to cause a problem on a modern
machine. Our motivation here then is to pick a representation such that
the implementation can be easily understood.
At the core of a representation is a list of coordinates of blocks
relative to the key square of the piece. We store an infinite list of
these corresponding to the blocks used by a piece after successive left
rotations.
\begin{code}
newtype Piece = Piece [[(Vector, Vector)]]
\end{code}
This makes the implementation of \hsfunction{blocks} simple---we just
return the first list.
\begin{code}
blocks :: Piece -> [(Vector, Vector)]
blocks (Piece xss) = head xss
\end{code}
The code to calculate the maximum extents is based on the output of
\hsfunction{blocks} in the way hinted at in
Section~\ref{sec:abs_pieces}.
\begin{code}
extent_down :: Piece -> Vector
extent_down p = maximum $ map snd $ blocks p
extent_left :: Piece -> Vector
extent_left p = negate $ minimum $ map fst $ blocks p
extent_right :: Piece -> Vector
extent_right p = maximum $ map fst $ blocks p
\end{code}
To rotate a piece left we just remove the first element of the list; the
correctness of this follows from the definition of the list above.
Rotating right is the same as rotating left 3 times so we drop the first
3 elements of the list.
\begin{code}
rot_left :: Piece -> Piece
rot_left (Piece xss) = Piece (tail xss)
rot_right :: Piece -> Piece
rot_right (Piece xss) = Piece (drop 3 xss)
\end{code}
The list of pieces is just that---the second part of the name is
intended to be descriptive of the shape of the piece, but this is more
successful in some cases than others.
\begin{code}
pieces :: [Piece]
pieces = [piece_I, piece_L, piece_J, piece_T, piece_2, piece_5, piece_O]
\end{code}
We conclude with the actual definitions of the pieces.
\begin{code}
piece_I, piece_L, piece_J, piece_T, piece_2, piece_5, piece_O :: Piece
piece_I = Piece (cycle [p1, p2])
where p1 = [(0, -1), (0, 0), (0, 1), (0, 2)]
p2 = [(-1, 0), (0, 0), (1, 0), (2, 0)]
piece_L = Piece (cycle [p1, p2, p3, p4])
where p1 = [(0, -1), (0, 0), (0, 1), (1, 1)]
p2 = [(-1, 0), (0, 0), (1, 0), (1, -1)]
p3 = [(-1, -1), (0, -1), (0, 0), (0, 1)]
p4 = [(-1, 1), (-1, 0), (0, 0), (1, 0)]
piece_J = Piece (cycle [p1, p2, p3, p4])
where p1 = [(0, -1), (0, 0), (0, 1), (-1, 1)]
p2 = [(-1, 0), (0, 0), (1, 0), (1, 1)]
p3 = [(1, -1), (0, -1), (0, 0), (0, 1)]
p4 = [(-1, -1), (-1, 0), (0, 0), (1, 0)]
piece_T = Piece (cycle [p1, p2, p3, p4])
where p1 = [(-1, 0), (0, 0), (1, 0), (0, 1)]
p2 = [(0, -1), (0, 0), (0, 1), (1, 0)]
p3 = [(-1, 0), (0, 0), (1, 0), (0, -1)]
p4 = [(0, -1), (0, 0), (0, 1), (-1, 0)]
piece_2 = Piece (cycle [p1, p2])
where p1 = [(1, 0), (0, 0), (0, -1), (-1, -1)]
p2 = [(0, 0), (0, -1), (-1, 0), (-1, 1)]
piece_5 = Piece (cycle [p1, p2])
where p1 = [(-1, 0), (0, 0), (0, -1), (1, -1)]
p2 = [(0, 0), (0, -1), (1, 0), (1, 1)]
piece_O = Piece (repeat [(0, 0), (0, 1), (1, 0), (1, 1)])
\end{code}