racket/collects/games/parcheesi/admin.ss
2008-02-24 21:27:36 +00:00

175 lines
6.1 KiB
Scheme

(module admin mzscheme
(require "board.ss"
"moves.ss"
"interfaces.ss"
mzlib/class
mzlib/list)
(provide game%
game-observer<%>)
(define all-colors '(green red blue yellow))
(define game-observer<%>
(interface ()
introduce ;; color string -> void
taking-turn ;; color dice -> void
took-turn ;; color board -> void
game-over)) ;; (union string #f) (union color #f) -> void
(define game%
(class* object% (game<%>)
(define die (new die%))
(define players '())
(define colors all-colors)
(define board (new-board))
(define observer #f)
(define/public (set-observer wf)
(unless (is-a? wf game-observer<%>)
(error 'set-observer "expected a game-observer<%> object, got ~e" wf))
(set! observer wf))
(define/public (register player)
(when (null? colors)
(error 'add-player "cannot add more than four players"))
(set! players (append players (list (new splayer% (player player) (color (car colors))))))
(set! colors (cdr colors)))
;; -> (union player #f)
;; #f indicates that everyone cheated
(define/public (start)
(unless (= 4 (length players))
(error 'start "expected 4 players to be registered, but there are ~a" (length players)))
(for-each (lambda (player) (send player start-game observer)) players)
(let loop ()
(for-each (lambda (player)
(unless (winner)
(take-player-turn player)))
players)
(unless (winner)
(loop)))
(let ([winner (winner)])
(when observer
(if (object? winner)
(send observer game-over (send winner get-name) (send winner get-color))
(send observer game-over #f #f)))
(list (if (object? winner)
(send winner get-name)
#f)
(map (lambda (x) (send x get-name))
(filter (lambda (x) (send x get-cheated?)) players)))))
(define/private (take-player-turn player)
(let doubles-loop ([count 1])
(let-values ([(doubles? roll) (send die roll board (send player get-color))])
(when observer
(send observer taking-turn (send player get-color) roll))
(cond
[(and doubles? (= count 3))
(set! board (send player doubles-penalty board))
(when observer
(send observer took-turn (send player get-color) board))]
[else
(set! board (send player do-move board roll))
(when observer
(send observer took-turn (send player get-color) board))
(when doubles?
(doubles-loop (+ count 1)))]))))
(define/private (winner)
(cond
[(ormap (lambda (player) (and (send player won? board) player))
players)
=>
(lambda (x) x)]
[(andmap (lambda (player) (send player get-cheated?))
players)
'everyone-cheated]
[else #f]))
(super-new)))
(define splayer%
(class object%
(init-field [player player]
[color color])
(define name #f)
(define cheated? #f)
(define/public (get-cheated?) cheated?)
(define/public (get-color) color)
(define/public (get-name) name)
(define/public (won? board)
(equal? 4 (length (filter (lambda (x) (eq? (pawn-color x) color)) (board-home board)))))
(define/private (cheated . args)
(display (string-append (format "~s cheated! " color)
(apply format args)
"\n"))
(set! cheated? #t))
(define/public (start-game observer)
(unless cheated?
(with-handlers ([exn? (lambda (x) (cheated "start-game error ~a" (exn-message x)))])
(let ([res-name (send player start-game color)])
(cond
[(string? res-name)
(set! name res-name)
(when observer
(send observer introduce color name))
name]
[else (cheated "expected a string for the name, got ~s" name)])))))
(define/public (do-move board dice)
(cond
[cheated? board]
[else
(with-handlers ([exn:bad-move?
(lambda (x)
(cheated "~s" (exn-message x))
(remove-player board))])
(let ([moves (with-handlers ([exn? (lambda (x) (list 'error (exn-message x)))])
(send player do-move board dice))])
(cond
[(and (list? moves) (andmap move? moves))
(take-turn color board dice moves)]
[else
(cheated "wrong result ~s" moves)
(remove-player board)])))]))
(define/private (remove-player board)
(board-doubles-penalty
(board-doubles-penalty
(board-doubles-penalty
(board-doubles-penalty board color)
color)
color)
color))
(define/public (doubles-penalty board)
(cond
[cheated? board]
[else
(with-handlers ([exn? (lambda (x)
(cheated "doubles-penalty: ~a\n" (exn-message x))
(void))])
(send player doubles-penalty))
(board-doubles-penalty board color)]))
(super-new)))
(define die%
(class object%
(define/public (roll board color)
(let* ([die1 (+ 1 (random 6))]
[die2 (+ 1 (random 6))]
[doubles? (= die1 die2)])
(if (and doubles? (board-all-in? board color))
(values doubles? (list die1 die2 (- 7 die1) (- 7 die2)))
(values doubles? (list die1 die2)))))
(super-new))))