tetris-0.27177: Board.hs
module Board where
import Util
--------------------------------
-- Cell
data Cell = Empty | Gray | Red | Yellow | Purple | Green | Blue | Orange | Cyan
deriving Eq
cellColor :: (Num t, Num t1, Num t2) => Cell -> (t, t1, t2)
cellColor cell =
case cell of
Gray -> (128, 128, 128)
Red -> (255, 0, 0)
Yellow -> (255, 255, 0)
Purple -> (255, 0, 255)
Green -> (0, 255, 0)
Blue -> (0, 0, 255)
Orange -> (255, 128, 0)
Cyan -> (0, 255, 255)
--------------------------------
-- BlockType
data BlockType = BlockI | BlockO | BlockS | BlockZ | BlockJ | BlockL | BlockT
blockPattern :: BlockType -> [[Int]]
blockPattern BlockI = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 1, 1, 1, 1], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
blockPattern BlockO = [[1, 1], [1, 1]]
blockPattern BlockS = [[0, 1, 1], [1, 1, 0]]
blockPattern BlockZ = [[1, 1, 0], [0, 1, 1]]
blockPattern BlockJ = [[0, 0, 0], [1, 1, 1], [1, 0, 0]]
blockPattern BlockL = [[0, 0, 0], [1, 1, 1], [0, 0, 1]]
blockPattern BlockT = [[0, 0, 0], [1, 1, 1], [0, 1, 0]]
blockRotPattern :: BlockType -> Int -> [[Int]]
blockRotPattern blktype rot = rotate rot $ blockPattern blktype
blockCell :: BlockType -> Cell
blockCell BlockI = Red
blockCell BlockO = Yellow
blockCell BlockS = Purple
blockCell BlockZ = Green
blockCell BlockJ = Blue
blockCell BlockL = Orange
blockCell BlockT = Cyan
-- 乱数でブロックを選ぶ
randBlockType :: IO BlockType
randBlockType = fmap (blockTypes !!) (randN (length blockTypes))
blockTypes :: [BlockType]
blockTypes = [BlockI, BlockO, BlockS, BlockZ, BlockJ, BlockL, BlockT]
--------------------------------
-- Board
type Board = [[Cell]]
boardWidth,boardHeight :: Int
boardWidth = 10 + 2
boardHeight = 20 + 4
emptyLine :: [Cell]
emptyLine = [Gray] ++ replicate (boardWidth - 2) Empty ++ [Gray]
emptyBoard :: Board
emptyBoard = replicate (boardHeight-1) emptyLine ++ [bottom]
where
bottom = (replicate boardWidth Gray)
inBoard :: Int -> Int -> Bool
inBoard x y = 0 <= x && x < boardWidth && 0 <= y && y < boardHeight
boardRef :: [[Cell]] -> Int -> Int -> Cell
boardRef board x y =
if inBoard x y
then board !! y !! x
else Empty
boardSet :: [[a]] -> Int -> Int -> a -> [[a]]
boardSet board x y c =
if inBoard x y
then replace board y (replace (board !! y) x c)
else board
canMove :: Board -> BlockType -> Int -> Int -> Int -> Bool
canMove board blktype x y rot = not $ or $ concat $ idxmap2 isHit pat
where
pat = blockRotPattern blktype rot
isHit (_dx,_dy) 0 = False
isHit (dx,dy) 1 = inBoard (x+dx) (y+dy) && boardRef board (x+dx) (y+dy) /= Empty
storeBlock :: Board -> BlockType -> Int -> Int -> Int -> Board
storeBlock board blktype x y rot = board'
where
pat = blockRotPattern blktype rot
patWithIdx = concat $ idxmap2 pair pat
board' = foldl store board $ map fst $ filter ((== (1::Int)) . snd) patWithIdx
store bord (dx,dy) = boardSet bord (x+dx) (y+dy) (blockCell blktype)
getFilledLines :: [[Cell]] -> [Int]
getFilledLines board = map fst $ filter (isFilled . snd) $ zip [0..] $ init board
where
isFilled = all (/= Empty) . init . tail
eraseLines :: Board -> [Int] -> Board
eraseLines = foldl (\rs y -> replace rs y emptyLine)
fallLines :: Board -> [Int] -> Board
fallLines = foldl (\rs y -> emptyLine : remove y rs)
landingY :: Board -> BlockType -> Int -> Int -> Int -> Int
landingY board blktype x y rot = loop y
where
loop z
| canMove board blktype x (z+1) rot = loop (z+1)
| otherwise = z
graynize :: [[Cell]] -> Int -> [[Cell]]
graynize board y = replace board y $ map (\x -> if x == Empty then Empty else Gray) $ board !! y