288 lines
8.9 KiB
Racket
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!)))
|
|
|