-- | This module defines the state of a running Karel program. module KarelState where import Prelude hiding (Either(..)) import KarelSyntax -- -- * Positions and directions -- -- | A cartesian coordinate, representing a position in the world. type Pos = (Int,Int) -- | Get the position next to the given position, offset by one square -- in the indicated *cardinal* direction. neighbor :: Card -> Pos -> Pos neighbor North (x,y) = (x,y+1) neighbor South (x,y) = (x,y-1) neighbor East (x,y) = (x+1,y) neighbor West (x,y) = (x-1,y) -- | Get a cardinal direction relative to the current one. cardTurn :: Dir -> Card -> Card cardTurn Front c = c cardTurn Back North = South cardTurn Back South = North cardTurn Back East = West cardTurn Back West = East cardTurn Left North = West cardTurn Left South = East cardTurn Left East = North cardTurn Left West = South cardTurn Right North = East cardTurn Right South = West cardTurn Right East = South cardTurn Right West = North -- -- * World state -- -- | The state of the world is represented by a function that returns -- for each position: -- * Nothing, if the position is a wall -- * Just k, if the position is clear and has k beepers -- You can assume k is always >= 0. type World = Pos -> Maybe Int -- | Is the given position clear? isClear :: Pos -> World -> Bool isClear p w = w p /= Nothing -- | Is there a beeper at the given position? hasBeeper :: Pos -> World -> Bool hasBeeper p w = maybe False (>0) (w p) -- | Increment the number of beepers at the given position. incBeeper :: Pos -> World -> World incBeeper p w = \q -> if p == q then case w q of Nothing -> Just 1 Just i -> Just (i+1) else w q -- | Decrement the number of beepers at the given position. Note that this -- function can yield a world with negative beepers, so you should make -- sure to only decrement the beepers at a position after first checking -- to make sure there is at least one beeper there (using `hasBeeper`). decBeeper :: Pos -> World -> World decBeeper p w = \q -> if p == q then fmap (subtract 1) (w q) else w q -- -- * Robot state -- -- | The state of the robot is represented by a triple containing: -- * the current position -- * the current facing (cardinal direction) -- * the number of beepers in the beeper bag type Robot = (Pos,Card,Int) -- ** Robot position -- | The robot's position. getPos :: Robot -> Pos getPos (p,_,_) = p -- | Get a position relative to the robot's current facing and position. relativePos :: Dir -> Robot -> Pos relativePos d (p,c,_) = neighbor (cardTurn d c) p -- | Set the robot's position. setPos :: Pos -> Robot -> Robot setPos p (_,c,b) = (p,c,b) -- | Update the robot's position using an update function. updatePos :: (Pos -> Pos) -> Robot -> Robot updatePos f (p,c,b) = (f p,c,b) -- ** Robot facing -- | The robot's facing (cardinal direction). getFacing :: Robot -> Card getFacing (_,c,_) = c -- | Set the robot's facing. setFacing :: Card -> Robot -> Robot setFacing c (p,_,b) = (p,c,b) -- | Update the robot's facing using an update function. updateFacing :: (Card -> Card) -> Robot -> Robot updateFacing f (p,c,b) = (p,f c,b) -- ** Beeper bag -- | The number of beepers in the beeper bag. getBag :: Robot -> Int getBag (_,_,b) = b -- | Is the beeper bag empty? isEmpty :: Robot -> Bool isEmpty (_,_,b) = b <= 0 -- | Increment the number of beepers in the bag. incBag :: Robot -> Robot incBag (p,c,b) = (p,c,b+1) -- | Decrement the number of beepers in the bag. decBag :: Robot -> Robot decBag (p,c,b) = (p,c,b-1) -- -- * Statement results -- -- | The result of executing a statement. -- -- * OK: The statement executed successfully, so return the updated state -- of the world and the robot. OK to execute the next statement. -- -- * Done: Produced only by the Shutdown statement. This returns the final -- state of the robot. No further statements should be executed. -- -- * Error: An error occurred. Includes a string for reporting error messages. -- No further statements should be executed. -- data Result = OK World Robot | Done Robot | Error String instance Show Result where show (OK _ r) = "OK: " ++ show r show (Done r) = "Done: " ++ show r show (Error s) = "Error: " ++ s -- | Applies a function to the result if its an OK, otherwise returns -- the result unchanged. onOK :: (World -> Robot -> Result) -> Result -> Result onOK f (OK w r) = f w r onOK _ result = result