(let ((time (gettimeofday)))
(set! *random-state*
(seed->random-state (+ (car time)
(cdr time)))))
(define print-help (lambda ()
(display "usage:\n ")
(display (car (command-line)))
(display " [options]\n")
(newline)
(display " options are:\n -h prints this help screen\n -v
prints version informations\n\n")))
(define print-version (lambda ()
(display "schemedice v. 0.0.1\n")))
(define process-command-line (lambda (args)
(if (eqv? 0 (length args)) (game))
(let ((cur-cmd (car args)))
(cond ((or (string=? cur-cmd "--help") (string=? cur-cmd "-h"))
(print-help) (exit))
((or (string=? cur-cmd "--version") (string=? cur-cmd "-v")) (print-
version) (exit))))
(process-command-line (cdr args))))
(define bet (lambda (money)
(display "Place your bet: ")
(let ((current-bet (read)))
(if (not (integer? current-bet))
(begin
(display "Insert a number\n")
(bet money))
(if (or (<= current-bet 0) (> current-bet money))
(begin
(display "You have to place a bet between 1 and ") (display
money) (newline)
(bet money))
current-bet)))))
(define choose-number (lambda ()
(display "Choose a number between 1 and 6: ")
(let ((number (read)))
(if (not (integer? number))
(chose-number)
(if (or (< number 1) (> number 6))
(choose-number)
number)))))
(define roll-dice (lambda (user-number)
(define dice-result (+ 1 (random 6)))
(display "Dice: ") (display dice-result) (newline)
(if (eqv? user-number dice-result)
#t
#f)))
(define play-again? (lambda () ;tutta la funzione non funziona!!!
(display "Do you want to play again? [y/n] ")
(let ((answer (read)))
(set! answer (object->string answer))
(if (not (string? answer))
(begin
(display "Answer 'yes' (Y, y) or 'no' (N, n)\n")
(play-again?))
(cond ((or (string=? (string-downcase answer) "y") (string=?
(string-downcase answer) "yes")) (game))
((or (string=? (string-downcase answer) "n") (string=? (string-
downcase answer) "no")) (exit))
(else (play-again?)))))))
(define game (lambda ()
(let play ((money 10000))
(display "You have ") (display money) (display " euros\n")
(let ((current-bet (bet money))
(number (choose-number)))
(if (roll-dice number)
(begin
(set! money (+ money (* 6 current-bet)))
(play money))
(begin
(set! money (- money current-bet))
(if (eqv? money 0)
(begin
(display "You have no money left :(\n")
(play-again?))
(play money))))))))
(process-command-line (command-line))
> Hi all!
> I made a minigame in scheme (guile) to teach myself the language. My
> question is: am I getting it right about functional languages? I mean,
> is this the right way to implement this game in scheme or I am totally
> out?
> [...]
> (define process-command-line (lambda (args)
> (if (eqv? 0 (length args)) (game))
> (let ((cur-cmd (car args)))
> (cond ((or (string=? cur-cmd "--help") (string=? cur-cmd "-h"))
> (print-help) (exit))
> ((or (string=? cur-cmd "--version") (string=? cur-cmd "-v")) (print-
> version) (exit))))
> (process-command-line (cdr args))))
What if game returns? Since (= (length args) 0), (car args) will produce
an error (in scheme; in Common Lisp it would return NIL).
So it would be better to write:
(define (process-command-line args)
(if (= 0 (length args))
(game)
(let ((cur-cmd (car args)))
(cond ((or (string=? cur-cmd "--help")
(string=? cur-cmd "-h"))
(print-help))
((or (string=? cur-cmd "--version")
(string=? cur-cmd "-v"))
(print-version))
(else (display "unknown option: ")
(display cur-cmd)))
(process-command-line (cdr args)))))
Avoiding to call procedures such as exit in this function allow you to
test it interactively. You can always write a simple main function:
(define (main args)
(process-command-line args)
(exit))
and call that at the end of the script.
> (define bet (lambda (money)
> (display "Place your bet: ")
> (let ((current-bet (read)))
> (if (not (integer? current-bet))
> (begin
> (display "Insert a number\n")
The test doesn't match the error message.
So write:
(cond
((not (real? current-bet))
(display "Please, bet a non-complex number.") (newline)
(bet money))
> (bet money))
> (if (or (<= current-bet 0) (> current-bet money))
> (begin
> (display "You have to place a bet between 1 and ") (display
> money) (newline)
> (bet money))
> current-bet)))))
The error message doesn't match the test!
If you type 0.1 it will be accepted!
So write:
((and (<= 1 current-bet) (<= current-bet money))
current-bet)
(else
(display "You must bet between 1 and ")
(display money) (display " inclusive.")
(newline)
(bet money)))
And use cond instead of nested if/begin.
> (define choose-number (lambda ()
> (display "Choose a number between 1 and 6: ")
Is it a number or an integer?
> (let ((number (read)))
> (if (not (integer? number))
> (chose-number)
> (if (or (< number 1) (> number 6))
> (choose-number)
> number)))))
>
> (define roll-dice (lambda (user-number)
> (define dice-result (+ 1 (random 6)))
I prefer to use let than local define.
> (display "Dice: ") (display dice-result) (newline)
> (if (eqv? user-number dice-result)
> #t
> #f)))
>
> (define play-again? (lambda () ;tutta la funzione non funziona!!!
> (display "Do you want to play again? [y/n] ")
> (let ((answer (read)))
> (set! answer (object->string answer))
Avoid set!. Write:
(let ((answer (object->string (read))))
> (if (not (string? answer))
Useless test, since you convert to a string.
> (begin
> (display "Answer 'yes' (Y, y) or 'no' (N, n)\n")
> (play-again?))
> (cond ((or (string=? (string-downcase answer) "y") (string=?
> (string-downcase answer) "yes")) (game))
Perhaps you should have written:
(let ((answer (string-downcase (object->string (read)))))
> ((or (string=? (string-downcase answer) "n") (string=? (string-
> downcase answer) "no")) (exit))
Avoid exit in your functions. How can you run them if you're constantly
exiting?
> (else (play-again?)))))))
>
> (define game (lambda ()
> (let play ((money 10000))
> (display "You have ") (display money) (display " euros\n")
> (let ((current-bet (bet money))
> (number (choose-number)))
> (if (roll-dice number)
Use cond.
> (begin
> (set! money (+ money (* 6 current-bet)))
> (play money))
There's no reason to use set! ! Write:
(play (+ money (* 6 current-bet)))
> (begin
> (set! money (- money current-bet))
> (if (eqv? money 0)
> (begin
> (display "You have no money left :(\n")
> (play-again?))
> (play money))))))))
Again... Write:
((<= current-bet money)
(play (- money current-bet)))
(else
(display "There's not enough money to bet so much.")
(newline)
(play-again?))) ; why? If there remains money?
Therefore:
(define (game)
(let play ((money 10000))
(display "You have ") (display money) (display " euros") (newline)
(cond
((= 0 money)
(display "You lost everything.") (newline)
(if (play-again?)
(game)))
(else
(let ((current-bet (bet money)))
(cond
((roll-dice (choose-number))
(display "You win!") (newline)
(play (+ money (* 6 current-bet))))
(else
(play (- money current-bet)))))))))
Here is how you can test your functions if you avoid using side effects
such as (exit), or doing more than one thing per function (such as
calling (game) from play-again?:
[pjb@201 Desktop]$ guile
guile> (define (play-again?)
(display "Do you want to play again? [y/n] ")
(let ((answer (string-downcase (object->string (read)))))
(cond
((or (string=? answer "y")
(string=? answer "yes"))
#t)
((or (string=? answer "n")
(string=? answer "no"))
#f)
(else
(display "Answer 'yes' (Y, y) or 'no' (N, n)") (newline)
(play-again?)))))
guile> (play-again?)
Do you want to play again? [y/n] si
Answer 'yes' (Y, y) or 'no' (N, n)
Do you want to play again? [y/n] YEss
Answer 'yes' (Y, y) or 'no' (N, n)
Do you want to play again? [y/n] no
#f
guile> (play-again?)
Do you want to play again? [y/n] y
#t
guile>
> (process-command-line (command-line))
(main (command-line))
You can also use (load "game.scm") to load the functions you defined in
a file, if you comment out this last line calling main. You can still
test the whole program with (process-command-line '("-h")), etc.
--
__Pascal Bourguignon__
http://www.informatimago.com