-- | Types and functions for manipulating vectors. module Hs2048.Vector ( Vector , canShift , empty , emptyIndexes , parse , render , score , set , shift ) where import Data.List (group) import Data.Maybe (isJust, isNothing) import Data.Monoid ((<>)) import qualified Hs2048.Tile as T {- | Represents a row or column on the game board. By convention, a vector has 4 tiles. -} type Vector = [T.Tile] {- | Determines if the vector can be shifted. >>> canShift [Nothing, Just 2, Nothing, Nothing] True -} canShift :: Vector -> Bool canShift v = shift v /= v {- | Returns an empty vector of the given size. >>> empty 4 [Nothing,Nothing,Nothing,Nothing] -} empty :: Int -> Vector empty = flip replicate T.empty {- | Returns the indexes that don't contain tiles. >>> emptyIndexes [Nothing, Just 2, Nothing, Nothing] [0,2,3] -} emptyIndexes :: Vector -> [Int] emptyIndexes = fmap fst . filter (isNothing . snd) . zip [0 ..] {- | Parses a string as a vector. This is the inverse of 'render'. >>> parse "- 2 - -" [Nothing,Just 2,Nothing,Nothing] -} parse :: String -> Vector parse = fmap T.parse . words {- | Renders a vector as a string. This is the inverse of 'parse'. >>> render [Nothing, Just 2, Nothing, Nothing] "- 2 - -" -} render :: Vector -> String render = unwords . fmap T.render {- | Calculates the score of a vector. >>> score [Nothing, Just 2, Just 4, Just 8] 20 -} score :: Vector -> Int score = sum . fmap T.score {- | Sets a tile at the given index in the vector. >>> set [Nothing, Nothing, Nothing, Nothing] (Just 2) 1 [Nothing,Just 2,Nothing,Nothing] -} set :: Vector -> T.Tile -> Int -> Vector set v t i = zipWith go [0 ..] v where go i' t' = if i' == i then t else t' {- | Shifts a vector toward the head. The output vector will be the same size as the input vector, padded with @Nothing@. >>> shift [Nothing, Just 2, Nothing, Nothing] [Just 2,Nothing,Nothing,Nothing] Like tiles will be combined. >>> shift [Just 2, Nothing, Just 2, Just 2] [Just 4,Just 2,Nothing,Nothing] Any number of tiles can be combined in one shift. >>> shift [Just 2, Just 2, Just 4, Just 4] [Just 4,Just 8,Nothing,Nothing] -} shift :: Vector -> Vector shift v = take n (v' <> empty n) where n = length v v' = group (filter isJust v) >>= go go (Just a : Just b : ts) = Just (a + b) : go ts go ts = ts