diff options
| -rw-r--r-- | net/ricketyspace/ror/eight/graphics/README.txt | 3 | ||||
| -rw-r--r-- | net/ricketyspace/ror/eight/graphics/brigand.bmp | bin | 0 -> 6960 bytes | |||
| -rw-r--r-- | net/ricketyspace/ror/eight/graphics/hydra.png | bin | 0 -> 11948 bytes | |||
| -rw-r--r-- | net/ricketyspace/ror/eight/graphics/orc.gif | bin | 0 -> 1980 bytes | |||
| -rw-r--r-- | net/ricketyspace/ror/eight/graphics/orc.png | bin | 0 -> 10504 bytes | |||
| -rw-r--r-- | net/ricketyspace/ror/eight/graphics/orcSprite.png | bin | 0 -> 7949 bytes | |||
| -rw-r--r-- | net/ricketyspace/ror/eight/graphics/player.bmp | bin | 0 -> 92856 bytes | |||
| -rw-r--r-- | net/ricketyspace/ror/eight/graphics/slime.bmp | bin | 0 -> 21056 bytes | |||
| -rw-r--r-- | net/ricketyspace/ror/eight/orc.rkt | 889 | 
9 files changed, 892 insertions, 0 deletions
diff --git a/net/ricketyspace/ror/eight/graphics/README.txt b/net/ricketyspace/ror/eight/graphics/README.txt new file mode 100644 index 0000000..5895c77 --- /dev/null +++ b/net/ricketyspace/ror/eight/graphics/README.txt @@ -0,0 +1,3 @@ +Graphics from github.com/racket/realm + +Commit: 973041cb6a5c696b99b79a diff --git a/net/ricketyspace/ror/eight/graphics/brigand.bmp b/net/ricketyspace/ror/eight/graphics/brigand.bmp Binary files differnew file mode 100644 index 0000000..7bbd3c0 --- /dev/null +++ b/net/ricketyspace/ror/eight/graphics/brigand.bmp diff --git a/net/ricketyspace/ror/eight/graphics/hydra.png b/net/ricketyspace/ror/eight/graphics/hydra.png Binary files differnew file mode 100644 index 0000000..b495920 --- /dev/null +++ b/net/ricketyspace/ror/eight/graphics/hydra.png diff --git a/net/ricketyspace/ror/eight/graphics/orc.gif b/net/ricketyspace/ror/eight/graphics/orc.gif Binary files differnew file mode 100644 index 0000000..5553322 --- /dev/null +++ b/net/ricketyspace/ror/eight/graphics/orc.gif diff --git a/net/ricketyspace/ror/eight/graphics/orc.png b/net/ricketyspace/ror/eight/graphics/orc.png Binary files differnew file mode 100644 index 0000000..d269b6a --- /dev/null +++ b/net/ricketyspace/ror/eight/graphics/orc.png diff --git a/net/ricketyspace/ror/eight/graphics/orcSprite.png b/net/ricketyspace/ror/eight/graphics/orcSprite.png Binary files differnew file mode 100644 index 0000000..cdc223c --- /dev/null +++ b/net/ricketyspace/ror/eight/graphics/orcSprite.png diff --git a/net/ricketyspace/ror/eight/graphics/player.bmp b/net/ricketyspace/ror/eight/graphics/player.bmp Binary files differnew file mode 100644 index 0000000..c6a04a0 --- /dev/null +++ b/net/ricketyspace/ror/eight/graphics/player.bmp diff --git a/net/ricketyspace/ror/eight/graphics/slime.bmp b/net/ricketyspace/ror/eight/graphics/slime.bmp Binary files differnew file mode 100644 index 0000000..208c188 --- /dev/null +++ b/net/ricketyspace/ror/eight/graphics/slime.bmp diff --git a/net/ricketyspace/ror/eight/orc.rkt b/net/ricketyspace/ror/eight/orc.rkt new file mode 100644 index 0000000..5a7aaad --- /dev/null +++ b/net/ricketyspace/ror/eight/orc.rkt @@ -0,0 +1,889 @@ +#lang racket + +#| + +   From github.com/racket/realm + +   Commit: 973041cb6a5c696b99b79a  +|# + +#|  +   The Orc game  +   ------------- + +   The Orc game is a turn-based battle game between monsters and the player.  + +   The player encounters a room full of monsters of all kinds, including  +   orcs, hydras, slimes, and brigands. They are ready to attack. It is +   the player's task to get rid of the monsters.  + +   When the game starts up, it is the player's turn, meaning she is given  +   permission to attack a (randomly chosen number) of times. The player uses +   nine keys to play +    -- With the four arrow keys the player navigates among the twelve monsters. +    -- With "s", "f", and "h",  +    -- the player can 's'tab a specific monster,  +    -- the player may 'f'lail at several monsters;  +    -- the player may 'h'eal herself.  +   When the player runs out of attacks, all live monsters attack the player.  +   After that, it is the player's turn again.  +  +   Just in case, the player can end a turn prematurely with "e".  + +   Play +   ---- +  +   Run and evaluate  +     (start-game) +   This will pop up a window that displays the player's vitals, the orcs and +   their basic state, and the game instructions.  +|# + + +(require 2htdp/image 2htdp/universe) + +;                                                                           +;                                                                           +;                                                                           +;     ;;;                           ;;; ;;;                   ;;        ;;  +;    ;   ;                           ;   ;                     ;         ;  +;   ;     ; ;; ;;;   ;;; ;           ;   ;   ;;;;   ;; ;;;     ;     ;;; ;  +;   ;     ;  ;;     ;;  ;;           ; ; ;  ;    ;   ;;        ;    ;   ;;  +;   ;     ;  ;      ;                ; ; ;  ;    ;   ;         ;    ;    ;  +;   ;     ;  ;      ;                ; ; ;  ;    ;   ;         ;    ;    ;  +;    ;   ;   ;      ;    ;           ; ; ;  ;    ;   ;         ;    ;   ;;  +;     ;;;   ;;;;;    ;;;;             ; ;    ;;;;   ;;;;;    ;;;;;   ;;; ;; +;                                                                           +;                                                                           +;                                                                           +;                                                                           + +;; The OrcWorld as Data: +(struct orc-world (player lom attack# target) #:transparent #:mutable) +;; A OrcWorld is a (orc-world Player [listof Monster] Nat Nat) +;; The third field of the world refers to the number of attacks left. +;; The fourth field refers to the position of the next attack target. + +(struct player (health agility strength) #:transparent #:mutable) +;; A Player is a (player Nat Nat Nat) +;; The player's fields correspond to hit points, strength, agility.  + +(struct monster (image [health #:mutable]) #:transparent) +(struct orc monster (club) #:transparent) +(struct hydra monster () #:transparent) +(struct slime monster (sliminess) #:transparent) +(struct brigand monster () #:transparent) +;; A Monster is a (monster Image Nat) +;;    (moster i h) is a monster at position i in the list with health h +;; Each monster is equipped with the index number,  +;; which is used to identify the current target. +;;  +;; An Orc is an (orc Nat Nat Nat) +;; A Slime is a (slime Nat Nat Nat) +;; A Brigrand is a (brigand Nat Nat) +;; A Hydra is a (hydra Nat Nat)     +;;  +;; The four monster types all inherit the id and health fields from monster.  +;; Two have additional attributes:  +;; -- (orc i h c) means the orc's club has strength c +;; -- (slime i h a) means the slime can reduce the player's agility by a  + +;; ----------------------------------------------------------------------------- +;; THE CONSTANTS IN THE WORLD  + +;; player attributes  +(define MAX-HEALTH 35) +(define MAX-AGILITY 35) +(define MAX-STRENGTH 35) + +;; depending on other player attributes,  +;; the game picks the number of attacks, flailing and stabbing damage  +(define ATTACKS# 4) +(define STAB-DAMAGE 2) +(define FLAIL-DAMAGE 3) +(define HEALING 8) + +;; monster attributes  +(define MONSTER# 12) +(define PER-ROW 4) +(unless (zero? (remainder MONSTER# PER-ROW)) +  (error 'constraint "PER-ROW must divide MONSTER# evenly into rows")) + +(define MONSTER-HEALTH0 9) +(define CLUB-STRENGTH 8) +(define SLIMINESS 5) + +(define HEALTH-DAMAGE -2) +(define AGILITY-DAMAGE -3) +(define STRENGTH-DAMAGE -4) + +;; string constants  +(define STRENGTH "strength") +(define AGILITY "agility") +(define HEALTH "health") +(define LOSE  "YOU LOSE") +(define WIN "YOU WIN") +(define DEAD "DEAD") +(define REMAINING "Remaining attacks ") +(define INSTRUCTIONS-2 "Select a monster using the arrow keys") +(define INSTRUCTIONS-1 +  "Press S to stab a monster | Press F to Flail wildly | Press H to Heal") + +;; graphical constants  +(define HEALTH-BAR-HEIGHT 12) +(define HEALTH-BAR-WIDTH  50) + +;; compute constants for image frames  +(define ORC     (bitmap "graphics/orc.png")) +(define HYDRA   (bitmap "graphics/hydra.png")) +(define SLIME   (bitmap "graphics/slime.bmp")) +(define BRIGAND (bitmap "graphics/brigand.bmp")) + +(define PIC-LIST (list ORC HYDRA SLIME BRIGAND)) +(define w (apply max (map image-width PIC-LIST))) +(define h (apply max (map image-height PIC-LIST))) + +;; images: player, monsters, constant texts +(define PLAYER-IMAGE  (bitmap "graphics/player.bmp")) + +(define FRAME (rectangle w h 'outline 'white)) +(define TARGET (circle (- (/ w 2) 2) 'outline 'blue)) + +(define ORC-IMAGE     (overlay ORC FRAME)) +(define HYDRA-IMAGE   (overlay HYDRA FRAME)) +(define SLIME-IMAGE   (overlay SLIME FRAME)) +(define BRIGAND-IMAGE (overlay BRIGAND FRAME)) + +(define V-SPACER (rectangle 0 10 "solid" "white")) +(define H-SPACER (rectangle 10 0 "solid" "white")) + +;; fonts & texts & colors +(define AGILITY-COLOR "blue") +(define HEALTH-COLOR "crimson") +(define STRENGTH-COLOR "forest green")  +(define MONSTER-COLOR "crimson") +(define MESSAGE-COLOR "black") +(define ATTACK-COLOR "crimson") + +(define HEALTH-SIZE (- HEALTH-BAR-HEIGHT 4)) +(define DEAD-TEXT-SIZE (- HEALTH-BAR-HEIGHT 2)) +(define INSTRUCTION-TEXT-SIZE 16) +(define MESSAGES-SIZE 40) + +(define INSTRUCTION-TEXT +  (above  +   (text INSTRUCTIONS-2 (- INSTRUCTION-TEXT-SIZE 2) "blue") +   (text INSTRUCTIONS-1 (- INSTRUCTION-TEXT-SIZE 4) "blue"))) + +(define DEAD-TEXT (text DEAD DEAD-TEXT-SIZE "crimson")) + +;                                   +;                                   +;                                   +;   ;;; ;;;            ;            +;    ;; ;;                          +;    ;; ;;   ;;;;    ;;;    ;; ;;   +;    ; ; ;  ;    ;     ;     ;;  ;  +;    ; ; ;   ;;;;;     ;     ;   ;  +;    ;   ;  ;    ;     ;     ;   ;  +;    ;   ;  ;   ;;     ;     ;   ;  +;   ;;; ;;;  ;;; ;;  ;;;;;  ;;; ;;; +;                                   +;                                   +;                                   +;                                   + +;; Start the game +(define (start-game)  +  (big-bang (initialize-orc-world) +            (on-key player-acts-on-monsters)  +            (to-draw render-orc-battle) +            (stop-when end-of-orc-battle? render-the-end))) + +;; -> OrcWorld   +;; creates an orc-world ready for battling orcs  +(define (initialize-orc-world) +  (define player0 (initialize-player)) +  (define lom0 (initialize-monsters)) +  (orc-world player0 lom0 (random-number-of-attacks player0) 0)) + +;; OrcWorld Key-Event -> OrcWorld +;; act on key events by the player, if the player has attacks left +(define (player-acts-on-monsters w k) +  (cond  +    [(zero? (orc-world-attack# w)) w] +     +    [(key=? "s" k) (stab w)] +    [(key=? "h" k) (heal w)] +    [(key=? "f" k) (flail w)] +     +    [(key=? "right" k) (move-target w +1)] +    [(key=? "left" k)  (move-target w -1)] +    [(key=? "down" k)  (move-target w (+ PER-ROW))] +    [(key=? "up" k)    (move-target w (- PER-ROW))] +     +    [(key=? "e" k) (end-turn w)] +;;    [(key=? "n" k) (initialize-orc-world)] +     +    [else w]) +  (give-monster-turn-if-attack#=0 w) +  w) + +;; OrcWorld -> Image  +;; renders the orc world +(define (render-orc-battle w) +  (render-orc-world w (orc-world-target w) (instructions w))) + +;; OrcWorld -> Boolean +;; is the battle over? i.e., the player lost or all monsters are dead  +(define (end-of-orc-battle? w) +  (or (win? w) (lose? w))) + +;; OrcWorld -> Image  +;; render the final orc world  +(define (render-the-end w) +  (render-orc-world w #f (message (if (lose? w) LOSE WIN)))) + +;; ----------------------------------------------------------------------------- + +;; WORLD MANAGEMENT +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;                                   +;                                   +;                                   +;    ;;;;;             ;            +;      ;                     ;      +;      ;    ;; ;;    ;;;    ;;;;;   +;      ;     ;;  ;     ;     ;      +;      ;     ;   ;     ;     ;      +;      ;     ;   ;     ;     ;      +;      ;     ;   ;     ;     ;   ;  +;    ;;;;;  ;;; ;;;  ;;;;;    ;;;   +;                                   +;                                   +;                                   +;                                   + +;; -> Player  +;; create a player with maximal capabilities  +(define (initialize-player)  +  (player MAX-HEALTH MAX-AGILITY MAX-STRENGTH)) + +;; -> [Listof Monster] +;; create a list of random monsters of length MONSTER-NUM,  +(define (initialize-monsters) +  ;; Nat -> Monster +  ;; makes a random monster  +  (define (create-monster _) +    (define health (random+ MONSTER-HEALTH0)) +    (case (random 4) +      [(0) (orc ORC-IMAGE health (random+ CLUB-STRENGTH))] +      [(1) (hydra HYDRA-IMAGE health)] +      [(2) (slime SLIME-IMAGE health (random+ SLIMINESS))] +      [(3) (brigand BRIGAND-IMAGE health)] +      [else (error "can't happen")])) +  (build-list MONSTER# create-monster)) + +;; Player -> Nat +;; compute a feasible number of attacks the player may execute  +(define (random-number-of-attacks p) +  (random-quotient (player-agility p) +                   ATTACKS#)) + +;                                                                           +;                                                                           +;                                                                           +;   ;;; ;;;                 ;;;;;;                                          +;    ;   ;                   ;   ;                           ;              +;    ;  ;    ;;;;   ;;; ;;;  ; ;   ;;;  ;;;  ;;;;   ;; ;;   ;;;;;    ;;;;;  +;    ; ;    ;    ;   ;   ;   ;;;    ;    ;  ;    ;   ;;  ;   ;      ;    ;  +;    ;;;    ;;;;;;   ;   ;   ; ;     ;  ;   ;;;;;;   ;   ;   ;       ;;;;   +;    ;  ;   ;         ; ;    ;       ;  ;   ;        ;   ;   ;           ;  +;    ;   ;  ;         ; ;    ;   ;    ;;    ;        ;   ;   ;   ;  ;    ;  +;   ;;;  ;;  ;;;;;     ;    ;;;;;;    ;;     ;;;;;  ;;; ;;;   ;;;   ;;;;;   +;                      ;                                                    +;                    ;;;                                                    +;                                                                           +;                                                                           + +;; ----------------------------------------------------------------------------- +;; player actions  + +;; OrcWorld Nat -> Void +;; Effect: reduces the target by a given amount +;; > (move-target  +;;     (orc-world (player 5 5 5) (list (monster 0 2) (monster 1 3)) 1 0) +;;     1) +;; (orc-world (player 5 5 5) (list (monster 0 2) (monster 1 3)) 1 1) +(define (move-target w n)  +  (set-orc-world-target! w (modulo (+ n (orc-world-target w)) MONSTER#))) + +;; OrcWorld -> Void  +;; Effect: ends the player's turn by setting the number of attacks to 0 +(define (end-turn w) +  (set-orc-world-attack#! w 0)) + +;; OrcWorld -> Void  +;; Effect: reduces the number of remaining attacks for this turn  +;;   and increases the player's health level  +(define (heal w) +  (decrease-attack# w) +  (player-health+ (orc-world-player w) HEALING)) + +;; OrcWorld -> Void  +;; Effect: reduces a targeted monster's health +(define (stab w) +  (decrease-attack# w) +  (define target (current-target w)) +  (define damage  +    (random-quotient (player-strength (orc-world-player w))  +                     STAB-DAMAGE)) +  (damage-monster target damage)) + +;; OrcWorld -> Void +;; Effect: damages a random number of live monsters,  +;;   determined by strength of the player +;;   starting with the currently targeted monster  +(define (flail w) +  (decrease-attack# w) +  (define target (current-target w)) +  (define alive (filter monster-alive? (orc-world-lom w))) +  (define pick#  +    (min +     (random-quotient (player-strength (orc-world-player w))  +                      FLAIL-DAMAGE) +     (length alive))) +  (define getem (cons target (take alive pick#))) +  (for-each (lambda (m) (damage-monster m 1)) getem)) + +;; OrcWorld -> Void +;; Effect: decrease number of remaining attacks  +(define (decrease-attack# w) +  (set-orc-world-attack#! w (sub1 (orc-world-attack# w)))) + +;; Monster Nat -> Void +;; Effect: reduces the hit-strength of a monster +(define (damage-monster m delta) +  (set-monster-health! m (interval- (monster-health m) delta))) + +;; World -> Monster  +(define (current-target w) +  (list-ref (orc-world-lom w) (orc-world-target w))) + +;; ----------------------------------------------------------------------------- +;; monster action + +;; OrcWorld -> Void +;; if it is the monsters turn, they attack +;; > (orc-world (player 4 4 4) empty 3 3) +;; (orc-world (player 4 4 4) empty 3 3) +(define (give-monster-turn-if-attack#=0 w) +  (when (zero? (orc-world-attack# w)) +    (define player (orc-world-player w)) +    (all-monsters-attack-player player (orc-world-lom w)) +    (set-orc-world-attack#! w (random-number-of-attacks player)))) + +;; Player [Listof Monster] -> Void  +;; Each monster attacks the player +(define (all-monsters-attack-player player lom)  +  ;; Monster -> Void +  (define (one-monster-attacks-player monster) +    (cond +      [(orc? monster) +       (player-health+ player (random- (orc-club monster)))] +      [(hydra? monster) +       (player-health+ player (random- (monster-health monster)))] +      [(slime? monster)  +       (player-health+ player -1) +       (player-agility+ player (random- (slime-sliminess monster)))] +      [(brigand? monster)  +       (case (random 3) +         [(0) (player-health+ player HEALTH-DAMAGE)] +         [(1) (player-agility+ player AGILITY-DAMAGE)] +         [(2) (player-strength+ player STRENGTH-DAMAGE)])])) +  ;; -- IN --  +  (for-each one-monster-attacks-player (filter monster-alive? lom))) + +;; ----------------------------------------------------------------------------- +;; actions on player  + +;; [Player -> Nat] [Player Nat -> Void] Nat -> Player Nat -> Void  +;; effect: change player's selector attribute by adding delta, but max out +(define (player-update! setter selector max-value) +  (lambda (player delta) +    (setter player  +            (interval+ (selector player) delta max-value)))) + +;; Player Nat -> Void +(define player-health+  +  (player-update! set-player-health! player-health MAX-HEALTH)) + +;; Player Nat -> Void  +(define player-agility+  +  (player-update! set-player-agility! player-agility MAX-AGILITY)) + +;; Player Nat -> Void  +(define player-strength+  +  (player-update! set-player-strength! player-strength MAX-STRENGTH)) + +;                                                                           +;                                                                           +;                                                                           +;   ;;;;;                       ;;                     ;                    +;    ;   ;                       ;                                          +;    ;   ;   ;;;;   ;; ;;    ;;; ;   ;;;;   ;; ;;;   ;;;    ;; ;;    ;;; ;; +;    ;   ;  ;    ;   ;;  ;  ;   ;;  ;    ;   ;;        ;     ;;  ;  ;   ;;  +;    ;;;;   ;;;;;;   ;   ;  ;    ;  ;;;;;;   ;         ;     ;   ;  ;    ;  +;    ;  ;   ;        ;   ;  ;    ;  ;        ;         ;     ;   ;  ;    ;  +;    ;   ;  ;        ;   ;  ;   ;;  ;        ;         ;     ;   ;  ;   ;;  +;   ;;;   ;  ;;;;;  ;;; ;;;  ;;; ;;  ;;;;;  ;;;;;    ;;;;;  ;;; ;;;  ;;; ;  +;                                                                        ;  +;                                                                    ;;;;   +;                                                                           +;                                                                           + +;; OrcWorld Boolean Image -> Image +;; draws all the monsters and the player, then adds message  +(define (render-orc-world w with-target additional-text) +  (define i-player  (render-player (orc-world-player w))) +  (define i-monster (render-monsters (orc-world-lom w) with-target)) +  (above V-SPACER +         (beside H-SPACER +                 i-player +                 H-SPACER H-SPACER H-SPACER  +                 (above i-monster +                        V-SPACER V-SPACER V-SPACER  +                        additional-text) +                 H-SPACER) +         V-SPACER)) + +;; Player -> Image +;; render player with three status bars +(define (render-player p) +  (above/align  +   "left" +   (status-bar (player-strength p) MAX-STRENGTH STRENGTH-COLOR STRENGTH) +   V-SPACER +   (status-bar (player-agility p) MAX-AGILITY AGILITY-COLOR AGILITY) +   V-SPACER +   (status-bar (player-health p) MAX-HEALTH HEALTH-COLOR HEALTH) +   V-SPACER V-SPACER V-SPACER +   PLAYER-IMAGE)) + +;; Nat Nat Color String -> Image +;; creates a labeled rectangle of width/max proportions +;; assume: (<= width max) +(define (status-bar v-current v-max color label) +  (define w (* (/ v-current v-max) HEALTH-BAR-WIDTH)) +  (define f (rectangle w HEALTH-BAR-HEIGHT 'solid color)) +  (define b (rectangle HEALTH-BAR-WIDTH HEALTH-BAR-HEIGHT 'outline color)) +  (define bar (overlay/align 'left 'top f b)) +  (beside bar H-SPACER (text label HEALTH-SIZE color))) + +;; String -> Image  +(define (message str) +  (text str MESSAGES-SIZE MESSAGE-COLOR)) + +;; OrcWorld -> Image +(define (instructions w) +  (define na (number->string (orc-world-attack# w))) +  (define ra (string-append REMAINING na)) +  (above (text ra INSTRUCTION-TEXT-SIZE ATTACK-COLOR) INSTRUCTION-TEXT)) + +;; [Listof Monster] [Opt Nat] -> Image +;; add all monsters on lom, including status bar +;; label the target unless it isn't called for  +(define (render-monsters lom with-target) +  ;; the currently targeted monster (if needed)  +  (define target +    (if (number? with-target)  +        (list-ref lom with-target) +        'a-silly-symbol-that-cannot-be-eq-to-an-orc)) +   +  ;; Monster -> Image  +  (define (render-one-monster m) +    (define image +      (if (eq? m target) +          (overlay TARGET (monster-image m)) +          (monster-image m))) +    (define health (monster-health m)) +    (define health-bar +      (if (= health 0) +          (overlay DEAD-TEXT (status-bar 0 1 'white "")) +          (status-bar health MONSTER-HEALTH0 MONSTER-COLOR ""))) +    (above health-bar image)) +   +  (arrange (map render-one-monster lom))) + +;; [Listof Image] -> Image  +;; break a list of images into rows of PER-ROW  +(define (arrange lom) +  (cond +    [(empty? lom) empty-image] +    [else (define row-image (apply beside (take lom PER-ROW))) +          (above row-image (arrange (drop lom PER-ROW)))])) + + +;                                   +;                                   +;                                   +;   ;;;;;;              ;;    ;;;   +;    ;   ;               ;   ;   ;  +;    ; ;    ;; ;;    ;;; ;       ;  +;    ;;;     ;;  ;  ;   ;;       ;  +;    ; ;     ;   ;  ;    ;      ;   +;    ;       ;   ;  ;    ;     ;    +;    ;   ;   ;   ;  ;   ;;          +;   ;;;;;;  ;;; ;;;  ;;; ;;   ;;    +;                                   +;                                   +;                                   +;                                   + +;; OrcWorld -> Boolean +;; Has the player won? +;; > (orc-world (player 1 1 1) (list (monster 0 0)) 0 0) +;; #t +(define (win? w) +  (all-dead? (orc-world-lom w))) + +;; OrcWorld -> Boolean +;; Has the player lost? +;; > (lose? (orc-world (player 0 2 2) empty 0 0)) +;; #t +(define (lose? w)  +  (player-dead? (orc-world-player w))) + +;; Player -> Boolean +;; Is the player dead? +;; > (orc-world (player 1 0 1) empty 0 0) +;; #t +(define (player-dead? p) +  (or (= (player-health p) 0)  +      (= (player-agility p) 0) +      (= (player-strength p) 0))) + +;; [Listof Monster] -> Boolean +;; Are all the monsters in the list dead?s +;; > (all-dead? (orc-world (player 5 5 5) (list (monster 1 0)) 0 1)) +;; #t +(define (all-dead? lom) +  (not (ormap monster-alive? lom))) + +;; Monster -> Boolean +;; Is the monster alive? +(define (monster-alive? m) +  (> (monster-health m) 0)) + + +;                                   +;                                   +;                                   +;     ;;                            +;      ;                            +;     ; ;   ;;  ;;  ;;  ;;   ;;;;;  +;     ; ;    ;   ;   ;  ;   ;    ;  +;     ; ;    ;   ;    ;;     ;;;;   +;     ;;;    ;   ;    ;;         ;  +;    ;   ;   ;  ;;   ;  ;   ;    ;  +;   ;;; ;;;   ;; ;; ;;  ;;  ;;;;;   +;                                   +;                                   +;                                   +;                                   + +;; Nat Nat -> Nat +;; a random number between 1 and the (quotient x y) +(define (random-quotient x y) +  (define div (quotient x y)) +  (if (> 0 div) 0 (random+ (add1 div)))) + +;; Nat -> Nat  +;; (random+ n) creates a random number in [1,n] +(define (random+ n) +  (add1 (random n))) + +;; Nat -> Nat  +;; (random+ n) creates a random number in [-n,-1] +(define (random- n) +  (- (add1 (random n)))) + +;; Nat Nat [Nat] -> Nat  +;; subtract n from m but stay in [0,max-value] +(define (interval- n m (max-value 100)) +  (min (max 0 (- n m)) max-value)) + +;; Nat Nat [Nat] -> Nat  +;; subtract n from m but stay in [0,max-value] +(define (interval+ n m (max-value 100)) +  (interval- n (- m) max-value)) + +;                                           +;                                           +;                                           +;   ;;;;;;                                  +;   ;  ;                     ;              +;      ;     ;;;;    ;;;;;  ;;;;;    ;;;;;  +;      ;    ;    ;  ;    ;   ;      ;    ;  +;      ;    ;;;;;;   ;;;;    ;       ;;;;   +;      ;    ;            ;   ;           ;  +;      ;    ;       ;    ;   ;   ;  ;    ;  +;     ;;;    ;;;;;  ;;;;;     ;;;   ;;;;;   +;                                           +;                                           +;                                           +;                                           + +(module+ test +   +  (require rackunit rackunit/text-ui) +   +  ;; Test structs +  (define WORLD0 (orc-world (initialize-player) empty 0 0)) +  (define WORLD1 (struct-copy orc-world (initialize-orc-world) [attack# 5])) +  (define (WORLD2) (struct-copy orc-world (initialize-orc-world) [attack# 0])) +  ;; these are random worlds +  (define AN-ORC (orc 'image 0 5)) +  (define A-SLIME (slime 'image 1 6)) +  (define A-HYDRA (hydra 'image 2)) +  (define A-BRIGAND (brigand 'image 3)) +   +  ;; testing move-target  +   +  (check-equal? (let ([w (orc-world 'dummy 'dummy 'dummy 0)]) +                  (move-target w +1) +                  w) +                (orc-world 'dummy 'dummy 'dummy 1)) +  (check-equal? (let ([w (orc-world 'dummy 'dummy 'dummy 0)]) +                  (move-target w -1) +                  w) +                (orc-world 'dummy 'dummy 'dummy (- MONSTER# 1))) +  (check-equal? (let ([w (orc-world 'dummy 'dummy 'dummy 0)]) +                  (move-target w (- PER-ROW)) +                  w) +                (orc-world 'dummy 'dummy 'dummy (- MONSTER# PER-ROW))) +  (check-equal? (let ([w (orc-world 'dummy 'dummy 'dummy 1)]) +                  (move-target w (+ PER-ROW)) +                  w) +                (orc-world 'dummy 'dummy 'dummy (+ PER-ROW 1))) +  (check-equal? (begin +                  (move-target WORLD1 0) +                  WORLD1) +                WORLD1) +  (check-equal? (let () +                  (define w (struct-copy orc-world WORLD1)) +                  (move-target w 4) +                  w) +                (struct-copy orc-world WORLD1 [target (+ 4 (orc-world-target WORLD1))])) +  (check-equal? (current-target WORLD1)  +                (first (orc-world-lom WORLD1))) +   +  ;; testing basic player manipulations +   +  (check-equal? (let ([p (player 1 0 0)]) +                  (player-health+ p 5) +                  p) +                (player 6 0 0)) +  (check-equal? (let ([p (player 0 1 0)]) +                  (player-agility+ p 5) +                  p) +                (player 0 6 0)) +   +  (check-equal? (let ([p (player 0 0 1)]) +                  (player-strength+ p 5) +                  p) +                (player 0 0 6)) +   +  (check-equal? (let ([p (player 5 5 5)]) +                  (all-monsters-attack-player p (list (orc 'image 1 1))) +                  p) +                (player 4 5 5)) +   +  (check-equal? (let ([p (player 5 5 5)]) +                  (all-monsters-attack-player p (list (hydra 'image 1))) +                  p) +                (player 4 5 5)) +   +  (check-equal? (let ([p (player 5 5 5)]) +                  (all-monsters-attack-player p (list (slime 'image 1 1))) +                  p) +                (player 4 4 5)) +   +  (check member  +         (let ([p (player 5 5 5)])  +           (all-monsters-attack-player p (list (brigand 'image 1))) +           p) +         (list (player 3 5 5) +               (player 5 2 5) +               (player 5 5 1))) +   +  ;; Properties +  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +   +  ;; Property: +  ;; the output will always be in [1, (/ X Y)] +  (define (prop:rand-frac-range i) +    (test-begin  +     (for ([i (in-range i)]) +       (define x (random 4294967087)) +       (define y (random 4294967087)) +       (check-true (<= 1 (random-quotient x y) (add1 (/ x y))))))) +   +  ;; Property:  +  ;; The number of the monsters in the list is equal to +  ;; MONSTER-NUM +  (define (prop:monster-init-length i) +    (test-begin +     (for ([i (in-range i)]) +       (check-true (= MONSTER# +                      (length (initialize-monsters))))))) +   +  ;; Property:  +  ;; the player will have less points in at least one of its +  ;; fields +  (define (prop:monster-attack-player-dec i) +    (test-begin +     (for ([i (in-range i)]) +       (define pl (player MAX-HEALTH MAX-AGILITY MAX-STRENGTH)) +       (define mon (first (initialize-monsters))) +       (begin  +         (all-monsters-attack-player pl (list mon)) +         (check-true (or (< (player-health pl) MAX-HEALTH) +                         (< (player-agility pl) MAX-AGILITY) +                         (< (player-strength pl) MAX-STRENGTH))))))) +   +  ;; Property:  +  ;; If there are monster, then the player will  +  ;; have less points in at least one of its fields +  (define (prop:monsters-attack-player-dec i) +    (test-begin  +     (for ([i (in-range i)]) +       (define pl (player MAX-HEALTH MAX-AGILITY MAX-STRENGTH)) +       (define monsters (initialize-monsters)) +       (define wor (orc-world pl monsters 0 0)) +       (begin  +         (all-monsters-attack-player pl monsters) +         (check-true (or (< (player-health pl) MAX-HEALTH) +                         (< (player-agility pl) MAX-AGILITY) +                         (< (player-strength pl) MAX-STRENGTH))))))) +   +  ;; Property: The health of the targeted monster, m,  +  ;; is less than what it was. and +  ;; [(sub1 (monster-health m)),  +  ;; (- (monster-health m)  +  ;;    (/ (player-strength (orc-world-player w)) 2))] +  (define (prop:stab!-health i) +    (test-begin +     (for ([i (in-range i)]) +       (begin (define mon (first(initialize-monsters))) +              (define ht (monster-health mon)) +              (define pl (random-player)) +              (define w (orc-world pl (list mon) 2 0)) +              (stab w) +              (check-true (> ht (monster-health (first (orc-world-lom w))))))))) +   +  ;; random-player: -> Player +  ;; creates a random player +  (define (random-player) +    (player (add1 (random MAX-HEALTH)) +            (add1 (random MAX-AGILITY)) +            (add1 (random MAX-STRENGTH)))) +   +  ;; testing initializers +  (prop:monster-init-length 1000) +  (check-true (monster? (first (initialize-monsters)))) +  (check-true (> 10 (monster-health (first (initialize-monsters))))) +  (check-equal? (length (initialize-monsters)) MONSTER#) +  (check-equal? (length (orc-world-lom WORLD1)) MONSTER#) +  (check-true (>= (let ([p (initialize-player)]) +                    (player-health p)) +                  (let ([p (initialize-player)]) +                    (all-monsters-attack-player p (list AN-ORC)) +                    (player-health p)))) +  (check-true (> (player-health (initialize-player))  +                 (let ([p (initialize-player)]) +                   (all-monsters-attack-player p (list A-HYDRA)) +                   (player-health p)))) +  (check-true (< (let ([p (initialize-player)]) +                   (all-monsters-attack-player p (list A-SLIME)) +                   (player-agility p)) +                 (let ([p (initialize-player)]) +                   (player-agility p)))) +  (check-true (let ([p (initialize-player)]) +                (all-monsters-attack-player p (list A-BRIGAND)) +                (or (= (player-health p) +                       (- (player-health (initialize-player)) 2)) +                    (= (player-agility p) +                       (- (player-agility (initialize-player)) 3)) +                    (= (player-strength p) +                       (- (player-strength (initialize-player)) 4))))) +  (check-equal? (length (orc-world-lom WORLD1)) MONSTER#) +  (check-equal? (orc-world-player WORLD1) (orc-world-player WORLD1)) +   +  ;; testing the-monster's attacks +   +  (prop:monster-attack-player-dec 1000) +  (prop:monsters-attack-player-dec 1000) +  (check-true (or (> (player-health (orc-world-player (WORLD2))) +                     (player-health (orc-world-player  +                                     (let ([w (WORLD2)]) +                                       (all-monsters-attack-player (orc-world-player w) (orc-world-lom w)) +                                       w)))) +                  (> (player-strength (orc-world-player (WORLD2))) +                     (player-strength (orc-world-player +                                       (let ([w (WORLD2)]) +                                         (all-monsters-attack-player (orc-world-player w) (orc-world-lom w)) +                                         w)))) +                  (> (player-agility (orc-world-player (WORLD2))) +                     (player-agility (orc-world-player +                                      (let ([w (WORLD2)]) +                                        (all-monsters-attack-player (orc-world-player w) (orc-world-lom w)) +                                        w)))))) +   +  ;; testing the player's actions +   +  (prop:stab!-health 1000) +  (test-begin (define o (orc 'image 0 5)) +              (damage-monster o 5) +              (check-equal? o (orc 'image 0 5))) +  (test-begin (define o (orc 'image 0 5)) +              (damage-monster o 0) +              (check-equal? o (orc 'image 0 5))) +  (check-equal? (player-health (orc-world-player +                                (let ()  +                                  (define w (struct-copy orc-world WORLD1)) +                                  (heal w) +                                  w))) +                (min MAX-HEALTH +                     (+ 8 (player-health (orc-world-player WORLD1))))) +   +  (check-equal? (length (orc-world-lom  +                         (let () +                           (define w (struct-copy orc-world WORLD1)) +                           (stab w) +                           w))) +                MONSTER#) +   +  ;; testing game predicates +   +  (check-false (lose? WORLD0)) +  (check-true (lose? (orc-world (player 0 30 30) empty 0 0))) +  (check-true (all-dead? (list (orc 'image 0 0) (hydra 'image 0)))) +  (check-true (all-dead? (list AN-ORC))) +  (check-true (win? (orc-world (initialize-player) (list (orc 'image 0 0)) 0 0))) +  (check-true (win? (orc-world (initialize-player) (list AN-ORC) 0 0))) +  (check-true (end-of-orc-battle? (orc-world (initialize-player) (list (orc 'image 0 0)) 0 0))) +  (check-true (end-of-orc-battle? (orc-world (initialize-player) (list AN-ORC) 0 0))) +  (check-true (end-of-orc-battle? (orc-world (player 0 30 30) empty 0 0))) +  (check-true (player-dead? (player 0 2 5))) +  (check-false (player-dead? (initialize-player))) +  (check-false (not (monster-alive? A-HYDRA))) +  (check-true (monster-alive? (monster 'image 1))) +  (check-false (monster-alive? (orc 'image 0 0))) +   +  ;; testing utilities  +   +  (prop:rand-frac-range 1000) +   +  "all tests run")  | 
