Class was cancelled due to illness and unavailability of teachers.
Animations. Details to come.
This class was devoted to work time on our final drawing projects. The requirements for this project are:
We started the class by collecting everyone's ideas for what they would like to make. Ideas included: a village with many houses, a city with many skyscrapers, a forest with many trees, and so on.
Since several students wanted stars in the sky, we worked together to create a picture of a starry night sky. This is a challenging project, with a trick to it, and we approached it in several smaller steps.
First, we set up the base program like this:
main = drawingOf(night)
night = sky & stars
sky = solidRectangle(20, 20)
star = colored(solidCircle(0.05), white)
stars = blank
Notice that we haven't yet decided how to define the stars variable, but the rest of the program is working. From here on, we'll just try different definitions for stars, and the rest of the program will remain the same. A first attempt would use a simple list comprehension, like this:
stars = pictures([ translated(star, x, y) | x <- [-10 .. 10], y <- [-10 .. 10] ])
This definition certainly fills the night sky with stars! But it places them into an even grid, which looks wrong! The problem is that when we separate the ranges of x and y with a comma in a list comprehension, this means to combine each value of x with every value of y. So at each x coordinate, there's a full column of stars in all possible y coordinates! That isn't what we wanted at all, so the alternative is to separate them with another vertical line:
stars = pictures([ translated(star, x, y) | x <- [-10 .. 10] | y <- [-10 .. 10] ])
If we try this, we're still disappointed. There are no longer full columns of stars, but all the stars sit on the same diagonal line. The reason is that this type of list comprehension combines each value of x with the corresponding value of y. That means the first x pairs with the first y, for a star at (-10, -10), then (-9, -9), then (-8, -8), and so on. If you look at these points, you will see that they all fall along that diagonal line.
To fix this, we pull out a trick: the computer already knows a function called shuffled, which mixes a list around into a random order, just like shuffling a deck of cards. The shuffled function needs two parameters: the list, and a number that tells it which order to choose. This gives us a final attempt:
stars = pictures([ translated(star, x, y) | x <- shuffled([-10 .. 10], 1)
| y <- [-10 .. 10] ])
We've only shuffled one of the x and y ranges. This is okay, because it means that each y gets paired with a random x - the next one from the shuffled list. We could have shuffled both lists, as well, but it's not necessary. However, if we do shuffle both lists, it's important to pass different numbers for the second parameter! Why? Well, if we passed the same parameter, we'd shuffle both lists in exactly the same way. So even though the individual numbers could be in a random order, we'd still match each x with the same y, and get stars in a single diagonal line again! That's why shuffled needs that second parameter (called a seed): it tells the function which of the many possible orders it should choose.
Altogether, this gives us a nice randomized field of stars. You don't need to use this in your project, but you're welcome to!
In this class, we continued working on the list comprehension challenges from last week.
We did one of the challenges together. In this challenge, we set out to create the pattern of blocks in challenge #15. The first step was to identify a basic building block, which we built from two solid polygons:
cube = colored(solidPolygon([(0, -2), ( 2, -1), ( 2, 1), (0, 0)]), black)
& colored(solidPolygon([(0, 0), (-2, 1), (-2, -1), (0, -2)]), gray(0.5))
Once we have one cube, it's easy enough to make a row of them. A cube extends from -2 to 2 in the x dimension, so its width is 4, and we want to place the cubes 4 units apart.
row = pictures([translated(cube, x, 0) | x <- [-12, -8 .. 8 ])
The final picture of made up of staggered rows, so before we can stack rows vertically, we need to define a staggered pair of them.
rowPair = row & translated(row, 2, 3)
Finally, we just stack row pairs to cover the screen.
design = pictures([ translated(rowPair, 0, y) | y <- [-12, -6 .. 12] ])
main = drawingOf(design)
We learned a few things in creating this design:
This pattern is helpful in some of the other list comprehensions, as well. The same structure used here can be used to create the brick pattern, for example, and a similar strategy helps with the dot pattern in challenge #16.
In this class, we worked on list comprehension challenges, which can be found here. We're working in teams, to see who can complete the most challenges.
We learned about list comprehensions with multiple variables. These are like the list comprehensions from last week, but have several clauses on the right of the vertical line. Each clause defines a different variable, and the values it is chosen from. The next example shows a grid, made of squares:
main = drawingOf(grid)
grid = pictures([ translated(rectangle(0.9, 0.9), x, y)
| x <- [-10 .. 10], y <- [-10 .. 10]])
The comma between the two clauses means there are squares drawn for each possible combination of choices for x and y. Another choice is to combine each x only with the corresponding choice of y, which is done by using another vertical line:
main = drawingOf(grid)
grid = pictures([ translated(rectangle(0.9, 0.9), x, y)
| x <- [-10 .. 10] | y <- [-10 .. 10]])
By combining the drawing operations you already know, and these list comprehensions with several clauses, you should be able to create most of the pictures from the challenges document!
After sharing some cool projects from the previous week, we learned about list comprehensions. As a starting point, consider this project, which was similar to one created by several students.
main = drawingOf(target)
target = circle(1) & circle(2) & circle(3) & circle(4)
This works, but it repeats itself. Maybe this is okay if you want 4 circles. Imagine you wanted 10, or 100, or 1000! Typing the circle function over and over would take a long time. Luckily, we're working with a computer that's very good at working out repeated tasks, if we just tell it what to do.
The first step will be to use a list of pictures. We've used lists before, for polygons, which needed a list of points. Remember these facts about lists:
Once you have a list of pictures, you can combine it into a single picture using the pictures function. This function acts like the & symbol, except that it is used once for a whole list of pictures, rather than having to used over and over. Putting all of this together, you can write this program:
main = drawingOf(target)
target = pictures([circle(1), circle(2), circle(3), circle(4)])
This isn't really any better than our first program, yet! In fact, it's worse. The reason we wrote it this way, though, is that there are some tricks for working with lists that we can use, now that we've got a list in our program, to make the program much shorter. We'll use the first of these now.
Here's how a list comprehension works. When writing a list that repeats a lot, inside the square brackets, instead of just listing all the elements separated by commas, you can instead provide a pattern that will be followed by everything in the list, and substitution rules for that pattern. The two halves are separated by a vertical line (hold Shift and press \). The pattern goes on the left of the vertical line, and the substitution rules go on the right. So the list comprehension looks like: [ pattern | substitution rules ]
.
When writing the pattern, you just write the common parts of each thing in the list, but use a new variable name for each part that changes. In our case, the pattern looks like circle(r)
. The name of the circle function, and the parentheses, are always the same. The radius is different each time, so we gave it a variable name, r. (Of course, you can choose whatever name you like, as long as you're consistent!)
Substitution rules tell the computer what to plug in when it finds those variables. In our case, we want to say that r should come from the list [1, 2, 3, 4]
. The phrase "comes from" is written as <-
, which looks a bit like a backward arrow. The substitution rules altogether look like r <- [1, 2, 3, 4]
.
Let's assemble the entire list comprehension: [ circle(r) | r <- [1, 2, 3, 4] ]
Giving us the following complete program:
main = drawingOf(target)
target = pictures([ circle(r) | r <- [1, 2, 3, 4] ])
Just the list comprehension makes our original program only slightly shorter. You might be curious if it was really worthwhile. But imagine that instead of four circles, we'd wanted ten. Now the list comprehension is much nicer. But imagine we wanted 1000. We've avoid writing the circle function each time, but so far, you'd still need to type out a list of a thousand numbers.
There's an easier way. Another way to write the list [1, 2, 3, 4]
is to write [1 .. 4]
. This is called a list range. If you just give the starting number, then two dots, then the ending number, the computer can fill in all numbers in between (counting by one). If you'd prefer to count by something besides one, you need only give the first two numbers, as in: [0.1, 0.2 .. 15]
(which describes a list of the radius for a hundred and fifty circles!)
Putting this together, we can build a much more impressive program to draw all of those circles:
main = drawingOf(target)
target = pictures([ circle(r) | r <- [1, 2, 3, 4] ])
Now that we've worked through one example of a list comprehension in detail, it's time to practice some more. We ended the class with a sequence of practice problems, to rewrite different programs using a list comprehension.
Problem 1: Horizontal row of circles
Starting program (link):
main = drawingOf(circles)
circles = translated(solidCircle(1/2), -9, 0) &
translated(solidCircle(1/2), -8, 0) &
translated(solidCircle(1/2), -7, 0) &
translated(solidCircle(1/2), -6, 0) &
translated(solidCircle(1/2), -5, 0) &
translated(solidCircle(1/2), -4, 0) &
translated(solidCircle(1/2), -3, 0) &
translated(solidCircle(1/2), -2, 0) &
translated(solidCircle(1/2), -1, 0) &
translated(solidCircle(1/2), 0, 0) &
translated(solidCircle(1/2), 1, 0) &
translated(solidCircle(1/2), 2, 0) &
translated(solidCircle(1/2), 3, 0) &
translated(solidCircle(1/2), 4, 0) &
translated(solidCircle(1/2), 5, 0) &
translated(solidCircle(1/2), 6, 0) &
translated(solidCircle(1/2), 7, 0) &
translated(solidCircle(1/2), 8, 0) &
translated(solidCircle(1/2), 9, 0)
Problem 2: Vertical column of circles
Starting program (link):
main = drawingOf(circles)
circles = translated(solidCircle(1), 0, -9) &
translated(solidCircle(1), 0, -7) &
translated(solidCircle(1), 0, -5) &
translated(solidCircle(1), 0, -3) &
translated(solidCircle(1), 0, -1) &
translated(solidCircle(1), 0, 1) &
translated(solidCircle(1), 0, 3) &
translated(solidCircle(1), 0, 5) &
translated(solidCircle(1), 0, 7) &
translated(solidCircle(1), 0, 9)
Problem 3: Diagonal line of boxes
Starting program (link):
main = drawingOf(boxes)
boxes = translated(rectangle(1, 1), -9, 9) &
translated(rectangle(1, 1), -7, 7) &
translated(rectangle(1, 1), -5, 5) &
translated(rectangle(1, 1), -3, 3) &
translated(rectangle(1, 1), -1, 1) &
translated(rectangle(1, 1), 1, -1) &
translated(rectangle(1, 1), 3, -3) &
translated(rectangle(1, 1), 5, -5) &
translated(rectangle(1, 1), 7, -7) &
translated(rectangle(1, 1), 9, -9)
Problem 4: Star made of rotated squares
Starting program (link):
main = drawingOf(star)
star = rotated(rectangle(10, 10), 15) &
rotated(rectangle(10, 10), 30) &
rotated(rectangle(10, 10), 45) &
rotated(rectangle(10, 10), 60) &
rotated(rectangle(10, 10), 75) &
rotated(rectangle(10, 10), 90)
Each of these programs can be rewritten into a program with a list comprehension!
We had a lot of new students this week! As a result, we split the class into two parts:
Because the class was largely review and independent student work, there is no video for this week.
We started this class by sharing some projects from the previous week, and reviewing the idea of top-down design. Students made various projects out of rectangles and circles by combining them, and applying the four transformations: translation, rotation, scaling, and coloring.
We then talked about several new kinds of shapes:
A program that draws text looks like this:
main = drawingOf(name)
name = text("Chris")
The letters that should appear in the picture itself are in quotation marks. This tells the computer that Chris isn't a variable or something else in the programming language, but just a bunch of letters. So this draws the letters C, h, r, i, and s in the center of the screen. Like any picture, you can change the location, direction, size, and color by using the transformation functions (translated
, rotated
, scaled
, and colored
).
To draw an arc, the computer needs three things:
Both angles are oriented as in this diagram:
A program that draws an arc looks like this:
main = drawingOf(smile)
smile = arc(180, 360, 5)
Notice that 360 degrees and 0 degrees are the same direction in the picture. Still, it matters which one is used. An arc from 0 degrees to 180 degrees goes through all the angles between 0 and 180, so it is the top half of the circle. But an arc from 180 degrees to 360 degrees goes through all the angles between 180 and 360, so it's the bottom half of the circle. (However, changing the order of the two angles doesn't change the shape.) Finally, the radius of 5 tells how large of a circle to trace out parts of.
Sectors take exactly the same parameters as arcs. But instead of drawing a part of an outlined circle, they draw a filled in pie slice. A program that draws a sector looks like this:
main = drawingOf(pie)
pie = sector(60, 120, 5)
The final kind of shape we talked about was the polygon. While all the other shapes we've learned are pretty specific, polygons can be used to make nearly any shape you like! Any shape with straight edges for sides is a polygon. But even shapes with curved edges can be drawn approximately by picking points along the edges and connecting them with straight lines instead. The result won't be perfect, but it will be close. This is a powerful enough idea that a lot of professional video games are actually made with nothing but polygons!
Drawing polygons relies on an understanding of the coordinate plane. A coordinate plane looks something like this:
To describe a point on the plane, you first decide which number on the horizontal (x) axis the number is above or below. This is the x coordinate. Then you decide which number on the vertical (y) axis the number is beside. This is the y coordinate. Then you can describe a point as (x, y). For example:
The parameter to the polygon function is a list of the points at the corners (called vertices) of the polygon. In CodeWorld, a list always starts with an opening square bracket, "[", and ends with a closing square bracket, "]". These brackets belong inside the parentheses for the polygon function, since they are part of the parameter. A program to draw a star looks something like this:
main = drawingOf(star & coordinatePlane)
star = polygon([(0, 8), ( 2, 3), ( 7, 2), ( 3, -1), ( 5, -7),
(0, -4), (-5, -7), (-3, -1), (-7, 2), (-2, 3)])
There's a lot of punctuation in the definition of star, which you have to get exactly right! The definition includes all of these:
Once you've got the punctuation down, though, you can make any shape you like.
Your challenge for next week is to choose a picture that makes creative uses of arcs, sectors, text, and/or polygons. Be original and express yourself! You'll have a chance to show off your work in next week's class, and you can also use it as the basis for your first big project in the class, which we'll work on next week.
In this class, we reviewed some of the ideas of CodeWorld from the previous week. This includes simple shapes like circles and rectangles, combining shapes with the & sign, and modifying shapes by changing their color, translating, rotating, and scaling. We reviewed how to define variables, and how they combine to describe the picture that you want. All of these topics are covered in the notes for Class #1, below.
Next, we talked about designing programs with CodeWorld.
Top-down design is a technique used for building more complex programs out of simpler pieces. In CodeWorld, we apply top-down design by breaking down a picture we'd like to draw into several parts. For example, suppose you wanted to draw a picture of a park. You might start out by defining a variable to represent the entire picture, like this:
main = drawingOf(park)
park = bench & trees & grass & sky
This program is not complete! The words bench
, trees
, grass
, and sky
are not known to the computer, so trying to run the program will produce an error message, saying that these variables are not "in scope". Think of "not in scope" as slang for saying the computer doesn't know the meaning of a word. To complete the program, you'll need to define the remaining words.
bench = ...
trees = ...
grass = ...
sky = ...
In general, each of these pieces might itself be made up of several parts. And each of those might have several parts. But eventually, you will reach shapes simple enough that they can be described using shapes that are already known, such as circles and rectangles! When you reach that point, you're done.
It might take a while to get there, though, and until all of these variables are defined, you can't even run your program to see what it looks like so far! That's no good. So instead of just jumping in and defining everything, we can start by writing a very simple definition of all of the parts. To do this, it helps to use the blank variable, which is just a picture with nothing in it.
bench = blank
trees = blank
grass = blank
sky = blank
By defining the parts of the program that you haven't finished yet as "blank", you can run your program, and see the results of your program as you write it. Much better!
We finished by practicing on our first class project. Everyone chose a picture with several parts to create on their own.
(Unfortunately, the video recording of this class was lost. There is no video for class #2.)
In today's class, we got started with computer programming!
Several of you were familiar with some programming languages, such as JavaScript, Scratch, or PHP. A programming language is just a special language that the computer understands, which you can use to tell it what you'd like your programs to do. For this class, we are using a programming language called Haskell, and we'll use a web site called CodeWorld, which is at http://code.world.
Our very first computer program looked like this:
main = drawingOf(codeWorldLogo)
When typing this program, we discovered a few things:
That's a lot of words! Don't worry, though. We'll have plenty of time to practice.
Our first program drew a picture that the computer already knows about: the CodeWorld logo. But that's no fun! You probably want to draw your own picture instead. To draw your own picture, you'll need to describe it in the Haskell language, so that the computer knows what to draw. We moved on to draw a simple ball:
main = drawingOf(ball)
ball = circle(5)
This program defines the word main to be a drawing of a ball. Then it defines the word ball to mean a circle, with a radius of 5 units. The entire screen where your program runs has a width and height of 20 units, so a radius of 5 fits nicely onto the screen. We've now learned a new function that the computer knows: circle. The function solidCircle is similar, but fills in the circle, as well.
We can also color our shapes. We now modified the previous program, to produce:
main = drawingOf(blueBall)
ball = circle(5)
blueBall = colored(ball, blue)
Things we learned while writing this program include:
For our final step, we set out to add a stripe to our ball. The program we wrote was this:
main = drawingOf(stripedRedBall)
ball = circle(3)
redBall = colored(ball, red)
stripe = solidRectangle(6, 1)
stripedRedBall = stripe & redBall
Some things we learned when writing this are:
Finally, we talked about the translated, scaled and rotated functions. These functions are similar to colored, in that they start with any picture, and produce a different picture that is just like the first one except for one change. The change is: