;;; File: wumpus.lisp -*- Mode: Lisp; Syntax: Common-Lisp; -*- ;;;; The Wumpus World Environment (defstructure (wumpus-world (:include grid-environment (size (@ 6 6)) (aspec '(aimless-wumpus-agent)) (bspec '((at edge wall) (* 1 gold) (* 1 wumpus) (at all (p 0.2 pit)))))) "A dangerous world with pits and wumpuses, and some gold.") (defstructure (wumpus-agent-body (:include agent-body (contents (list (make-arrow))))) "The default wumpus agent body is given an arrow.") (defstructure (gold (:include object (name "$") (size 0.1)))) (defstructure (pit (:include object (name "O")))) (defstructure (arrow (:include object (name "!") (size 0.01)))) (defstructure (wumpus (:include object (name "W") (alive? t) (size 0.7)))) ;;;; Defining the generic functions (defmethod update-fn ((env wumpus-world)) ;; See if anyone died (for each agent in (environment-agents env) do (when (find-object-if #'deadly? (object-loc (agent-body agent)) env) (kill (agent-body agent)))) ;; Sounds dissipate (for each object in (grid-environment-objects env) do (setf (object-sound object) nil)) ;; Do the normal thing (call-next-method)) (defmethod termination? ((env wumpus-world)) "End when some agent climbs out, or for the default reason (everyone dead)." (or (call-next-method) (some #'(lambda (agent) (and (equal (op (agent-action agent)) 'climb) (equal (object-loc (agent-body agent)) (grid-environment-start env)))) (environment-agents env)))) (defmethod performance-measure ((env wumpus-world) agent) "Score 1000 for getting the gold, with penalty of 10000 if dead and penalty of 1 for each step taken." (let ((agent-body (agent-body agent))) (- (if (some #'gold-p (object-contents agent-body)) 1000 0) (if (object-alive? agent-body) 0 10000) (environment-step env)))) (defmethod get-percept ((env wumpus-world) agent) "Perceive stench, breeze, glitter, bump, and sound." (let ((loc (object-loc (agent-body agent)))) (list ;; stench breeze glitter bump sound (if (find-object-or-neighbor-if #'wumpus-p loc env) 'stench) (if (find-object-or-neighbor-if #'pit-p loc env) 'breeze) (if (find-object-if #'gold-p loc env) 'glitter) (if (object-bump (agent-body agent)) 'bump) (some #'object-sound (grid-environment-objects env))))) (defmethod legal-actions ((env wumpus-world)) "In the wumpus world, agents can move around, grab gold and shoot arrows." '(climb shoot grab release speak forward turn)) (defun deadly? (object) "Pits and live wumpuses are deadly." (or (pit-p object) (and (wumpus-p object) (object-alive? object)))) ;;;; Actions (defmethod climb ((env wumpus-world) agent-body) "Climb out of the cave." (declare-ignore agent-body env) ;; Only effect is to end the game; see termination? nil) (defmethod shoot ((env wumpus-world) agent-body) (let ((arrow (find-if #'arrow-p (object-contents agent-body)))) (when arrow (setf (object-contents agent-body) (delete arrow (object-contents agent-body))) (propagate-arrow (object-loc agent-body) (object-heading agent-body) env)))) (defun propagate-arrow (loc heading env) "An arrow keeps going until it kills something or hits a wall." (let ((new-loc (add-locs loc heading))) (cond ((find-object-if #'object-alive? new-loc env) (kill (find-object-if #'object-alive? new-loc env))) ((find-object-if #'obstacle-p new-loc env)) (t (propagate-arrow new-loc heading env))))) (defun kill (object) "Make the object no longer alive." (when (object-alive? object) (setf (object-alive? object) nil) (setf (object-sound object) 'scream)))