module VM where
|
|
import Data.Hashable (hash, Hashable(..))
|
|
import qualified Data.List as List
|
|
import Data.Set (Set)
|
|
import qualified Data.Set as Set
|
|
|
|
import Datatypes.Board (Board(..))
|
|
import qualified Datatypes.Board as Board
|
|
import Datatypes.Cell (Cell(..))
|
|
import qualified Datatypes.Cell as Cell
|
|
import Datatypes.Game (Game(..), Command(..))
|
|
import qualified Datatypes.Game as Game
|
|
import Datatypes.Unit (Unit(..))
|
|
import qualified Datatypes.Unit as Unit
|
|
|
|
data Notes = OK
|
|
| GameOver
|
|
| Lock { rowsCleaned :: Int }
|
|
| ErrorSamePosition
|
|
| ErrorGameEnded
|
|
deriving (Show,Eq)
|
|
|
|
moveScore :: Int -> Int -> Int -> Int
|
|
moveScore size lines linesOld = points + lineBonus where
|
|
points = size + 100 * ((1 + lines) * lines) `div` 2
|
|
lineBonus = if linesOld > 1
|
|
then floor (fromIntegral ((linesOld - 1) * points) / 100)
|
|
else 0
|
|
|
|
lockUnit :: Game -> Game
|
|
lockUnit game = game {
|
|
board = newBoard,
|
|
units = newUnits otherUnits,
|
|
visitedUnits = initialVisitedUnits otherUnits,
|
|
oldLines = clearedLines,
|
|
score = Game.score game + newScore
|
|
} where
|
|
(currentUnit:otherUnits) = Game.units game
|
|
newUnits [] = []
|
|
newUnits (u:us) | u `Unit.collidesWith` newBoard = []
|
|
newUnits us = us
|
|
mergedBoard = currentUnit `Unit.mergeWith` (Game.board game)
|
|
(newBoard, clearedLines) = Board.clearLines mergedBoard
|
|
size = Set.size $ Unit.members currentUnit
|
|
newScore = moveScore size clearedLines (Game.oldLines game)
|
|
initialVisitedUnits [] = Set.empty
|
|
initialVisitedUnits (u:us) = Set.singleton (hash u)
|
|
|
|
step :: Game -> Command -> (Game, Notes)
|
|
step game@(Game { units = [] }) command = (game, ErrorGameEnded)
|
|
step game command =
|
|
let (unit:otherUnits) = Game.units game
|
|
newUnit = applyCommand unit command
|
|
board = Game.board game
|
|
shouldLock = newUnit `Unit.collidesWith` board || newUnit `Unit.isOutsideOf` board
|
|
newVisitedUnits = Set.insert (hash newUnit) (Game.visitedUnits game)
|
|
updatedGame = Game.notifyCommand (lockUnit game) command
|
|
in
|
|
if shouldLock
|
|
then
|
|
if Game.isCompleted updatedGame
|
|
then (updatedGame, GameOver)
|
|
else (updatedGame, Lock (Game.oldLines updatedGame))
|
|
else
|
|
if Set.member (hash newUnit) (Game.visitedUnits game)
|
|
then (game { units = newUnit:otherUnits }, ErrorSamePosition)
|
|
else (Game.notifyCommand (game { units = newUnit:otherUnits, visitedUnits = newVisitedUnits }) command, OK)
|
|
|
|
applyCommand :: Unit -> Command -> Unit
|
|
applyCommand unit MoveW = Unit.map move unit where
|
|
move (x, y) = (x - 1, y)
|
|
applyCommand unit MoveE = Unit.map move unit where
|
|
move (x, y) = (x + 1, y)
|
|
applyCommand unit MoveSW = Unit.map move unit where
|
|
move (x, y) = (x - ((y + 1) `mod` 2), y + 1)
|
|
applyCommand unit MoveSE = Unit.map move unit where
|
|
move (x, y) = (x + (y `mod` 2), y + 1)
|
|
applyCommand (Unit pivot members) RotateClockwise = Unit pivot (Set.map transform members) where
|
|
transform cell = Cell.rotateClockwise pivot cell
|
|
applyCommand (Unit pivot members) RotateCounterclockwise = Unit pivot (Set.map transform members) where
|
|
transform cell = Cell.rotateCounterclockwise pivot cell
|