-- 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