Jair Trejo

Iteration in Racket II - A simple game loop

Iteration is useful to go through some data you have. But it can also be helpful for data you don’t (yet) have.

Consider a game of number-guessing. In a set number of attempts, you have to guess a number picked in secret by the computer. To help you along, the computer lets you know if your guess is too high, or too low.

A Racket implementation could be:

(define (guess-game)
  (define secret (random 100))
  (displayln "Which number am I thinking?")
  (define won?
    (for/or ([_ 7]
             [input (in-lines)])
      (define guess (string->number input))
      (cond
        [(> guess secret) (displayln "Go lower") #f]
        [(< guess secret) (displayln "Go higher") #f]
        [(= guess secret) #t])))
  (cond
    [won? (displayln "You win!")]
    [else (displayln (format "You lose! It was ~a" secret))]))

The for/or form at the core goes through a user’s attempts, and for each step it is deciding whether the user won, represented by returning true, or hasn’t won yet, returning false.

We use for/or because it will collect those intermediate results and stop as soon as one of them is true, regardless of how much is “left” of the sequences it is iterating through. The result will be the or of the results: true if the user ever won, or false if they never did.

The first iterated sequence is 7, shorthand for (in-range 7), which is a convenient way of going through the numbers 0 to 7 (or doing something 7 times.) The second one is (in-lines), which is a sequence of lines read from standard input. It blocks until it sees a new line, and stops if it sees EOF (so the game can be stopped by hitting Ctrl+D.)

Both sequences are iterated in parallel. As long as the user doesn’t win, the game will keep going until seven rounds have happened or input has been terminated.

The entire for/or call is an expression, so we can use the resulting true or false to display a Victory or Defeat message.

The for/* forms also support a break clause that you can execute anywhere in the loop body to stop the iteration imperatively. But I think it’s nice that in this example we can express each step’s result as a value, and that the combination of those values is meaningful.

What other game loops could be expressed as an or, an and or some other comprehension?