;;; File:
;;;   dicegame.ss
;;; Version:
;;;   1.1 of November 2002
;;; 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 "webutils.ss")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Utilities

;;; Procedure:
;;;   hidden
;;; Parameters:
;;;   name, a string
;;;   value, a string
;;; Purpose:
;;;   Builds a hidden field for an HTML input form.
;;; Produces:
;;;   hidden-code, a string
;;; Preconditions:
;;;   name consists of only letters and numbers.
;;;   value does not have any quotation marks.
;;; Postconditions:
;;;   hidden-code is valid HTML for a field with name name
;;;     and value value.
(define hidden
  (lambda (name value)
    (string-append 
      "<input type=\"hidden\" name=\""
      name
      "\" value=\""
      value
      "\">")))

;; 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-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 pair-a-dice
  (lambda ()
    (+ (roll-a-die) (roll-a-die))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Page Generators

;;; 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
        (string-append
          (heading 1 "Welcome to the Dice Game")
          (string #\newline)
          (paragraph "You start with $50.  We repeatedly roll dice.  If we roll an odd number, you win $1.  If we roll an even number, I win $1.") 
          (string #\newline)
          (paragraph "Click below to play!")
          (string #\newline)
          (form "dicegame.cgi" "Roll!" (hidden "account" "100")))))))

;;; 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)
    (game-page-helper 
      account 
      (pair-a-dice))))

  (define game-page-helper
    (lambda (account diceroll)
      (let ((new-account (+ account (if (odd? diceroll) 1 -1))))
        (make-page 
          (head "A Dice Game")
          (body 
            (string-append
              (heading 1 (if (odd? diceroll) "You Win!" "I Win!"))
              (string #\newline)
              (paragraph 
                (string-append "The roll was " (number->string diceroll)))
              (string #\newline)
              (paragraph 
                (string-append "You now have $" 
                  (number->string new-account)))
              (string #\newline)
              (form "dicegame.cgi" "Roll!"
                (hidden "account"
                  (number->string new-account)))
            ) ; string-append
          ) ; body
        ) ; make-page
      ) ; let
    ) ; lambda
  ) ; define game-page-helper

;;; 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 "You Lose!")
      (body 
        (string-append
          (heading 1 "You Lose!")
          (string #\newline)
          (paragraph "You can't roll again; you ran out of money!")
          (string #\newline)
          (form "dicegame.cgi" "Start Again" ""))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Interface

;;; Name:
;;;   account
;;; Type:
;;;   integer
;;; Value:
;;;   The amount of money the user has.
(define account
  (string->number (get-cgi-param 'account "-1")))

;;; 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 ()
    (cond
      ((= account -1) (start-page))
      ((= account 0) (end-page))
      (else (game-page account)))))
