In the first few days of class, you have received a crash-course introduction to programming in Racket, in particular with images. Furthermore, you also learned about algorithmic decomposition and its importance in computer programming. In this project, we’ll practice these techniques further by playing around with images.
In this course, we’re concerned about writing good code. What does that look like? Good programs have two qualities we’re looking after:
External correctness is observable in the sense that we can run a program and determine that its behavior is correct. In contrast, internal correctness concerns the design of our program: Is it readable? Does it follow the design guidelines outlined in the exercise write-up and otherwise adhere to good coding conventions?
External correctness is often a given—we always want to write programs that do the right thing. However, we’ll find in this course that internal correctness is just as important! Computer programs are not just “consumed” by computers. Other people will read and even modify our programs. In particular, you will find that in three months (or perhaps sooner), you will feel like “another person”, forgetting what you were thinking when you were designing the program. So it is important that we build habits that are conducive to writing readable code.
As we may have discussed previously, programming is not a spectator sport (really, few things are in this world). You need to write programs to learn how to program. You often need to write programs to learn to think computationally. The labs and projects will be you primary vehicle for this sort of practice. This alone may be enough for some of you to master Racket programming. But for many people, you will need additional practice to truly master these concepts.
One way to do this is through “playing around.” What we mean by this is programming for the purposes of exploring a programming language or its libraries, rather than a specific end-product. This is how many of us approach learning a new language. We may have a few starting points in our back pockets, but as we write, we are less concerned about finishing the task at hand as we are about understanding the new environment. This exploration usually involves investigating and answering questions such as “How do I do X in this language?” or “How does feature X that I don’t understand compare to feature Y that I do understand?” or even “How does this language lead me to think differently about algorithm design?”
Because you are beginning programmers, your questions will likely be markedly simpler: “How can I even make a thing happen?” Hnd “how do I type a thing?”. But nevertheless, “playing around” lets you tackle some of those ideas. You might start with one our lab exercises that you developed with a peer as a starting point and then change the code in ways that are novel to you. Or you might start from scratch and try to reproduce something you have seen or written before. There is no right way to go about “play”. Its the attitude that’s important: one of exploration and asking and answering questions rather than focusing on the final product.
For this mini project, you will create three files: spaceship.rkt, freestyle.rkt, and my-image-utils.rkt.
The particular contents of each are detailed below.
For additional details on turning on this assignment and interpreting your feedback from it, please consult the Gradescope page.
For this first part of the demo, your goal is to define an image called rainbow-spaceship that looks like this:

Here are the details of the rainbow-spaceship image:
The “horizontal pyramid” effect is due to how the image library places sub-images with beside when they are different heights.
Smaller images are automatically centered vertically relative to the taller images.
For example the left-most red stripe is vertically centered relative to the red-orange two-stack of stripes next to it.
Make sure that the definition of rainbow-spaceship mimics its structure.
Also pay special attention to remove redundancy from your code using define and, as appropriate, functions.
We do not yet have the machinery to elegantly capture the growing, symmetric nature of the columns of the spaceship.
However, note the relationship between the stripes of each successive column.
How can you capture this relationship in code?
Please put your definition in the file spaceship.rkt
Now that you’ve had a taste for manipulating images and using define and functions to reduce redundancy, you will now get the opportunity to play around making images of some complexity.
As discussed, this is open-ended: we have no particular image for you to draw and only some requirements about how you design your program.
Feel free to try the following starting points:
To encourage you to practice algorithmic decomposition, your program must follow these design requirements:
define command. These sub-images should be independent of each other (i.e., not defined in terms of each other), but can then be combined together.define should be evocative of what the image is.
It should be pithy, a few words at most, but at the same time descriptive.
Racket programming conventions say that these names should be in all lowercase with dashes between words, e.g., names-like-this.define that is the overall image, which you should call my-image.Your program should include introductory documentation, as below. (All of your Racket files should include similar documentation.)
# lang racket
(require csc151)
; freestyle.rkt
;
; An amazing image of <....> I've created.
;
; CSC-151 SPring 2024
; Mini Project 1, Part 2
; Author: Stu Dent
; Date: 2024-01-31
; Acknowledgements: ...
; (...code below here...)
;
(define my-image...)
Other than this, there are no minimum requirements regarding limits, code size, or complexity. Have fun with it!
Please put your definitions in the file freestyle.rkt.
As you have likely noted, as your images and programs grow in complexity, it is helpful to write procedures (functions, subroutines) that encapsulate and parameterize a piece of code. For example, you may find that you regularly want to build “blocks” by overlaying an outline on a solid figure.
;;; (block size color) -> image?
;;; size : non-negative-integer?
;;; color : color?
;;; Create a square block of the specified size and color
(define block
(lambda (size color)
(overlay (square size 'outline 'black)
(square size 'solid color))))
Write five (5) procedures that you think will be useful in building more complex images.
Create a list of five images, one build from each procedure, and call that list examples.
(define examples (list (block 20 "red") ...))
Once again, there are no minimum requirements regarding limits, code size, or complexity.
Please put our procedures and the examples list in the file my-image-utils.rkt
This part is optional.
Take the image you generated in part two and turn it into a procedure, generate-my-image, with at least two parameters (e.g., color and size) so that someone can easily make variants of that image.
Provide a call to your procedure that generates the same image you used in part 2. Call it my-image-alt.
(define my-image-alt (generate-my-image ...))
Provide a call to your procedure that generates a substantially different image. Call it my-other-image.
(define my-other-image (generate-my-image ...))
All three new new definitions (for generate-my-image, my-image-alt, and my-other-image) should go in the file freestyle.rkt.
You are under no obligation to use additional functions or language features beyond what we have introduced in the first week or so of the class.
However, You may feel limited by the functions we have discussed so far.
If so, you are free to reference the documentation for the csc151 library for some of the functions available (see the “Reference” link at the top of the page). Note that that both the library and the documentation are “in process”. If there’s something you’d like, you might ask Prof. Rebelsky about it.
Note that this documentation may not be entirely comprehensible to you yet! That is fine. If you choose to explore this library in more detail, we recommend experimenting with these functions in the interactions pane and figure out how they work before throwing them into your code. Remember, if you adapt any code from this library’s documentation, you should cite that you did so in a comment in your code!
Can I use procedures we haven’t covered yet that I find in the reference pages or in the Racket documentation or elsewhere?
Yes, provided you cite them.
What exactly does the generate-my-image function do?
generate-my-imagetakes some parameters (colors, numbers) and generates an image similar to the freestyle image you created in part two. It is intended to be a generalized version of that image.
I would like to know a little more about (define my-image ...), (define my-image-alt ...), etc and what those terms mean and do.
As you know, we use
defineto associate a value with a name. For example, we might write(define two-circles (beside (circle 20 "red") (circle 20 "black"))). These definitions are much like those, except that instead of using a supplied procedure, likebesideorcircle, you will be using a procedure you have defined yourself.
Could you explain the difference between generate-my-image, my-image-alt, and my-other-image?
generate-my-imageis a procedure. It should return an image that looks something like the image you created earlier.
my-image-altshould be the result of a call to generate-my-image. It demonstrates thatgenerate-my-imagecan create something that looks identical to (or nearly identical to) the original image.
my-other-imageshould also be the result of a call togenerate-my-image. It demonatrates thatgenerate-my-imagecan create something that looks similar to, but different from, the original image.
Could you give an example?
Consider the “fancy house” from the reading on user-defined procedures. We might call that
my-image.
We wrote a
(fancy-house size color)procedure in the corresponding lab. That’s a model of the kind of thing we’d expect forgenerate-my-image. Depending on how we defined that procedure, we would write something like(define my-image-alt (fancy-house 50 "black"))to get another house that looks identical tomy-image.
Of course, since you writing
generate-my-image, you would use(define my-image-alt (generate-my-image 50 "black"))or something like that.
But the point of
fancy-house(orgenerate-my-image) isn’t just that we can make an identical house (or other image). We should also be able to make variants. So we might write(define my-other-image (fancy-house 100 "yellow"))to show that we can make different size and diffent color houses.
Once again, you would write
(define my-other-image (generate-my-image 100 "yellow"))or something like that.
For part 4 of the MP1, how can my-image-alt and my-image be compared if we had to edit the code to make the generation procedure?
You shouldn’t alter your code from part 2 to make part 4. Suppose this is my code from part 2.
(define ayes
(beside (solid-circle 20 "blue")
(solid-rectangle 20 1 (rgb 0 0 0 0))
(solid-circle 20 "blue")))
(define smile
(overlay (above (solid-circle 50 "yellow")
(solid-rectangle 1 10 (rgb 0 0 0 0)))
(solid-circle 50 "red")))
(define my-image
(overlay (above ayes smile)
(solid-circle 150 "yellow")
(outlined-circle 150 "black" 5)))
For part 4, I’d write something like the following.
(define make-eyes
(lambda (eye-color)
(beside (solid-circle 20 eye-color)
(solid-rectangle 20 1 (rgb 0 0 0 0))
(solid-circle 20 eye-color))))
(define make-smile
(lambda (smile-color face-color)
(overlay (above (solid-circle 50 face-color)
(solid-rectangle 1 10 (rgb 0 0 0 0)))
(solid-circle 50 smile-color))))
(define generate-my-image
(lambda (face-color eye-color smile-color)
(overlay (above (make-eyes eye-color)
(make-smile smile-color face-color))
(solid-circle 150 face-color)
(outlined-circle 150 "black" 5))))
Now, I’d write the following to generate the image.
(define my-image-alt (generate-my-image "yellow" "blue" "red"))
And something like the following to generate an alternate
(define my-other-image (generate-my-image "green" "red" "black"))
I hope that makes sense.
In grading these assignment, we will look for the following for each level. We may also identify other characteristics that move your work between levels.
You should read through the rubric and verify that your submission meets the rubric.
Submissions that lack any of these characteristics will get an I.
[ ] Includes the three specified files (correctly named).
[ ] Includes an appropriate header on each file that indicates the course, author, etc.
[ ] Code runs in DrRacket.
Submissions that lack any of these characteristics will get an R or below.
[ ] In Part 1, creates the correct spaceship.
[ ] In Part 2, includes at least five sub-images.
[ ] In Part 2, includes at least one procedure.
[ ] In Part 2, the image is correctly named `my-image`.
[ ] In Part 3, includes five helper procedures.
[ ] In Part 3, each helper procedure has at least one parameter.
[ ] In Part 3, includes the required `examples` list, which has the required form.
[ ] In Part 4, the procedure has at least two parameters.
[ ] In Part 4, the procedure is correctly named `generate-my-image`.
[ ] In Part 4, there is a call to generate `my-image-alt`.
[ ] In Part 4, there is a call to generate `my-other-image`.
Submissions that lack any of these characteristics will get an M or below.
[ ] In Part 1, code is concise and avoids repetition.
[ ] In Part 2, image is particularly interesting or creative.
[ ] In Part 3, one or more of the helper procedures is especially innovative.
[ ] In Part 4, `my-image-alt` appears the same as `my-image`.
[ ] In Part 4, `my-other-image` appears different from `my-image`.
[ ] In Part 4, decomposes the procedure.