racket/collects/htdp/hangman.rkt
2010-04-27 16:50:15 -06:00

288 lines
8.9 KiB
Racket

#lang mzscheme
(require "error.ss"
"draw-sig.ss"
"big-draw.ss"
mzlib/class
mzlib/unit
mzlib/etc
lang/prim
mred)
(provide
hangman
hangman-list
)
(define-higher-order-primitive hangman hangman/proc (make-word reveal draw-next))
(define-higher-order-primitive hangman-list hangman-list/proc (reveal-list draw-next))
(provide-signature-elements draw^)
#| ------------------------------------------------------------------------
The Basic Constants |#
(define TITLE "Hangman")
(define WELCOME "Welcome to Hangman")
(define WINNER "We have a winner!")
(define LOSER "This is the end, my friend. The word was ~a.")
(define SMALL_A (char->integer #\a))
(define LETTERS
(build-list 26 (lambda (i) (format "~a" (integer->char (+ SMALL_A i))))))
(define TRUMPET
(make-object bitmap%
(build-path (collection-path "icons") "trumpet.xbm")
'xbm))
(define PARTS (vector 'right-leg 'left-leg 'left-arm 'right-arm 'body 'head 'noose))
(define NUMBER-OF-GUESSES (vector-length PARTS))
;; char->symbol : char -> symbol
(define (char->symbol c)
(string->symbol (format "~a" c)))
;; word->list : symbol -> (listof letter)
(define (word->list sym)
(map char->symbol (string->list (symbol->string sym))))
;; WORDS : (listof (list letter letter letter))
(define WORDS
(map word->list
'(and
are
but
cat
cow
dog
eat
fee
gal
hat
inn
jam
kit
lit
met
now
owl
pet
rat
sea
the
usa
vip
was
zoo)))
;; WORDS2 : (listof (listof letter))
(define WORDS2
(append (map word->list
'(apple
bottle
cattle
drscheme
elephant
folder
gift
hangman
idle
jet
knowledge
length
macintosh
north
ottoman
public
queue
rattlesnake
snow
toddler
under
virus
xylaphon
yellow
zombie))
WORDS))
;; ------------------------------------------------------------------------
;; The GUI
;; two communication channels to GUI; init with setup-gui
(define status-message #f)
(define message-panel #f)
;; setup-gui : str ->* message% panel%
;; to produce a status message and a panel where winning/losing can be announced
;; effect: set up a new frame, arrange the GUI, and display (blank) status word
(define (setup-gui status)
(local (#| --------------------------------------------------------------
The GUI Layout: (computed as a function of constants)
------------------------------------------------
| |
| a ... z "Check" "Status" word |
| choice% button% message% message% |
| |
| Welcome/Winner/Loser |
| message% |
------------------------------------------------
|#
(define frame (make-object frame% TITLE #f 100 50))
(define verti (make-object vertical-panel% frame))
(define panel (make-object horizontal-panel% verti))
(define _1 (send panel set-alignment 'center 'center))
(define choice (make-object choice% "Guess:" LETTERS panel void))
; (make-object message% " " panel);; added for looks
(define check (make-object button% "Check" panel
(lambda (x y)
(check-guess
(char->symbol
(list-ref LETTERS
(send choice get-selection)))))))
(define _2 (make-object message% " Status: " panel))
(define m (make-object message% (uncover status) panel))
(define p (make-object horizontal-panel% verti))
(define _3 (send p set-alignment 'center 'center))
(define _4 (make-object message% WELCOME p)))
(set! status-message m)
(set! message-panel p)
(send frame show #t)))
;; ------------------------------------------------------------------------
;; The functions for communicating with the GUI
;; a-winner! : -> void
;; effect: signal win and disable game
(define (a-winner!)
(send message-panel change-children (lambda (x) null))
(make-object message% WINNER message-panel)
(make-object message% TRUMPET message-panel))
;; a-loser! : -> void
;; effect: signal loss and disable game
(define (a-loser!)
(send message-panel change-children (lambda (x) null))
(make-object message% (format LOSER (uncover chosen)) message-panel))
;; ------------------------------------------------------------------------
;; The functions for playing the game
;; check-guess : symbol -> word
;; to check whether guess occurs in the chosen word, using reveal
;; effect: update the status word
(define (check-guess guess)
(let ((result (reveal chosen status guess)))
(cond
[(equal? result chosen)
(send status-message set-label (uncover chosen))
(a-winner!)]
[(equal? result status)
(draw-next-part (select-piece!))
(when (the-end?) (a-loser!))]
[else
(set! status result)
(send status-message set-label (uncover status))])))
;; uncover : word -> string
;; to translate the current word into a string,
;; using abstraction breaking struct-ref
(define (uncover a-word)
(error 'hangman "impossible"))
;; pieces-left : index into PARTS
(define pieces-left NUMBER-OF-GUESSES)
(define (init-guesses)
(set! pieces-left NUMBER-OF-GUESSES))
;; select-piece! : -> void
;; effect: to decrease pieces-left and to pick the next thing to be drawn
(define (select-piece!)
(if (> pieces-left 0)
(begin
(set! pieces-left (sub1 pieces-left))
(vector-ref PARTS pieces-left))
;; (<= pieces-left 0)
(vector-ref PARTS 1)))
;; the-end? : -> boolean
;; to check whether the hangman is complet
(define (the-end?) (zero? pieces-left))
;; USER INTERFACE to student
;; chosen : word
(define chosen 10)
;; status : word
(define status 10)
;; reveal : (word word letter -> word)
(define (reveal chosen status guess)
(error 'hangman "appply hangman first!"))
;; draw-next-part : (symbol -> #t)
(define (draw-next-part s)
(error 'hangman "appply hangman first!"))
;; hangman :
;; (letter letter letter -> word)
;; (word word letter -> word)
;; (symbol -> true)
;; ->
;; true
;; effects: set up game status, draw noose, show frame
;; depends on: words are structures
(define (hangman/proc mw rv dr)
(check-proc 'hangman mw 3 '1st "3 arguments")
(check-proc 'hangman rv 3 '2nd "3 arguments")
(check-proc 'hangman dr 1 '3rd "1 argument")
(set! chosen (apply mw (list-ref WORDS (random (length WORDS)))))
(set! status (mw '_ '_ '_))
;; make uncover work for structs
(set! uncover
(lambda (a-word)
;; abstraction breaking hack.
(parameterize ([current-inspector (dynamic-require ''drscheme-secrets 'drscheme-inspector)])
(unless (struct? a-word)
(error 'hangman "expected a struct, got: ~e" a-word))
(let ([word-vec (struct->vector a-word)])
(unless (= (vector-length word-vec) 4)
(error 'hangman "expected words to be structures with three fields, found ~a fields"
(- (vector-length word-vec) 1)))
(format "~a~a~a"
(vector-ref word-vec 1)
(vector-ref word-vec 2)
(vector-ref word-vec 3))))))
(initialize rv dr status))
;; word2 = (listof letter)
;; hangman-list : (word2 word2 letter -> word2) (symbol -> #t) -> void
;; effects: set up game status, draw noose, show frame
(define (hangman-list/proc rv dr)
(check-proc 'hangman-list rv 3 '1st "3 arguments")
(check-proc 'hangman-list dr 1 '2nd "1 argument")
(set! chosen (list-ref WORDS2 (random (length WORDS2))))
(set! status (build-list (length chosen) (lambda (x) '_)))
;; make uncover work for lists
(set! uncover
(lambda (word)
(apply string-append (map (lambda (x) (format "~a" x)) word))))
(initialize (check-fun-res rv list? "list of symbols")
(check-fun-res dr boolean? "boolean")
status))
;; initialize : as it says ...
;; the gui, the guess, the picture, the reveal function the draw-next function
(define (initialize rv dr status)
(init-guesses)
(set! reveal rv)
(set! draw-next-part dr)
(setup-gui status)
(draw-next-part (select-piece!)))