Number guessing game in Racket I decided to learn a Lisp-ish language and I am  discovering a very weird and new syntax. The obligatory first program, that also uses all the fundamentals, is the number guessing game.


Did I define too few or too many constants?
Is my code properly spaced and indented?
Is there a cleaner way to exit the loop than error?
Is it too imperative? Is it even possible to write the number guessing game functionally?
Is it OK to define null and set!?




#lang racket

(define NUM 12)
(define TRIES 6)
(define CORRECT "Correct! Well guessed!")
(define TOO_LOW "... Too Low ...")
(define TOO_HIGH "... Too high ...")

(define (num_message player_num)
  (if (= player_num NUM)
      CORRECT
       (if (&gt; player_num NUM)
          TOO_HIGH
          TOO_LOW      
      )
    )
  )

(define (number-guessing-game)
  (define out null)
  (for ([i (range TRIES)])
    (set! out (num_message (read)))
    (print out)
    (when (equal? out CORRECT)
        (print "You won")
        (error "Exiting")
    )
  )
  (print "Too many attempts")
)

(number-guessing-game)
Winning shouldn't be considered an error.  If you want a way to exit from the for-loop, use #:break.  But for and set! aren't really idiomatic in Lisp-like languages.  Instead, it is more common to use recursion and let, which conform to the functional programming paradigm.

In num_message, you can use a cond instead of nested ifs.

I'm not happy about the use of a string comparison to detect the winning condition: the number-guessing-game function needs to know what congratulatory message to expect from num_message.  You've worked around that problem by defining some string constants, which gives them more importance than one would expect.

It would be more elegant for num_message not to have NUM fixed, and for number-guessing-game not to have NUM and TRIES fixed.

The number-guessing-game function would be clearer if it were broken up, because it is actually doing two things: play one guess, and conditionally repeat.

The print calls should probably be displayln instead, to also write a trailing newline.

Conventionally, words in identifiers are separated-by-hyphens rather than underscores.  Closing parentheses don't get their own line.

#lang racket

(define NUM 12)
(define TRIES 6)

(define (num-message delta)
  (cond [(&lt; delta 0) "... Too low ..."]
        [(&gt; delta 0) "... Too high ..."]
        [else        "Correct! Well guessed!"]))

(define (guess-number target)
  (let ([delta (- (read) target)])
       (displayln (num-message delta))
       (zero? delta)))

(define (number-guessing-game target tries)
  (cond [(zero? tries)         (displayln "Too many attempts")]
        [(guess-number target) (displayln "You won")]
        [else                  (number-guessing-game target (- tries 1))]))

(number-guessing-game NUM TRIES)