--=============================================== -- CS:5810 Formal Methods in Software Engineering -- Fall 2017 -- -- Cesare Tinelli --=============================================== /* This Alloy model models a dynamic domain involving several rovers moving on in a two-dimentional space. The model is based on the following facts about this domain. - There are one or more identical rovers. - Each rover can only move forward, or turn in place to the left or to the right. - Each rover can be turned on and off. We make the following simplifying modeling choices: 1) We adopt an interleaving model of time, where only one action is performed, by one of the rovers, at a time. 2) The two dimentional space is a discrete grid, with the X-coordinate growing indefinitely in the West-East direction and the Y-coordinate growing indefinitely in the South-North direction (see picture below). 3) Rovers move only by one position at a time and along the X,Y axes. 4) As a consequence of (2) and (3), a rover turns left or right by exactly 90 degrees. 4) A rover can move only in the direction it is facing. ----------------------------------------------------------------------- y . . . . . . . . . . . . . . . . . . c4 | | | | | | ... --------------------- c3 | | | | | | ... --------------------- c2 | | | | | | ... --------------------- c1 | | | | | | ... --------------------- c0 | | | | | | ... --------------------- c0 c1 c2 c3 c4 ... x */ --open util/ordering [Time] as T open util/ordering [Coor] as C --sig Time {} -- Coordinates, strictly ordered sig Coor {} -- Position models the individual positions in the grid sig Position { x: Coor, y: Coor } -- The four cardinal directions enum Direction { North, South, East, West } enum RunStatus { On, Off } some sig Rover { -- direction rover is facing at any one time var dir: Direction, -- rover's position at any one time var pos: Position, -- rover's on/off status and any one time var status: RunStatus } -------------------------------------- -- Auxiliary predicates and functions -------------------------------------- -- holds iff the on/off status of the rovers in R does not change pred no_on_changes [R: set Rover] { all r: R | r.status' = r.status } -- holds iff the position of the rovers in R does not change pred no_position_changes [R: set Rover] { all r: R | r.pos' = r.pos } -- holds iff the direction of the rovers in R does not change pred no_direction_changes [R: set Rover] { all r: R | r.dir' = r.dir } -- returns the position next to p along direction d, if any, -- and the empty set otherwise fun next_pos [p: Position, d: Direction]: Position { -- pos_north_of_p (resp., pos_south_of_p, pos_east_of_p, and pos_west_of_p) -- consists of the position immediately north (resp. south, east, and west) -- of p, if such position exits. Otherwise, it is the empty set let pos_north_of_p = { q: Position | q.x = p.x and q.y = C/next[p.y] } | let pos_south_of_p = { q: Position | q.x = p.x and q.y = C/prev[p.y] } | let pos_east_of_p = { q: Position | q.y = p.y and q.x = C/next[p.x] } | let pos_west_of_p = { q: Position | q.y = p.y and q.x = C/prev[p.x] } | (d = North) => pos_north_of_p else (d = South) => pos_south_of_p else (d = East) => pos_east_of_p else pos_west_of_p } -- Note that pos_south_of_p, say, is empty if there is no position immediately -- south of p because in that case C/prev[p.y] is empty. Since q.y is a singleton -- by definition of the Position signature, the condition of the comprehension -- expression is false, hence there are no q's that satisfy it. -- The same argument applies to the other let variables. ------------- -- Operators ------------- pred turn_on [rov: Rover] { -- preconditions -- rover is off rov.status = Off -- postconditions -- rover is on rov.status' = On -- frame conditions -- all other rovers stay on or off as they were no_on_changes[Rover - rov] -- No rover changes direction no_direction_changes[Rover] -- No rover changes position no_position_changes[Rover] } pred turn_off [rov: Rover] { -- preconditions -- rover is on rov.status = On -- postconditions -- rover is off rov.status' = Off -- frame conditions -- all other rovers stay on or off as they were no_on_changes[Rover - rov] -- No rover changes direction no_direction_changes[Rover] -- No rover changes position no_position_changes[Rover] } pred turn_left [rov: Rover] { -- preconditions -- rover is on rov.status = On -- postconditions -- direction changes let d = rov.dir | let nd = ( (d = North) => West else (d = West) => South else (d = South) => East else North ) | rov.dir' = nd -- frame conditions -- all rovers stay on or off as they were no_on_changes[Rover] -- No other rover changes direction no_direction_changes[Rover - rov] -- No rover changes position no_position_changes[Rover] } pred turn_right [rov: Rover] { -- preconditions -- rover is on rov.status = On -- postconditions -- direction changes let d = rov.dir | let nd = ( (d = North) => East else (d = West) => North else (d = South) => West else South ) | rov.dir' = nd -- frame conditions -- all rovers stay on or off as they were no_on_changes[Rover] -- No other rover changes direction no_direction_changes[Rover - rov] -- No rover changes position no_position_changes[Rover] } pred go [rov: Rover, d: Direction] { -- preconditions -- rover is on rov.status = On -- d is rover's direction rov.dir = d let np = next_pos[rov.pos, d] | { -- precondition some np --postcondition rov.pos' = np } -- frame conditions -- all rovers stay on or off as they were no_on_changes[Rover] -- No rover changes direction no_direction_changes[Rover] -- No other rover changes position no_position_changes[Rover - rov] } --------------------- -- Transition system --------------------- -- this predicate defines all possible transitions pred transition { some r: Rover | turn_on [r] or turn_off [r] or turn_left [r] or turn_right [r] or (some d: Direction | go [r, d]) } one sig R1 extends Rover {} one sig P0 extends Position {} -- P0 is the origin position of the coordinate system fact { P0.x = C/first P0.y = C/first } -- This predicate defines a possible initial state condition. -- It is satisfied by states in which rover R1 is at the origin position, -- facing East and turned off, while the other rovers, if any, are at -- a different position than R1's pred init [] { R1.pos = P0 R1.dir = East R1.status = On all r: Rover - R1 | R1.pos != r.pos } ------------------- -- System predicate ------------------- -- Denotes all possible executions of the system from a state -- that satisfies the init condition pred System { init always transition } -- Sample "goal" state condition. -- The predicate is satisfied by states in which R1 is not -- at the origin and is facing north pred goalReached { R1.pos != P0 R1.dir = North } -- This predicate holds iff it is possible for the transition system -- to reach a state that satisfies the properties specified in goal, -- when starting from an initial state satisfying the initial state -- condition init pred goalCheck { one Rover -- for simplicity System eventually goalReached } -- check if the goal state is reachable run goalCheck for 5