Video Game Design and Coding

Week 26 packet

Student instructions, tasks, and checks. The answer key is not in this packet. Links stay as text. This page does not embed a clip or run a file.

# Week 26 — Procedural does not mean arbitrary

## VGC-L126 — Represent a room as a grid

**Objective:** Map cell coordinates to pixel positions.

**Time:** 55 minutes

### Instruction
A grid turns a room into indexed data. C-GRID uses six rows and eight columns, with zero for open and one for blocked. The array is accessed as cells[y][x], not cells[x][y]. At forty pixels per cell, the center of cell (3,2) is (140,100). Rows increase downward. Draw the data first, then render it. The grid’s visible rectangles are not colliders until you explicitly create matching physics geometry.

### Worked example
A four-cell corridor reserved before random walls guarantees a required path. Other open cells may still be isolated, so the guarantee must be stated narrowly.

### Assignment
1. Draw a six-row/eight-column map and label three cell centers.
2. Compute centers for (0,0), (3,2) and (7,5).

### Acceptance checks
- Centers are (20,20), (140,100) and (300,220).
- Row/column indexing is consistent.

**Submit:** Grid worksheet and coordinate calculations.

**Question:** Why add half a cell when placing an actor at a cell center?

**Support:** Use the fixed 8 by 6 grid in C-GRID. Keep the guaranteed top-row/right-column path empty before adding any random walls. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Compare three integer seeds while checking the same route invariant. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---

## VGC-L127 — Generate reproducible variation

**Objective:** Use a local RandomNumberGenerator and an explicit seed.

**Time:** 55 minutes

### Instruction
A seeded generator produces a repeatable sequence for the same engine version and calls. C-GRID creates a local RandomNumberGenerator, assigns seed 2468 and samples each eligible cell. A fixed seed helps reproduce a reported layout. Global random calls elsewhere should not accidentally change this generator’s sequence. Record the seed with every test case. Reproducibility is not a claim that all future engine versions guarantee identical random sequences; keep the pinned course version for comparisons.

### Worked example
A four-cell corridor reserved before random walls guarantees a required path. Other open cells may still be isolated, so the guarantee must be stated narrowly.

Scaffold C-GRID — A deterministic grid with a guaranteed clear route
Setup: Attach to Node2D GridLab. This example draws, not physics, so the assignment adds matching wall collision only after validation. Grid is 8 columns by 6 rows at 40 pixels per cell. Row 0 and column 7 are a guaranteed clear route from (0,0) to (7,5). RNG seed is a nonpersonal integer.
```gdscript
extends Node2D
const COLS: int = 8
const ROWS: int = 6
const CELL: int = 40
var cells: Array = []
func _ready() -> void:
    var rng := RandomNumberGenerator.new()
    rng.seed = 2468
    for y in range(ROWS):
        var row: Array[int] = []
        for x in range(COLS):
            var blocked: bool = y != 0 and x != COLS - 1 and rng.randf() < 0.25
            row.append(1 if blocked else 0)
        cells.append(row)
    queue_redraw()
func _draw() -> void:
    for y in range(cells.size()):
        for x in range(cells[y].size()):
            var color := Color.DIM_GRAY if cells[y][x] == 1 else Color.BEIGE
            draw_rect(Rect2(x * CELL, y * CELL, CELL - 2, CELL - 2), color)
```

### Assignment
1. Run C-GRID twice with seed 2468 and compare maps.
2. Change to 1357 and record the new map.

### Acceptance checks
- Same seed and version reproduce the same layout.
- Different seed changes at least some eligible cells in the observed sample.

**Submit:** Grid project and two-seed comparison.

**Question:** Why store the seed in a procedural bug report?

**Support:** Use the fixed 8 by 6 grid in C-GRID. Keep the guaranteed top-row/right-column path empty before adding any random walls. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Compare three integer seeds while checking the same route invariant. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---

## VGC-L128 — Guarantee a route before adding obstacles

**Objective:** Preserve a design invariant in the generator.

**Time:** 55 minutes

### Instruction
Random wall placement can disconnect start and goal. C-GRID reserves row zero and column seven as open, guaranteeing a simple route from cell (0,0) to (7,5). Random obstacles occur only outside that route. This does not prove every open cell is connected, but it proves the required path exists. State exactly what your invariant guarantees. Test many seeds by inspecting the reserved cells rather than relying only on a visual impression.

### Worked example
A four-cell corridor reserved before random walls guarantees a required path. Other open cells may still be isolated, so the guarantee must be stated narrowly.

Scaffold C-GRID — A deterministic grid with a guaranteed clear route
Setup: Attach to Node2D GridLab. This example draws, not physics, so the assignment adds matching wall collision only after validation. Grid is 8 columns by 6 rows at 40 pixels per cell. Row 0 and column 7 are a guaranteed clear route from (0,0) to (7,5). RNG seed is a nonpersonal integer.
```gdscript
extends Node2D
const COLS: int = 8
const ROWS: int = 6
const CELL: int = 40
var cells: Array = []
func _ready() -> void:
    var rng := RandomNumberGenerator.new()
    rng.seed = 2468
    for y in range(ROWS):
        var row: Array[int] = []
        for x in range(COLS):
            var blocked: bool = y != 0 and x != COLS - 1 and rng.randf() < 0.25
            row.append(1 if blocked else 0)
        cells.append(row)
    queue_redraw()
func _draw() -> void:
    for y in range(cells.size()):
        for x in range(cells[y].size()):
            var color := Color.DIM_GRAY if cells[y][x] == 1 else Color.BEIGE
            draw_rect(Rect2(x * CELL, y * CELL, CELL - 2, CELL - 2), color)
```

Scaffold C-TESTS — A tiny pure-function test harness
Setup: Attach to Node TestLab. If using command line, create a separate SceneTree harness rather than running this Node script with --script. Run current TestLab scene in editor; assert reports a failed condition in Debugger. No external test framework or package is needed.
```gdscript
extends Node
func bounded_score(old_score: int, change: int) -> int:
    return clampi(old_score + change, 0, 999)
func _ready() -> void:
    assert(bounded_score(10, 2) == 12)
    assert(bounded_score(0, -3) == 0)
    assert(bounded_score(998, 9) == 999)
    print("3 score checks passed")
```

### Assignment
1. Add an assertion or loop checking every reserved route cell is zero.
2. Test seeds 0 through 19.

### Acceptance checks
- All twenty generated maps preserve the route.
- The claim does not overstate full-map connectivity.

**Submit:** Route-invariant test source and twenty-seed result log.

**Question:** Does this reserved path guarantee every open cell is reachable?

**Support:** Use the fixed 8 by 6 grid in C-GRID. Keep the guaranteed top-row/right-column path empty before adding any random walls. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Compare three integer seeds while checking the same route invariant. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---

## VGC-L129 — Create physics walls from grid data

**Objective:** Keep rendered and collision geometry driven by the same cells.

**Time:** 55 minutes

### Instruction
Build a Wall.tscn with a StaticBody2D, forty-by-forty RectangleShape2D and matching visual. For each blocked grid cell, instantiate the wall under an unscaled Walls Node2D and set its position to Vector2(x*40+20, y*40+20). Do not also place independent invisible walls by hand. If regenerating, remove the old walls before adding new ones. The player’s diameter must fit an open cell with comfortable clearance; use a smaller test player if necessary.

### Worked example
A four-cell corridor reserved before random walls guarantees a required path. Other open cells may still be isolated, so the guarantee must be stated narrowly.

Scaffold C-GRID — A deterministic grid with a guaranteed clear route
Setup: Attach to Node2D GridLab. This example draws, not physics, so the assignment adds matching wall collision only after validation. Grid is 8 columns by 6 rows at 40 pixels per cell. Row 0 and column 7 are a guaranteed clear route from (0,0) to (7,5). RNG seed is a nonpersonal integer.
```gdscript
extends Node2D
const COLS: int = 8
const ROWS: int = 6
const CELL: int = 40
var cells: Array = []
func _ready() -> void:
    var rng := RandomNumberGenerator.new()
    rng.seed = 2468
    for y in range(ROWS):
        var row: Array[int] = []
        for x in range(COLS):
            var blocked: bool = y != 0 and x != COLS - 1 and rng.randf() < 0.25
            row.append(1 if blocked else 0)
        cells.append(row)
    queue_redraw()
func _draw() -> void:
    for y in range(cells.size()):
        for x in range(cells[y].size()):
            var color := Color.DIM_GRAY if cells[y][x] == 1 else Color.BEIGE
            draw_rect(Rect2(x * CELL, y * CELL, CELL - 2, CELL - 2), color)
```

Scaffold C-BODY — Reusable top-down player with collision
Setup: Create CharacterBody2D root Player at (80,180); add CollisionShape2D with CircleShape2D radius 14. Attach this script to Player and save Player.tscn. Use C-FREE-MOVE input actions. Root collision layer 1, mask 2. Create a separate StaticBody2D Wall on layer 2 mask 1 with RectangleShape2D collision and a Polygon2D rectangle as visible child. Instance both under an unscaled Node2D World. Do not put a body inside another physics body.
```gdscript
extends CharacterBody2D
@export var speed: float = 160.0
func _draw() -> void:
    draw_circle(Vector2.ZERO, 14.0, Color.CORNFLOWER_BLUE)
    draw_line(Vector2(-8, 0), Vector2(8, 0), Color.WHITE, 3.0)
func _physics_process(_delta: float) -> void:
    velocity = Input.get_vector("move_left", "move_right", "move_up", "move_down") * speed
    move_and_slide()
```

### Assignment
1. Instantiate walls for every cell with value one.
2. Compare wall count with the number of blocked entries and walk the reserved route.

### Acceptance checks
- One blocked cell produces one wall.
- Visuals and colliders agree and the route remains traversable.

**Submit:** Grid-to-physics checkpoint and wall-count/path test.

**Question:** Why derive visuals and colliders from the same data?

**Support:** Use the fixed 8 by 6 grid in C-GRID. Keep the guaranteed top-row/right-column path empty before adding any random walls. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Compare three integer seeds while checking the same route invariant. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---

## VGC-L130 — Regenerate without leaking old objects

**Objective:** Reset generated state before building the next room.

**Time:** 55 minutes

### Instruction
Generation is also a reset problem. Clear the cells array before generating a new grid and free previous Walls children before replacing them. queue_free is deferred, so avoid counting old and new children in the same immediate check; wait until a later frame or use a fresh container. Reset player position to the start cell and clear velocity. Record the chosen seed in the HUD or readme using a nonpersonal integer. Repeated generation must not duplicate walls or leave stale collision.

### Worked example
A four-cell corridor reserved before random walls guarantees a required path. Other open cells may still be isolated, so the guarantee must be stated narrowly.

Scaffold C-GRID — A deterministic grid with a guaranteed clear route
Setup: Attach to Node2D GridLab. This example draws, not physics, so the assignment adds matching wall collision only after validation. Grid is 8 columns by 6 rows at 40 pixels per cell. Row 0 and column 7 are a guaranteed clear route from (0,0) to (7,5). RNG seed is a nonpersonal integer.
```gdscript
extends Node2D
const COLS: int = 8
const ROWS: int = 6
const CELL: int = 40
var cells: Array = []
func _ready() -> void:
    var rng := RandomNumberGenerator.new()
    rng.seed = 2468
    for y in range(ROWS):
        var row: Array[int] = []
        for x in range(COLS):
            var blocked: bool = y != 0 and x != COLS - 1 and rng.randf() < 0.25
            row.append(1 if blocked else 0)
        cells.append(row)
    queue_redraw()
func _draw() -> void:
    for y in range(cells.size()):
        for x in range(cells[y].size()):
            var color := Color.DIM_GRAY if cells[y][x] == 1 else Color.BEIGE
            draw_rect(Rect2(x * CELL, y * CELL, CELL - 2, CELL - 2), color)
```

### Assignment
1. Add a Regenerate control that rebuilds from a selected test seed.
2. Regenerate five times and compare cell/wall counts after cleanup.

### Acceptance checks
- Counts match one room, not the sum of previous rooms.
- Player always begins in the safe reserved cell.

**Submit:** Regeneration checkpoint and five-cycle count log.

**Question:** Why can child counts briefly include freed nodes?

**Support:** Use the fixed 8 by 6 grid in C-GRID. Keep the guaranteed top-row/right-column path empty before adding any random walls. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Compare three integer seeds while checking the same route invariant. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---