Looks like it would make a nice, involved exercise if you told the students what the right world type should be.
Did you consider making the world be a list of keys and a posn (inserting and removing from the list in the key handler and "interpreting" the list of keys in the tick handler)? Robby On Friday, July 30, 2010, Matthias Felleisen <matth...@ccs.neu.edu> wrote: > > I have finally taken the time to design a controller for an object that > allows the use of multiple arrow keys. Two insights: it is doable and it > is a truly insightful exercise on state machines. Most of the 'bullet' > points at the top of the program came about because I designed and explored. > BUT, the program is quite complex. If you think about it for a moment, > that's no surprise however. It is working with nine possible states: > no arrow pressed, one of four cardinal arrows pressed, two keys > pressed simultaneously. > > I think "Brown" kids should be able to design this kind of function > w/o a problem. For "non-Brown" kids, I suspect we would have to wait > until they have a solid handle of a lot of design ideas. > > QUESTION: should I incorporate this into the docs? Into HtDP/2e (part 1 > is already extremely long). > > -- Matthias > > > > (require 2htdp/universe) > (require 2htdp/image) > > ;; > ----------------------------------------------------------------------------- > ;; PROGRAM PURPOSE: move a red circle while one or two arrow keys are pressed > ;; > ----------------------------------------------------------------------------- > > ;; move only when one or two arrow keys are pressed > ;; when a third arrow key is pressed, ignore it > ;; when a single arrow key is pressed and a contradictory arrow is pressed, > ;; ignore it too > ;; This program is a nine-state state machine with respect to the direction > ;; in which the object is moving. > > ;; Boolean -> World > (define (move-by-arrows debug?) > (big-bang WORLD0 > (on-key key-handler) > (on-release release-handler) > (on-tick move) > (to-draw render) > (state debug?))) > > ;; > ----------------------------------------------------------------------------- > ;; Data Definitions & Constant Definitions > > ;; Cardinal is one of: > ;; -- "left" > ;; -- "right" > ;; -- "up" > ;; -- "down" > ;; interpretation: the four cardinal directions as strings and keyevents > > (define-struct world (dir posn)) > ;; World is: (world Direction Posn) > ;; the current direction and the current position of the circle > > ;; Direction is one of: (Yes, I mean these 10 definitions) > (define BLANK (make-posn 0 0)) > (define LEFT (make-posn -1 0)) > (define RIGHT (make-posn +1 0)) > (define UP (make-posn 0 -1)) > (define DOWN (make-posn 0 +1)) > (define DOWNLEFT (make-posn -1 +1)) > (define DOWNRIGHT (make-posn +1 +1)) > (define UPLEFT (make-posn -1 -1)) > (define UPRIGHT (make-posn +1 -1)) > ;; the direction as determined by a sequence of 0, 1, or 2 keys held down > > ;; physical constants > (define SIZE 300) > (define MID (/ SIZE 2)) > (define WORLD0 (make-world BLANK (make-posn MID MID))) > > ;; graphical constants > (define DOT (circle 3 "solid" "red")) > (define MT (empty-scene SIZE SIZE)) > > ;; > ----------------------------------------------------------------------------- > ;; World KeyEvent -> World > ; combine current direction and KeyEvent to create new direction > > (check-expect (key-handler (make-world UP (make-posn 0 0)) "left") > (make-world UPLEFT (make-posn 0 0))) > (check-expect (key-handler (make-world DOWNRIGHT (make-posn 0 0)) "left") > (make-world DOWNRIGHT (make-posn 0 0))) > > (check-expect (key-handler (make-world UP (make-posn 0 0)) "down") > (make-world UP (make-posn 0 0))) > (check-expect (key-handler (make-world DOWNRIGHT (make-posn 0 0)) "down") > (make-world DOWNRIGHT (make-posn 0 0))) > > (define (key-handler ws key) > (if (cardinal? key) > (make-world (key-proper (world-dir ws) key) (world-posn ws)) > ws)) > > ;; KeyEvent -> Boolean > (define (cardinal? key) > (cond > [(string=? "left" key) true] > [(string=? "right" key) true] > [(string=? "up" key) true] > [(string=? "down" key) true] > [else false])) > > ;; Direction Cardinal -> Direction > > (check-expect (key-proper UP "left") UPLEFT) > (check-expect (key-proper DOWNRIGHT "left") DOWNRIGHT) > (check-expect (key-proper RIGHT "up") UPRIGHT) > > ;; Direction Cardinal -> Direction > (define (key-proper ws key) > (cond > [(equal? BLANK ws) > (cond > [(string=? "left" key) LEFT] > [(string=? "right" key) RIGHT] ;; arbitrary decision > [(string=? "up" key) UP] > [(string=? "down" key) DOWN])] > [(equal? LEFT ws) > (cond > [(string=? "left" key) ws] > [(string=? "right" key) ws] ;; arbitrary decision > [(string=? "up" key) UPLEFT] ;; (string-append key ws) wouldn't be > typable > [(string=? "down" key) DOWNLEFT])] > [(equal? RIGHT ws) > (cond > [(string=? "left" key) ws] ;; arbitrary decision > [(string=? "right" key) ws] > [(string=? "up" key) UPRIGHT] > [(string=? "down" key) DOWNRIGHT])] > [(equal? UP ws) > (cond > [(string=? "left" key) UPLEFT] > [(string=? "right" key) UPRIGHT] > [(string=? "up" key) ws] > [(string=? "down" key) ws])] ;; arbitrary decision > [(equal? DOWN ws) > (cond > [(string=? "left" key) DOWNLEFT] > [(string=? "right" key) DOWNRIGHT] > [(string=? "up" key) ws] > [(string=? "down" key) ws])] > [(equal? UPLEFT ws) ws] > [(equal? UPRIGHT ws) ws] > [(equal? DOWNLEFT ws) ws] > [(equal? DOWNRIGHT ws) ws])) > > ;; > ----------------------------------------------------------------------------- > ;; World KeyEvent -> World > > (check-expect (release-handler (make-world UP (make-posn 0 0)) "left") > (make-world UP (make-posn 0 0))) > (check-expect (release-handler (make-world LEFT (make-posn 0 0)) "left") > (make-world BLANK (make-posn 0 0))) > (check-expect (release-handler (make-world DOWNRIGHT (make-posn 0 0)) "down") > (make-world RIGHT (make-posn 0 0))) > > (define (release-handler ws key) > (if (cardinal? key) > (make-world (release-proper (world-dir ws) key) (world-posn ws)) > ws)) > > ;; Direction Cardinal -> Direction > (define (release-proper ws card) > (cond > [(equal? BLANK ws) ws] ;; IF hold down several keys, release in some order > [(equal? LEFT ws) (if (string=? "left" card) BLANK ws)] > [(equal? RIGHT ws) (if (string=? "right" card) BLANK ws)] > [(equal? UP ws) (if (string=? "up" card) BLANK ws)] > [(equal? DOWN ws) (if (string=? "down" card) BLANK ws)] > [(equal? UPLEFT ws) > (cond > [(string=? "left" card) UP] > [(string=? "right" card) ws] > [(string=? "up" card) LEFT] > [(string=? "down" card) ws])] > [(equal? UPRIGHT ws) > (cond > [(string=? "left" card) ws] > [(string=? "right" card) UP] > [(string=? "up" card) RIGHT] > [(string=? "down" card) ws])] > [(equal? DOWNLEFT ws) > (cond > [(string=? "left" card) DOWN] > [(string=? "right" card) ws] > [(string=? "up" card) ws] > [(string=? "down" card) LEFT])] > [(equal? DOWNRIGHT ws) > (cond > [(string=? "left" card) ws] > [(string=? "right" card) DOWN] > [(string=? "up" card) ws] > [(string=? "down" card) RIGHT])])) > > ;; > ----------------------------------------------------------------------------- > ;; World -> World > (check-expect (move WORLD0) WORLD0) > > (define (move ws) > (make-world (world-dir ws) (posn+ (world-posn ws) (world-dir ws)))) > > ;; > ----------------------------------------------------------------------------- > ;; Posn Posn -> Posn > (define (posn+ p q) > (make-posn (+ (posn-x p) (posn-x q)) (+ (posn-y p) (posn-y q)))) > > ;; > ----------------------------------------------------------------------------- > ; World -> Image > (define (render ws) > (place-image DOT (posn-x (world-posn ws)) (posn-y (world-posn ws)) MT)) > > _________________________________________________ > For list-related administrative tasks: > http://lists.racket-lang.org/listinfo/dev > _________________________________________________ For list-related administrative tasks: http://lists.racket-lang.org/listinfo/dev