168 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
		
		
			
		
	
	
			168 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
| 
								 | 
							
								-- | 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
							 |