haskell-karel/Karel.hs
Matej Focko e8972e94e5
Implement first version
Signed-off-by: Matej Focko <me@mfocko.xyz>
2020-11-08 16:14:08 +01:00

147 lines
3.2 KiB
Haskell

{-# LANGUAGE LambdaCase #-}
module Karel
( -- data types
Vector,
Direction (..),
Karel (..),
-- public interface
step,
turnLeft,
pickBeeper,
putBeeper,
frontIsClear,
beepersPresent,
facingNorth,
beepersInBag,
-- for creating world
worldWithDimensions,
setBeepersAt,
addWallAt,
removeWallAt,
-- for creating Karel
defaultRobot,
positionOfRobot,
directionOfRobot,
beepersOfRobot,
)
where
import World
( Direction (..),
Vector,
World,
addWallAt,
getBeepersAt,
hasWallAt,
positionWithinWorld,
removeWallAt,
setBeepersAt,
worldWithDimensions,
)
data Karel = Karel
{ position :: Vector,
direction :: Direction,
beepers :: Int,
world :: World
}
deriving (Show, Eq)
defaultRobot :: World -> Karel
defaultRobot itsWorld =
Karel
{ position = (1, 1),
direction = East,
beepers = 0,
world = itsWorld
}
positionOfRobot :: Vector -> Karel -> Karel
positionOfRobot robot_position robot
| not $ positionWithinWorld (world robot) robot_position = error "Invalid position"
| otherwise =
robot
{ position = robot_position
}
directionOfRobot :: Direction -> Karel -> Karel
directionOfRobot robot_direction robot =
robot
{ direction = robot_direction
}
beepersOfRobot :: Int -> Karel -> Karel
beepersOfRobot robot_beepers robot
| robot_beepers < 0 = error "Invalid count of beepers"
| otherwise =
robot
{ beepers = robot_beepers
}
stepVector :: Direction -> Vector
stepVector = \case
North -> (0, 1)
East -> (1, 0)
South -> (0, -1)
West -> (-1, 0)
leftTurn :: Direction -> Direction
leftTurn = \case
North -> West
West -> South
South -> East
East -> North
step :: Karel -> Karel
step robot
| hasWallAt position' direction' (world robot) = error "Cannot pass through the wall"
| otherwise =
robot
{ position = (x + dx, y + dy)
}
where
position' = position robot
direction' = direction robot
(x, y) = position'
(dx, dy) = stepVector direction'
turnLeft :: Karel -> Karel
turnLeft robot =
robot
{ direction = leftTurn $ direction robot
}
pickBeeper :: Karel -> Karel
pickBeeper robot
| beepersAtPosition <= 0 = error "There are no beepers to pick up"
| otherwise =
robot
{ beepers = beepers robot + 1,
world = setBeepersAt (position robot) (beepersAtPosition - 1) (world robot)
}
where
beepersAtPosition = getBeepersAt (position robot) (world robot)
putBeeper :: Karel -> Karel
putBeeper robot
| beepers robot <= 0 = error "There are no beepers to put down"
| otherwise =
robot
{ beepers = beepers robot - 1,
world = setBeepersAt (position robot) (beepersAtPosition + 1) (world robot)
}
where
beepersAtPosition = getBeepersAt (position robot) (world robot)
frontIsClear :: Karel -> Bool
frontIsClear robot = not $ hasWallAt (position robot) (direction robot) (world robot)
beepersPresent :: Karel -> Bool
beepersPresent robot = (> 0) $ getBeepersAt (position robot) (world robot)
facingNorth :: Karel -> Bool
facingNorth = (== North) . direction
beepersInBag :: Karel -> Bool
beepersInBag = (> 0) . beepers