HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Simon Says in Scheme

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
schemesayssimon

Problem

Ok, it's not completely like Simon Says, since it's text-based. There is nothing about memorizing based on visual cues and/or sound here, just memorizing long sequences of numbers. I wanted to do it in the language I write the least code in, Scheme (w/ Chicken), and see what happens.

(use posix)
(use (srfi 1))

(define button-count 4)

(define (clear-screen)
  (system "cls"))

(define (make-delay)
  (sleep 2))

(define (show-sequence sequence)
  (if (not (null? sequence))
    (begin
      (clear-screen)
      (make-delay)
      (write (car sequence))
      (make-delay)
      (show-sequence (cdr sequence)))
    (begin
      (clear-screen)
      (write-line "GO!"))))

(define (show-score score)
  (format #t "Your score: ~A~%" score))

(define (get-player-input)
  (read-line))

(define (make-sequence length)
  (map random (make-list length button-count)))

(define (player-sequence-matches? target-sequence)
  (cond
    [(null? target-sequence) #t]
    [(string=? (get-player-input) (number->string (car target-sequence)))
     (player-sequence-matches? (cdr target-sequence))]
    [else #f]))

(define (simon-says difficulty)
  (let [(current-sequence (make-sequence difficulty))]
    (show-sequence current-sequence)
    (if (player-sequence-matches? current-sequence)
      (+ 1 (simon-says (+ difficulty 1)))
      0)))

(define (play-simon-says)
  (show-score (simon-says 1)))


If you want to run it on something Unix, you'll need to change (system "cls") to (system "clear").

Solution

-
Ideally I'd like to use termcap or terminfo or the like to clear the terminal without shelling out. Alas, it seems Chicken doesn't have an egg for either termcap or terminfo. (It does have an egg for curses, but I consider that to be too heavyweight for just clearing a terminal.)

-
I'd probably use for-each for iterating through the items in show-sequence, rather than the recursive approach you have:

(define (show-sequence seq)
  (for-each (lambda (x)
              (clear-screen)
              (make-delay)
              (display x)    ;; not write
              (make-delay))
            seq)
  (clear-screen)
  (display "GO!\n"))


-
Not a huge fan of making a get-player-input function that's exactly the same as read-line. I'd just call read-line directly.

-
Your definition of make-sequence is quite clever, but it's not very idiomatic. Since you have SRFI 1 loaded, I'd just use list-tabulate:

(define (make-sequence len)  ;; not length
  (list-tabulate len (lambda (_) (random button-count))))


-
You can simplify player-sequence-matches? by using SRFI 1's every:

(define (player-sequence-matches? seq)
  (every (lambda (x)
           (= (string->number (read-line)) x))
         seq))


And since Chicken has cut builtin, you can simplify even further:

(define (player-sequence-matches? seq)
  (every (cut = (string->number (read-line)) <>) seq))


-
For calculating the total score, I'd rather accumulate the score, rather than use recursion to hold the score. Like so:

(define (simon-says difficulty)
  (let loop ((difficulty difficulty)
             (score 0))
    (define seq (make-sequence difficulty))
    (show-sequence seq)
    (if (player-sequence-matches? seq)
        (loop (add1 difficulty) (add1 score))
        score)))


This way, the function is totally tail-recursive.

Code Snippets

(define (show-sequence seq)
  (for-each (lambda (x)
              (clear-screen)
              (make-delay)
              (display x)    ;; not write
              (make-delay))
            seq)
  (clear-screen)
  (display "GO!\n"))
(define (make-sequence len)  ;; not length
  (list-tabulate len (lambda (_) (random button-count))))
(define (player-sequence-matches? seq)
  (every (lambda (x)
           (= (string->number (read-line)) x))
         seq))
(define (player-sequence-matches? seq)
  (every (cut = (string->number (read-line)) <>) seq))
(define (simon-says difficulty)
  (let loop ((difficulty difficulty)
             (score 0))
    (define seq (make-sequence difficulty))
    (show-sequence seq)
    (if (player-sequence-matches? seq)
        (loop (add1 difficulty) (add1 score))
        score)))

Context

StackExchange Code Review Q#71954, answer score: 3

Revisions (0)

No revisions yet.