Skip to main content
0 / 19
Chapter V of VI

Similarity with Functions

Define reusable functions with parameters, then call them at different scales to draw flags from around the world. Scaling a parameter is exactly what similarity in geometry means — same shape, different size — and by the end of this chapter you'll have built a small library of parameterised shape-builders.

Creating Functions

A function is a named block of code you can call whenever you need it. Instead of copying the same loop every time you want a square, you define it once in a function and call it by name.

A function definition has three parts:

  • the keyword func
  • a name you choose (e.g. drawSquare), followed by parentheses ()
  • a body in braces { } — the code that runs when the function is called
Swift
// Define the function once...
func drawSquare() {
    for _ in 1...4 {
        pen.addLine(distance: 100)
        pen.turn(degrees: 90)
    }
}

// ...then call it as many times as you like.
drawSquare()
pen.move(distance: 120)
drawSquare()
pen.move(distance: 120)
drawSquare()

Functions promote DRY code — Don't Repeat Yourself. If you need to change the square size, you only change it in one place and every call updates automatically.

Where does the pen end up after drawing a square? The four turns add up to 4 × 90° = 360° — a full rotation. That means the pen finishes facing the same direction it started, and because the four sides form a closed shape, it's back at the starting position. This is why you can call drawSquare() twice with a pen.move in between and get two separate squares in a row.
Defining vs. calling. Writing func drawSquare() { ... } defines the function — nothing is drawn yet. Writing drawSquare() on its own line calls it — that's what actually runs the code. You define once; you call as many times as you need.
Creating functions
Curriculum Connections
ConceptConnection
Functions & ModellingA function in programming is analogous to a function in mathematics: it encapsulates a process. Calling a function repeatedly mirrors evaluating f(x) for multiple inputs — though here, with no parameters yet, it's a constant function that always draws the same shape.

Octagons

Write a function called drawOctagon() that draws a regular octagon (8 sides, exterior angle 45°). Call it three times to draw a row of three octagons.

Hint: a regular polygon's exterior angle is 360° ÷ number of sides. For an octagon that's 360° ÷ 8 = 45°. If you imagine walking along the edge of the shape, you turn by the exterior angle at each corner — the interior angle (135° for an octagon) is what you'd measure inside the corner, not what you turn through.

Octagons
Why 360° / n? If you walked all the way around the edge of a closed regular polygon, your total turning would have to add up to one full rotation — 360° — so you'd end up facing the same direction you started. Divide that total evenly across n corners and each turn is 360° / n. Triangle: 120°. Square: 90°. Pentagon: 72°. Hexagon: 60°. Octagon: 45°. The pattern works for any regular polygon.
Watch your octagon size! A regular octagon with side length s is roughly 2.4 × s wide. With s = 40 that's about 97 units — so moving 110 between calls leaves a ~13-unit gap. If you use a bigger side length, make sure to move by more than 2.4 × s or your octagons will overlap.
Solution
func drawOctagon() {
    for _ in 1...8 {
        pen.addLine(distance: 40)
        pen.turn(degrees: 45)
    }
}

// Three octagons in a row, separated by pen.move
drawOctagon()
pen.move(distance: 110)
drawOctagon()
pen.move(distance: 110)
drawOctagon()
Curriculum Connections
ConceptConnection
Geometry — Regular PolygonsA regular octagon has 8 sides and exterior angles of 360° / 8 = 45°. Its interior angles are 180° − 45° = 135°. The exterior angle formula 360° / n applies to any regular polygon — this is the first glimpse of a pattern that generalises in Section 04.

Parameters

Functions become far more powerful when they accept parameters — values passed in by the caller. The function uses the parameter name like a variable inside its body.

Swift
func drawSquare(size: Double) {
    for _ in 1...4 {
        pen.addLine(distance: size)
        pen.turn(degrees: 90)
    }
}

// Draw squares of different sizes:
drawSquare(size: 50)
drawSquare(size: 100)
drawSquare(size: 150)

You can have multiple parameters, separated by commas. Swift uses argument labels at the call site for readability:

Multiple parameters
func drawRectangle(width: Double, height: Double) {
    for _ in 1...2 {
        pen.addLine(distance: width)
        pen.turn(degrees: 90)
        pen.addLine(distance: height)
        pen.turn(degrees: 90)
    }
}

drawRectangle(width: 200, height: 100)
Parameters
Curriculum Connections
ConceptConnection
Functions & ModellingA parameterised function is a direct model of a mathematical function f(x). The parameter is the input, and the drawn shape is the output. Multiple parameters map to multi-variable functions f(x, y).

Different Sizes

Write a function drawRegularPolygon(sides: Int, sideLength: Double) that draws any regular polygon. Use it to draw a triangle, square, pentagon, hexagon, and octagon, each a different size, arranged in a row.

Different sizes
Solution
func drawRegularPolygon(sides: Int, sideLength: Double) {
    let angle = 360.0 / Double(sides)
    for _ in 1...sides {
        pen.addLine(distance: sideLength)
        pen.turn(degrees: angle)
    }
}

drawRegularPolygon(sides: 3, sideLength: 60)
pen.move(distance: 80)
drawRegularPolygon(sides: 4, sideLength: 60)
pen.move(distance: 80)
drawRegularPolygon(sides: 5, sideLength: 60)
pen.move(distance: 80)
drawRegularPolygon(sides: 6, sideLength: 60)
pen.move(distance: 80)
drawRegularPolygon(sides: 8, sideLength: 60)
Curriculum Connections
ConceptConnection
Geometry — Regular PolygonsA single parameterised function replaces six separate loop blocks. The exterior angle formula 360°/n shows a direct inverse relationship between number of sides and turn angle.

Different Shapes

Write a function drawRectangle(width: Double, height: Double, color: Color) that draws a filled rectangle of any size and colour. Use it to create a colourful grid of rectangles — 3 columns, 3 rows, each a different colour and proportion.

Different shapes
Solution
func drawRectangle(width: Double, height: Double, color: Color) {
    let shape = Pen()
    shape.fillColor = color
    for _ in 1...2 {
        shape.addLine(distance: width)
        shape.turn(degrees: 90)
        shape.addLine(distance: height)
        shape.turn(degrees: 90)
    }
    pen.addShape(pen: shape)
}
Curriculum Connections
ConceptConnection
Geometry — Area & PerimeterRectangles with the same perimeter can have different areas (and vice versa). Experimenting with width and height demonstrates this inverse relationship.

Denmark

The Danish flag (Dannebrog) is a red rectangle with a white cross. The cross divides the flag into four rectangles. Write a function to draw the flag, with parameters for overall width and height.

Traditional proportions: 28 × 21 units; cross bars are 4 units wide; vertical bar is offset left of centre.

Flag of Denmark
Watch out for these bugs: let p = Pen() must be var p = Pen() (Pens are mutated via method calls), and pen.addShape(pen: p) is wrong — addShape(pen: p) is a free function, not a method on pen. Nordic crosses are also offset, not centred: Denmark's vertical bar sits at x = 10 out of 28, dividing the flag into 10 | 4 | 14.
Solution
func drawDenmark(scale: Double) {
    // Red background (28 × 21)
    var bg = Pen()
    bg.fillColor = .red
    bg.penColor = .red
    for _ in 1...2 {
        bg.addLine(distance: 28 * scale)
        bg.turn(degrees: 90)
        bg.addLine(distance: 21 * scale)
        bg.turn(degrees: 90)
    }
    addShape(pen: bg)

    // White horizontal bar (full width, 4 units tall, centred vertically)
    var hBar = Pen()
    hBar.penColor = .white
    hBar.fillColor = .white
    hBar.goto(x: 0, y: 8.5 * scale)
    for _ in 1...2 {
        hBar.addLine(distance: 28 * scale)
        hBar.turn(degrees: 90)
        hBar.addLine(distance: 4 * scale)
        hBar.turn(degrees: 90)
    }
    addShape(pen: hBar)

    // White vertical bar (full height, 4 units wide, offset left)
    var vBar = Pen()
    vBar.penColor = .white
    vBar.fillColor = .white
    vBar.goto(x: 10 * scale, y: 0)
    for _ in 1...2 {
        vBar.addLine(distance: 4 * scale)
        vBar.turn(degrees: 90)
        vBar.addLine(distance: 21 * scale)
        vBar.turn(degrees: 90)
    }
    addShape(pen: vBar)
}

drawDenmark(scale: 10)
Curriculum Connections
ConceptConnection
Ratio & ProportionFlag dimensions use fixed ratios (28:21 = 4:3). Scaling by a constant multiplier preserves proportions — an application of ratio and scale factor.

Sweden

The Swedish flag is blue with a yellow Scandinavian cross (same offset proportions as Denmark but with a blue background and gold cross). Adapt your Denmark function to draw the Swedish flag.

Proportions: 16 × 10 units; vertical bar 2 units wide at position 5 from left; horizontal bar 2 units wide at position 4 from top.

Flag of Sweden
Same pattern as Denmark — Nordic cross, offset toward the hoist, two separate white bars for the horizontal and vertical. For a 16 × 10 flag, the vertical bar sits at x = 5 (splitting 5 | 2 | 9) and the horizontal bar at y = 4 (splitting 4 | 2 | 4). Two flags, two dimensions, same structure — this is geometric similarity.
Solution
func drawSweden(scale: Double) {
    // Blue background (16 × 10)
    var bg = Pen()
    bg.fillColor = .blue
    for _ in 1...2 {
        bg.addLine(distance: 16 * scale)
        bg.turn(degrees: 90)
        bg.addLine(distance: 10 * scale)
        bg.turn(degrees: 90)
    }
    addShape(pen: bg)

    // Yellow horizontal bar (full width, 2 units tall, y = (10 - 2)/2 = 4)
    var hBar = Pen()
    hBar.penColor = .yellow
    hBar.fillColor = .yellow
    hBar.goto(x: 0, y: 4 * scale)
    for _ in 1...2 {
        hBar.addLine(distance: 16 * scale)
        hBar.turn(degrees: 90)
        hBar.addLine(distance: 2 * scale)
        hBar.turn(degrees: 90)
    }
    addShape(pen: hBar)

    // Yellow vertical bar (full height, 2 units wide, offset left at x = 5)
    var vBar = Pen()
    vBar.penColor = .yellow
    vBar.fillColor = .yellow
    vBar.goto(x: 5 * scale, y: 0)
    for _ in 1...2 {
        vBar.addLine(distance: 2 * scale)
        vBar.turn(degrees: 90)
        vBar.addLine(distance: 10 * scale)
        vBar.turn(degrees: 90)
    }
    addShape(pen: vBar)
}

drawSweden(scale: 8)
Curriculum Connections
ConceptConnection
Ratio & ProportionIdentifying how Denmark and Sweden share a cross design but with different proportions reinforces the concept of scaled geometric similarity.

Norway

Norway's flag has a red background with a blue cross outlined in white. Build on your Scandinavian cross function to add a second, slightly wider white cross drawn underneath the blue one.

Flag of Norway
Layered rendering (painter's algorithm). The white border around the blue cross isn't drawn as an outline — it's what's left over from the white layer after the narrower blue cross is painted on top. Paint wider → paint narrower → the difference becomes the border. This trick is used throughout the chapter (Union Jack fimbriation, Tonga, Switzerland).
Solution
func drawNorway(scale: Double) {
    // Norway proportions: 22 × 16
    // Cross centre: x = 6 from left, thicknesses: white = 4, blue = 2

    // --- Helper: draws a rectangle at a given position ---
    func drawRect(x: Double, y: Double,
                  w: Double, h: Double, color: UIColor) {
        var p = Pen()
        p.penColor = color
        p.fillColor = color
        p.goto(x: x * scale, y: y * scale)
        for _ in 1...2 {
            p.addLine(distance: w * scale)
            p.turn(degrees: 90)
            p.addLine(distance: h * scale)
            p.turn(degrees: 90)
        }
        addShape(pen: p)
    }

    // Layer 1: Red background (22 × 16)
    drawRect(x: 0, y: 0, w: 22, h: 16, color: .red)

    // Layer 2: White cross (thickness 4)
    drawRect(x: 0, y: 6, w: 22, h: 4, color: .white)  // horizontal
    drawRect(x: 6, y: 0, w: 4, h: 16, color: .white)  // vertical

    // Layer 3: Blue cross (thickness 2, centred within white)
    drawRect(x: 0, y: 7, w: 22, h: 2, color: .blue)   // horizontal
    drawRect(x: 7, y: 0, w: 2, h: 16, color: .blue)   // vertical
}

drawNorway(scale: 6)
Curriculum Connections
ConceptConnection
Geometry — Concentric ShapesDrawing a white cross wider than the blue cross creates an outline via layering — analogous to the concept of border width as the difference between outer and inner dimensions.

St Andrew

The flag of Scotland features a white diagonal cross (saltire) on a blue background. Draw two diagonal lines connecting opposite corners of the flag's rectangle.

Hint: use trigonometry (or Pythagoras) to calculate the diagonal length from the width and height.

Flag of St Andrew
Why not just draw two thick diagonal lines? Stroked lines with lineWidth have line caps that extend beyond their endpoints — the white would spill past every corner of the flag. Use a filled polygon instead: a 4-vertex parallelogram with corners pinned to the flag's corners and short edges along the top and bottom. A polygon can never escape its own vertices.
Why t = w/5? For a 5:3 flag (30 × 18), an edge offset of t = 6 = w/5 produces a perpendicular bar thickness of exactly h/5 = 3.6 — the traditional 1/5-of-hoist proportion for the Scottish flag. This is a nice accident of the 5:3 ratio where sqrt((4w/5)² + h²) = w, so the thickness formula simplifies beautifully.
Solution
import Foundation
import UIKit

func drawStAndrew(scale: Double) {
    // St Andrew's Cross (Saltire): 5:3 ratio, e.g. 30 × 18
    let w: Double = 30 * scale
    let h: Double = 18 * scale

    // Saltire "thickness" measured as offset along the horizontal edges.
    // For a 5:3 flag, t = w/5 → perpendicular bar thickness of h/5.
    let t: Double = w / 5

    // --- Blue background ---
    var bg = Pen()
    bg.fillColor = .blue
    for _ in 1...2 {
        bg.addLine(distance: w)
        bg.turn(degrees: 90)
        bg.addLine(distance: h)
        bg.turn(degrees: 90)
    }
    addShape(pen: bg)

    // --- White saltire bar 1: bottom-left → top-right ---
    // Vertices (CCW): BL corner → bottom edge offset → TR corner → top edge offset
    let bar1 = Polygon(vertices: [
        Point(x: 0,     y: 0),   // bottom-left corner
        Point(x: t,     y: 0),   // bottom edge, offset right by t
        Point(x: w,     y: h),   // top-right corner
        Point(x: w - t, y: h)    // top edge, offset left by t
    ])
    addFilledPolygon(bar1, fillColor: .white, borderColor: .white, lineWidth: 1)

    // --- White saltire bar 2: top-left → bottom-right ---
    let bar2 = Polygon(vertices: [
        Point(x: 0,     y: h),   // top-left corner
        Point(x: t,     y: h),   // top edge, offset right by t
        Point(x: w,     y: 0),   // bottom-right corner
        Point(x: w - t, y: 0)    // bottom edge, offset left by t
    ])
    addFilledPolygon(bar2, fillColor: .white, borderColor: .white, lineWidth: 1)
}

drawStAndrew(scale: 8)
Curriculum Connections
ConceptConnection
Pythagoras' TheoremThe diagonal of a rectangle with width w and height h has length √(w² + h²). This is a direct application of Pythagoras' theorem.

St Patrick

The St Patrick's Cross is a red diagonal saltire (like St Andrew's but red on white). Draw it using the same approach as St Andrew, but with a red diagonal and white background.

Flag of St Patrick
Rule of two — time to consider a refactor. St Andrew and St Patrick differ only in their colours. After writing both, you should recognise that a single drawSaltireFlag(bgColor:, crossColor:) helper would eliminate the duplication. This is the first "two instances" moment in the chapter — keep an eye out for a third (the Union Jack saltires) and extract!
Solution
import Foundation
import UIKit

func drawStPatrick(scale: Double) {
    // St Patrick's Cross: 5:3 ratio, e.g. 30 × 18
    let w: Double = 30 * scale
    let h: Double = 18 * scale
    let t: Double = w / 5

    // --- White background ---
    var bg = Pen()
    bg.fillColor = .white
    bg.penColor = .white
    for _ in 1...2 {
        bg.addLine(distance: w)
        bg.turn(degrees: 90)
        bg.addLine(distance: h)
        bg.turn(degrees: 90)
    }
    addShape(pen: bg)

    // --- Red saltire bar 1: bottom-left → top-right ---
    let bar1 = Polygon(vertices: [
        Point(x: 0,     y: 0),
        Point(x: t,     y: 0),
        Point(x: w,     y: h),
        Point(x: w - t, y: h)
    ])
    addFilledPolygon(bar1, fillColor: .red, borderColor: .red, lineWidth: 1)

    // --- Red saltire bar 2: top-left → bottom-right ---
    let bar2 = Polygon(vertices: [
        Point(x: 0,     y: h),
        Point(x: t,     y: h),
        Point(x: w,     y: 0),
        Point(x: w - t, y: 0)
    ])
    addFilledPolygon(bar2, fillColor: .red, borderColor: .red, lineWidth: 1)
}

drawStPatrick(scale: 8)
Curriculum Connections
ConceptConnection
Geometry — SimilaritySt Patrick's Cross is geometrically identical to St Andrew's — the same shape, different colours. This demonstrates that geometric properties are independent of colour.

St George

England's flag is a white background with a red cross (like Denmark but centred). Use your Scandinavian cross function with centred proportions.

Flag of St George
Centring formula. For a centred cross of thickness t on a flag of width w and height h, the horizontal bar sits at y = (h − t) / 2 and the vertical bar at x = (w − t) / 2. For a 30 × 20 flag with thickness 4, that's x = 13 and y = 8. Unlike Denmark/Sweden/Norway where the cross is offset toward the hoist, St George's is symmetric on both axes — D₂ symmetry (2 reflection axes + 180° rotation).
Solution
import Foundation
import UIKit

func drawStGeorge(scale: Double) {
    // St George's Cross: 30 × 20, cross width 4, centred

    func drawRect(x: Double, y: Double,
                  w: Double, h: Double, color: UIColor) {
        var p = Pen()
        p.penColor = color
        p.fillColor = color
        p.goto(x: x * scale, y: y * scale)
        for _ in 1...2 {
            p.addLine(distance: w * scale)
            p.turn(degrees: 90)
            p.addLine(distance: h * scale)
            p.turn(degrees: 90)
        }
        addShape(pen: p)
    }

    // White background (30 × 20)
    drawRect(x: 0, y: 0, w: 30, h: 20, color: .white)

    // Red cross — centred both ways
    drawRect(x: 0,  y: 8, w: 30, h: 4, color: .red)   // horizontal
    drawRect(x: 13, y: 0, w: 4, h: 20, color: .red)   // vertical
}

drawStGeorge(scale: 5)
Curriculum Connections
ConceptConnection
Geometry — Line SymmetrySt George's Cross has two lines of symmetry (horizontal and vertical) and 4-fold rotational symmetry — a symmetry group of order 4.

Union Jack

The Union Jack is a combination of St George's Cross, St Andrew's Cross, and St Patrick's Cross. Use your functions from sections 09–11 to layer all three on the same flag. The order is: blue background → St Andrew (white) → St Patrick (red, thinner) → St George (red, centred).

Union Jack
Why hexagons, not parallelograms? A single saltire works fine as a 4-vertex parallelogram (as in St Andrew), but the Union Jack layers two saltires of different thicknesses — white on top of red. A parallelogram's long edges have direction (w − t, h) which depends on the thickness t, so the white and red bars end up at subtly different angles and the red appears to "twist" inside the white. The 6-vertex hexagon pins the long edges to direction (w, h) exactly — the true flag diagonal — regardless of thickness.
Spec-driven proportions. All four thicknesses are fractions of the hoist: white saltire = h/5, red saltire = h/15, white cross backing = h/3, red cross = h/5. The white borders around every red shape come out to exactly h/15 on each side — that's what makes the fimbriation look visually consistent across the whole flag.
Solution
import Foundation
import UIKit

func drawUnionJack(scale: Double) {
    // Union Jack: 2:1 ratio, e.g. 60 × 30
    let w: Double = 60 * scale
    let h: Double = 30 * scale
    let diag: Double = sqrt(w*w + h*h)

    // Heraldic thicknesses (as fractions of the hoist)
    let whiteSaltireT: Double = h / 5    // St Andrew — 1/5 of hoist
    let redSaltireT:   Double = h / 15   // St Patrick — 1/15 of hoist
    let whiteCrossT:   Double = h / 3    // St George backing — 1/3 of hoist
    let redCrossT:     Double = h / 5    // St George — 1/5 of hoist

    // --- Helpers ---

    func drawRect(x: Double, y: Double,
                  w rw: Double, h rh: Double, color: UIColor) {
        var p = Pen()
        p.penColor = color
        p.fillColor = color
        p.goto(x: x, y: y)
        for _ in 1...2 {
            p.addLine(distance: rw)
            p.turn(degrees: 90)
            p.addLine(distance: rh)
            p.turn(degrees: 90)
        }
        addShape(pen: p)
    }

    // Draws one saltire as two hexagons — one per diagonal — with long edges
    // EXACTLY parallel to the flag's main diagonal (w, h). T = perpendicular
    // thickness. eh/ew = offsets where the strip hits the flag edges.
    func drawSaltire(color: UIColor, perpendicular T: Double) {
        let eh = T * diag / (2 * h)
        let ew = T * diag / (2 * w)

        // Bar 1: bottom-left → top-right diagonal (hexagon, CCW)
        let bar1 = Polygon(vertices: [
            Point(x: 0,      y: 0),          // BL corner
            Point(x: eh,     y: 0),          // bottom edge entry
            Point(x: w,      y: h - ew),     // right edge exit
            Point(x: w,      y: h),          // TR corner
            Point(x: w - eh, y: h),          // top edge exit
            Point(x: 0,      y: ew)          // left edge entry
        ])
        addFilledPolygon(bar1, fillColor: color, borderColor: color, lineWidth: 1)

        // Bar 2: top-left → bottom-right diagonal (hexagon, CCW)
        let bar2 = Polygon(vertices: [
            Point(x: 0,      y: h),          // TL corner
            Point(x: 0,      y: h - ew),     // left edge entry
            Point(x: w - eh, y: 0),          // bottom edge exit
            Point(x: w,      y: 0),          // BR corner
            Point(x: w,      y: ew),         // right edge exit
            Point(x: eh,     y: h)           // top edge entry
        ])
        addFilledPolygon(bar2, fillColor: color, borderColor: color, lineWidth: 1)
    }

    func drawCenteredCross(color: UIColor, thickness t: Double) {
        drawRect(x: 0,           y: (h - t) / 2, w: w, h: t, color: color)
        drawRect(x: (w - t) / 2, y: 0,           w: t, h: h, color: color)
    }

    // --- Layer order (bottom → top) ---

    // 1. Blue background
    drawRect(x: 0, y: 0, w: w, h: h, color: .blue)

    // 2. White St Andrew saltire
    drawSaltire(color: .white, perpendicular: whiteSaltireT)

    // 3. Red St Patrick saltire (centred in the white, parallel to it)
    drawSaltire(color: .red, perpendicular: redSaltireT)

    // 4. White backing for St George (fimbriation)
    drawCenteredCross(color: .white, thickness: whiteCrossT)

    // 5. Red St George cross
    drawCenteredCross(color: .red, thickness: redCrossT)
}

drawUnionJack(scale: 4)
Curriculum Connections
ConceptConnection
Geometry — CompositionThe Union Jack is a superposition of three geometric designs. Function composition in code mirrors geometric composition — combining simple shapes to create complex ones.

Filled Star

Write a function drawStar(points: Int, size: Double, color: Color) that draws a filled star with any number of points. Use it to draw three stars of different sizes side by side.

Filled star
Why 720°? A regular polygon path winds once (360°). A star polygon {5/2} winds twice — the path overlaps itself, completing two loops before returning to the start. So the total turn angle over n vertices is 720°, giving 720/n per vertex. More generally, a {n/k} star polygon winds k times and has a turn angle of 360·k/n.
Which {n/k} values are valid? A star polygon is a single-pass star only when gcd(n, k) = 1 and k ≥ 2. So {5/2}, {7/2}, {7/3}, {9/2}, {9/4} all work. But {6/2} has gcd 2 and degenerates to a triangle traced twice — for a 6-pointed Star of David you need two overlapping triangles, not a single {n/k} polygon.
Solution
import UIKit

func drawStarNK(at position: Point, n: Int, k: Int,
                size: Double, color: UIColor) {
    // General star polygon {n/k} turn angle = 360 × k / n
    // Produces a valid single-pass star when gcd(n, k) = 1 and k ≥ 2
    let angle = 360.0 * Double(k) / Double(n)

    var star = Pen()
    star.penColor = color
    star.fillColor = color
    star.goto(x: position.x, y: position.y)

    for _ in 0..<n {
        star.addLine(distance: size)
        star.turn(degrees: angle)
    }
    addShape(pen: star)
}

drawStarNK(at: Point(x: -120, y: 0), n: 5, k: 2, size: 80, color: .systemYellow)  // classic 5-star
drawStarNK(at: Point(x:    0, y: 0), n: 7, k: 2, size: 80, color: .systemOrange)  // 7/2 star
drawStarNK(at: Point(x:  120, y: 0), n: 7, k: 3, size: 80, color: .systemRed)     // 7/3 star (denser)
Curriculum Connections
ConceptConnection
Geometry — Star PolygonsA {n/2} star polygon turns 2×360°/n at each vertex. The turn angle formula derives from the exterior angle theorem for star polygons.

Australia

The Australian flag has a blue background, the Union Jack in the top-left, a large 7-pointed star below it, and the Southern Cross (5 stars) on the right. Use your Union Jack and star functions to draw a simplified version.

Flag of Australia
Introducing drawStarCentered. Section 13's drawStarNK positions a star by its starting vertex, which is awkward when you need to place a star at a specific geometric point (like the centre of a flag element). drawStarCentered takes the star's centre directly and orients one vertex straight up — exactly what flag composition needs. It's derived analytically, so it works for any valid {n/k}.
Why {7/3} for the Commonwealth Star? The real Australian flag has distinctly spiky stars with deep valleys between the points — that's {7/3}, the densest valid 7-point star. {7/2} is gentler and looks wrong. Because drawStarCentered takes n and k as parameters, switching from gentle to spiky is a one-line change. The Southern Cross uses {7/3} for the 4 large stars and {5/2} for Epsilon Crucis.
Canton scaling. The Union Jack canton is exactly half the flag's width and half its height. Because drawUnionJack at scale 1 produces a 60 × 30 flag, passing scale / 2 produces a (30·scale) × (15·scale) canton — which matches w/2 × h/2 of the 60 × 30 Australian flag. Same proportions, half the size: geometric similarity through scaling.
Solution
import Foundation
import UIKit

// ============================================================
// Star polygon {n/k} centred at a point, oriented point-up.
// Cleaner than drawStarNK for composition — pass the centre.
// ============================================================
func drawStarCentered(at center: Point, n: Int, k: Int,
                      size: Double, color: UIColor) {
    // Circumradius: s = 2R·sin(πk/n) ⇒ R = s/(2·sin(πk/n))
    let R = size / (2 * sin(.pi * Double(k) / Double(n)))

    // Place V₀ at top of circle (point-up), aim pen at V₁.
    // atan2 of (V₁ − V₀) simplifies to 180° + 180°k/n.
    let initialHeadingDeg = 180.0 + 180.0 * Double(k) / Double(n)
    let turnDeg = 360.0 * Double(k) / Double(n)

    var star = Pen()
    star.penColor = color
    star.fillColor = color

    star.goto(x: center.x, y: center.y + R)
    star.turn(degrees: initialHeadingDeg)

    for _ in 0..<n {
        star.addLine(distance: size)
        star.turn(degrees: turnDeg)
    }
    addShape(pen: star)
}

// ============================================================
// Union Jack — now accepts a position
// ============================================================
func drawUnionJack(at pos: Point, scale: Double) {
    let w: Double = 60 * scale
    let h: Double = 30 * scale
    let diag: Double = sqrt(w*w + h*h)

    let whiteSaltireT: Double = h / 5
    let redSaltireT:   Double = h / 15
    let whiteCrossT:   Double = h / 3
    let redCrossT:     Double = h / 5

    func drawRect(x: Double, y: Double,
                  w rw: Double, h rh: Double, color: UIColor) {
        var p = Pen()
        p.penColor = color
        p.fillColor = color
        p.goto(x: pos.x + x, y: pos.y + y)
        for _ in 1...2 {
            p.addLine(distance: rw)
            p.turn(degrees: 90)
            p.addLine(distance: rh)
            p.turn(degrees: 90)
        }
        addShape(pen: p)
    }

    func drawSaltire(color: UIColor, perpendicular T: Double) {
        let eh = T * diag / (2 * h)
        let ew = T * diag / (2 * w)

        let bar1 = Polygon(vertices: [
            Point(x: pos.x,          y: pos.y),
            Point(x: pos.x + eh,     y: pos.y),
            Point(x: pos.x + w,      y: pos.y + h - ew),
            Point(x: pos.x + w,      y: pos.y + h),
            Point(x: pos.x + w - eh, y: pos.y + h),
            Point(x: pos.x,          y: pos.y + ew)
        ])
        addFilledPolygon(bar1, fillColor: color, borderColor: color, lineWidth: 1)

        let bar2 = Polygon(vertices: [
            Point(x: pos.x,          y: pos.y + h),
            Point(x: pos.x,          y: pos.y + h - ew),
            Point(x: pos.x + w - eh, y: pos.y),
            Point(x: pos.x + w,      y: pos.y),
            Point(x: pos.x + w,      y: pos.y + ew),
            Point(x: pos.x + eh,     y: pos.y + h)
        ])
        addFilledPolygon(bar2, fillColor: color, borderColor: color, lineWidth: 1)
    }

    func drawCenteredCross(color: UIColor, thickness t: Double) {
        drawRect(x: 0,           y: (h - t) / 2, w: w, h: t, color: color)
        drawRect(x: (w - t) / 2, y: 0,           w: t, h: h, color: color)
    }

    // Layer order (bottom → top)
    drawRect(x: 0, y: 0, w: w, h: h, color: .blue)
    drawSaltire(color: .white, perpendicular: whiteSaltireT)
    drawSaltire(color: .red,   perpendicular: redSaltireT)
    drawCenteredCross(color: .white, thickness: whiteCrossT)
    drawCenteredCross(color: .red,   thickness: redCrossT)
}

// ============================================================
// AUSTRALIAN FLAG
// ============================================================
func drawAustralia(at pos: Point, scale: Double) {
    let w: Double = 60 * scale
    let h: Double = 30 * scale

    // 1. Blue background
    var bg = Pen()
    bg.penColor = .blue
    bg.fillColor = .blue
    bg.goto(x: pos.x, y: pos.y)
    for _ in 1...2 {
        bg.addLine(distance: w)
        bg.turn(degrees: 90)
        bg.addLine(distance: h)
        bg.turn(degrees: 90)
    }
    addShape(pen: bg)

    // 2. Union Jack canton (top-left quarter, scale/2)
    drawUnionJack(at: Point(x: pos.x, y: pos.y + h / 2),
                  scale: scale / 2)

    // 3. Commonwealth Star — {7/3} for the spiky-heptagram look
    drawStarCentered(
        at: Point(x: pos.x + w / 4, y: pos.y + h / 4),
        n: 7, k: 3,
        size: h * 0.31,
        color: .white
    )

    // 4. Southern Cross — large stars {7/3}, Epsilon {5/2}
    let largeSize: Double = h * 0.14
    let smallSize: Double = h / 13

    // Gamma Crucis (top)
    drawStarCentered(
        at: Point(x: pos.x + 0.72 * w, y: pos.y + 0.80 * h),
        n: 7, k: 3, size: largeSize, color: .white
    )
    // Beta Crucis (left)
    drawStarCentered(
        at: Point(x: pos.x + 0.58 * w, y: pos.y + 0.52 * h),
        n: 7, k: 3, size: largeSize, color: .white
    )
    // Delta Crucis (right)
    drawStarCentered(
        at: Point(x: pos.x + 0.82 * w, y: pos.y + 0.46 * h),
        n: 7, k: 3, size: largeSize, color: .white
    )
    // Alpha Crucis (bottom)
    drawStarCentered(
        at: Point(x: pos.x + 0.72 * w, y: pos.y + 0.20 * h),
        n: 7, k: 3, size: largeSize, color: .white
    )
    // Epsilon Crucis (small 5-point)
    drawStarCentered(
        at: Point(x: pos.x + 0.76 * w, y: pos.y + 0.40 * h),
        n: 5, k: 2, size: smallSize, color: .white
    )
}

drawAustralia(at: Point(x: -120, y: -60), scale: 7)
Curriculum Connections
ConceptConnection
Geometry — Scale & PositionFitting the Union Jack into one quadrant requires halving its dimensions — a scale factor of 0.5. Positions are calculated relative to flag dimensions using fractions.

Netherlands

The Dutch flag is three equal horizontal stripes: red (top), white (middle), blue (bottom). Write a function drawTricolour(color1: Color, color2: Color, color3: Color, width: Double, height: Double) and draw the Netherlands flag.

Flag of the Netherlands
Y increases upward! Because the coordinate system has +y going up, the bottom stripe lives at y = 0 and the top stripe at y = 2 × stripeH. Students who write stripes top-to-bottom end up with the flag upside down. The drawTricolour function below takes colours in reading order (top, middle, bottom) but draws them bottom-up to match the coordinate system.
Avoid pen.move + pen.turn navigation. Using a shared pen with turn(270); move(40); turn(90) between stripes relies on global state and breaks the moment you draw two shapes in a row. Use goto(x:, y:) to position each stripe absolutely — it's stateless, composable, and much easier to debug.
Solution
import Foundation
import UIKit

// --- Foundation helper: draws one filled stripe at (x, y) ---
func drawStripe(x: Double, y: Double,
                width w: Double, height h: Double, color: UIColor) {
    var p = Pen()
    p.penColor = color
    p.fillColor = color
    p.goto(x: x, y: y)
    for _ in 1...2 {
        p.addLine(distance: w)
        p.turn(degrees: 90)
        p.addLine(distance: h)
        p.turn(degrees: 90)
    }
    addShape(pen: p)
}

// --- Reusable tricolour helper: 3 equal horizontal stripes ---
// color1 = TOP, color2 = MIDDLE, color3 = BOTTOM.
// y increases upward, so the BOTTOM stripe is drawn first.
func drawTricolour(color1: UIColor, color2: UIColor, color3: UIColor,
                   width: Double, height: Double) {
    let stripeH = height / 3
    drawStripe(x: 0, y: 0,           width: width, height: stripeH, color: color3)
    drawStripe(x: 0, y: stripeH,     width: width, height: stripeH, color: color2)
    drawStripe(x: 0, y: 2 * stripeH, width: width, height: stripeH, color: color1)
}

// --- Dutch flag: red (top), white (middle), blue (bottom), 3:2 ratio ---
func drawNetherlands(scale: Double) {
    drawTricolour(
        color1: .red,     // top
        color2: .white,   // middle
        color3: .blue,    // bottom
        width:  30 * scale,
        height: 20 * scale
    )
}

drawNetherlands(scale: 6)
Curriculum Connections
ConceptConnection
Geometry — AreaThree equal horizontal stripes each occupy 1/3 of the total area. Total area = width × height; each stripe area = width × (height/3).

France

The French tricolour has three vertical stripes: blue, white, red. Reuse your stripe function from the Netherlands exercise, but rotate it 90° for vertical stripes.

Flag of France
The "rotate 90°" idea, properly. The exercise hint says "swap width and height" — that's almost right but not quite. Swapping the parameters of drawTricolour still divides the (now-swapped) height by 3, producing horizontal stripes in a different aspect ratio. The correct transformation is to swap which axis gets divided and which gets stepped along: divide width by 3 (not height) and step along x (not y). The cleanest expression of this is a separate drawTricolourVertical function — same three lines, with (x, y) and (w, h) roles swapped.
Solution
import Foundation
import UIKit

// drawStripe is the same helper from Section 15 Netherlands.
// Repeated here for a self-contained France solution.
func drawStripe(x: Double, y: Double,
                width w: Double, height h: Double, color: UIColor) {
    var p = Pen()
    p.penColor = color
    p.fillColor = color
    p.goto(x: x, y: y)
    for _ in 1...2 {
        p.addLine(distance: w)
        p.turn(degrees: 90)
        p.addLine(distance: h)
        p.turn(degrees: 90)
    }
    addShape(pen: p)
}

// --- 3 equal VERTICAL stripes ---
// color1 = LEFT (hoist), color2 = MIDDLE, color3 = RIGHT (fly).
// Each stripe has FULL height and 1/3 width; step along x, not y.
func drawTricolourVertical(color1: UIColor, color2: UIColor, color3: UIColor,
                            width: Double, height: Double) {
    let stripeW = width / 3
    drawStripe(x: 0,           y: 0, width: stripeW, height: height, color: color1)
    drawStripe(x: stripeW,     y: 0, width: stripeW, height: height, color: color2)
    drawStripe(x: 2 * stripeW, y: 0, width: stripeW, height: height, color: color3)
}

// --- French flag: blue (hoist), white, red (fly), 3:2 ratio ---
func drawFrance(scale: Double) {
    drawTricolourVertical(
        color1: .blue,    // hoist (left)
        color2: .white,   // middle
        color3: .red,     // fly (right)
        width:  30 * scale,
        height: 20 * scale
    )
}

drawFrance(scale: 6)
Curriculum Connections
ConceptConnection
Geometry — TransformationsRotating the stripe from horizontal to vertical is a 90° transformation. The same function with swapped width/height parameters achieves this in code.

Positions

Update your flag functions to accept an x and y parameter so the flag can be drawn at any position on the canvas. Draw the flags of France, Netherlands, and Denmark side by side on the same canvas using positions.

Positions
This is where drawRectAt is born. Up to Section 16, each flag was either drawing its own rectangles inline (Denmark/Sweden/Norway/St George) or using the drawStripe helper from Netherlands/France which drew at origin only. Section 17 generalises both into one helper: drawRectAt(at: Point, w:, h:, color:) that takes a Point for position. From Section 18 onward, almost every flag uses this helper — keep it at the top of your playground file.
Why pass a Point instead of separate x and y? A Point is a single value you can store in a variable, put in an array, or compute from other points. Separate x and y parameters force you to pass two numbers everywhere and can't be abstracted over. This is the difference between thinking "a location" (a single concept) and "two numbers" (two concepts). It matters once you start composing drawings.
Solution
import Foundation
import UIKit

// Filled rectangle at an absolute position.
// This single helper replaces every inline rectangle loop from Sections 06–16.
func drawRectAt(at pos: Point,
                w: Double, h: Double, color: UIColor) {
    var p = Pen()
    p.penColor = color
    p.fillColor = color
    p.goto(x: pos.x, y: pos.y)
    for _ in 1...2 {
        p.addLine(distance: w)
        p.turn(degrees: 90)
        p.addLine(distance: h)
        p.turn(degrees: 90)
    }
    addShape(pen: p)
}

// Now each flag function takes (at: Point, scale: Double) and uses drawRectAt.
// Example: France, Netherlands, Denmark side by side on the same canvas.

func drawFrance(at pos: Point, scale: Double) {
    let w: Double = 30 * scale
    let h: Double = 20 * scale
    let stripe = w / 3
    drawRectAt(at: Point(x: pos.x,              y: pos.y), w: stripe, h: h, color: .blue)
    drawRectAt(at: Point(x: pos.x + stripe,     y: pos.y), w: stripe, h: h, color: .white)
    drawRectAt(at: Point(x: pos.x + 2 * stripe, y: pos.y), w: stripe, h: h, color: .red)
}

func drawNetherlands(at pos: Point, scale: Double) {
    let w: Double = 30 * scale
    let h: Double = 20 * scale
    let stripe = h / 3
    drawRectAt(at: Point(x: pos.x, y: pos.y + 2 * stripe), w: w, h: stripe, color: .red)
    drawRectAt(at: Point(x: pos.x, y: pos.y + stripe),     w: w, h: stripe, color: .white)
    drawRectAt(at: Point(x: pos.x, y: pos.y),              w: w, h: stripe, color: .blue)
}

func drawDenmark(at pos: Point, scale: Double) {
    let w: Double = 28 * scale
    let h: Double = 21 * scale
    drawRectAt(at: Point(x: pos.x,              y: pos.y),
               w: w, h: h, color: .red)
    drawRectAt(at: Point(x: pos.x,              y: pos.y + 8.5 * scale),
               w: w, h: 4 * scale, color: .white)
    drawRectAt(at: Point(x: pos.x + 10 * scale, y: pos.y),
               w: 4 * scale, h: h, color: .white)
}

// Three flags side by side, centred around origin
drawFrance(     at: Point(x: -105, y: -20), scale: 2)
drawNetherlands(at: Point(x:  -35, y: -20), scale: 2)
drawDenmark(    at: Point(x:   35, y: -21), scale: 2)
Curriculum Connections
ConceptConnection
Coordinate GeometrySpecifying x and y positions is the Cartesian coordinate system applied to canvas layout. Moving to position (x, y) mirrors plotting a point on the coordinate plane.

Switzerland

The Swiss flag is a red square with a white cross made from two equal rectangles overlapping at the centre. The cross is centred, and the rectangles have a 1:6 proportion relative to the flag size. Draw it using your rectangle function.

Flag of Switzerland
D₄ symmetry — the only one in the chapter. Switzerland is the only flag with full dihedral symmetry of order 8: four rotations (0°, 90°, 180°, 270°) and four reflections (horizontal, vertical, both diagonals). You need both a square flag and a centred cross with equal-width arms — neither alone is sufficient. Every other "centred cross" flag (St George) is on a non-square rectangle and loses the diagonal reflections.
Inclusion–exclusion for the cross area. The cross is two overlapping rectangles: a horizontal bar and a vertical bar that share a square patch at the centre. Cross area = 2 × (arm × long) − arm². The − arm² subtracts the double-counted centre square. You don't need this to draw the flag (painting over is fine), but it's the simplest non-trivial example of inclusion–exclusion.
Solution
import Foundation
import UIKit

// Filled rectangle at an absolute position (from Section 17)
func drawRectAt(at pos: Point,
                w: Double, h: Double, color: UIColor) {
    var p = Pen()
    p.penColor = color
    p.fillColor = color
    p.goto(x: pos.x, y: pos.y)
    for _ in 1...2 {
        p.addLine(distance: w)
        p.turn(degrees: 90)
        p.addLine(distance: h)
        p.turn(degrees: 90)
    }
    addShape(pen: p)
}

func drawSwitzerland(at pos: Point, scale: Double) {
    // Swiss flag: 1:1 square, cross arms 1/6 wide × 2/3 long
    let size: Double = 30 * scale
    let arm  = size / 6       // cross thickness
    let long = size * 2 / 3   // length of each cross bar

    // 1. Red square background
    drawRectAt(at: pos, w: size, h: size, color: .red)

    // 2. White horizontal bar (long × arm, centred)
    drawRectAt(
        at: Point(x: pos.x + (size - long) / 2,
                  y: pos.y + (size - arm)  / 2),
        w: long, h: arm, color: .white
    )

    // 3. White vertical bar (arm × long, centred)
    drawRectAt(
        at: Point(x: pos.x + (size - arm)  / 2,
                  y: pos.y + (size - long) / 2),
        w: arm, h: long, color: .white
    )
}

// Draw centred on the canvas origin
drawSwitzerland(at: Point(x: -90, y: -90), scale: 6)
Curriculum Connections
ConceptConnection
Geometry — Symmetry & AreaThe Swiss cross has 4-fold rotational symmetry and 4 lines of reflection symmetry. The overlapping rectangles share an area, so the cross area = 2 × (arm × long) − arm² (subtract the double-counted centre square).

Tonga

Tonga's flag is red with a white rectangle in the top-left corner containing a red cross. Use your positioned rectangle function and cross function to draw it.

Flag of Tonga
Nested coordinate frames. Tonga is the first flag with two "top-lefts" to reason about. First, the canton sits at the flag's top-left: cantonPos.y = pos.y + h − cantonH (remember, y increases upward, so the "top" is the highest y). Second, the cross is centred inside the canton using the formula (boxDim − shapeDim) / 2. This is the simplest example of local vs. world coordinates — the cross is positioned relative to the canton, which is positioned relative to the flag.
Generalising drawCrossInBox. Switzerland (Section 18) had a square box. Tonga has a rectangular canton. The centring helper works for any (boxW, boxH) — pass in whatever dimensions the containing shape has. Section 18 could even be retrofitted to call this helper too.
Solution
import Foundation
import UIKit

// Filled rectangle at an absolute position (from Section 17)
func drawRectAt(at pos: Point,
                w: Double, h: Double, color: UIColor) {
    var p = Pen()
    p.penColor = color
    p.fillColor = color
    p.goto(x: pos.x, y: pos.y)
    for _ in 1...2 {
        p.addLine(distance: w)
        p.turn(degrees: 90)
        p.addLine(distance: h)
        p.turn(degrees: 90)
    }
    addShape(pen: p)
}

// --- Generalised helper: centred plus-cross inside any rectangular box ---
// (works for both Switzerland and Tonga's canton)
func drawCrossInBox(at pos: Point,
                    boxW: Double, boxH: Double,
                    arm: Double, long: Double,
                    color: UIColor) {
    // Horizontal bar: length `long`, thickness `arm`
    drawRectAt(
        at: Point(x: pos.x + (boxW - long) / 2,
                  y: pos.y + (boxH - arm)  / 2),
        w: long, h: arm, color: color
    )
    // Vertical bar: thickness `arm`, length `long`
    drawRectAt(
        at: Point(x: pos.x + (boxW - arm)  / 2,
                  y: pos.y + (boxH - long) / 2),
        w: arm, h: long, color: color
    )
}

// --- Tonga: red field, white canton top-left, red cross inside canton ---
func drawTonga(at pos: Point, scale: Double) {
    // Flag: 2:1 ratio (e.g. 48 × 24)
    let w: Double = 48 * scale
    let h: Double = 24 * scale

    // Canton: ~3/8 of flag width × 1/2 of flag height, top-left
    let cantonW = w * 3 / 8
    let cantonH = h / 2
    let cantonPos = Point(x: pos.x, y: pos.y + h - cantonH)

    // Red cross proportions within the canton
    let arm  = cantonH / 5
    let long = cantonH * 4 / 5

    // 1. Red background
    drawRectAt(at: pos, w: w, h: h, color: .red)

    // 2. White canton (top-left)
    drawRectAt(at: cantonPos, w: cantonW, h: cantonH, color: .white)

    // 3. Red cross centred inside the canton
    drawCrossInBox(
        at: cantonPos,
        boxW: cantonW, boxH: cantonH,
        arm: arm, long: long,
        color: .red
    )
}

drawTonga(at: Point(x: -96, y: -48), scale: 4)
Curriculum Connections
ConceptConnection
Geometry — Composite ShapesThe white canton is a fraction of the total flag area. Calculating positions of shapes within other shapes applies fractional reasoning and proportional thinking.

Puerto Rico

Puerto Rico's flag has five alternating red and white horizontal stripes, with a blue equilateral triangle on the left containing a white star. Combine your stripe function, triangle loop, and star function to draw it.

Flag of Puerto Rico
Introducing drawStarCentered. This is the first exercise where you need to place a star at a specific geometric point — the centroid of the blue triangle. drawStarNK from Section 13 positions by the starting vertex and faces east, which is awkward for composition. drawStarCentered takes the centre directly and orients point-up. Formulas: R = size / (2·sin(πk/n)) and initial heading = 180° + 180°k/n. Both derived analytically — no magic constants.
sin(60°) = √3/2. This constant comes up everywhere: hexagons, 30-60-90 triangles, honeycombs, equilateral triangles. Derive it yourself: bisect an equilateral triangle of side s down the middle. The half-base is s/2, the hypotenuse is s, and by Pythagoras the height is √(s² − (s/2)²) = √(3s²/4) = s·√3/2. For Puerto Rico, the triangle's base = flag height, so the apex extends horizontally by h·√3/2 ≈ 0.866·h.
Triangle centroid. For any triangle, the centroid (centre of mass) is the average of the three vertices: ((ax+bx+cx)/3, (ay+by+cy)/3). For an equilateral triangle specifically, the centroid coincides with the incenter, circumcenter, and orthocenter — all four triangle centres collapse to the same point. This only happens for equilateral triangles.
Solution
import Foundation
import UIKit

// =====================================================================
// SHARED HELPERS
// =====================================================================

// Filled rectangle at an absolute position
func drawRectAt(at pos: Point,
                w: Double, h: Double, color: UIColor) {
    var p = Pen()
    p.penColor = color
    p.fillColor = color
    p.goto(x: pos.x, y: pos.y)
    for _ in 1...2 {
        p.addLine(distance: w)
        p.turn(degrees: 90)
        p.addLine(distance: h)
        p.turn(degrees: 90)
    }
    addShape(pen: p)
}

// General star polygon {n/k} centred at a point, oriented point-up.
// Works for any valid {n/k} — {5/2} pentagram, {7/2} heptagram, {7/3} spiky.
func drawStarCentered(at center: Point, n: Int, k: Int,
                      size: Double, color: UIColor) {
    // Circumradius: chord length s = 2R·sin(πk/n) ⇒ R = s/(2·sin(πk/n))
    let R = size / (2 * sin(.pi * Double(k) / Double(n)))

    // Place V₀ at top of circle (point-up), aim pen at V₁.
    let initialHeadingDeg = 180.0 + 180.0 * Double(k) / Double(n)
    let turnDeg = 360.0 * Double(k) / Double(n)

    var star = Pen()
    star.penColor = color
    star.fillColor = color

    star.goto(x: center.x, y: center.y + R)
    star.turn(degrees: initialHeadingDeg)

    for _ in 0..<n {
        star.addLine(distance: size)
        star.turn(degrees: turnDeg)
    }
    addShape(pen: star)
}

// =====================================================================
// PUERTO RICO
// =====================================================================

func drawPuertoRico(at pos: Point, scale: Double) {
    // Flag: 3:2 ratio (e.g. 60 × 40)
    let w: Double = 60 * scale
    let h: Double = 40 * scale
    let stripeH = h / 5

    // 1. Five horizontal stripes — red/white/red/white/red (bottom → top)
    let stripes: [UIColor] = [.red, .white, .red, .white, .red]
    for (i, color) in stripes.enumerated() {
        drawRectAt(
            at: Point(x: pos.x, y: pos.y + Double(i) * stripeH),
            w: w, h: stripeH, color: color
        )
    }

    // 2. Blue equilateral triangle, base on left edge, apex pointing right
    // Apex horizontal offset = side × sin(60°) = h × √3 / 2
    let apexX = pos.x + h * sqrt(3) / 2
    let topLeft = Point(x: pos.x, y: pos.y + h)
    let botLeft = Point(x: pos.x, y: pos.y)
    let apex    = Point(x: apexX, y: pos.y + h / 2)

    let tri = Triangle(a: topLeft, b: botLeft, c: apex)
    addFilledTriangle(
        tri,
        fillColor: .blue,
        borderColor: .blue,
        lineWidth: 1
    )

    // 3. White 5-pointed star at the triangle's centroid
    // Centroid of equilateral triangle = (pos.x + h√3/6, pos.y + h/2)
    let centroidX = pos.x + h * sqrt(3) / 6
    let centroidY = pos.y + h / 2
    let starSize  = h / 3

    drawStarCentered(
        at: Point(x: centroidX, y: centroidY),
        n: 5, k: 2,
        size: starSize,
        color: .white
    )
}

drawPuertoRico(at: Point(x: -90, y: -60), scale: 8)
Curriculum Connections
ConceptConnection
Geometry — TrianglesThe triangle is equilateral with its apex touching the right edge of the canton. Its height equals the flag height; side length = height / sin(60°) = height × 2/√3.

United States

The US flag has 13 alternating red and white horizontal stripes, and a blue canton in the top-left with 50 stars arranged in staggered rows. Use loops, your stripe function, and your star function to draw a simplified version.

Challenge: arrange the 50 stars in the correct 6/5 alternating row pattern (6 rows of 5, 5 rows of 4 with offset).

Flag of the United States
Parity determines the endpoint colours. With 13 stripes (odd), both the bottom stripe (i = 0) and the top stripe (i = 12) are red — because i % 2 == 0 is true for both. If the flag had an even number of stripes, the two ends would be different colours. This is why the US flag has 7 red stripes and 6 white, not 6-and-6.
Two ternary expressions encode the entire star grid. starsInRow = (row % 2 == 0) ? 5 : 4 picks the count for each row, and xOffset = (row % 2 == 0) ? colSpacing/2 : colSpacing shifts odd rows by half a column so they sit between the stars of adjacent even rows. This creates a rhombic (brick-wall) lattice — the same pattern that appears in crystallography, hexagonal close-packing, and actual brick walls.
Top-down row indexing in a bottom-up coordinate system. The loop treats row = 0 as the top of the canton, but y increases upward. The conversion: rowY = cantonPos.y + cantonH − (row + 0.5) × rowSpacing. The + 0.5 centres each star in its row's vertical band.
Solution
import Foundation
import UIKit

// Assumes drawRectAt and drawStarCentered from earlier sections are defined.

func drawUSA(at pos: Point, scale: Double) {
    // Flag: 19:10 ratio
    let w: Double = 38 * scale
    let h: Double = 20 * scale
    let stripeH = h / 13

    // 1. 13 stripes, bottom-up — red/white alternating, both ends red
    for i in 0..<13 {
        let color: UIColor = (i % 2 == 0) ? .red : .white
        drawRectAt(
            at: Point(x: pos.x, y: pos.y + Double(i) * stripeH),
            w: w, h: stripeH, color: color
        )
    }

    // 2. Blue canton — top-left, covering the top 7 stripes, 0.4 × flag width
    let cantonW = w * 0.4
    let cantonH = 7 * stripeH
    let cantonPos = Point(x: pos.x, y: pos.y + h - cantonH)
    drawRectAt(at: cantonPos, w: cantonW, h: cantonH, color: .blue)

    // 3. 50 stars — 11 rows alternating 5/4/5/4/...
    let rows = 11
    let maxStars = 5
    let rowSpacing = cantonH / Double(rows)
    let colSpacing = cantonW / Double(maxStars)
    let starSize = rowSpacing * 0.7

    for row in 0..<rows {
        let starsInRow = (row % 2 == 0) ? 5 : 4
        // 5-star rows: offset by half a column
        // 4-star rows: offset by a full column
        let xOffset = (row % 2 == 0) ? colSpacing / 2 : colSpacing

        // Row 0 = top of canton; y decreases as row increases
        let rowY = cantonPos.y + cantonH - (Double(row) + 0.5) * rowSpacing

        for col in 0..<starsInRow {
            let starX = cantonPos.x + xOffset + Double(col) * colSpacing

            // drawStarCentered takes the centre directly — no offsets needed.
            drawStarCentered(
                at: Point(x: starX, y: rowY),
                n: 5, k: 2,
                size: starSize,
                color: .white
            )
        }
    }
}

drawUSA(at: Point(x: -76, y: -40), scale: 4)
Curriculum Connections
ConceptConnection
Patterns & Nested LoopsThe staggered star arrangement is generated by a nested loop where the inner count and x-offset depend on whether the row index is even or odd — connecting number patterns to geometric layout.