-
Notifications
You must be signed in to change notification settings - Fork 0
/
KarelState.hs
167 lines (129 loc) · 4.53 KB
/
KarelState.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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