A Square
You have been drawing squares since Chapter 1. This final chapter brings every skill together to build a whole neighbourhood from reusable components. Let's start by recapping the foundation — a function that draws a square of any size.
func drawSquare(pen: Pen, size: Double) { for _ in 1...4 { pen.move(size) pen.turn(90) } }
Notice that the pen is passed as a parameter. This is an important design pattern — it means drawSquare can work with any pen, in any colour, wherever the pen happens to be positioned. All the functions you write in this chapter should follow the same pattern.
You will also need to reposition the pen between components without drawing a line. Use penUp() and penDown() for this:
// Lift the pen, travel, lower it again pen.penUp() pen.move(50) // no line is drawn pen.penDown() pen.move(50) // drawing resumes
It is worth writing a small jump helper that you can use throughout this chapter:
func jump(pen: Pen, distance: Double) { pen.penUp() pen.move(distance) pen.penDown() }
With these two building blocks — a drawing function and a movement helper — you are ready to assemble a neighbourhood.
| Standard | Connection |
|---|---|
| Geometry — Properties of Shapes | A square has four equal sides and four right angles (90° turns). Encoding these constraints in a function ensures every drawn square satisfies both conditions automatically. |
A Triangle
Every building in our scene needs a roof. Write a function drawRoof(pen:base:) that draws an equilateral triangle — this gives a clean, symmetric roof shape.
Remember from Chapter 3: for an equilateral triangle (three equal sides, three 60° angles), the pen needs to turn by the exterior angle at each vertex. What is the exterior angle of an equilateral triangle?
// Draw an equilateral triangle roof. // The pen starts at the bottom-left corner, facing right. // After the call, the pen is at the bottom-right corner, facing right. // (The base itself is NOT drawn — it is the top edge of the wall below.) func drawRoof(pen: Pen, base: Double) { // your code here }
// An equilateral triangle has exterior angles of 360° ÷ 3 = 120°. // The pen turns left 60° to head up the left slope, reaches the apex, // then turns right 120° to head down the right slope. func drawRoof(pen: Pen, base: Double) { pen.turn(-60) // face upper-right at 60° above horizontal pen.move(base) // go to apex pen.turn(120) // turn to face lower-right at 60° below horizontal pen.move(base) // go to bottom-right corner pen.turn(-60) // back to original heading (facing right) } // Test it let pen = Pen() pen.color = .red drawRoof(pen: pen, base: 100)
The three turns sum to −60° + 120° + (−60°) = 0°, which means the pen ends up facing the same direction it started — a useful property when composing shapes.
| Standard | Connection |
|---|---|
| Geometry — Triangles | Exterior angle theorem: the exterior angle at each vertex equals the supplement of the interior angle. For an equilateral triangle, each interior angle = 60°, so each exterior angle = 120°. The total of all exterior angles = 360°. |
A Cross
Churches need a cross! Write drawCross(pen:size:) that draws a plus-sign cross as two perpendicular strokes of equal length. Use penUp() and penDown() to lift the pen between strokes so no unwanted lines appear.
The cross should be symmetric: each arm extends size / 2 from the centre point.
// Draw a plus-sign cross. // The pen starts at the BOTTOM of the vertical stroke, facing right. // After the call it returns to the same position and heading. func drawCross(pen: Pen, size: Double) { let half = size / 2 // Step 1: draw the vertical stroke (up through the centre to the top) // Step 2: penUp back to the centre, then left to the start of the horizontal stroke // Step 3: draw the horizontal stroke // Step 4: return pen to original position and heading // your code here }
func drawCross(pen: Pen, size: Double) { let half = size / 2 // ── Vertical stroke ────────────────────────────────── pen.turn(-90) // face upward pen.move(size) // draw from bottom to top // Lift pen, go back to centre pen.penUp() pen.turn(180) // face downward pen.move(half) // back to centre, no drawing pen.turn(-90) // face left pen.move(half) // go to left end of horizontal arm pen.turn(180) // face right pen.penDown() // ── Horizontal stroke ───────────────────────────────── pen.move(size) // draw from left end to right end // ── Return to start position, facing right ──────────── pen.penUp() pen.turn(180) // face left pen.move(half) // back to centre pen.turn(90) // face down pen.move(half) // back to bottom pen.turn(-90) // face right (original heading) pen.penDown() } // Test it let pen = Pen() pen.color = .white drawCross(pen: pen, size: 40)
| Standard | Connection |
|---|---|
| Geometry — Perpendicular Lines | The two strokes of the cross are perpendicular (intersecting at 90°). Using turn(-90) and turn(90) encodes perpendicularity directly in the code. |
A Church
Now compose your three building-block functions into a complete church. Write drawChurch(pen:size:) that draws:
- A square body (colour:
.gray) - A triangular roof sitting on top (colour:
.red) - A small cross at the apex of the roof (colour:
.white)
This is the key skill of Chapter 6: carefully repositioning the pen between each component using penUp() and penDown().
drawSquare the pen is back at its starting position, facing right. To reach the top-left corner of the square, turn left and move size upward. After drawRoof the pen is at the top-right corner of the roof base, facing right.
func drawChurch(pen: Pen, size: Double) { // 1. Draw the body pen.color = .gray drawSquare(pen: pen, size: size) // 2. Move to top-left corner of body, then draw roof // your code here // 3. Move to the apex of the roof, then draw cross // your code here }
func drawChurch(pen: Pen, size: Double) { // ── 1. Body ────────────────────────────────────────── pen.color = .gray drawSquare(pen: pen, size: size) // Pen is back at bottom-left, facing right. // ── 2. Roof ─────────────────────────────────────────── // Move to top-left corner (turn up, walk size, turn right) pen.penUp() pen.turn(-90) // face up pen.move(size) // arrive at top-left of body pen.turn(90) // face right pen.penDown() pen.color = .red drawRoof(pen: pen, base: size) // Pen is now at top-right of roof base, facing right. // ── 3. Cross at the apex ────────────────────────────── // Apex is at base/2 to the left and base*sin(60°) ≈ base*0.87 up. // Simpler: move left to centre of roof base, then up to apex height. pen.penUp() pen.turn(180) // face left pen.move(size / 2) // reach horizontal centre pen.turn(-90) // face up pen.move(size * 0.87) // move up approx to apex (sin 60° ≈ 0.87) pen.turn(90) // face right pen.penDown() pen.color = .white drawCross(pen: pen, size: size * 0.2) } // Test let pen = Pen() drawChurch(pen: pen, size: 80)
| Standard | Connection |
|---|---|
| Geometry — Modelling with Shapes | A church is modelled as a composite of three primitive shapes. Each component occupies a specific region — identifying those regions and computing their relative positions requires spatial reasoning and coordinate thinking. |
Row of Churches
Draw a row of five churches, each 10 points larger than the last. The first church should have size: 40, the last size: 80. Leave a gap of 20 points between each church.
Because drawChurch always leaves the pen at the bottom-left of the church, facing right, moving to the start of the next church is straightforward.
let pen = Pen() let gap = 20.0 for i in 0...4 { let size = 40.0 + Double(i) * 10 // draw one church then move pen right to the next start position // your code here }
let pen = Pen() let gap = 20.0 for i in 0...4 { let size = 40.0 + Double(i) * 10 drawChurch(pen: pen, size: size) // After drawChurch the pen is at bottom-left facing right. // Jump right by the church's width plus the gap. jump(pen: pen, distance: size + gap) }
| Standard | Connection |
|---|---|
| Functions & Sequences | The church sizes form an arithmetic sequence: 40, 50, 60, 70, 80 — a common difference of 10. The loop variable i acts as the index; size = 40 + 10i is a linear function of i. |
A Pretty House
A house is more detailed than a church. Write drawHouse(pen:size:) that includes:
- A rectangular body (width =
size, height =size * 0.75) - A triangular roof on top (call
drawRoof) - A rectangular door centred at the bottom (width =
size * 0.25, height =size * 0.4) - Two square windows, one on each side of the door (side =
size * 0.18)
Write helper functions drawRect and drawWindow first — it will make drawHouse much cleaner.
// Draw a rectangle (width × height), pen at bottom-left facing right. func drawRect(pen: Pen, width: Double, height: Double) { // your code here } func drawHouse(pen: Pen, size: Double) { let bodyH = size * 0.75 let doorW = size * 0.25 let doorH = size * 0.4 let winSide = size * 0.18 // 1. Body // 2. Roof // 3. Door (centred horizontally at the bottom) // 4. Left window and right window // your code here }
func drawRect(pen: Pen, width: Double, height: Double) { for _ in 1...2 { pen.move(width) pen.turn(90) pen.move(height) pen.turn(90) } } func drawHouse(pen: Pen, size: Double) { let bodyH = size * 0.75 let doorW = size * 0.25 let doorH = size * 0.4 let winSide = size * 0.18 // ── 1. Body ─────────────────────────────────────────── pen.color = .yellow drawRect(pen: pen, width: size, height: bodyH) // Pen back at bottom-left, facing right. // ── 2. Roof ──────────────────────────────────────────── pen.penUp() pen.turn(-90) pen.move(bodyH) pen.turn(90) pen.penDown() pen.color = .red drawRoof(pen: pen, base: size) // Pen at top-right of roof base, facing right. // ── 3. Door (centred at bottom of body) ─────────────── // Return to bottom-left, move right by (size - doorW) / 2 pen.penUp() pen.turn(180) pen.move(size) // back to top-left of roof base pen.turn(-90) // face down pen.move(bodyH) // down to bottom-left of body pen.turn(-90) // face right pen.move((size - doorW) / 2) pen.penDown() pen.color = .brown drawRect(pen: pen, width: doorW, height: doorH) // Pen at left side of door, facing right. // ── 4. Left window ──────────────────────────────────── // Back to bottom-left, then position for left window pen.penUp() pen.turn(180) pen.move((size - doorW) / 2) // back to bottom-left pen.turn(-90) pen.move(bodyH * 0.45) // up to window height pen.turn(90) pen.move(size * 0.08) // inset from left edge pen.penDown() pen.color = .cyan drawSquare(pen: pen, size: winSide) // ── 5. Right window ─────────────────────────────────── pen.penUp() pen.move(size * 0.08 + winSide + doorW + size * 0.08) pen.penDown() pen.color = .cyan drawSquare(pen: pen, size: winSide) } // Test let pen = Pen() drawHouse(pen: pen, size: 120)
| Standard | Connection |
|---|---|
| Geometry — Proportional Reasoning | All sub-components are proportional to size: the door is 25% as wide, windows are 18%, the body is 75% as tall. Scaling the house means scaling every component uniformly — a real-world application of ratio and proportion. |
A Fence Post
A classic picket fence post is a rectangle with a pointed top. Write drawFencePost(pen:width:height:) that draws the rectangular body and then calls drawRoof to add the pointed top.
The total visual height of the post is height (body) + the equilateral triangle's height above it. The body should have colour .white.
func drawFencePost(pen: Pen, width: Double, height: Double) { // 1. Draw rectangular body (width × height) // 2. Move to top-left corner // 3. Call drawRoof(pen: pen, base: width) // 4. Return pen to original position at bottom-left, facing right // your code here }
func drawFencePost(pen: Pen, width: Double, height: Double) { // ── 1. Rectangular body ────────────────────────────── pen.color = .white drawRect(pen: pen, width: width, height: height) // Pen at bottom-left, facing right. // ── 2. Pointed top ─────────────────────────────────── pen.penUp() pen.turn(-90) pen.move(height) pen.turn(90) pen.penDown() drawRoof(pen: pen, base: width) // Pen at top-right of body, facing right. // ── 3. Return to bottom-left ───────────────────────── pen.penUp() pen.turn(90) pen.move(height) pen.turn(90) pen.move(width) pen.turn(180) pen.penDown() } // Test let pen = Pen() drawFencePost(pen: pen, width: 18, height: 60)
| Standard | Connection |
|---|---|
| Geometry — Composite Figures | The total area of the fence post = area of rectangle + area of equilateral triangle. If rectangle height = h and width = w, the triangle height = w·√3/2. Calculating the total area requires combining both formulas. |
A Whole Fence
Use a loop to draw a fence of 8 posts. The posts should be 18 wide and 60 tall, with a 12-point gap between them.
After drawing all the posts, go back and draw a horizontal rail across the middle of the posts (at half the post height) to complete the fence.
let pen = Pen() let postW: Double = 18 let postH: Double = 60 let gap: Double = 12 let count = 8 // 1. Draw all posts in a row // your code here // 2. Return to left, climb to half height, draw horizontal rail // your code here
let pen = Pen() let postW: Double = 18 let postH: Double = 60 let gap: Double = 12 let count = 8 // ── 1. Posts ───────────────────────────────────────────── for _ in 1...count { drawFencePost(pen: pen, width: postW, height: postH) jump(pen: pen, distance: postW + gap) } // Pen is now to the right of the last post, facing right. // ── 2. Rail ─────────────────────────────────────────────── let totalWidth = Double(count) * (postW + gap) - gap pen.penUp() pen.turn(180) // face left pen.move(totalWidth + postW) // all the way back to start pen.turn(-90) // face up pen.move(postH / 2) // halfway up the post pen.turn(90) // face right pen.penDown() pen.color = .white pen.move(totalWidth) // draw the rail
| Standard | Connection |
|---|---|
| Functions — Linear Expressions | Total fence width = n × (postW + gap) − gap. For 8 posts: 8 × 30 − 12 = 228. This is a linear function of n — the same expression that arises when summing fence sections or tiles in a pattern. |
Broom Broom
Every neighbourhood needs cars! Write drawCar(pen:size:) that draws a simple side-on car using three parts:
- A wide rectangular body (width =
size, height =size * 0.3) - A narrower rectangular cabin on top (width =
size * 0.55, height =size * 0.28, insetsize * 0.18from the left) - Two square wheels (side =
size * 0.2) at the bottom, positionedsize * 0.1in from each end
func drawCar(pen: Pen, size: Double) { let bodyH = size * 0.3 let cabinW = size * 0.55 let cabinH = size * 0.28 let cabinIn = size * 0.18 // inset from left let wheelS = size * 0.2 let wheelIn = size * 0.1 // wheel inset from each end // 1. Draw body // 2. Move to cabin start, draw cabin // 3. Draw left wheel, then right wheel // your code here }
func drawCar(pen: Pen, size: Double) { let bodyH = size * 0.3 let cabinW = size * 0.55 let cabinH = size * 0.28 let cabinIn = size * 0.18 let wheelS = size * 0.2 let wheelIn = size * 0.1 // ── 1. Body ────────────────────────────────────────── pen.color = .blue drawRect(pen: pen, width: size, height: bodyH) // Pen at bottom-left, facing right. // ── 2. Cabin ───────────────────────────────────────── pen.penUp() pen.turn(-90) pen.move(bodyH) // up to top of body pen.turn(90) pen.move(cabinIn) // inset from left pen.penDown() pen.color = .cyan drawRect(pen: pen, width: cabinW, height: cabinH) // Pen at cabin bottom-left (= start of cabin), facing right. // ── 3. Wheels ───────────────────────────────────────── // Return to body bottom-left first pen.penUp() pen.turn(180) pen.move(cabinIn) // back to body left edge pen.turn(-90) pen.move(bodyH) // down to bottom of body pen.turn(-90) // face right // Left wheel (inset from left end) pen.move(wheelIn) pen.penDown() pen.color = .black drawSquare(pen: pen, size: wheelS) // Pen at left-wheel bottom-left, facing right. // Right wheel pen.penUp() pen.move(size - 2 * wheelIn - wheelS) // gap between wheels pen.penDown() drawSquare(pen: pen, size: wheelS) } // Test let pen = Pen() drawCar(pen: pen, size: 150)
| Standard | Connection |
|---|---|
| Geometry — Scale and Proportion | Every dimension of the car is a fixed proportion of size. Changing size scales all components simultaneously — a direct application of scaling figures and maintaining ratios. The gap between wheels = size − 2·wheelIn − wheelS is an algebraic expression derived from fitting shapes within a bounded space. |
Neighbourhood Scene
The grand finale! Use every function you have written to draw a complete suburban neighbourhood. Your scene must include at least:
- Two houses of different sizes
- One church
- A fence in front of one house
- One car on the street
Plan your scene on paper first — sketch where each building goes, estimate the sizes, and work out how far apart they should be. Then translate that plan into code, one component at a time.
jump helper to reposition the pen between buildings. Remember that after each draw… call the pen returns to the bottom-left of that component, facing right — so jumping right by the component's width gets you to the next gap.
let pen = Pen() // ── Plan your scene here ───────────────────────────────── // House 1 // ... // Gap between buildings jump(pen: pen, distance: 30) // Church // ... // Fence, car, House 2 ...
// One possible arrangement — yours will be different! let pen = Pen() // ── House 1 ────────────────────────────────────────────── drawHouse(pen: pen, size: 100) jump(pen: pen, distance: 25) // ── Church ──────────────────────────────────────────────── drawChurch(pen: pen, size: 80) jump(pen: pen, distance: 25) // ── House 2 (bigger) ───────────────────────────────────── drawHouse(pen: pen, size: 130) // ── Fence in front of House 2 ──────────────────────────── // Move pen below and to the left of House 2 pen.penUp() pen.turn(180) pen.move(130) // back to left edge of House 2 pen.turn(-90) // face down pen.move(30) // drop below house pen.turn(90) // face right pen.penDown() for _ in 1...7 { drawFencePost(pen: pen, width: 16, height: 40) jump(pen: pen, distance: 16 + 6) } // ── Car on the street ───────────────────────────────────── pen.penUp() pen.turn(180) pen.move(7 * (16 + 6) + 130) // far left past all buildings pen.turn(-90) pen.move(60) // down to street level pen.turn(90) pen.penDown() drawCar(pen: pen, size: 140)
| Standard | Connection |
|---|---|
| Geometry — Mathematical Modelling | Designing a scene requires applying geometric methods to solve a real design problem: fitting buildings of different sizes within a bounded canvas, using spatial reasoning to plan positions, and using algebraic expressions to compute coordinates and gaps. |