Moving beyond world/universe - inversion-of-control

I thought I'd try a simple GUI app using the world/universe mutation-free approach,  but trying to implement the 'world/universe' program design myself.
I've got my little sketch below, but I quickly came to conclusion that while I could use the teachpack, I don't know how to achieve the teachpack functionality myself.
I'm guessing I should use continuations, but that doesn't seem to be the approach in the universe.rkt source.
I could always just stuff the program into the canvas class, (as earlier games like slidey and same seem to do), but I really want to get a handle on  how to implement the 'world/universe' style of program control.
;;;;----
#lang racket/gui
; simple drawing program
; mousedown starts recording a list of points
; mousechanged starts recording a new list
; paint callback paints the list of lists as lines.
(define diagramframe (new frame% [label "paint"] [width 300]
[height 300] [x 1000][y 300]))
;(define lines '(((0 . 0) (0 . 300) (250 . 250) (150 . 176))))
(define lines '(((0 . 0) (0 . 300) (250 . 250) (150 . 176))
                ((10 . 4) (280 . 10))))
(define paintcanvas%
  (class canvas%
    (init-field mouse-event-callback)
    (super-new)
    (define dc (send this get-dc))
    (define/override (on-event mouse-event)
      (mouse-event-callback mouse-event))))
(define (paint-cb c dc) 
  (for-each (λ (line) (send dc draw-lines line)) lines))
(define (me-cb mouse-event)
(let ((x (send mouse-event get-x))
        (y (send mouse-event get-y)))
    (when (and (send mouse-event get-left-down)
               (send mouse-event moving?))
      (if (send mouse-event button-changed?)
          ; if true append as new list
          '() 
          ; if false append existing list
          '()))))
(define Paintcanvas (new paintcanvas%
                         [parent diagramframe]
[paint-callback paint-cb]
[mouse-event-callback me-cb]))
(define (main world)
 (when world (main (??? world)))
  (send diagramframe show #t))
 
(main lines)
(send diagramframe show #t)
;;-----

Here is how I would do it.
Note that the GUI event loop acts as the program main.
Use timer events to implement on-tick.
#lang racket/gui
;;;
;;; WORLD
;;;
(define-struct world (lines))
(define the-world (make-world '((0 . 0) (0 . 300) (250 . 250) (150 . 176) (10 . 4) (280 . 10))))
;;;
;;; USER LAND
;;;
(define (on-mouse-event world event)
(if (and (send event get-left-down)
(send event moving?)
#; (send event button-changed?))
(let ((x (send event get-x))
(y (send event get-y)))
(make-world (cons (cons x y) (world-lines world))))
world))
(define (on-paint world dc)
(send dc draw-lines
(map pair->point (world-lines world))))
(define (pair->point p)
(make-object point% (car p) (cdr p)))
;;;
;;; SYSTEM
;;;
(define user:on-paint on-paint)
(define diagramframe (new frame% [label "paint"] [width 300] [height 300] [x 1000][y 300]))
(define paintcanvas%
(class canvas%
(inherit get-dc refresh)
(super-new)
(define/override (on-paint)
(send (get-dc) suspend-flush)
(user:on-paint the-world (get-dc))
(send (get-dc) resume-flush))
(define/override (on-event mouse-event)
(let* ([old-world the-world]
[new-world (on-mouse-event the-world mouse-event)])
(if (eq? old-world new-world)
(super on-event mouse-event)
(begin
(set! the-world new-world)
(refresh)))))))
(define paintcanvas (new paintcanvas% [parent diagramframe]))
(send diagramframe show #t)

Related

DrRacket. Keybinding to join lines

How can I make a keyboard shortcut in DrRacket to join the current and next lines without spaces between them? And when several lines are selected the command has to join them together.
#lang s-exp framework/keybinding-lang
(keybinding "m:j" (λ (editor evt)
(define pos (send editor get-start-position))
(define line (send editor position-line pos))
...
))
Join lines with Alt+J keyboard shortcut.
Save the file and add it to DrRacket:
Edit > Keybindings > Add User-defined Keybindings..
Then restart the program.
#lang s-exp framework/keybinding-lang
(keybinding
"m:j" ; Join lines - Alt+J
(λ (editor evt)
;;
(define (join-lines start-line end-line)
(cond [(>= start-line end-line) (void)]
[else (join-with-next-line (sub1 end-line))
(join-lines start-line (sub1 end-line))]))
;;
(define (join-with-next-line cur-line)
(remove-trailing-spaces cur-line)
(define start-del-pos (send editor line-end-position cur-line))
(define end-del-pos (send editor line-end-position (add1 cur-line)))
(define text (send editor get-text start-del-pos end-del-pos))
(define spaces (regexp-match #px"\\s+" text))
(when spaces
(define space-len (string-length (first spaces)))
(send editor delete start-del-pos (+ start-del-pos space-len))))
;;
(define (remove-trailing-spaces line)
(define start-pos (send editor line-start-position line))
(define end-pos (send editor line-end-position line))
(define text (send editor get-text start-pos end-pos))
(define spaces (regexp-match #px"\\s+$" text))
(when spaces
(define space-len (string-length (first spaces)))
(send editor delete (- end-pos space-len) end-pos)))
(define pos1 (send editor get-start-position))
(define pos2 (send editor get-end-position))
(define line1 (send editor position-line pos1))
(define line2 (send editor position-line pos2))
(define line1-end-pos (send editor line-end-position line1))
(cond [(= line1 line2) (join-with-next-line line2)]
[else
(send editor begin-edit-sequence)
(join-lines line1 line2)
(send editor set-position line1-end-pos line1-end-pos)
(send editor end-edit-sequence)])
))

Programming Breakout in BSL

I am currently trying to code 'Breakout' in BSL but I'm stuck as I
don't know how to add a welcome and a game-over screen to my code. It is
supposed that the game starts with the welcome screen and when it is
clicked with the mouse on the screen the actual game should start. When
the ball collides with the lower edge of the screen a game-over-screen
is supposed to appear.
I appreciate any tips given!
(define WIDTH 200)
(define HEIGHT 200)
(define BALL-RADIUS 10)
(define BALL-IMG (circle BALL-RADIUS "solid" "red"))
(define MT (empty-scene WIDTH HEIGHT))
(define GAME-OVER
(place-image (text "Game-over" 30 "black")
100 100
MT))
(define WELCOME
(place-image (text "Welcome" 30 "black")
100 100
MT))
(define-struct vel (delta-x delta-y))
; a Vel is a structure: (make-vel Number Number)
; interp. the velocity vector of a moving object
(define-struct ball (loc velocity))
; a Ball is a structure: (make-ball Posn Vel)
; interp. the position and velocity of a object
(define RACKET (rectangle 30 10 "solid" "grey"))
(define-struct world-state (ball racket))
; A WorldState is a structure. (make-world-state Ball Location of
Racket)
; interp. current velocity and location of ball, current location of
racket
; Posn Vel -> Posn
; applies q to p and simulates the movement in one clock tick
(check-expect (posn+vel (make-posn 5 6) (make-vel 1 2))
(make-posn 6 8))
(define (posn+vel p q)
(make-posn (+ (posn-x p) (vel-delta-x q))
(+ (posn-y p) (vel-delta-y q))))
; Ball -> Ball
; computes movement of ball in one clock tick
(define (move-ball ball)
(make-ball (posn+vel (ball-loc ball)
(ball-velocity ball))
(ball-velocity ball)))
; A Collision is either
; - "top"
; - "down"
; - "left"
; - "right"
; - "racket"
; - "none"
; interp. the location where a ball collides with a wall
; Posn -> Collision
; detects with which of the walls (if any) or the racket the ball
collides
(define (collision world-state)
(cond
[(<= (posn-x (ball-loc (world-state-ball world-state))) BALL-
RADIUS) "left"]
[(<= (posn-y (ball-loc (world-state-ball world-state))) BALL-
RADIUS) "top"]
[(>= (posn-x (ball-loc (world-state-ball world-state))) (- WIDTH
BALL-RADIUS)) "right"]
[(>= (posn-y (ball-loc (world-state-ball world-state))) (- HEIGHT
BALL-RADIUS)) "down"]
[(and (>= (posn-y (ball-loc (world-state-ball world-state))) (-
HEIGHT BALL-RADIUS 10))
(<= (- (posn-x (world-state-racket world-state)) 15)
(posn-x (world-state-racket world-state))
(+ (posn-x (world-state-racket world-state)) 15)))
"racket"]
[else "none"]))
; Vel Collision -> Vel
; computes the velocity of an object after a collision
(define (bounce vel collision)
(cond [(or (string=? collision "left")
(string=? collision "right"))
(make-vel (- (vel-delta-x vel))
(vel-delta-y vel))]
[(or (string=? collision "top")
(string=? collision "racket"))
(make-vel (vel-delta-x vel)
(- (vel-delta-y vel)))]
[else vel]))
; render
; WorldState -> Image
; renders ball and
racket at their position
(check-expect (image? (render INITIAL-STATE)) #true)
(define (render world-state)
(place-image BALL-IMG
(posn-x (ball-loc (world-state-ball world-state)))
(posn-y (ball-loc (world-state-ball world-state)))
(place-image RACKET
(posn-x (world-state-racket world-state))
195
(empty-scene WIDTH HEIGHT))))
;tick
; WorldState -> WorldState
; moves ball to its next location
(check-expect (tick INITIAL-STATE) (make-world-state (make-ball (make-posn 21 14) (make-vel 1 2)) (make-posn 20 195)))
(define (tick world-state)
(make-world-state (move-ball (make-ball (ball-loc (world-state-ball world-state))
(bounce (ball-velocity (world-state-ball world-state))
(collision world-state))))
(world-state-racket world-state)))
; A Location is a structure: (make-posn Number Number)
; interp. x and y coordinate of a location on screen.
(define Loc (make-posn 1 1))
; A MouseEvent is one of
; - "button-down"
; - "button-up"
; - "drag"
; - "move"
; - "enter"
; - "leave"
; interp. mouse events, e.g., mouse movements or mouse clicks
(define MOUSE-CLICK "button-down")
; mouse
;
; Game Number Number MouseEvent -> WorldState
; Update position of racket when the mouse moves
;mouse-template
(define (mouse-template world-state mouse-loc-x mouse-loc-y MouseEvent)
(cond
((string=? MouseEvent "button-down")...)
((string=? MouseEvent "button-up")...)
((string=? MouseEvent "drag")...)
((string=? MouseEvent "move")...)
((string=? MouseEvent "enter")...)
((string=? MouseEvent "leave")...)
(else ...)))
(define (mouse world-state mouse-loc-x mouse-loc-y MouseEvent)
(cond
[(and (string=? MouseEvent "move")
(>= mouse-loc-y 180))
(make-world-state (world-state-ball world-state)
(make-posn mouse-loc-x 195))]
[else world-state]))
(define INITIAL-BALL (make-ball (make-posn 20 12)
(make-vel 1 2)))
(define INITIAL-RACKET (make-posn 20 195))
(define INITIAL-STATE (make-world-state INITIAL-BALL INITIAL-RACKET))
; WorldState -> WorldState
; Starts the game
(define (main state)
(big-bang state
(on-tick tick 0.01)
(to-draw render)
(on-mouse mouse)))
; start with INITIAL-STATE
You now have three different states:
welcome
playing
game-over
So far you have only had "playing" and you used your world structure to represent that.
Now you need to introduce two new structures: welcome and game-over.
Rename your old render function render-world (or render-playing).
Then write something like:
(define (render w)
(cond
[(welcome? w) (render-welcome w)]
[(world? w) (render-world w)]
[(game-over? w) (render-game-over w)]))
Of course your initial state should become an instance of welcome, and you also need three different tick functions.

Run command at time of object creation

I have following code for a simple class (stack):
#lang racket
(define stackClass%
(class object%
(super-new)
(init-field (mystack '(A B C)))
(define/public (push n)
(set! mystack (cons n mystack)))
(define/public (pop)
(cond [(empty? mystack) #f]
[else (define res (car mystack))
(set! mystack (rest mystack))
res] ))
(define/public (show)
mystack)
mystack ; I want to output mystack at time of creation of class object; not working here;
)); end class;
; USAGE:
(define sc (new stackClass%))
(send sc push 1)
(send sc push 2)
(send sc show)
(send sc pop)
(send sc show)
Output:
'(2 1 A B C)
2
'(1 A B C)
I want to output mystack at time of creation of class object. However, it is not working in this code, though there is no error being shown.
Using following instead of (init-field (mystack '(A B C))) also does not help:
(init (L '(A B C)))
(define mystack L)
How can I run a piece of code at the time of class creation?
Yes, your code is running fine. In addition, "mystack" is being evaluated. However, it produces no output. If you want to display something, you can use one of a number of functions. Try replacing mystack with (println mystack) in your code.

How to paint on previous picture in the functional way in Racket

This code maintains number of ticks, but all ways paints on a new screen
#lang racket
(require 2htdp/universe)
(require 2htdp/image)
(define pasaulis (empty-scene 100 100))
(define (trace-circle t)
(place-image (circle 5 "solid" "blue")
(+ 50(* 40 (cos (/ t 100))))
(+ 50(* 40 (sin (/ t 100))))
pasaulis))
(define (render t)
(text (number->string t) 12 "red"))
(big-bang 1000000
(on-tick sub1 1/500)
(to-draw trace-circle)
(stop-when zero?)
(record? true))
The following code does maintain picture, but I could not have number of ticks passed to the pain handler.
#lang racket
(require 2htdp/universe)
(require 2htdp/image)
(require picturing-programs)
(define (move-right-10 picture)
(beside (rectangle 10 0 "solid" "white")
picture))
(big-bang pic:calendar
(on-draw show-it 500 100)
(on-tick move-right-10 1/22))
I just cannot figure it out. how to have draw function with number of ticks, while modifying the screen. Is the big-bang function suitable at all for the purpose?
Here is an example:
#lang racket
(require 2htdp/universe)
(require 2htdp/image)
(struct world (ticks pic))
(define pasaulis (empty-scene 1000 1000))
(define (render w)
(world-pic w))
(define (handle-on-tick w)
(define t (world-ticks w))
(define p (world-pic w))
(define new-ticks (+ t 1))
(define new-pic (overlay/xy (text (number->string t) 12 "red")
(* -10 t) (* -10 t)
p))
(world new-ticks new-pic))
(define (stop? w)
(= (world-ticks w) 50))
(big-bang (world 0 pasaulis)
(on-tick handle-on-tick 1/5)
(to-draw render)
(stop-when stop?))

Racket: Why can't set canvas's background in windows

This my code:
#lang racket
(require racket/gui)
(define-values (screen-width screen-height) (get-display-size))
(define *app-width* 400)
(define *app-height* 125)
(define *vm-frame* (new frame%
[label "Test"]
[width *app-width*]
[height *app-height*]
[x (- (quotient screen-width 2) (quotient *app-width* 2))]
[y (- (quotient screen-height 2) (quotient *app-height* 2))]
))
(define frame-canvas%
(class canvas%
(super-new)
(inherit get-dc)
(define/override (on-paint)
(let ([my-dc (get-dc)])
(send my-dc clear)
(send my-dc set-background "black")))))
(let ([frame-canvas (new frame-canvas% [parent *vm-frame*])])
(send *vm-frame* show #t))
On mac os, this is normal, show a app with a black background.
But on windows, background is white.
Why? Racket version is 5.3, it's a 5.3 bug?
Reverse the order of set-background and clear.
The call to clear uses the color of the current background to do the fill. If somehow your on-paint were called at least more than once, then you would observe a black background, since the second time around, the current background color would be black. I suspect this is what explains the platform-specific difference you observed.