DrRacket: put two objects into big-bang - racket

How can i put two objects with its own coordinates
(define padle1 (rectangle 10 30 "solid" "red"))
(define padle2 (rectangle 10 30 "solid" "red"))
(define (place-dot-at ... ) ...)
into bin-bang function
(big-bang ...
[to-draw place-dot-at])
Can i use list of padles
(define new-list (list padle1 padle2))

big-bang stores only one piece of information, usually called the "world state." All the functions that work with big-bang, like your drawing function, tick handler, and so on, must accept that world state as a single parameter.
It's up to you to decide what to store in your world state. If you want to store two locations (one for each paddle), a list or struct is the way to go. For instance, here is how you might define a struct called world that can hold two positions at once.
; Create a type called `world` that holds two locations.
(define-struct world [paddle1 paddle2])
; Create a variable to store the initial state of the world.
(define initial-world (make-world (make-posn 0 100) (make-posn 300 100)))
When you write your drawing function, it must accept the entire world state at once:
(define (draw-game world)
(place-image
paddle1
(posn-x (world-paddle1 world))
(posn-y (world-paddle1 world))
(place-image
paddle2
(posn-x (world-paddle2 world))
(posn-y (world-paddle2 world))
BACKGROUND)))
In your big-bang, treat the world state like any other kind of data:
(big-bang
initial-world
[to-draw draw-game])

I recommend making a draw-paddle function that draws a single paddle on top of an image i.
(define (draw-paddle p i)
(overlay/xy (rectangle ...) ; the paddle
50 70 ; coordinates on paddle on screen
i)) ; image of what's previously drawn
Then make a function that draws all paddles in a list on top of an image i.
(define (draw-paddles ps i)
(cond
[(empty? ps) i] ; no more paddles to draw
[else (draw-paddles
(rest ps) ; draw the rest of the paddles on top
(draw-paddle (first p) i))])) ; of the first paddle
; ontop of i
Then finally you can made:
(define (on-draw state)
(draw-paddles (list paddle1 paddle2) my-background))
If you don't have a background you can make one with empty-image or rectangle.

Related

Racket/Beginner Student Language Confusion

I am trying to animate the word "floccinaucinihilipilification" letter by letter. Right now it displays the complete word in the animation window, but I am lost on how to animate it so it will count up from the first character to the last, looping back to 0.
(define LONG-WORD "floccinaucinihilipilification")
; cycle-spelling : String -> Image
; display an animation of a long
; word being spelled out
(define a (string-length LONG-WORD))
(define TXT
(text (substring LONG-WORD 0 a) 30 "black"))
(define BG
(empty-scene 400 400))
(define (cycle-spelling a)
(place-image TXT 200 200 BG))
(animate cycle-spelling)
See what animate does:
(animate create-image) → natural-number/c
create-image : (-> natural-number/c scene?)
opens a canvas and starts a clock that ticks 28 times per second. Every time the clock ticks, DrRacket applies create-image to the number of ticks passed since this function call. The results of these function calls are displayed in the canvas. The simulation runs until you click the Stop button in DrRacket or close the window. At that point, animate returns the number of ticks that have passed.
So you have to base your code on the number of ticks, passed to create-image function.
(animate cycle-spelling)
(define (cycle-spelling ticks) ... )
Start with (quotient ticks 28), value of this expression increases each second by 1.
Looping is created with modulo, so after some experimenting, you should have something like this:
(modulo (quotient ticks 28) (+ (string-length long-word) 1))
Rest of the code will be similar.
Following code animates given word letter by letter and then loops back to 0.
#lang racket
(require 2htdp/universe)
(require 2htdp/image)
(define long-word "floccinaucinihilipilification")
(define speed 3) ; try also 7, 14, 28 ...
(define bg
(empty-scene 400 400))
(define (cycle-spelling ticks)
(place-image (text (substring long-word 0
(modulo (quotient ticks speed)
(+ (string-length long-word) 1)))
30 "black")
200 200 bg))
(animate cycle-spelling)

Animation in Racket

Running program window So I'm doing this exercise from pictured programs and I've got the program to do as the exercise wants, but the image in the interaction window looks like a bunch of stacked backgrounds. I'm missing something with this program and I don't know what it is. Interaction window after program runs
Also I might add that this must be done without conditionals, for loops etc... advanced functions.
You can see that it overlays a white background that keeps the program from populating that space and I can't seem to find a way around this. I've literally brute forced every possible combination that I can think of and this is the best I could come with at this moment.
Exercise explanation is below:
Write an animation that starts with a blank screen, and each half
second adds a small dot at a completely random location — both the x coordinate and the
y coordinate are chosen at random.
Hint: Since you need to keep all the previous dots and add one more, your “model”
should probably be an image rather than a number. How can you add a dot at a specified
location to an existing image?
Hint: It would be especially nice if you could start the animation with any image, of
any size or shape, and it would sprinkle dots at random all over that background, without
rewriting the handlers.
; Random dots
(define DOT
(circle 4 "solid" "black"))
(define blank-screen
(rectangle 200 200 "solid" "white"))
(define (next-dot x)
(overlay/xy x
(min 200 (random 200)) (min 200 (random 200))
(old-dot DOT)))
(define (old-dot x)
(overlay/xy x
(min 200 (random 200)) (min 200 (random 200))
blank-screen))
(big-bang DOT
(on-tick next-dot 1/2)
(on-draw old-dot 200 200))
; Random dots
(define DOT
(circle 4 "solid" "black"))
(define blank-screen
(rectangle 200 200 "solid" "white"))
(define (next-dot x)
(overlay/xy x
(random 200) (random 200)
DOT))
(define (old-dot x)
(overlay/xy x
(random 200) (random 200)
blank-screen))
(big-bang DOT
(on-tick next-dot 1/2)
(on-draw old-dot 200 200))

How to use only one move function for all shapes

I have a problem with move function in my code.
I need it to be :
one function which can move all shapes or,
multiple functions with the same name.
So far I have move functions with diffrent names for point, circle and polygon.
I can't figure out how to make move function for picture.
If you guys can help me with that move function for picture and edit all the move function so they work like I described at beginning.
;
; POINT
;
(defun make-point ()
(list (list 0 0) :black))
(defun x (point)
(caar point))
(defun y (point)
(cadar point))
(defun set-x (point new-x)
(setf (caar point) new-x)
point)
(defun set-y (point new-y)
(setf (cadar point) new-y)
point)
(defun move (point dx dy)
(set-x point (+ (x point) dx))
(set-y point (+ (y point) dy))
point)
;
; CIRCLE
;
(defun make-circle ()
(list (make-point) 1 :black))
(defun center (circle)
(car circle))
(defun radius (circle)
(cadr circle))
(defun set-radius (circle new-rad)
(if (> 0 new-rad)
(format t "Polomer ma byt kladne cislo, zadali ste : ~s" new-rad)
(setf (cadr circle) new-rad))
circle)
(defun movec (circle dx dy)
(move (center circle) dx dy)
circle)
;
; POLYGON
;
(defun make-polygon ()
(list nil :black))
(defun items (shape)
(car shape))
(defun set-items (shape val)
(setf (car shape) val)
shape)
(defun movep (polygon dx dy)
(mapcar (lambda (b) (move b dx dy)) (items polygon))
polygon)
;
; PICTURE
;
(defun make-picture ()
(list nil :black))
;(defun movepi (picture dx dy))
; items, set-items used for polygon and picture
Your objects are just lists, you will have a hard time distinguishing among different kinds of shapes. You could add a keyword, a tag type, in front of your lists (e.g. :point, :circle, etc.) to better dispatch your move operations according to that tag, but then that would be reinventing the wheel, a.k.a. objects.
Simple functions and lists
one function which can move all shapes
You can do that, provided you can dispatch on the actual type of object you are working with. move should be able to know what kind of shape is being moved. Change your data-structures if you can to add the type of object as the CAR of your lists, and use a CASE to dispatch and then move each object as needed.
or multiple functions with the same name.
This is not possible, at least in the same package.
CLOS
(defpackage :pic (:use :cl))
(in-package :pic)
Multiple shapes have a color, so let's define a class that represent objects which have a color component:
(defclass has-color ()
((color :initarg :color :accessor color)))
If you are unfamiliar with CLOS (Common Lisp Object System), the above defines a class named has-color, with no superclass and a single slot, color. The accessor names both the reader and writer generic functions, such that you can do (color object) to retrieve an object, and (setf (color object) color) to set the color of an object to a color. The :initarg is used to define the keyword argument that is to be used in make-instance.
Here below, we define a point, which has a color and additional x and y coordinates.
(defclass point (has-color)
((x :initarg :x :accessor x)
(y :initarg :y :accessor y)))
The same for a circle:
(defclass circle (has-color)
((center :initarg :center :accessor center)
(radius :initarg :radius :accessor radius)))
And a polygon:
(defclass polygon (has-color)
((points :initarg :points :accessor points)))
Finally, a picture is a sequence of shapes:
(defclass picture ()
((shapes :initarg :shapes :accessor shapes)))
You can make a circle as follows:
(make-instance 'circle
:center (make-instance 'point :x 10 :y 30)
:color :black))
You could also define shorter constructor functions, if you wanted.
Now, you can use a generic function to move your objects. You first define it with DEFGENERIC, which declares the signature of the generic function, as well as additional options.
(defgeneric move (object dx dy)
(:documentation "Move OBJECT by DX and DY"))
Now, you can add methods to that generic function, and your generic function will dispatch to them based on one or more specializers and/or qualifiers.
For example, you move a point as follows:
(defmethod move ((point point) dx dy)
(incf (x point) dx)
(incf (y point) dy))
You can see that we specialize move based on the class of the first parameter, here named point. The method is applied when the value bound to point is of class point. The call to INCF implicitly calls (setf x) and (setf y), defined above.
Moving a circle means moving its center:
(defmethod move ((circle circle) dx dy)
(move (center circle) dx dy))
You can specialize a generic function on any class, for example the standard SEQUENCE class. It moves all the objects in the sequence with the same offsets:
(defmethod move ((sequence sequence) dx dy)
(map () (lambda (object) (move object dx dy)) sequence))
This is useful for polygons:
(defmethod move ((polygon polygon) dx dy)
(move (points polygon) dx dy))
And also for pictures:
(defmethod move ((picture picture) dx dy)
(move (shapes picture) dx dy))
Immutable version
You could also make move build new instances, but that requires to somehow make copies of existing objects. A simple approach consists in having a generic function which fills a target instance with a source instance:
(defgeneric fill-copy (source target)
(:method-combination progn))
The method combination here means that all methods that satisfy fill-copy are run, instead of only the most specific one. The progn suggests that all methods are run in a progn block, one after the other. With the above definition, we can define a simple copy-object generic function:
(defgeneric copy-object (source)
(:method (source)
(let ((copy (allocate-instance (class-of source))))
(fill-copy source copy)
copy)))
The above defines a generic function named copy-object, as well as a default method for an object of type T (any object).
ALLOCATE-INSTANCE creates an instance but does not initialize it. The method uses FILL-COPY to copy slot values.
You can for example define how to copy the color slot of any object that has a color:
(defmethod fill-copy progn ((source has-color) (target has-color))
(setf (color target) (color source)))
Notice that you have multiple dispatch here: both the source and target objects must be of class has-color for the method to be called. The progn method combination allows to distribute the job of fill-copy among different, decoupled, methods:
(defmethod fill-copy progn ((source point) (target point))
(setf (x target) (x source))
(setf (y target) (y source)))
If you give a point to fill-copy, two methods can be applied, based on the class hierarchy of point: the one defined for has-color, and the one specialized on the point class (for both arguments). The progn method combination ensures both are executed.
Since some slots can be unbound, it is possible that fill-copy fails. We can remedy to that by adding an error handler around fill-copy:
(defmethod fill-copy :around (source target)
(ignore-errors (call-next-method)))
The (call-next-method) form calls the other methods (those defined by the progn qualifier), but we wrap it inside ignore-errors.
Here no color is defined, but the copy succeeds:
(copy-object (make-point :x 30 :y 20))
=> #<POINT {1008480D93}>
We can now keep our existing, mutating, move methods, and wrap them in a :around specialized method that first make a copy:
(defmethod move :around (object dx dy)
;; copy and mutate
(let ((copy (copy-object object)))
(prog1 copy
(call-next-method copy dx dy))))
In order to see what happens, define a method for PRINT-OBJECT:
(defmethod print-object ((point point) stream)
(print-unreadable-object (point stream :identity t :type t)
(format stream "x:~a y:~a" (x point) (y point))))
And now, moving a point creates a new point:
(let ((point (make-instance 'point :x 10 :y 20)))
(list point (move point 10 20)))
=> (#<POINT x:10 y:20 {1003F7A4F3}> #<POINT x:20 y:40 {1003F7A573}>)
You would still need to change the method for the SEQUENCE type, which currently discards the return values of move, but apart from that there is little change to make to existing code.
Note also that the above approach is mostly used as a way to describe the various uses of CLOS, and in practice you would probably choose one way or another to move points (mutable or not), or you would have different functions instead of a single generic one (e.g. mut-move and move).
Rough sketch, tag shapes:
(defun p (x y) (list x y))
(defun make-shape (type points colour data)
(list* type points colour data))
(defmacro defshape (name args &key verify-points verify-args)
"define the function (make-NAME points ARGS...)
to make a shape of type :NAME. Optionally
evaluate the form VERIFY-ARGS with the
lambda-list ARGS bound and call the
function VERIFY-POINTS with the points of
the shape, ignoring its result."
(let ((type (intern name (symbol-package :key)))
(fun (intern (concatenate 'String "MAKE-" name) (symbol-package name)))
(all (gensym "ARGS"))
(colour (gensym "COLOUR"))
(points (gensym "POINTS")))
`(defun ,fun (,points ,colour &rest ,all)
(destructuring-bind ,args ,all
,verify-args
,(if verify-points `(funcall ,verify-points ,points))
(make-shape ,type ,points ,colour ,all))))
(defun singlep (list) (and list (null (cdr list))))
(defshape point () :verify-points #'singlep
(defshape circle (radius) :verify-args (assert (realp radius) radius)
:verify-points #'singlep)
(defshape polygon ())
You can use this:
CL-USER> (make-circle (list (p 0 0)) :black 2)
(:CIRCLE ((0 0)) :BLACK)
CL-USER> (make-point (list (p 1 2)) :blue)
(:POINT ((1 2)) :BLUE)
CL-USER> (make-polygon (list (p 0 0) (p 0 1) (p 1 0)) :red)
(:POLYGON ((0 0) (0 1) (1 0)) :RED)
And you can write some functions:
(defun map-points (function shape)
(destructuring-bind (type points colour &rest data)
shape
(make-shape type (mapcar function points) colour data)))
And apply them:
CL-USER> (map-points (lambda (p) (list (1+ (first p)) (second p))) '(:POLYGON ((0 0) (0 1) (1 0)) :RED))
(:POLYGON ((1 0) (1 1) (2 0)) :RED)
And solve your problem:
(defun move (dx dy shape)
(map-points (lambda (p) (destructuring-bind (x y) p (list (+ x dx) (+ y dy)))) shape))
Another thing you might want is a big case based on the type (ie CAR) of the shape, of you dispatch based on mapping the type to something in a hash table, or putting something in its symbol plist.

Racket: How do I keep an image from going off screen to appear on the other side?

I'm currently making a game where your character must dodge objects going on and off the scene.
My problem is that, when the objects go off the scene, they do not reappear on the other side.
Each object is represented by a posn structure, and they move a certain distance on the scene each time there's a tick from left to right and vise versa. I've attached which part of the code I think needs to be edited.
For clarity, a world is a struct containing a chicken and car, both of which are structs containing x and y posns.
And MOVE-CAR is a constant set to (add1 (random 49)) that determines how fast or slow the car will move during gameplay.
;update-world: world -> world
;purpose: updates the position of the car
(define (update-world a-world)
(make-world (world-chicken a-world) (move-horiz (world-car a-world) (* -1 MOVE-CAR))))
;move-horiz: posn number -> posn
;purpose: moves the posn left or right
(define (move-horiz a-posn delta-x)
(make-posn (+ delta-x (posn-x a-posn)) (posn-y a-posn)))
If more code is necessary, I will try and sift through the rest of what I have.
Any help is much appreciated.
To calculate the new x position, you are currently using:
(+ delta-x (posn-x a-posn))
If the result is between 0 and the width, this gives the correct result.
If the result is greater than the width, then the new x should be 0.
If the result is less than 0, then the new x should be the width.
Let's write a function adjust-x that adjusts the x-position you have calculated:
(define WIDTH 100)
(define (adjust-x x)
(cond
[(and (<= 0 x) (<= x WIDTH)) x]
[(> x WIDTH) 0]
[(< x 0) WIDTH]
Then you can change move-horiz to:
(define (move-horiz a-posn delta-x)
(make-posn (adjust-x (+ delta-x (posn-x a-posn)))
(posn-y a-posn)))

Smoother projectile motion in Racket?

I'm playing a little with Racket big-bang mechanism, but I cannot get both smooth and fast going projectile. There's so much ugly flickering. Here's my code:
(require 2htdp/universe
2htdp/image)
(define gx 0)
(define gy 0.35)
(struct ballstate (x y vx vy) #:transparent)
(define startstate (ballstate 10 590 7 -20))
(define (make-new-state old)
(define newvx (+ (ballstate-vx old) gx))
(define newvy (+ (ballstate-vy old) gy))
(ballstate (+ (ballstate-x old) newvx)
(+ (ballstate-y old) newvy)
newvx
newvy))
(define (main)
(big-bang startstate
[on-tick make-new-state]
[to-draw place-ball-at]
[on-key reset]))
(define (place-ball-at s)
(place-image (circle 10 "solid" "red")
(ballstate-x s)
(ballstate-y s)
(empty-scene 800 600)))
(define (reset s ke)
startstate)
(main)
The question is: how to make it better, faster, smoother and flicker-free?
Here are two things that might help:
The on-tick clause takes an optional parameter that determines the time between two ticks. The default is 1/28, so if you lower this you will get more frames resulting in a smoother animation.
If your program takes longer than the time between each tick to produce an image, you will see stuttering. Precomputing everything that can be precomputed is a good thing. For example, there is no reason to produce a new empty scene each time, so below I have simply stored it in a variable.
(define (main)
(big-bang startstate
[on-tick make-new-state 1/50]
[to-draw place-ball-at]
[on-key reset]))
(define background (empty-scene 800 600))
(define (place-ball-at s)
(place-image (circle 10 "solid" "red")
(ballstate-x s)
(ballstate-y s)
background))