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 |