You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
4.7 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. module VM where
  2. import Data.Hashable (hash, Hashable(..))
  3. import qualified Data.List as List
  4. import Data.Set (Set)
  5. import qualified Data.Set as Set
  6. import Datatypes.Board (Board(..))
  7. import qualified Datatypes.Board as Board
  8. import Datatypes.Cell (Cell(..))
  9. import qualified Datatypes.Cell as Cell
  10. import Datatypes.Game (Game(..), Command(..))
  11. import qualified Datatypes.Game as Game
  12. import Datatypes.Unit (Unit(..))
  13. import qualified Datatypes.Unit as Unit
  14. data Notes = OK
  15. | GameOver
  16. | Lock { rowsCleaned :: Int }
  17. | ErrorSamePosition
  18. | ErrorGameEnded
  19. deriving (Show,Eq)
  20. cmdToString :: [Command] -> String
  21. cmdToString [] = ""
  22. cmdToString cmds@(s:ss) = case smartConvert cmds of
  23. Just word -> word ++ cmdToString (drop (length word) cmds)
  24. Nothing -> dumbConvert s : cmdToString ss
  25. dumbConvert MoveW = 'p'
  26. dumbConvert MoveE = 'b'
  27. dumbConvert MoveSW = 'a'
  28. dumbConvert MoveSE = 'l'
  29. dumbConvert RotateClockwise = 'd'
  30. dumbConvert RotateCounterclockwise = 'k'
  31. smartConvert cmds = innerSmartConvert cmds Game.powerPhrases where
  32. innerSmartConvert _ [] = Nothing
  33. innerSmartConvert cmds (p:ps) = if p `List.isPrefixOf` cmds
  34. then Just $ Game.phraseConverter p
  35. else innerSmartConvert cmds ps
  36. moveScore :: Int -> Int -> Int -> Int
  37. moveScore size lines linesOld = points + lineBonus where
  38. points = size + 100 * ((1 + lines) * lines) `div` 2
  39. lineBonus = if linesOld > 1
  40. then floor (fromIntegral ((linesOld - 1) * points) / 100)
  41. else 0
  42. checkSpawn :: Game -> Game
  43. checkSpawn game@(Game { units = [] }) = game
  44. checkSpawn game@(Game { units = (u:us), board = b }) =
  45. if u `Unit.collidesWith` b
  46. then game { Game.units = [] }
  47. else game
  48. lockUnit :: Game -> Game
  49. lockUnit game = game {
  50. board = newBoard,
  51. units = otherUnits,
  52. visitedUnits = initialVisitedUnits otherUnits,
  53. oldLines = clearedLines,
  54. score = Game.score game + newScore
  55. } where
  56. (currentUnit:otherUnits) = Game.units game
  57. mergedBoard = currentUnit `Unit.mergeWith` (Game.board game)
  58. (newBoard, clearedLines) = Board.clearLines mergedBoard
  59. size = Set.size $ Unit.members currentUnit
  60. newScore = moveScore size clearedLines (Game.oldLines game)
  61. initialVisitedUnits [] = Set.empty
  62. initialVisitedUnits (u:us) = Set.singleton (hash u)
  63. step :: Game -> Command -> (Game, Notes)
  64. step game@(Game { units = [] }) command = (game, ErrorGameEnded)
  65. step game command =
  66. let (unit:otherUnits) = Game.units game
  67. newUnit = applyCommand unit command
  68. board = Game.board game
  69. shouldLock = newUnit `Unit.collidesWith` board || newUnit `Unit.isOutsideOf` board
  70. newVisitedUnits = Set.insert (hash newUnit) (Game.visitedUnits game)
  71. updatedGame = (checkSpawn $ lockUnit game) { history = command:(Game.history game), phrasesCache = Game.updateCache (Game.phrasesCache game) (command:(Game.history game)) }
  72. in
  73. if shouldLock
  74. then
  75. if Game.completed updatedGame
  76. then (updatedGame, GameOver)
  77. else (updatedGame, Lock (Game.oldLines updatedGame))
  78. else
  79. if Set.member (hash newUnit) (Game.visitedUnits game)
  80. then (game { units = newUnit:otherUnits }, ErrorSamePosition)
  81. else (game { units = newUnit:otherUnits, visitedUnits = newVisitedUnits, history = command:(Game.history game), phrasesCache = Game.updateCache (Game.phrasesCache game) (command:(Game.history game))}, OK)
  82. applyCommand :: Unit -> Command -> Unit
  83. applyCommand unit MoveW = Unit.map move unit where
  84. move (x, y) = (x - 1, y)
  85. applyCommand unit MoveE = Unit.map move unit where
  86. move (x, y) = (x + 1, y)
  87. applyCommand unit MoveSW = Unit.map move unit where
  88. move (x, y) = (x - ((y + 1) `mod` 2), y + 1)
  89. applyCommand unit MoveSE = Unit.map move unit where
  90. move (x, y) = (x + (y `mod` 2), y + 1)
  91. applyCommand (Unit pivot members) RotateClockwise = Unit pivot (Set.map transform members) where
  92. transform cell = Cell.rotateClockwise pivot cell
  93. applyCommand (Unit pivot members) RotateCounterclockwise = Unit pivot (Set.map transform members) where
  94. transform cell = Cell.rotateCounterclockwise pivot cell