Vous êtes sur la page 1sur 22

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;; This is the file game.scm

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; ---------------------------------------------------------------------------

;;; Simple object system with inheritance

(define (ask object message . args) ;; See your Scheme manual to explain `.'

(let ((method (get-method object message)))

(if (method? method)

(apply method (cons object args))

(error "No method" message (cadr method)))))

(define (get-method object message)

(object message))

(define (no-method name)

(list 'no-method name))

(define (method? x)

(not (no-method? x)))

(define (no-method? x)

(if (pair? x)

(eq? (car x) 'no-method)

#f))

;;; ----------------------------------------------------------------------------

;;; Persons, places, and things will all be kinds of named objects

(define (make-named-object name)

(lambda (message)
(case message

((name) (lambda (self) name))

(else (no-method name)))))

;;; Persons and things are mobile since their places can change

(define (make-mobile-object name location)

(let ((named-obj (make-named-object name)))

(lambda (message)

(case message

((place) (lambda (self) location))

((install)

(lambda (self)

(ask location 'add-thing self)))

;; Following method should never be called by the user...

;; it is a system-internal method.

;; See CHANGE-PLACE instead

((set-place)

(lambda (self new-place)

(set! location new-place)

'place-set))

(else (get-method named-obj message))))))

(define (make&install-mobile-object name place)

(let ((mobile-obj (make-mobile-object name place)))

(ask mobile-obj 'install) mobile-obj))

;;; A thing is something that can be owned

(define (make-thing name birthplace)

(let ((owner 'nobody)

(mobile-obj (make-mobile-object name birthplace)))

(lambda (message)

(case message

((owner) (lambda (self) owner))


((ownable?) (lambda (self) true))

((owned?) (lambda (self) (not (eq? owner 'nobody))))

;; Following method should never be called by the user...

;; it is a system-internal method.

;; See TAKE and LOSE instead.

((set-owner)

(lambda (self new-owner)

(set! owner new-owner)

'owner-set))

(else (get-method mobile-obj message))))))

(define (make&install-thing name birthplace)

(let ((thing (make-thing name birthplace)))

(ask thing 'install)

thing))

;;; Implementation of places

(define (make-place name)

(let ((neighbor-map '())

(things '())

(named-obj (make-named-object name)))

(lambda (message)

(case message

((things) (lambda (self) things))

((neighbors)

(lambda (self) (map cdr neighbor-map)))

((exits)

(lambda (self) (map car neighbor-map)))

((neighbor-towards)

(lambda (self direction)

(let ((places (assq direction neighbor-map)))

(if places

(cdr places)
#f))))

((add-neighbor)

(lambda (self direction new-neighbor)

(cond ((assq direction neighbor-map)

(display-message (list "Direction already assigned"

direction name))

#f)

(else (set! neighbor-map

(cons (cons direction new-neighbor) neighbor-map))

#t))))

((accept-person?)

(lambda (self person)

#t))

;; Following two methods should never be called by the user...

;; they are system-internal methods. See CHANGE-PLACE instead.

((add-thing)

(lambda (self new-thing)

(cond ((memq new-thing things)

(display-message (list (ask new-thing 'name)

"is already at" name))

#f)

(else (set! things (cons new-thing things))

#t))))

((del-thing)

(lambda (self thing)

(cond ((not (memq thing things))

(display-message (list (ask thing 'name)

"is not at" name))

#f)

(else (set! things (delq thing things)) ;; DELQ defined

#t)))) ;; below

(else (get-method named-obj message))))))


;;; ----------------------------------------------------------------------------

;;; Implementation of people

(define (association-procedure proc select)

(define (helper lst)

(cond ((null? lst) '())

((proc (car lst)) (select (car lst)))

(else (helper (cdr lst)))))

(lambda (list)

(helper list)))

(define find-object

(lambda (name objlist)

((association-procedure

(lambda (obj) (equal? (ask obj 'name) name))

(lambda (obj) obj)) objlist)))

(define (make-person name birthplace threshold)

(let ((possessions '())

(mobile-obj (make-mobile-object name birthplace)))

(lambda (message)

(case message

((person?) (lambda (self) true))

((possessions) (lambda (self) possessions))

((list-possessions)

(lambda (self)

(ask self 'say

(cons "I have"

(if (null? possessions)

'("nothing")

(map (lambda (p) (ask p 'name))

possessions))))

possessions))

((say)
(lambda (self list-of-stuff)

(display-message

(append (list "At" (ask (ask self 'place) 'name)

":" name "says --")

(if (null? list-of-stuff)

'("Oh, nevermind.")

list-of-stuff)))

'said))

((have-fit)

(lambda (self)

(ask self 'say '("Yaaaah! I am upset!"))

'I-feel-better-now))

((look-around)

(lambda (self)

(let ((other-things

(map (lambda (thing) (ask thing 'name))

(delq self ;; DELQ

(ask (ask self 'place) ;; defined

'things))))) ;; below

(ask self 'say (cons "I see" (if (null? other-things)

'("nothing")

other-things)))

other-things)))

((take)

(lambda (self thing)

(cond ((symbol? thing) ; referencing object by name

(let ((obj (find-object thing (ask (ask self 'place) 'things))))

(if (null? obj)

#f

(ask self 'take obj))))

((memq thing possessions)

(ask self 'say

(list "I already have" (ask thing 'name)))


#t)

((and (let ((things-at-place (ask (ask self 'place) 'things)))

(memq thing things-at-place))

(is-a thing 'ownable?))

(if (ask thing 'owned?)

(let ((owner (ask thing 'owner)))

(ask owner 'lose thing)

(ask owner 'have-fit))

'unowned)

(ask thing 'set-owner self)

(set! possessions (cons thing possessions))

(ask self 'say

(list "I take" (ask thing 'name)))

#t)

(else

(display thing)

(display-message

(list "You cannot take" (ask thing 'name)))

#f))))

((lose)

(lambda (self thing)

(cond ((symbol? thing) ; referencing object by name

(let ((obj (find-object thing (ask (ask self 'place) 'things))))

(if (null? obj)

#f

(ask self 'lose obj))))

((eq? self (ask thing 'owner))

(set! possessions (delq thing possessions)) ;; DELQ

(ask thing 'set-owner 'nobody) ;; defined

(ask self 'say ;; below

(list "I lose" (ask thing 'name)))

#t)
(else

(display-message (list name "does not own"

(ask thing 'name)))

#f))))

((move)

(lambda (self)

(cond ((= (random threshold) 0)

(ask self 'act)

#t))))

((act)

(lambda (self)

(let ((new-place (random-neighbor (ask self 'place))))

(if new-place

(ask self 'move-to new-place)

#f)))) ; All dressed up and no place to go

((move-to)

(lambda (self new-place)

(let ((old-place (ask self 'place)))

(cond ((eq? new-place old-place)

(display-message (list name "is already at"

(ask new-place 'name)))

#f)

((ask new-place 'accept-person? self)

(change-place self new-place)

(for-each (lambda (p) (change-place p new-place))

possessions)

(display-message

(list name "moves from" (ask old-place 'name)

"to" (ask new-place 'name)))

(greet-people self (other-people-at-place self new-place))

#t)

(else

(display-message (list name "can't move to"


(ask new-place 'name))))))))

((go)

(lambda (self direction)

(let ((old-place (ask self 'place)))

(let ((new-place (ask old-place 'neighbor-towards direction)))

(cond (new-place

(ask self 'move-to new-place))

(else

(display-message (list "You cannot go" direction

"from" (ask old-place 'name)))

#f))))))

((install)

(lambda (self)

(add-to-clock-list self)

((get-method mobile-obj 'install) self)))

(else (get-method mobile-obj message))))))

(define (make&install-person name birthplace threshold)

(let ((person (make-person name birthplace threshold)))

(ask person 'install)

person))

;;; A troll is a kind of person (but not a kind person!)

(define (make-troll name birthplace threshold)

(let ((person (make-person name birthplace threshold)))

(lambda (message)

(case message

((act)

(lambda (self)

(let ((others (other-people-at-place self (ask self 'place))))

(if (not (null? others))

(ask self 'eat-person (pick-random others))

((get-method person 'act) self)))))


((eat-person)

(lambda (self person)

(ask self 'say

(list "Growl.... I'm going to eat you,"

(ask person 'name)))

(go-to-heaven person)

(ask self 'say

(list "Chomp chomp." (ask person 'name)

"tastes yummy!"))

'*burp*))

(else (get-method person message))))))

(define (make&install-troll name birthplace threshold)

(let ((troll (make-troll name birthplace threshold)))

(ask troll 'install)

troll))

(define (go-to-heaven person)

(for-each (lambda (item) (ask person 'lose item))

(ask person 'possessions))

(ask person 'say

'("

Dulce et decorum est

pro computatore mori!"

))

(ask person 'move-to heaven)

(remove-from-clock-list person)

'game-over-for-you-dude)

(define heaven (make-place 'heaven)) ; The point of no return


;;; --------------------------------------------------------------------------

;;; Clock routines

(define *clock-list* '())

(define *the-time* 0)

(define (initialize-clock-list)

(set! *clock-list* '())

'initialized)

(define (add-to-clock-list person)

(set! *clock-list* (cons person *clock-list*))

'added)

(define (remove-from-clock-list person)

(set! *clock-list* (delq person *clock-list*)) ;; DELQ defined below

'removed)

(define (clock)

(newline)

(display "---Tick---")

(set! *the-time* (+ *the-time* 1))

(for-each (lambda (person) (ask person 'move))

*clock-list*)

'tick-tock)

(define (current-time)

*the-time*)

(define (run-clock n)

(cond ((zero? n) 'done)

(else (clock)

(run-clock (- n 1)))))
;;; --------------------------------------------------------------------------

;;; Miscellaneous procedures

(define (is-a object property)

(let ((method (get-method object property)))

(if (method? method)

(ask object property)

#f)))

(define (change-place mobile-object new-place) ; Since this bridges the gap

(let ((old-place (ask mobile-object 'place))) ; between MOBILE-OBJECT and

(ask mobile-object 'set-place new-place) ; PLACE, it is best it not

(ask old-place 'del-thing mobile-object)) ; be internal to either one.

(ask new-place 'add-thing mobile-object)

'place-changed)

(define (other-people-at-place person place)

(filter (lambda (object)

(if (not (eq? object person))

(is-a object 'person?)

#f))

(ask place 'things)))

(define (greet-people person people)

(if (not (null? people))

(ask person 'say

(cons "Hi"

(map (lambda (p) (ask p 'name))

people)))

'sure-is-lonely-in-here))

(define (display-message list-of-stuff)


(newline)

(for-each (lambda (s) (display s) (display " "))

list-of-stuff)

'message-displayed)

(define (random-neighbor place)

(pick-random (ask place 'neighbors)))

(define (pick-random lst)

(if (null? lst)

#f

(list-ref lst (random (length lst))))) ;; See manual for LIST-REF

(define (delq item lst)

(cond ((null? lst) '())

((eq? item (car lst)) (delq item (cdr lst)))

(else (cons (car lst) (delq item (cdr lst))))))

;;; -------------------------------------------------------------------

;;; Other interesting procedures

(define (make&install-sd-card name birthplace id)

(let ((card (make-sd-card name birthplace id)))

(ask card 'install)

card))

(define (make-sd-card name birthplace idnumber)

(let ((id idnumber)

(thing (make-thing name birthplace)))

(lambda (message)

(case message

((sd-card?) (lambda (self) true))

((id) (lambda (self) id))


(else (get-method thing message))))))

(define (copy-sd-card card)

(let ((name (symbol-append 'copy-of- (ask card 'name)))

(place (ask card 'place))

(id (ask card 'id)))

(make&install-sd-card name place id)))

;;; -------------------------------------------------------------------

;;; symbol-append is available in MIT-GNU Scheme but not in DrScheme

(define (symbol-append sym1 sym2)

(string->symbol (string-append

(symbol->string sym1)

(symbol->string sym2))))

;;; -------------------------------------------------------------------

;;; show-thing needs to be ported over to DrScheme

;(define (show thing)

; (define (global-environment? frame)

; (environment->package frame))

; (define (pp-binding name value width)

; (let ((value* (with-string-output-port

; (lambda (port)

; (if (pair? value)

; (pretty-print value port #F (+ width 2))

; (display value port))))))

; (newline)

; (display name)

; (display ": ")

; (display (make-string (- width (string-length name)) #\Space))


; (if (pair? value)

; (display (substring value* (+ width 2) (string-length value*)))

; (display value*))))

; (define (show-frame frame)

; (if (global-environment? frame)

; (display "\nGlobal Environment")

; (let* ((bindings (environment-bindings frame))

; (parent (environment-parent frame))

; (names (cons "Parent frame"

; (map symbol->string (map car bindings))))

; (values (cons (if (global-environment? parent)

; 'global-environment

; parent)

; (map cadr bindings)))

; (width (reduce max 0 (map string-length names))))

; (for-each (lambda (n v) (pp-binding n v width))

; names values))))

; (define (show-procedure proc)

; (fluid-let ((*unparser-list-depth-limit* 4)

; (*unparser-list-breadth-limit* 4))

; (newline)

; (display "Frame:")

; (newline)

; (display " ")

; (if (global-environment? (procedure-environment proc))

; (display "Global Environment")

; (display (procedure-environment proc)))

; (newline)

; (display "Body:")

; (newline)

; (pretty-print (procedure-lambda proc) (current-output-port) #T 2)))

; (define (print-nicely thing)


; (newline)

; (display thing)

; (cond ((equal? #f thing)

; 'uninteresting)

; ((environment? thing)

; (show-frame thing))

; ((compound-procedure? thing)

; (show-procedure thing))

; (else 'uninteresting)))

; (print-nicely

; (or (if (integer? thing)

; (object-unhash thing)

; thing)

; thing)))

;;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;;

;; Code for adventure game

;;

;;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(initialize-clock-list)

;; Here we define the places in our world...

;;------------------------------------------

(define central-library (make-place 'central-library))

(define forum (make-place 'forum))

(define lt15 (make-place 'lt15))

(define arts-canteen (make-place 'arts-canteen))

(define com1-open-area (make-place 'com1-open-area))


(define sr1 (make-place 'sr1))

(define com1-classrooms (make-place 'com1-classrooms))

(define beng-office (make-place 'beng-office))

(define bing-office (make-place 'bing-office))

(define com1-lift-lobby-top (make-place 'com1-lift-lobby-top))

(define com1-lift-lobby-bottom (make-place 'com1-lift-lobby-bottom))

(define com1-ladies (make-place 'com1-ladies))

(define com1-gents (make-place 'com1-gents))

(define secret-portal (make-place 'secret-portal))

(define ben-office (make-place 'ben-office))

(define technical-services (make-place 'technical-services))

(define com1-research-labs (make-place 'com1-research-labs))

(define com1-staircase-top (make-place 'com1-staircase-top))

(define com1-student-area (make-place 'com1-student-area))

(define enchanted-garden (make-place 'enchanted-garden))

(define data-comm-lab2 (make-place 'data-comm-lab2))

(define com1-staircase-bottom (make-place 'com1-staircase-bottom))

;; One-way paths connect individual places in the world.

;;------------------------------------------------------

(define (can-go from direction to)

(ask from 'add-neighbor direction to))

(define (can-go-both-ways from direction reverse-direction to)

(can-go from direction to)

(can-go to reverse-direction from))

(can-go-both-ways forum 'up 'down central-library)


(can-go-both-ways bing-office 'north 'south central-library)

(can-go-both-ways lt15 'north 'south forum)

(can-go-both-ways arts-canteen 'east 'west lt15)

(can-go-both-ways lt15 'up 'down bing-office)

(can-go-both-ways com1-open-area 'north 'south lt15)

(can-go-both-ways com1-open-area 'east 'west sr1)

(can-go-both-ways com1-classrooms 'north 'south com1-open-area)

(can-go-both-ways beng-office 'north 'south com1-classrooms)

(can-go-both-ways com1-lift-lobby-top 'east 'west com1-open-area)

(can-go-both-ways com1-lift-lobby-bottom 'up 'down com1-lift-lobby-top)

(can-go-both-ways com1-ladies 'north 'south com1-lift-lobby-bottom)

(can-go-both-ways com1-gents 'north 'south com1-ladies)

(can-go-both-ways secret-portal 'up 'down com1-gents)

(can-go-both-ways ben-office 'east 'west secret-portal)

(can-go-both-ways com1-lift-lobby-bottom 'east 'west technical-services)

(can-go-both-ways com1-research-labs 'north 'south technical-services)

(can-go-both-ways com1-staircase-top 'north 'south com1-research-labs)

(can-go-both-ways com1-student-area 'up 'down technical-services)

(can-go-both-ways com1-student-area 'east 'west enchanted-garden)

(can-go-both-ways data-comm-lab2 'north 'south com1-student-area)

(can-go-both-ways com1-staircase-bottom 'north 'south data-comm-lab2)

(can-go-both-ways com1-staircase-bottom 'up 'down com1-staircase-top)

;; The important critters in our world...

;;---------------------------------------

(define you (make&install-person 'you enchanted-garden 9999)) ;; Your avatar

(define beng (make&install-person 'beng beng-office 3))

(define bing (make&install-person 'bing bing-office 2))

(define proffy (make&install-troll 'proffy lt15 4))


(define bing-card

(make&install-sd-card 'bing-card bing-office '888-12-3456))

(define beng-card

(make&install-sd-card 'beng-card beng-office '888-98-7654))

;;

;; Interactive game

;;

;; Helper procedure to tokenize an input string

(define (tokenize s)

(define (helper s begin end)

(if (equal? s "")

'()

(let ((char (string-ref s (- end 1))))

(cond ((= end (string-length s))

(cons begin end))

((and (= (+ 1 begin) end)

(char-whitespace? char))

(helper s (+ 1 begin) (+ 1 end)))

((char-whitespace? char)

(cons begin (- end 1)))

(else (helper s begin (+ 1 end)))))))

(define (helper2 s)

(if (null? s)

'()

(let ((next-token (helper s 0 1)))

(if (null? next-token)

'()
(let* ((begin (car next-token))

(end (cdr next-token)))

(cons (substring s begin end) (helper2 (substring s end (string-length


s)))))))))

(map string->symbol (if (equal? s "")

'()

(helper2 s))))

(define (play-game-interactive)

(display-game-state)

(apply ask (cons you (tokenize (prompt "Command:"))))

(clock)

(play-game-interactive))

(define (prompt prompt-string)

(display prompt-string)

(read-line))

(define (display-game-state)

(newline)

(display "You are at: ")

(display (ask (ask you 'place) 'name))

(newline)

(display "You see: ")

(display (map (lambda (p) (ask p 'name)) (ask (ask you 'place) 'things)))

(newline)

(display "Exits: ")

(display (ask (ask you 'place) 'exits))

(newline)

(newline))

; Uncomment line below to play in interactive mode

;(play-game-interactive)
;; The beginning of an ever-expanding automatic game script

;;----------------------------------------------------------

(define (play-game)

(define scheme-manual (make&install-thing 'scheme-manual lt15))

(ask beng 'look-around)

(ask beng 'go 'north)

(ask beng 'go 'north)

(ask (ask beng 'place) 'exits)

(ask beng 'go 'north)

(ask bing 'go 'down)

(ask beng 'take scheme-manual)

(ask beng 'go 'north)

(ask bing 'go 'north)

(ask bing 'look-around)

(ask bing 'take scheme-manual)

(ask beng 'go 'up)

(ask bing 'go 'south)

(ask proffy 'eat-person bing))

; Now, run the scripted game!

;(play-game)

(define flip
(let ((count 0))
(lambda ()
(begin
(set! count (- 1 count))
count))))

(define (make-flip)
(let ((count 0))
(lambda ()
(begin
(set! count (- 1 count))
count))))

(define flip (make-flip))


(define flap1 (flip))
(define (flap2) (flip))
(define flap3 flip)
(define (flap4) flip)

(define ice-cream (make-thing 'ice-cream arts-canteen))


(ask ice-cream 'set-owner beng)
(ask (ask ice-cream 'owner) 'name)

Vous aimerez peut-être aussi