;;; File: ;;; dicegame.ss ;;; Version: ;;; 1.2 of March 2003 ;;; Author: ;;; Samuel A. Rebelsky ;;; Contents: ;;; A CGI script that illustrates state variables (passed as hidden ;;; fields in a form) and the random procedure. ;;; Organization: ;;; Preparation - Stuff I need to do to get started. ;;; Utilities - Other stuff I need. ;;; Page Generators - Procedures for generating specific kinds of ;;; pages (when given particular values) ;;; Interface - Stuff to extract important values and then ;;; call the page generators. You'll find (page) here. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Preparation ;;; Look! We're going to use Sam's CGI library. What fun. (load "/home/rebelsky/Web/Scheme/webutils.ss") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Utilities ;; Procedure: ;;; roll-a-die ;;; Parameters: ;;; None ;;; Purpose: ;;; To simulate the rolling of one six-sided die. ;;; Produces: ;;; An integer between 1 and 6, inclusive. ;;; Preconditions: ;;; None. ;;; Postconditions: ;;; Returns an integer between 1 and 6, inclusive. ;;; It should be difficult (or impossible) to predict which ;;; number is produced. ;;; Citation: ;;; Based on a procedure written by Henry Walker or John D. Stone (define roll-a-die (lambda () (+ (random 6) ; a value in the range [0 .. 5] 1))) ; now in the range [1 .. 6] ;;; Procedure: ;;; pair-a-dice ;;; Parameters: ;;; [None] ;;; Purpose: ;;; Simulates the rolling of two dice. ;;; Produces: ;;; dice, a list of two integers. ;;; Preconditions: ;;; [None] ;;; Postconditions: ;;; Each element of dice is an integer between 1 and 6, inclusive. ;;; It is difficult to predict what values dice will contain. ;;; Across many rolls, each value between 1 and 6 will appear ;;; an approximately equal number of times in each position. (define pair-a-dice (lambda () (list (roll-a-die) (roll-a-die)))) ;;; Procedure: ;;; play-even-odds ;;; Parameters: ;;; dice, a list of two integers ;;; Purpose: ;;; Determines an outcome based on the roll of the dice. ;;; Produces: ;;; winnings, an integer ;;; Preconditions: ;;; Each integer in dice is between 1 and 6, inclusive. [Unverified] ;;; Postconditions: ;;; If the sum of the dice is even, winnings is 1. ;;; If the sum of the dice is odd, winnings is -1. (define play-evens-odds (lambda (dice) (let ((result (+ (car dice) (cadr dice)))) (if (even? result) 1 -1)))) ;;; Procedure: ;;; report-result ;;; Parameters: ;;; result, an integer ;;; Purpose: ;;; Reports on the result as a string. ;;; Produces: ;;; report, a string. ;;; Preconditions: ;;; [Standard] ;;; Postconditions: ;;; If result is positive, report is some variation of "You win!" ;;; If result is negative, report is some variation of "I won!" ;;; If result is 0, report is some variation of "We tie!" (define report-result (lambda (result) (cond ((> result 0) (sap "You win $" (number->string result) "!")) ((< result 0) (sap "Ha ha! You lose $" (number->string (- result)) "!")) (else "We tie!")))) ;;; Procedure: ;;; sum-a-dice ;;; Parameters: ;;; [None] ;;; Purpose: ;;; Simulates the rolling of two dice. ;;; Produces: ;;; dice-roll, an integer ;;; Preconditions: ;;; [None] ;;; Postconditions: ;;; dice-roll "simulates" the sum of two dice. ;;; The particular value of dice-roll is unpredictable. ;;; However, that value is between 2 and 12, inclusive. ;;; The distribution of values of dice-roll should be similar ;;; to the distribution of values of real dice. (define sum-a-dice (lambda () (+ (roll-a-die) (roll-a-die)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Page Generators (define sap string-append) ;;; Procedure ;;; start-page ;;; Parameters: ;;; [None] ;;; Purpose: ;;; Generates an HTML page that starts the game. ;;; Produces: ;;; A string that corresponds to that page. ;;; Preconditions: ;;; [None] ;;; Postconditions: ;;; The returned page is valid HTML. (define start-page (lambda () (make-page (head "A Dice Game") (body (sap (heading 1 "Welcome to the Dice Game") (string #\newline) (paragraph (sap "You start with $10. " "We repeatedly roll two dice. " "If their sum is even, you win $1. " "If their sum is odd, I win $1.")) (string #\newline) (paragraph "Click below to play!") (string #\newline) (form "dicegame.cgi" "Roll!" (paragraph (hidden "account" "10"))) valid))))) ;;; Procedure ;;; game-page ;;; Parameters: ;;; account, a number ;;; Purpose: ;;; Generates an HTML page that plays one round of the game and ;;; updates the account. ;;; Produces: ;;; A string that corresponds to that page. ;;; Preconditions: ;;; The account must be positive. ;;; Postconditions: ;;; The returned page is valid HTML. (define game-page (lambda (account) (let* ((roll (pair-a-dice)) (result (play-evens-odds roll)) (new-account (+ account result)) (summary (report-result result))) (make-page (head summary) (body (sap (heading 1 "A Dice Game") (paragraph (sap "You had $" (number->string account) ".")) (paragraph (sap "You rolled " (number->string (car roll)) " and " (number->string (cadr roll)) ".")) (paragraph summary) (paragraph (sap "You now have $" (number->string new-account) ".")) (form "dicegame.cgi" "Roll" (paragraph (hidden "account" (number->string new-account)))) valid)))))) ;;; Procedure ;;; end-page ;;; Parameters: ;;; [None] ;;; Purpose: ;;; Generates an HTML page that ends the game. ;;; Produces: ;;; A string that corresponds to that page. ;;; Preconditions: ;;; [None] ;;; Postconditions: ;;; The returned page is valid HTML. (define end-page (lambda () (make-page (head "Loser!") (body (string-append (heading 1 "You lost all of your money!") (string #\newline) (paragraph "You can't roll again; you ran out of money!") (string #\newline) (form "dicegame.cgi" "Start Again" "") valid))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Interface ;;; Procedure: ;;; page ;;; Parameters: ;;; None ;;; Purpose: ;;; Builds an HTML page according to specifications. ;;; Produces: ;;; A string that corresponds to the HTML page. ;;; Preconditions: ;;; greeting-page must be definedk, take one parameter, and generate a page. ;;; Postconditions: ;;; The returned page is as valid as anything greeting-page produces. (define page (lambda () (let ((account (string->number (get-nonempty-cgi-variable 'account "-1")))) (cond ((< account 0) (start-page)) ((= account 0) (end-page)) (else (game-page account))))))