Compare commits
2 Commits
cf99539856
...
e5acf76748
Author | SHA1 | Date | |
---|---|---|---|
e5acf76748 | |||
cc08b2ad89 |
103
KarelExamples.hs
Normal file
103
KarelExamples.hs
Normal file
|
@ -0,0 +1,103 @@
|
|||
-- | This module defines some example worlds and programs for testing
|
||||
-- your Karel interpreter.
|
||||
module KarelExamples where
|
||||
|
||||
import Prelude hiding (Either(..))
|
||||
import KarelSyntax
|
||||
import KarelState
|
||||
|
||||
|
||||
--
|
||||
-- * Trivial worlds
|
||||
--
|
||||
|
||||
-- | A world that is all walls.
|
||||
wallWorld :: World
|
||||
wallWorld = const Nothing
|
||||
|
||||
-- | A world that is completely empty of walls and beepers.
|
||||
emptyWorld :: World
|
||||
emptyWorld = const (Just 0)
|
||||
|
||||
-- | A robot that starts at the origin, facing north, with the
|
||||
-- given number of beepers.
|
||||
originBot :: Int -> Robot
|
||||
originBot b = ((0,0),North,b)
|
||||
|
||||
|
||||
--
|
||||
-- * Demo world
|
||||
--
|
||||
|
||||
-- | A 10x5 room with some beepers in a line, illustrated below.
|
||||
--
|
||||
-- XXXXXXXXXXXX
|
||||
-- X X
|
||||
-- X X
|
||||
-- X X
|
||||
-- X @ 2 3 4 X
|
||||
-- X X
|
||||
-- XXXXXXXXXXXX
|
||||
--
|
||||
-- The @ symbol is coordinate (1,1).
|
||||
-- Clear spaces are from (0,0) to (9,4).
|
||||
-- There are beepers at the following locations:
|
||||
-- * 2 beepers at (4,1)
|
||||
-- * 3 beepers at (6,1)
|
||||
-- * 4 beepers at (8,1)
|
||||
--
|
||||
demoWorld :: World
|
||||
demoWorld (4,1) = Just 2
|
||||
demoWorld (6,1) = Just 3
|
||||
demoWorld (8,1) = Just 4
|
||||
demoWorld (x,y) | x >= 0 && x < 10 &&
|
||||
y >= 0 && y < 5 = Just 0
|
||||
| otherwise = Nothing
|
||||
|
||||
-- | An initial robot state for the demo world. The robot starts at
|
||||
-- (1,1), facing East, with 1 beeper in its bag.
|
||||
demoBot :: Robot
|
||||
demoBot = ((1,1),East,1)
|
||||
|
||||
|
||||
--
|
||||
-- * Program generators
|
||||
--
|
||||
|
||||
-- | Generate a program that does the following n times:
|
||||
-- 1. moves in a straight line until it finds a beeper
|
||||
-- 2. picks it up
|
||||
-- 3. returns to its original position and facing.
|
||||
fetcher :: Int -> Prog
|
||||
fetcher n = ([("fetch",fetch)], main)
|
||||
where
|
||||
fetch = Block
|
||||
[ While (Not Beeper) -- do until we find a beeper:
|
||||
(If (Clear Front) -- can we move forward?
|
||||
Move -- if yes, then do it
|
||||
Shutdown) -- if not, shut down
|
||||
, PickBeeper ]
|
||||
main = Block
|
||||
[ Iterate n $ Block -- repeat n times:
|
||||
[ PutBeeper -- put a beeper down to mark our place
|
||||
, Move -- move forward one space
|
||||
, Call "fetch" -- go get a new beeper
|
||||
, Turn Back -- turn around
|
||||
, Move -- move forward one space
|
||||
, Call "fetch" -- go back to where we started
|
||||
, Turn Back ] -- turn back to our initial facing
|
||||
, Shutdown ]
|
||||
|
||||
-- | Generates a statement that moves the robot in a rectangle of
|
||||
-- the given dimensions.
|
||||
rectangle :: Int -> Int -> Stmt
|
||||
rectangle w h = Block
|
||||
[ While (Not (Facing North)) (Turn Right)
|
||||
, Iterate h Move
|
||||
, Turn Right
|
||||
, Iterate w Move
|
||||
, Turn Right
|
||||
, Iterate h Move
|
||||
, Turn Right
|
||||
, Iterate w Move
|
||||
, Turn Right ]
|
53
KarelSemantics.hs
Normal file
53
KarelSemantics.hs
Normal file
|
@ -0,0 +1,53 @@
|
|||
module KarelSemantics where
|
||||
|
||||
import Prelude hiding (Either(..))
|
||||
import Data.Function (fix)
|
||||
|
||||
import KarelSyntax
|
||||
import KarelState
|
||||
|
||||
|
||||
-- | Valuation function for Test.
|
||||
test :: Test -> World -> Robot -> Bool
|
||||
test (Not t) w r = not $ test t w r
|
||||
test (Facing c) w (_, c', _) = c == c'
|
||||
test (Clear d) w (p, c, _) = isClear (neighbor (cardTurn d c) p) w
|
||||
test Beeper w (p, _, _) = hasBeeper p w
|
||||
test Empty w (_, _, i) = i == 0
|
||||
|
||||
-- | Valuation function for Stmt.
|
||||
stmt :: Stmt -> Defs -> World -> Robot -> Result
|
||||
stmt Shutdown _ _ r = Done r
|
||||
stmt PickBeeper _ w r = let p = getPos r
|
||||
in if hasBeeper p w
|
||||
then OK (decBeeper p w) (incBag r)
|
||||
else Error ("No beeper to pick at: " ++ show p)
|
||||
stmt Move _ w (p, c, i) =
|
||||
case w np of
|
||||
Just n -> OK w (np, c, i)
|
||||
Nothing -> Error ("Blocked at: " ++ show np)
|
||||
where np = neighbor c p
|
||||
stmt PutBeeper _ w (p, c, i) = case i of
|
||||
0 -> Error ("No beeper to put.")
|
||||
n -> OK (incBeeper p w) (p, c, n - 1)
|
||||
stmt (Turn d) _ w (p, c, i) = OK w (p, cardTurn d c, i)
|
||||
stmt (Call m) e w r = case lookup m e of
|
||||
Just x -> stmt x e w r
|
||||
Nothing -> Error ("Undefined macro: " ++ m)
|
||||
stmt (Iterate n s) e w r = several (take n $ cycle [s]) e w r
|
||||
stmt (If c t el) e w r =
|
||||
if test c w r
|
||||
then stmt t e w r
|
||||
else stmt el e w r
|
||||
stmt (While t es) e w r =
|
||||
if test t w r
|
||||
then onOK (\w' r' -> stmt (While t es) e w' r') (stmt es e w r)
|
||||
else OK w r
|
||||
stmt (Block es) e w r = several es e w r
|
||||
|
||||
several :: [Stmt] -> Defs -> World -> Robot -> Result
|
||||
several xs d w r = foldl (\rs s -> onOK (\w' r' -> stmt s d w' r') rs) (OK w r) xs
|
||||
|
||||
-- | Run a Karel program.
|
||||
prog :: Prog -> World -> Robot -> Result
|
||||
prog (m,s) w r = stmt s m w r
|
167
KarelState.hs
Normal file
167
KarelState.hs
Normal file
|
@ -0,0 +1,167 @@
|
|||
-- | 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
|
40
KarelSyntax.hs
Normal file
40
KarelSyntax.hs
Normal file
|
@ -0,0 +1,40 @@
|
|||
-- | This module defines the syntax of the Karel language.
|
||||
module KarelSyntax where
|
||||
|
||||
|
||||
-- | A Karel program is a list of macro definitions and a statement to
|
||||
-- use as the body of the "main" function.
|
||||
type Prog = (Defs,Stmt)
|
||||
|
||||
-- | A macro name.
|
||||
type Macro = String
|
||||
|
||||
-- | A list of macro definitions.
|
||||
type Defs = [(Macro,Stmt)]
|
||||
|
||||
-- | Cardinal directions.
|
||||
data Card = North | South | East | West deriving (Eq,Show)
|
||||
|
||||
-- | Directions relative to the current facing.
|
||||
data Dir = Front | Back | Right | Left deriving (Eq,Show)
|
||||
|
||||
-- | Environment queries.
|
||||
data Test = Not Test -- boolean negation
|
||||
| Facing Card -- am I facing the given cardinal direction?
|
||||
| Clear Dir -- can I move in the given relative direction?
|
||||
| Beeper -- is there a beeper here?
|
||||
| Empty -- is my beeper bag empty?
|
||||
deriving (Eq,Show)
|
||||
|
||||
-- | Statements.
|
||||
data Stmt = Shutdown -- end the program
|
||||
| Move -- move forward
|
||||
| PickBeeper -- take a beeper
|
||||
| PutBeeper -- leave a beeper
|
||||
| Turn Dir -- rotate in place
|
||||
| Call Macro -- invoke a macro
|
||||
| Iterate Int Stmt -- fixed repetition loop
|
||||
| If Test Stmt Stmt -- conditional branch
|
||||
| While Test Stmt -- conditional loop
|
||||
| Block [Stmt] -- statement block
|
||||
deriving (Eq,Show)
|
122
KarelTests.hs
Normal file
122
KarelTests.hs
Normal file
|
@ -0,0 +1,122 @@
|
|||
-- | A module that contains several doctests for testing your interpreter.
|
||||
-- Note that if you want the tests to pass, you'll need to reverse engineer
|
||||
-- the error messages.
|
||||
--
|
||||
-- If you add your own tests, make sure to do that in the HW5 file, since you
|
||||
-- will not submit this file.
|
||||
module KarelTests where
|
||||
|
||||
import Prelude hiding (Either(..))
|
||||
|
||||
import KarelSyntax
|
||||
import KarelState
|
||||
import KarelSemantics
|
||||
import KarelExamples
|
||||
|
||||
|
||||
-- | Basic tests for move, turn, pick, and put.
|
||||
--
|
||||
-- >>> prog ([],Move) emptyWorld (originBot 0)
|
||||
-- OK: ((0,1),North,0)
|
||||
--
|
||||
-- >>> prog ([],Block [Turn Right,Move]) emptyWorld (originBot 0)
|
||||
-- OK: ((1,0),East,0)
|
||||
--
|
||||
-- >>> prog ([],Block [PutBeeper,PutBeeper]) emptyWorld (originBot 5)
|
||||
-- OK: ((0,0),North,3)
|
||||
--
|
||||
-- >>> prog ([],Block [PutBeeper,PickBeeper]) emptyWorld (originBot 5)
|
||||
-- OK: ((0,0),North,5)
|
||||
--
|
||||
-- >>> prog ([],Block [PutBeeper,Move,Turn Back,Move,PickBeeper]) emptyWorld (originBot 5)
|
||||
-- OK: ((0,0),South,5)
|
||||
--
|
||||
-- >>> prog ([],Move) wallWorld (originBot 0)
|
||||
-- Error: Blocked at: (0,1)
|
||||
--
|
||||
-- >>> prog ([],Block [PutBeeper,PutBeeper]) emptyWorld (originBot 1)
|
||||
-- Error: No beeper to put.
|
||||
--
|
||||
-- >>> prog ([],PickBeeper) emptyWorld (originBot 5)
|
||||
-- Error: No beeper to pick at: (0,0)
|
||||
--
|
||||
-- >>> prog ([],Block [PutBeeper,Move,PickBeeper]) emptyWorld (originBot 5)
|
||||
-- Error: No beeper to pick at: (0,1)
|
||||
|
||||
|
||||
-- | Conditional tests.
|
||||
--
|
||||
-- >>> prog ([],If Beeper (Turn Left) (Turn Right)) emptyWorld (originBot 0)
|
||||
-- OK: ((0,0),East,0)
|
||||
--
|
||||
-- >>> prog ([],If (Not Beeper) (Turn Left) (Turn Right)) emptyWorld (originBot 0)
|
||||
-- OK: ((0,0),West,0)
|
||||
--
|
||||
-- >>> prog ([],Block [PutBeeper,If Beeper (Turn Left) (Turn Right)]) emptyWorld (originBot 3)
|
||||
-- OK: ((0,0),West,2)
|
||||
--
|
||||
-- >>> prog ([],Block [PutBeeper,PickBeeper,If Beeper (Turn Left) (Turn Right)]) emptyWorld (originBot 3)
|
||||
-- OK: ((0,0),East,3)
|
||||
--
|
||||
-- >>> prog ([],If (Facing North) (Turn Left) (Turn Right)) emptyWorld (originBot 0)
|
||||
-- OK: ((0,0),West,0)
|
||||
--
|
||||
-- >>> prog ([],If (Facing South) (Turn Left) (Turn Right)) emptyWorld (originBot 0)
|
||||
-- OK: ((0,0),East,0)
|
||||
--
|
||||
-- >>> prog ([],If (Not Empty) PutBeeper Move) emptyWorld (originBot 3)
|
||||
-- OK: ((0,0),North,2)
|
||||
--
|
||||
-- >>> prog ([],If (Not Empty) PutBeeper Move) emptyWorld (originBot 0)
|
||||
-- OK: ((0,1),North,0)
|
||||
--
|
||||
-- >>> prog ([],If (Clear Front) Move Shutdown) emptyWorld (originBot 0)
|
||||
-- OK: ((0,1),North,0)
|
||||
--
|
||||
-- >>> prog ([],If (Clear Front) Move Shutdown) wallWorld (originBot 0)
|
||||
-- Done: ((0,0),North,0)
|
||||
|
||||
|
||||
-- | Test macros.
|
||||
--
|
||||
-- >>> prog ([("A",Turn Right),("B",Turn Left)],Call "A") emptyWorld (originBot 0)
|
||||
-- OK: ((0,0),East,0)
|
||||
--
|
||||
-- >>> prog ([("A",Turn Right),("B",Turn Left)],Call "B") emptyWorld (originBot 0)
|
||||
-- OK: ((0,0),West,0)
|
||||
--
|
||||
-- >>> prog ([("A",Turn Right),("B",Turn Left)],Call "C") emptyWorld (originBot 0)
|
||||
-- Error: Undefined macro: C
|
||||
|
||||
|
||||
-- | Test looping constructs.
|
||||
--
|
||||
-- >>> prog ([],Iterate 8 Move) demoWorld demoBot
|
||||
-- OK: ((9,1),East,1)
|
||||
--
|
||||
-- >>> prog ([],Iterate 9 Move) demoWorld demoBot
|
||||
-- Error: Blocked at: (10,1)
|
||||
--
|
||||
-- >>> prog ([],While (Clear Front) Move) demoWorld demoBot
|
||||
-- OK: ((9,1),East,1)
|
||||
--
|
||||
-- >>> prog ([],Block [Iterate 7 Move, While Beeper PickBeeper]) demoWorld demoBot
|
||||
-- OK: ((8,1),East,5)
|
||||
|
||||
|
||||
-- | Larger tests.
|
||||
--
|
||||
-- >>> prog (fetcher 4) demoWorld demoBot
|
||||
-- Done: ((1,1),East,5)
|
||||
--
|
||||
-- >>> prog (fetcher 9) demoWorld demoBot
|
||||
-- Done: ((1,1),East,10)
|
||||
--
|
||||
-- >>> prog (fetcher 10) demoWorld demoBot
|
||||
-- Done: ((9,1),East,9)
|
||||
--
|
||||
-- >>> prog ([],rectangle 8 3) demoWorld demoBot
|
||||
-- OK: ((1,1),North,1)
|
||||
--
|
||||
-- >>> prog ([],rectangle 3 8) demoWorld demoBot
|
||||
-- Error: Blocked at: (1,5)
|
Loading…
Reference in New Issue
Block a user