#| A Logo execution engine (ISL) |# (require 2htdp/image) (require 2htdp/universe) ;; ----------------------------------------------------------------------------- ;; constant definitions (define SIZE 400) (define MID (quotient SIZE 2)) (define STEP (quotient SIZE 200)) (define START (make-posn MID MID)) (define LEFT (make-posn (- STEP) 0)) (define RIGHT (make-posn STEP 0)) (define DOWN (make-posn 0 STEP)) (define UP (make-posn 0 (- STEP))) (define BG (empty-scene SIZE SIZE)) (define TC "lightseagreen") (define TS (quotient SIZE 20)) (define TA 330) (define TURTLE-DOWN (isosceles-triangle TS TA "solid" TC)) (define TURTLE-RIGHT (rotate 90 TURTLE-DOWN)) (define TURTLE-UP (rotate 90 TURTLE-RIGHT)) (define TURTLE-LEFT (rotate 90 TURTLE-UP)) ;; ----------------------------------------------------------------------------- ;; data representation (define-struct turtle (place dir todo)) ;; A TW (turtle world) is (make-turtle Posn Direction Command*) ;; A Direction is one of: ;; -- 'up ;; -- 'down ;; -- 'left ;; -- 'right ;; Command* is one of: ;; -- empty ;; -- (cons ACommand Command*) ;; ACommand is one of: ;; -- 'turn-left ;; -- 'turn-right ;; -- 'move (define ALL-COMMANDS '(turn-left turn-right move)) ;; Any -> Boolean ;; is x ACommand? (define (a-command? x) (and (symbol? x) (member x ALL-COMMANDS))) ;; interpretation: ;; (make-turtle p d c) means the turtle is at Posn p, it is facing in Direction d, ;; and it has the list of commands c left to execute. (define t0 (make-turtle START 'up '())) (define c1 '(turn-left move turn-right move)) (define t1 (make-turtle START 'up c1)) ;; after executing 'turn-left, we get (define c2 (rest c1)) (define t2 (make-turtle START 'left c2)) ;; after executing 'move, we get: (posn+ START LEFT) (define t3 (make-turtle (make-posn (- MID STEP) MID) 'left '(turn-right move))) (define full-round-trip '(turn-left turn-right turn-right turn-right turn-right)) ;; ----------------------------------------------------------------------------- ;; main function ;; Command* -> Posn ;; run a Turtle program on a turtle that starts in the middle and faces right, ;; compute the final position in which the turtle ends up ;; (This is a somewhat unusual case where you can test the main function well.) (check-expect (turtle-interpret c2) (make-posn (+ MID STEP) (+ MID STEP))) (check-expect (turtle-interpret full-round-trip) START) (check-expect (turtle-interpret '(turn-left move)) (make-posn MID (- MID STEP))) (define (turtle-interpret cmds) (turtle-place (big-bang (make-turtle START 'right cmds) [to-draw render-turtle] [on-tick execute-one-command] [check-with turtle?] [stop-when done?]))) ;; wish list functions ;; TW -> Image ;; render the current turtle world (define (render-turtle t) (place-image (pick-turtle (turtle-dir t)) (posn-x (turtle-place t)) (posn-y (turtle-place t)) BG)) ;; Direction -> Image ;; pick the appropriate turtle image (define (pick-turtle d) (cond [(symbol=? 'left d) TURTLE-LEFT] [(symbol=? 'right d) TURTLE-RIGHT] [(symbol=? 'down d) TURTLE-DOWN] [(symbol=? 'up d) TURTLE-UP])) ;; TW -> TW ;; execute the first argument on the todo list of commands (if any) (check-expect (execute-one-command t0) t0) (check-expect (execute-one-command t1) t2) (check-expect (execute-one-command t2) t3) (define (execute-one-command t) (if (empty? (turtle-todo t)) t (execute (turtle-place t) (turtle-dir t) (turtle-todo t)))) ;; Posn Direction Command* -> TW ;; execute one command ;; KNOWLEDGE: there is one command because execute-one-command checked (check-expect (execute START 'up '(turn-left move turn-right move)) t2) (check-expect (execute START 'left c2) t3) (define (execute p d c) (cond [(symbol=? (first c) 'turn-left) (make-turtle p (turn-left d) (rest c))] [(symbol=? (first c) 'turn-right) (make-turtle p (turn-right d) (rest c))] [(symbol=? (first c) 'move) (make-turtle (move p d) d (rest c))])) ;; Direction -> Direction ;; rotate the turtle's head by 90degrees to the left (define (turn-left d) (cond [(symbol=? 'left d) 'down] [(symbol=? 'down d) 'right] [(symbol=? 'right d) 'up] [(symbol=? 'up d) 'left])) ;; Direction -> Direction ;; rotate the turtle's head by 90degrees to the right (define (turn-right d) (cond [(symbol=? 'left d) 'up] [(symbol=? 'up d) 'right] [(symbol=? 'right d) 'down] [(symbol=? 'down d) 'left])) ;; Posn Direction -> Posn ;; move the turtle in its current direction (define (move p d) (posn+ p (cond [(symbol=? 'left d) (make-posn (- STEP) 0)] [(symbol=? 'right d) (make-posn (+ STEP) 0)] [(symbol=? 'down d) (make-posn 0 (+ STEP))] [(symbol=? 'up d) (make-posn 0 (- STEP))]))) ;; TW -> Boolean ;; is the todo list exhausted? (check-expect (done? t0) true) (check-expect (done? t1) false) (define (done? t) (empty? (turtle-todo t))) ;; Posn Posn -> Posn ;; add two posn, treating one as a vector (define (posn+ p q) (make-posn (+ (posn-x p) (posn-x q)) (+ (posn-y p) (posn-y q)))) ;; ----------------------------------------------------------------------------- ;; Nat -> Command* ;; generate a list of n random commands (check-expect (command-generator 0) '()) (check-expect (length (command-generator 10)) 10) (define (command-generator n) (cond [(zero? n) '()] [else (cons (random-command (random 3)) (command-generator (sub1 n)))])) ;; {0,1,2} -> ACommand ;; generate a command based on the given natural number (check-expect (a-command? (random-command (random 3))) true) (define (random-command n) (cond [(= n 0) 'move] [(= n 1) 'turn-left] [(= n 2) 'turn-right]))