# The Logic-Based Subsumption Architecture: - PowerPoint PPT Presentation

The Logic-Based Subsumption Architecture:

1 / 181
The Logic-Based Subsumption Architecture:

## The Logic-Based Subsumption Architecture:

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -
##### Presentation Transcript

1. The Logic-Based Subsumption Architecture: The Wumpus World

2. Introduction • The subsumption architecture was developed in the mid 80s by Rodney Brooks at MIT • It is a behavior-based approach, where the problem of control is decomposed into individual behavior-based modules, which the overall behavior of the system emerges from, as opposed to the old functional model, in which the behavior of the system comes from the combination of functional information processing units • The modules of behavior created with this decomposition are called behavioral layers • The layers are built on top of one another in increasing levels of competence to provide more complex system behavior

3. Introduction • The logic-based subsumption architecture (LSA) was developed at Stanford under John McCarthy, and is basically the same behavior-based approach as Brooks’, but instead the layers are implemented in first-order logic theories • Each layer has a set of inputs asserted as axioms, default assumptions, a theorem prover, a body containing the specific theory of that layer, and a goal to prove • The control is all done declaratively using logical theorems – you tell the layer what it needs to know to prove its goal, and it finds a solution • This improves over Brooks’ approach in that it is very scalable, extensible, and has high applicability to many domains, mostly because of its declarativeness

4. Introduction • In the LSA, each layer has the following processing loop: • Take in sensory inputs and assert them as axioms • Take in inputs from higher layers and assert them as axioms • Use the layer’s theory and inputs to try to prove the layer’s goal • If the goal is proven, transmit it to lower layers

5. Stanford Work • At Stanford, they used a Nomad office robot to illustrate their approach • They decomposed their mobile robot control system into the following layers: • Layer 3 : wide-range motion planning • Responsible for high-level robot motion planning • Layer 2 : local motion planning • Responsible for translating target landmarks into x,y coordinates • Layer 1 : destination-seeking • Responsible for simple movements to a goal location • Layer 0 : obstacle avoidance • Responsible for deciding what low-level action to perform (turn or move forward) • Layer -1 : halt or go • Responsible for sending actions to the robot or halting the robot

6. Stanford Work • Although theorem proving is generally known to be slow, the decomposition into layers made individual layer theories small, making theorem proving fast and efficient overall • The lowest layer, which is the one that needs to be most reactive, performed well enough for real-time robot control • The top layer took from a second to a minute to generate a target landmark for the next layer, and movements and turning were smooth and exhibited real-time behavior • The layers also operated concurrently, making the overall speed of the system fast and robust enough for real-time autonomous control in an office environment • They also used certain efficiency enhancements such as proof caching to increase speed

7. The Wumpus World • The wumpus world is a nice test bed for logic-based artificial intelligence agents • It is a grid of squares (typically 4x4) which represent rooms in a cave • A cave contains one wumpus, a number of pits, one pile of gold, and the agent • The agent’s goal is to get the gold and leave the cave • The agent starts at the lower left corner facing right • Walking into the wumpus or a pit results in the agent’s death, and the agent only knows what is in a certain square if it has visited that square (or can reason about the contents of a square) • The agent can reason as to the locations of pits and the wumpus because squares directly (not diagonally) adjacent to a pit have a breeze, and squares directly adjacent to a wumpus have a stench

8. The Wumpus World • The agent can perceive the stench and breeze, a glitter, which occurs in the square containing the gold, and a scream, which occurs when the wumpus is killed • The agent can perform the following actions: • Move into a square (up, down, left, or right) • Turn (clockwise or counterclockwise) • Grab the gold • Shoot an arrow – the agent has a single arrow which it can use to kill the wumpus, and it travels in a straight line until it hits a wall or the wumpus • Climb out of the cave (can only be performed in the start square) • The agent can safely move into a square with a dead wumpus • Worlds are randomly generated, so it is not always possible for the agent to accomplish its goal without dying or taking a risk

9. Wumpus World Decomposition • The Wumpus World problem can be decomposed using the LSA into the following layers: • High-level action decision • Responsible for deciding what high-level action to perform (grab gold, shoot arrow, explore cave, or leave cave) • Action planning • Responsible for constructing a plan as to how to perform a certain action (for grab gold and shoot arrow, this is trivial) • Split into separate “parallel” layers for grabbing gold, shooting arrow, exploring, and leaving • Action performance • Responsible for translating action sequences into actions for the agent • Other decompositions more true to Brooks-style subsumption were considered, such as a wall-finding behavior instead of specific planning, but in an environment as specific as the Wumpus World, the agent could get lost easily, among other problems • The world itself also sort of dictates a certain linear, sequential implementation, as opposed to a concurrent one

10. Wumpus World Decomposition High-level action decision Inputs: agent location, locations of gold, locations of stenches, assumptions about whether or not agent has the gold, agent has an arrow, agent can safely explore the map, the wumpus is dead Goal: prove there is an action to perform Grab gold Inputs: none Goal: none, just transmits grab action Shoot arrow Inputs: agent location, locations of wumpus, stenches Goal: prove there is a direction to shoot the arrow in Explore cave Inputs: agent location, locations of wumpus, stenches, pits, breezes, assumption about whether wumpus is dead or not, locations of visited and unvisited squares Goal: prove there is a sequence of actions to bring the agent to a safe, unvisited square (traveling along only visited squares) Leave cave Inputs: agent location, locations of visited squares Goal: prove there is a sequence of actions to bring the agent to the start square (traveling along only visited squares) Action performance Inputs: action sequence to perform Goal: none, just performs action sequence

11. Wumpus World Decomposition • This decomposition is somewhat similar to that of the Nomad • In the office environment, the Nomad chose a landmark to visit – in the wumpus world this is equivalent to a square to explore, or the start square if the agent decides to leave • A big difference between an office environment and the wumpus world is that the wumpus world is discrete • The Nomad didn’t have to do any specific planning to get to its goal location besides finding landmarks – it simply had to head in the basic direction, and lower layers would make sure it didn’t hit anything – in the wumpus world, since it is a world with discrete squares, planning must specifically choose which squares to visit, so create a sequence of actions to lead the agent along those squares, therefore there is no equivalent to the obstacle avoidance layer – obstacles can be considered as pits or a wumpus, but avoiding them is done internal to the explore layer

12. Implementation • The intent of the software is to create an agent for navigating the wumpus world using the LSA approach • The agent is designed to be fully cautious – it will move into a new square only if it can prove that new square is safe (i.e. has no pit and has no live wumpus) • It is written in Common Lisp • SNARK (SRI’s New Automated Theorem Prover) was chosen as the theorem prover to be employed, as opposed to PTTP which was used at Stanford • The code has 4 basic sections: • World generation • Layer code • Utility functions • Main control loop • All the control except turning is done with theorem proving • In the Nomad, default assumptions were handled with circumscription – in this implementation, this is done by procedural means when actions are performed

13. Implementation • The actual interface to the robot, one using LEGO Mindstorms and a Handy Board, is implemented in the following manner: • Functions governing specific, discrete actions (turn, move to next square, shoot, etc) were written in Interactive C • The robot is connected to the computer running LISP through a serial port • The robot is set up to wait for an incoming character from the serial port – when it gets one, it will call a certain action function, then reply with a character when the specific function call is complete, then continue to wait for another character • The LISP computer will, at the appropriate times, write to the serial port to request the robot do an action, then wait for the robot’s reply when the action is complete • The physical Wumpus World is a black surface with white borders for the squares • The robot has two wheels, each connected to a DC motor, and two reflective sensors

14. General Pseudocode • Making the world Generate an array of the given world size Each index has slots for whether or not it has a pit, breeze, wumpus, stench, and gold Place the wumpus in a random location Place (2) pits in (2) random locations Place the gold in a random location Place breezes in all squares adjacent to pits Place stenches in all squares adjacent to the wumpus

15. General Pseudocode • General layer structure: Initialize SNARK Set any layer-specific SNARK options Declare any necessary sorts and constants Assert layer input Assert layer theory Prove layer goal Return the answer

16. General Pseudocode • Main control loop Set up default SNARK options Make the world Loop run layer2 – depending on answer, run either layer1-grab, layer1-shoot, layer1-explore, or layer1-home run layer0 with result of the chosen layer1 exit the loop if layer1-home was chosen

17. General Pseudocode • Main data structures x by x by 5 array to represent the world x by x array to keep track of visited squares a length 4 array to hold assumptions A list to hold assertions about the wumpus A list to hold assertions about stenches A list to hold assertions about pits A list to hold assertions about breezes A list to hold assertions about gold - note: the wumpus and pit lists will never contain positive assertions about locations of wumpuses and pits because those would only be made if the agent walked into a square with a wumpus or a pit, which will never happen due to the agent’s cautious nature (except in the case the wumpus is dead and that square is visited) list of 2 to hold agent’s x,y position variable to hold agent’s orientation an output stream

18. Implementation ;;;;wumpus.lisp ;;;; loads all layer files ;;;; contains: ;;;; global variables/main data structures ;;;; world generation functions ;;;; utility functions ;;;; action functions ;;;; assertion- and assumption-related functions ;;;; set-up function ;;;; a main function ;;;load instructions: ;;; 1. (load "snark\\snark-system.lisp") ;;; 2. (make-snark-system) ;;; 3. (load "wumpus.lisp") ;;;convention is x-coordinate is first in array and y-coordinate is second ;;;x-direction is horizontal, y-direction is vertical ;;;handy for debugging (defun reload () (load "wumpus.lisp"))

19. Implementation: Global Variables ;;;output variables (defvar *outstream* t) ;general output stream for test functions (defvar *serial-port* nil) ;serial port stream for i/o ;;;world variables (defvar *dimension*) ;size of world (world is *dimension* by *dimension*) (defvar *world*) ;world 3d array (array is *dimension* by *dimension* by 5) (defvar *world-visits*) ;world 2d array to keep track of visited squares ;;;agent variables (defvar *coords* '(0 0)) ;agent coordinates (defvar *orientation* 'right) ;agent orientation - can have values of left,right,up,down ;;;assertion variables (defvar *pit-assertions* nil) ;assertions list containing pit info (defvar *wumpus-assertions* nil) ;assertions list containing wumpus info (defvar *gold-assertions* nil) ;assertions list containing gold info (defvar *breeze-assertions* nil) ;assertions list containing breeze info (defvar *stench-assertions* nil) ;assertions list containing stench info ;;assumptions array, assume can explore, not wumpus dead, not have gold, have arrow (defvar *assumptions*) ;;;flag variables (defvar *no-robot* nil) ;flag describing whether or not to interface to robot ;;;time variables (defvar *elapsed-time* 0) ;timing variable for main function

20. Pseudocode : World Generation Function out-of-bounds (number dimension) if number >= dimension or < 0 return true, else return false Function place-pit (x y) put a pit in x,y put breezes in all valid adjacent squares to x,y Function place-wumpus (x y) put a wumpus in x,y put stenches in all valid adjacent squares to x,y Function place-gold (x y) put gold in x,y Function init-world (world-size) initialize all global world-related variables Function make-world (world-size) init-world world-size place the wumpus in a random index place a pit in a random index place a second pit in a random index place the gold in a random index return the world Function make-custom-world (world-size object-list) init-world world-size for each object in object-list place the object in the map at given coordinates

21. Implementation : World Generation ;;;out-of-bounds function, checks to see if given number is out of map (defun out-of-bounds (x dimension) ;;determines if x is out of the array bounds (or (< x 0) (>= x dimension))) ;;;;functions to place objects in world ;;;; slot 0 = breeze, slot 1 = stench, slot 2 = pit, slot 3 = gold, slot 4 = wumpus ;;;place-pit function, places a pit in given coordinates and breezes all around ;;;side effect is changing *world* (defun place-pit (x y) ;;put the pit there (setf (aref *world* x y 2) 'pit) ;;make all squares adjacent to a pit breezy (unless (out-of-bounds (+ x 1) *dimension*) (setf (aref *world* (+ x 1) y 0) 'breeze)) (unless (out-of-bounds (- x 1) *dimension*) (setf (aref *world* (- x 1) y 0) 'breeze)) (unless (out-of-bounds (+ y 1) *dimension*) (setf (aref *world* x (+ y 1) 0) 'breeze)) (unless (out-of-bounds (- y 1) *dimension*) (setf (aref *world* x (- y 1) 0) 'breeze)))

22. Implementation : World Generation ;;;place-wumpus function, places a wumpus in given coordinates and stenches all ;;; around ;;;side effect is changing *world* (defun place-wumpus (x y) ;;put the wumpus there (setf (aref *world* x y 4) 'wumpus) ;;make all squares adjacent to a wumpus smelly (unless (out-of-bounds (+ x 1) *dimension*) (setf (aref *world* (+ x 1) y 1) 'stench)) (unless (out-of-bounds (- x 1) *dimension*) (setf (aref *world* (- x 1) y 1) 'stench)) (unless (out-of-bounds (+ y 1) *dimension*) (setf (aref *world* x (+ y 1) 1) 'stench)) (unless (out-of-bounds (- y 1) *dimension*) (setf (aref *world* x (- y 1) 1) 'stench))) ;;;place-gold function, places gold in given coordinates ;;;side effect is changing *world* (defun place-gold (x y) ;;put gold there (setf (aref *world* x y 3) 'gold))

23. Implementation : World Generation ;;;init-world function, initializes world-related global vars ;;;side effects are setting world-related global vars (defun init-world (size) ;;initialize *dimension* (setf *dimension* size) ;;initialize *world* - the 5 is for different "objects" that can be in square (setf *world* (make-array `(,*dimension* ,*dimension* 5))) ;;initialize *world-visits* (setf *world-visits* (make-array `(,*dimension* ,*dimension*)))) ;;;make-world function, generates a random wumpus world ;;;side effects are setting world-related global variables (defun make-world (size &key pits) ;;create empty world (init-world size) ;;choose where to put pits - could en up in same place, thats okay ;;check whether to use more pits for larger maps (if (eql pits :dynamic) (dotimes (x (dynamic-super-fun-pit-number-generator size)) (place-pit (random *dimension*) (random *dimension*))) (progn (place-pit (random *dimension*) (random *dimension*)) (place-pit (random *dimension*) (random *dimension*)))) ;;choose where to put wumpus, stenches (place-wumpus (random *dimension*) (random *dimension*)) ;;choose where to put gold (place-gold (random *dimension*) (random *dimension*)) *world* ;return the world )

24. Implementation : World Generation ;;;make-custom-world function, takes world size and a list of where to put things ;;;input for list is '((pit 0 0) (wumpus 1 2) (gold 2 3)...) ;;;theory assumes there is at exactly 1 wumpus, so should have one in list ;;;function assumes locations given do not exceed world size ;;;to get an empty world with this, just need to pass in '(nil) or '((blah)) or ;;; something like that ;;;also, if want say only use random locations for some things, can pass (random ;;; some-number) in, but would need to use backquote, e.g. something like (main 3 ;;; :use-world `((gold ,(random 3) ,(random 3)))) ;;;side effects are setting world-related global variables (defun make-custom-world (size world-list) ;;create empty world (init-world size) ;;go through world-list, place object in appropriate place (dolist (item world-list) (let ((object (first item))) (cond ((equal object 'pit) (place-pit (second item) (third item))) ((equal object 'gold) (place-gold (second item) (third item))) ((equal object 'wumpus) (place-wumpus (second item) (third item)))))) *world* ;return the world )

25. Pseudocode : Utility Functions Function print-list (the-list) unless the list is empty print the head of the-list print-list the rest of the-list Function breeze? (coordinates) if there is a breeze at coordinates return true, else false Function pit? (coordinates) if there is a pit at coordinates return true, else false Function stench? (coordinates) if there is a stench at coordinates return true, else false Function wumpus? (coordinates) if there is a wumpus at coordinates return true, else false Function gold? (coordinates) if there is gold at coordinates return true, else false Function show-square (coordinates) print the coordinates for each slot in the world square corresponding to coordinates print its value – either wumpus or nil, gold or nil ,etc

26. Pseudocode : Utility Functions Function print-world for each square in the world call show-square Function time-limit (dimension) return 5*dimension Function pit-number-generator (size) if size is 2 return 1 else return (size^2)/10 rounded

27. Implementation : Utility Functions ;;;print-list function, useful for printing assertions lists in a nice format (defun print-list (lst) ;;unless the list is empty (unless (null lst) (progn ;;print the head of the list (format *outstream* "~A~%" (car lst)) ;;print the rest of the list (print-list (cdr lst))))) ;;;functions to check if an object is at a given map location ;;;format is (object? coords) (defun breeze? (coords-list) ;;if there is a breeze at the current coordinates, return true, else false (if (equal (aref *world* (car coords-list) (cadr coords-list) 0) 'breeze) t nil)) (defun stench? (coords-list) ;;if there is a stench at the current coordinates, return true, else false (if (equal (aref *world* (car coords-list) (cadr coords-list) 1) 'stench) t nil)) (defun pit? (coords-list) ;;if there is a pit at the current coordinates, return true, else false (if (equal (aref *world* (car coords-list) (cadr coords-list) 2) 'pit) t nil)) (defun gold? (coords-list) ;;if there is gold at the current coordinates, return true, else false (if (equal (aref *world* (car coords-list) (cadr coords-list) 3) 'gold) t nil)) (defun wumpus? (coords-list) ;;if there is a wumpus at the current coordinates, return true, else false (if (equal (aref *world* (car coords-list) (cadr coords-list) 4) 'wumpus) t nil))

28. Implementation : Utility Functions ;;;show-square function, prints square contents in nice format for a move action ;;;side effect is printing to *outstream* (defun show-square (coords-list) ;;print the coordinates of the square and its contents (format *outstream* "moved to: ~A ~A ~A ~A ~A ~A, facing ~A~%" coords-list (aref *world* (car coords-list) (cadr coords-list) 0) (aref *world* (car coords-list) (cadr coords-list) 1) (aref *world* (car coords-list) (cadr coords-list) 2) (aref *world* (car coords-list) (cadr coords-list) 3) (aref *world* (car coords-list) (cadr coords-list) 4) *orientation*)) ;;;show-square2 function, prints square contents in nice format ;;;side effect is printing to *outstream* (defun show-square2 (coords-list) ;;print the coordinates of the square and its contents (format *outstream* "~A ~A ~A ~A ~A ~A~%" coords-list (aref *world* (car coords-list) (cadr coords-list) 0) (aref *world* (car coords-list) (cadr coords-list) 1) (aref *world* (car coords-list) (cadr coords-list) 2) (aref *world* (car coords-list) (cadr coords-list) 3) (aref *world* (car coords-list) (cadr coords-list) 4)))

29. Implementation : Utility Functions ;;;print-world function, prints the world in a nice format ;;;side effect is printing to *outstream* (defun print-world () ;;for each square in the world (dotimes (xdim *dimension*) (dotimes (ydim *dimension*) ;;print the square (show-square2 `(,xdim ,ydim))))) ;;;time-limit function ;;computes appropriate time limit for explore layer based on world size (defun time-limit (dimension) (* 5 dimension)) ;;function to compute appropriate number of pits to use for given map size (defun dynamic-super-fun-pit-number-generator (world-size) ;;if world size is 2 (if (eql world-size 2) ;;return 1 1 ;;else use formula - size*size/10 rounded (+ (round (/ (* world-size world-size) 10) 0.99)))) ;;0.99 is so 2.5 gets rounded to 3 instead of 2 ;;+ is so function only returns 1 value instead of the 2 returned by round

30. Pseudocode : Action Functions Function action (c) write c to serial port wait for reply from robot Functions backup,forward,left,right,grab,shoot-arrow,wumpus-killed,winner,loser call action for character relating to that action for the robot Function move (directions) – directions is a list looking like ‘(list up down ...) for all elements of directions except the first call do-move for that element (a direction) Function do-move (direction) face in direction change agent’s coordinates to reflect the move check if the agent walked into a pit or a live wumpus make-assertions for the new square show the square and the choice to move in direction Function rotate (orientation direction) – direction is clockwise or counterclockwise return the effect of turning in direction on orientation Function face (to-be-facing) turn clockwise or counterclockwise once or twice to make the agent’s orientation the same as to-be-facing change the agent’s orientation to the new orientation

31. Pseudocode : Action Functions Function shoot (direction) face in direction if the wumpus is in the square in that direction change the assumption the wumpus is alive else add to the assertions list that the wumpus is not in that square change the assumption that the agent has an arrow Function grab-gold change the assumption that the agent doesn’t have the gold

32. Implementation : Action Functions ;;;action function, writes character corresponding to an action to serial port unless not ;;; using robot ;;;side effect is writing to serial port stream (defun action (char) (unless *no-robot* (progn ;;write char (write-char char *serial-port*) ;;wait until robot finishes action - loops until a character is read, ignoring EOFs (loop (if (read-char *serial-port* nil) (return)))))) ;;;here are some wrapper functions for robot movement ;;;side effects are writing to serial port stream ;;backup function, backs up robot - for convenience only, not used for agent (defun backup () (action #\P)) ;;forward function, moves robot forward to next square (defun forward () (action #\G)) ;;left function, turns robot left 90 degrees (defun left () (action #\L)) ;;right function, turns robot right 90 degrees (defun right () (action #\R))

33. Implementation : Action Functions ;;grabbing gold function, emits a tone) (defun grab () (action #\B)) ;;shoot-arrow function, emits a beep (defun shoot-arrow () (action #\S)) ;;wumpus-killed function, emits two beeps (defun wumpus-killed () (action #\K)) ;;winner function, plays a short song (defun winner () (action #\W)) ;;loser function, plays two tones (defun loser () (action #\O)) ;;;move function - takes as arg '(list up down right left up up ... ) ;;;side effects are changing agent-related global variables (defun move (directions) ;;for each direction in the list (dolist (direction (cdr directions)) ;;move the agent in that direction (do-move direction)))

34. Implementation : Action Functions ;;;do-move function, takes up,left,right, or down and performs necessary turns, ;;;prints the results of the move, also updates *coords* ;;;assumes the direction given does not go outside the map ;;;side effects are updating assertion lists, *coords*, and *orientation*, writing to ;;; serial port stream (defun do-move (direction) ;;find out which direction it is (cond ;;if direction is up ((eql direction 'up) (progn ;;then face up (face 'up) ;;change the coords to reflect the move (setf (cadr *coords*) (+ (cadr *coords*) 1)) ;;check if the agent made a mistake (if (or (pit? *coords*) (and (wumpus? *coords*) (not (wumpus-dead?)))) (format *outstream* "died.~%")))) ;;if direction is down ((eql direction 'down) (progn ;;then face down (face 'down) ;;change *coords* to reflect the move (setf (cadr *coords*) (- (cadr *coords*) 1)) ;;check if the agent made a mistake (if (or (pit? *coords*) (and (wumpus? *coords*) (not (wumpus-dead?)))) (format *outstream* "died.~%"))))

35. Implementation : Action Functions ;;if direction is left ((eql direction 'left) (progn ;;then face left (face 'left) ;;change coords to reflect the move (setf (car *coords*) (- (car *coords*) 1)) ;;check if the agent made a mistake (if (or (pit? *coords*) (and (wumpus? *coords*) (not (wumpus-dead?)))) (format *outstream* "died.~%")))) ;;if direction is right ((eql direction 'right) (progn ;;then face right (face 'right) ;;change coords to reflect the move (setf (car *coords*) (+ (car *coords*) 1)) ;;check if the agent made a mistake (if (or (pit? *coords*) (and (wumpus? *coords*) (not (wumpus-dead?)))) (format *outstream* "died.~%"))))) ;;in all cases ;;get the new information about the new square moved into (make-assertions *coords*) ;;perform the move (forward) ;;print the move and the contents of the new square (show-square *coords*))

36. Implementation : Action Functions ;;;face function, takes the direction agent wants to be facing ;;;and turns agent to face that direction (if necessary) ;;;side effects are changing *orientation*, printing to *outstream*, writing to serial ;;; port stream (defun face (to-be-facing) ;;if the agent needs to turn twice (if (or (and (equal *orientation* 'up) (equal to-be-facing 'down)) (and (equal *orientation* 'down) (equal to-be-facing 'up)) (and (equal *orientation* 'left) (equal to-be-facing 'right)) (and (equal *orientation* 'right) (equal to-be-facing 'left))) ;;then turn twice (turn 'clockwise 2)) ;;if the agent needs to turn counterclockwise (if (or (and (equal *orientation* 'up) (equal to-be-facing 'left)) (and (equal *orientation* 'left) (equal to-be-facing 'down)) (and (equal *orientation* 'down) (equal to-be-facing 'right)) (and (equal *orientation* 'right) (equal to-be-facing 'up))) ;;then turn counterclockwise (turn 'counterclockwise 1)) ;;if the agent needs to turn clockwise (if (or (and (equal *orientation* 'up) (equal to-be-facing 'right)) (and (equal *orientation* 'right) (equal to-be-facing 'down)) (and (equal *orientation* 'down) (equal to-be-facing 'left)) (and (equal *orientation* 'left) (equal to-be-facing 'up))) ;;then turn clockwise (turn 'clockwise 1)) ;;in all cases, ;;change *orientation* to reflect the turn (setf *orientation* to-be-facing))

37. Implementation : Action Functions ;;;turn function, takes clockwise or counterclockwise and how many to turn ;;;side effects are printing to *outstream*, writing to serial port stream (defun turn (rotation amount) ;;print the turn (format *outstream* "turning ~A ~A time(s).~%" rotation amount) ;;do the turn ;;if to turn clockwise (if (equal rotation 'clockwise) (dotimes (x amount) ;;perform the turn (right)) ;;else turn counterclockwise (dotimes (x amount) ;;perform the turn (left)))) ;;;shoot function, takes a direction to shoot in, turns agent to face there if necessary ;;;side effects are changing *orientation*, *wumpus-assertions*, *assumptions*, ;;; writing to serial port stream, and printing to *outstream* (defun shoot (direction) ;;face the agent where it wants to shoot (face direction) ;;print the shot (format *outstream* "shot ~A~%" direction) ;;perform the shot (shoot-arrow)

38. Implementation : Action Functions ;;check if the wumpus was killed (cond ;;if the direction to shoot is up ((eql direction 'up) ;;if there is a wumpus where the agent is shooting (if (and (not (out-of-bounds (+ (cadr *coords*) 1) *dimension*)) (wumpus? `(,(car *coords*) ,(+ (cadr *coords*) 1)))) ;;then note that the wumpus was killed (progn (setf (aref *assumptions* 1) t) (format *outstream* "wumpus killed.~%")) ;;else add to assertions wumpus not there (if (not (out-of-bounds (+ (cadr *coords*) 1) *dimension*)) (my-assert *wumpus-assertions* `(not (wumpus ,(car *coords*) ,(+ (cadr *coords*) 1))))))) ;;if the direction to shoot is down ((eql direction 'down) ;;if there is a wumpus below (if (and (not (out-of-bounds (- (cadr *coords*) 1) *dimension*)) (wumpus? `(,(car *coords*) ,(- (cadr *coords*) 1)))) ;;then note the death of the wumpus (progn (setf (aref *assumptions* 1) t) (format *outstream* "wumpus killed.~%")) ;;else add to assertions wumpus not there (if (not (out-of-bounds (- (cadr *coords*) 1) *dimension*)) (my-assert *wumpus-assertions* `(not (wumpus ,(car *coords*) ,(- (cadr *coords*) 1)))))))

39. Implementation : Action Functions ;;if want to shoot right ((eql direction 'right) ;;if wumpus to the right (if (and (not (out-of-bounds (+ (car *coords*) 1) *dimension*)) (wumpus? `(,(+ (car *coords*) 1) ,(cadr *coords*)))) ;;then note the wumpus was killed (progn (setf (aref *assumptions* 1) t) (format *outstream* "wumpus killed.~%")) ;;else add to assertions wumpus not there (if (not (out-of-bounds (+ (car *coords*) 1) *dimension*)) (my-assert *wumpus-assertions* `(not (wumpus ,(+ (car *coords*) 1) ,(cadr *coords*))))))) ;;if want to shoot to the left ((eql direction 'left) ;;if wumpus is in the square to the left (if (and (not (out-of-bounds (- (car *coords*) 1) *dimension*)) (wumpus? `(,(- (car *coords*) 1) ,(cadr *coords*)))) ;;then note the death of the wumpus (progn (setf (aref *assumptions* 1) t) (format *outstream* "wumpus killed.~%")) ;;else add to assertions wumpus not there (if (not (out-of-bounds (- (car *coords*) 1) *dimension*)) (my-assert *wumpus-assertions* `(not (wumpus ,(- (car *coords*) 1) ,(cadr *coords*))))))))

40. Implementation : Action Functions ;;if wumpus was killed, say so (if (aref *assumptions* 1) (wumpus-killed)) ;;in all cases, ;;now out of an arrow, ;;change the assumption that the agent has an arrow (setf (aref *assumptions* 3) t)) ;;;grab gold function ;;;side effects are changing *assumptions*, writing to serial port stream (defun grab-gold () ;;change the assumption that the agent doesn't have the gold (setf (aref *assumptions* 2) t) ;;perform the grab (grab))

41. Pseudocode : Assertion Functions Have global lists to hold assertions about locations of: wumpus breeze pits gold stenches These lists will contain either e.g. (wumpus x y) or (not (wumpus x y)) Macro my-assert (assertions-list the-assertion) add the-assertion to assertions-list Function make-assertions (coordinates) mark that the square has been visited for each object that can be in a square (wumpus, stench, breeze, pit, gold) if the object is at coordinates my-assert (object x y) else my-assert (not (object x y)) – where x and y are the first and second elements of coordinates Function assert-all (assertions-list) call the SNARK function assert for each element of assertions-list Function assert-coords call the SNARK function assert on (at x y) – where x and y are first and second elements of current value of agent’s coordinates

42. Pseudocode : Assertion Functions Function assert-visited for each square in the array saying which squares are visited if the square has been visited call SNARK’s assert on (visited x y) Function assert-unvisited for each square in the array saying which squares are visited if the square has not been visited call SNARK’s assert on (not (visited x y)) Create a global array representing the assumptions: agent can explore wumpus is not dead agent does not have the gold agent has an arrow Function wumpus-dead? check the assumptions array if the wumpus is dead if so return true, else return false

43. Implementation : Assertion Functions ;;;my-assert macro, updates given assertion list with given assertion ;;;side effect is changing given assertions list (defmacro my-assert (assertions-list the-assertion) ;;add the given assertion to the given assertion list `(setf ,assertions-list (cons ,the-assertion ,assertions-list))) ;;;make-assertions function, makes assertions about given coordinates ;;;side effect is changing assertion lists (defun make-assertions (coords-list) ;;note that the square has been visited (setf (aref *world-visits* (car *coords*) (cadr *coords*)) t) ;;check for each percept or object ;;if there is a stench (if (stench? coords-list) ;;then assert so (my-assert *stench-assertions* `(stench ,(car coords-list) ,(cadr coords-list))) ;;else assert not so (my-assert *stench-assertions* `(not (stench ,(car coords-list) ,(cadr coords-list))))) ;;if there is a breeze (if (breeze? coords-list) ;;then assert so (my-assert *breeze-assertions* `(breeze ,(car coords-list) ,(cadr coords-list))) ;;else assert not so (my-assert *breeze-assertions* `(not (breeze ,(car coords-list) ,(cadr coords-list)))))

44. Implementation : Assertion Functions ;;if there is gold (if (gold? coords-list) ;;then assert so (my-assert *gold-assertions* `(gold ,(car coords-list) ,(cadr coords-list))) ;;else assert not so (my-assert *gold-assertions* `(not (gold ,(car coords-list) ,(cadr coords-list))))) ;;if there is a wumpus (if (wumpus? coords-list) ;;then assert so (my-assert *wumpus-assertions* `(wumpus ,(car coords-list) ,(cadr coords-list))) ;;else assert not so (my-assert *wumpus-assertions* `(not (wumpus ,(car coords-list) ,(cadr coords-list))))) ;;if there is a pit (if (pit? coords-list) ;;then assert so (my-assert *pit-assertions* `(pit ,(car coords-list) ,(cadr coords-list))) ;;else assert not so (my-assert *pit-assertions* `(not (pit ,(car coords-list) ,(cadr coords-list)))))) ;;;assert-all function, asserts all assertions in given assertion list for SNARK (defun assert-all (all-assertions) ;;for each element in the list, assert it (dolist (x all-assertions) (assert x)))

45. Implementation : Assertion Functions ;;;assert-coords function, asserts agent location for SNARK (defun assert-coords () ;;assert agent at current coords (assert `(at ,(car *coords*) ,(cadr *coords*)))) ;;;assert-unvisited function, asserts where agent has not visited (defun assert-unvisited () ;;for each square (dotimes (x *dimension*) (dotimes (y *dimension*) ;;if unvisited, assert so for SNARK (if (equal (aref *world-visits* x y) nil) (assert `(not (visited ,x ,y))) )))) ;;;assert-visited function, asserts where agent has visited (defun assert-visited () ;;for each square (dotimes (x *dimension*) (dotimes (y *dimension*) ;;if visited, assert so for SNARK (if (equal (aref *world-visits* x y) t) (assert `(visited ,x ,y)) ))))

46. Implementation : Assertion Functions ;;;make-assumptions function, asserts the assumptions for SNARK (defun make-assumptions () ;;if can explore (if (eql (aref *assumptions* 0) nil) ;;then assert so (assert '(can explore)) ;;else assert not so (assert '(not (can explore)))) ;;if wumpus not dead (if (eql (aref *assumptions* 1) nil) ;;then assert so (assert '(not (dead wumpus))) ;;else assert not so (assert '(dead wumpus))) ;;if dont have gold (if (eql (aref *assumptions* 2) nil) ;;assert so (assert '(not (have gold))) ;;else assert not so (assert '(have gold))) ;;if have arrow (if (eql (aref *assumptions* 3) nil) ;;assert so (assert '(have arrow)) ;;else assert not so (assert '(not (have arrow)))))

47. Implementation : Assertion Functions ;;;wumpus-dead? function, checks assumptions to see if wumpus dead (defun wumpus-dead? () ;;assumptions array has true in index 1 if wumpus is dead, nil otherwise (aref *assumptions* 1))

48. Implementation : Set-up ;;;set-up function, initializes global vars and SNARK options (defun set-it-up () (setf excl:*global-gc-behavior* ':auto) ;set garbage collection to just auto instead ;of auto and warn ;;initialize assertions lists (setf pit-assertions (list)) (setf wumpus-assertions (list)) (setf breeze-assertions (list)) (setf stench-assertions (list)) (setf gold-assertions (list)) (setf assumptions (make-array 4)) ;;initialize coords (setf coords (list 0 0)) ;;initialize orientation (setf orientation 'right) ;;initialize snark options (default-use-resolution) ;for general proofs (default-use-hyperresolution) ;for general proofs (default-use-paramodulation) ;for proofs using identity (default-use-code-for-numbers) ;for +, - (default-use-code-for-lists) ;for (list ...), (cons ...), (append ...), nil (default-use-answers-during-subsumption nil) ;unnecessary, degrades performance (default-print-rows-when-derived nil) ;suppress row printing (default-print-summary-when-finished nil) ;suppress summary printing (default-print-options-when-starting nil) ;suppress options printing (default-print-final-rows nil) ;suppress final row printing (default-print-symbol-table-warnings nil) ;suppress symbol table warnings (default-print-symbol-in-use-warnings nil) ;suppress symbol in use warnings

49. Implementation : Set-up (default-run-time-limit nil) ;only explore layer has explicit runtime limit (default-agenda-length-limit 9000) ;9000 seems to do it (initialize)) ;initialize SNARK ;;load layer files (load "layers.lisp") ;;;open and close wrapper functions for serial port (defun open-serial-port () (setf *serial-port* (open "com1" :direction :io))) (defun close-serial-port () (close *serial-port*)) ;;;use-robot function, enables interface to robot ;;;(use-robot) enables, (use-robot nil) disables ;;;side effect is changing *no-robot* (defun use-robot (&optional (use? t)) (setf *no-robot* (not use?)))

50. Pseudocode : Main Control Loop Function main (world-size) call set up function to initialize SNARK options make a world of world-size print the world make-assertions for the start square print the contents of the start square if the start square has a wumpus or a pit then the map is unfair, exit function if the start square has a breeze and no gold then the map is fair, but uninteresting since the agent will not be able to prove moving anywhere is safe, so exit function loop set the elapsed time to the current time call layer2 if layer2 returned grab call layer0 on the result of layer1-grab else if layer2 returned shoot call layer0 on the result of layer1-shoot else if layer2 returned explore call layer0 on the result of layer1-explore else if layer2 returned home call layer0 on the result of layer1-home exit the loop set elapsed time to (current time - elapsed time) check if the agent has the gold if so, print success else print failure print the elapsed time