haskell-snake-1.0.0: src/Game/HSnake/Snake.hs
-- Snake.hs: Snake-control logic
module Game.HSnake.Snake where
import Control.Lens
import Game.HSnake.Basic
import Data.List (nub, sort)
data Snake = Snake {
-- | Snake's position is a list containing all its elements.
-- The first element of this list is snake's head.
_position :: [Point],
_direction :: Direction,
-- | Snake's length
-- We need to keep it -- if we initialize new level with large
-- snake length, we initially get a shorter snake so
-- snake^.position is not the correct snake's measure
_len :: Int
} deriving (Show, Eq)
makeLenses ''Snake
-- | Increases snake's length by 1
increaseSnakeLength :: Snake -> Snake
increaseSnakeLength = len +~ 1
-- | Moves snake according to it's direction
moveSnake :: Snake -> Snake
moveSnake snake = position .~ newPosition $ snake
where
-- |check snake's length, if newPosition contains too many elements, remove them
newPosition = take (snake^.len)
((head (snake^.position) |+| directionToPoint (snake^.direction)) :
(snake^.position))
-- | Basically we check if there are 2 same elements in the 'position' list
checkCollision :: Snake -> Bool
checkCollision snake = outOfBoundary || selfCollision
where
snakePosition = snake^.position
mini = head $ sort snakePosition
maxi = head $ reverse $ sort snakePosition
outOfBoundary = (mini^.x < 1) || (mini^.y < 1) ||
(maxi^.x > numRectsX) || (maxi^.y > numRectsY)
selfCollision = (length (nub snakePosition)) /= (length snakePosition)
-- | Starting position & body
initialSnakePosition :: [Point]
initialSnakePosition = [Point 6 6]
initialSnakePositionBottom :: [Point]
initialSnakePositionBottom = [Point 6 11]
initialSnake :: Snake
initialSnake = Snake initialSnakePosition North 1
initialSnakeBottom :: Snake
initialSnakeBottom = Snake initialSnakePositionBottom North 1
snakeEatsApple :: Snake -> Point -> Bool
snakeEatsApple snake apple = apple `elem` (snake^.position)
-- | Don't allow to change to opposite direction immediately
tryChangeSnakeDirection :: Direction -> Direction -> Direction
tryChangeSnakeDirection d sd
| oppositeDirections d sd = sd
| otherwise = d
changeSnakeDirection :: Snake -> Direction -> Snake
changeSnakeDirection s d = direction .~ (tryChangeSnakeDirection d (s^.direction)) $ s