;;;
;;; A Reasoning Wumpus Agent Using SNePS
;;; Stuart C. Shapiro
;;; April 4, 1996
;;; Modified March 19, 1998 to use the SNePS Tell-Ask Interface
;;; Last modified March 30, 1998
;;;
;;; (load "/projects/snwiz/bin/sneps")

(in-package "COMMON-LISP-USER")

(load "/projects/shapiro/Sneps/tellask")
(import '(snepsul:Tell snepsul:Ask snepsul:Askifnot snepsul:Askwh
	  snepsul:Askwhnot))

(load "/projects/shapiro/AIclass/Aimacode/aima.lisp")
(aima-load 'agents)

(defun run-my-wumpus (&key (agents (list (SNePS-wumpus-agent 'S)))
			   (max-steps 5) (display t)
			   (env (make-wumpus-world :agents agents)))
  "Run a Wumpus world until the game is over."
  (print-environment-map env)
  (run-eval-environment env :max-steps max-steps :display display))

(defun SNePS-wumpus-agent (name)
  ;; a modification of the Aima stupid-wumpus-agent
  ;; by Stuart C. Shapiro
  (wumpus-agent
   name
   (let ((plan nil)		; Use for a plan of moves
         (wumpus-alive? t)	; T if the wumpus is alive
	 (got-gold? nil)	; T if the agent has the gold
	 (have-arrow? t)	; T if the agent has an arrow
	 (x 1) (y 1)		; The current location of the agent
	 (dx 1) (dy 0)		; Codes the direction the agent is looking
	 )
     (setf snip:*infertrace* nil) ; Turn off SNePS inference tracing
     ;; start with an empty SNePS network
     (tell "clearkb")
	;; A location is safe iff there's no wumpus there and no pit there.
     (tell "all(x) (Safe(x) <=> (~At(Wumpus, x) and ~At(Pit, x)))")
     ;; If the Wumpus is dead, it isn't at any location.
     (tell "Dead(Wumpus) => all(x)(Location(x) => ~At(Wumpus, x))")
     #'(lambda (percept)
	 (labels
	     ((move (movement)
		;; Adjusts x and y for the movement
		(cond ((eql movement 'forward)
		       (incf x dx)
		       (incf y dy))
		      ((equal movement '(turn right))
		       (psetf dx (if (zerop dx) dy 0)
			      dy (if (zerop dy) (- dx) 0)))
		      ((equal movement '(turn left))
		       (psetf dx (if (zerop dx) (- dy) 0)
			      dy (if (zerop dy) dx 0)))) 
		movement))
	   (block SNePS-AGENT
	     ;; If bump, back up
	     (when (wumpus-percept-bump percept)
	       (decf x dx) (decf y dy))
	     ;; If the agent has the gold and is at the cave entrance, climb.
	     (when (and got-gold? (= x y 1))
	       (return-from SNePS-AGENT 'climb))
	     ;; The nearby locations are locations.
	     (tell (format nil "Location(position(~a, ~a))!" x y))
	     (tell (format nil "Location(position(~a, ~a))!" x (1+ y)))
	     (tell (format nil "Location(position(~a, ~a))!" (1+ x) y))
	     (tell (format nil "Location(position(~a, ~a))!"  x (1- y)))
	     (tell (format nil "Location(position(~a, ~a))!"  (1- x) y))
	     ;; The current location is safe
	     (tell (format nil "Safe(position(~a, ~a))!" x y))
	     ;; If the wumpus screams, it's dead.
	     (when (wumpus-percept-sound percept)
	       (setf wumpus-alive? nil)
	       (format t "~%I killed the wumpus!~%")
	       #!((remove-from-context
		   (findassert min 1 max 1 arg (find a1 wumpus))))
	       (sneps:clear-infer)
	       (tell "Dead(Wumpus)!"))
	     (when wumpus-alive?
	       ;; If a stench, the wumpus is nearby,
	       ;; but if not, it isn't.
	       (let ((n 0))
		 (when (wumpus-percept-stench percept)
		   (setf n 1)
		   (format t "~%I smell the wumpus!~%"))
		 (tell (format
			nil
			"andor(~a,~a){At(Wumpus,position(~a,~a)),~
                                    At(Wumpus,position(~a,~a)),~
                                    At(Wumpus,position(~a,~a)),~
                                    At(Wumpus,position(~a,~a))}!"
			n n
			x (1+ y) (1+ x) y x (1- y) (1- x) y))))
	     (let ((i 0) (j 0))
	       ;; If a breeze, one or more pits are nearby,
	       ;; but if not, there aren't
	       (when (wumpus-percept-breeze percept)
		 (setf i 1 j 4)
		 (format t "~%I detect a pit.~%"))
	       (tell (format
			nil
			"andor(~a,~a){At(Pit,position(~a,~a)),~
                                    At(Pit,position(~a,~a)),~
                                    At(Pit,position(~a,~a)),~
                                    At(Pit,position(~a,~a))}!"
			i j
			x (1+ y) (1+ x) y x (1- y) (1- x) y)))
	     (sneps:clear-infer)
	     ;; Now decide what to do
	     (cond ((wumpus-percept-glitter percept)
		    ;; go for the gold
		    (setf got-gold? t)
		    (format t "~&I've got the gold!")
		    'grab)
		   ((wumpus-percept-bump percept) 
		    ;; left over from stupid-wumpus-agent
		    (setf plan '((turn right) (turn right) forward))
		    (format t "~&I bumped, so I've got a plan.")
		    (move (pop plan)))
		   (plan
		    ;; left over from stupid-wumpus-agent
		    (format t "~&I'm using my plan.")
		    (move (pop plan)))
		   ((ask (format nil
				 "Safe(position(~a, ~a))"
				 (+ x dx) (+ y dy)))
		    (format t "~&It's safe to go forward.~%")
		    (move 'forward))
		   ((and wumpus-alive? have-arrow?)
		    ;; Shoot away, just in case.
		    ;; This decision could be improved.
		    (setf have-arrow? nil)
		    'shoot)
		   (t 
		    ;; left over from stupid-wumpus-agent
		    (move (random-element '(forward forward (turn right)
					    (turn left)))))
		   )))))))