Video Game Design and Coding

Week 28 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 28 — Tests and debugging as daily tools

## VGC-L136 — Write tests before a small rule change

**Objective:** Predict behavior and encode it as assertions.

**Time:** 55 minutes

### Instruction
Use C-TESTS to see a small pure-function harness. The bounded_score rule adds a change and clamps the result to zero through 999. Write a new rule for a combo bonus: one through three deliveries earn two points each, while four or more earn an additional five-point bonus. Define zero and negative inputs explicitly before coding. Assertions compare actual and expected results. Start with a failing test in a duplicate or new lab, implement the function, then run all tests.

### Worked example
A capacity function tested at below-minimum, middle and above-maximum inputs is easier to trust than one demonstrated only at a normal value.

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. Write expected results for counts -1,0,1,3,4 and 6.
2. Implement the rule so invalid negative counts return zero.

### Acceptance checks
- Expected results are 0,0,2,6,13 and 17.
- All six assertions pass without changing expected values to hide errors.

**Submit:** Combo-rule lab and six assertions.

**Question:** Why is the first bonus-boundary test count 4 important?

**Support:** Use C-TESTS and one ordinary plus two boundary cases. Keep a known-good snapshot before injecting faults. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a property that should hold for many inputs and test a small finite range. 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-L137 — Choose boundaries and representative cases

**Objective:** Design a small test set that exposes common mistakes.

**Time:** 55 minutes

### Instruction
Large input spaces cannot always be exhausted. Test just below, at and just above a threshold, plus a normal middle case. For a bag capacity of five, try starting cargo zero, four and five with additions zero, one and ten. For the gate’s three Booleans, exhaustive testing remains practical. Choose the method based on the input space, not habit. Record why each case exists so the suite does not become many redundant checks with no purpose.

### Worked example
A capacity function tested at below-minimum, middle and above-maximum inputs is easier to trust than one demonstrated only at a normal value.

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")
```

Scaffold C-PUZZLE — A testable gate rule
Setup: Create Node root GateLab and attach this script. The pure gate_open function does not read input or change a scene. A level can call it with its inventory and lever state. Assertions run on start. For the assignment write additional tests before changing the rule.
```gdscript
extends Node
func gate_open(has_key: bool, lever_on: bool, alarm_on: bool) -> bool:
    return has_key and lever_on and not alarm_on
func _ready() -> void:
    assert(gate_open(true, true, false))
    assert(not gate_open(false, true, false))
    assert(not gate_open(true, false, false))
    assert(not gate_open(true, true, true))
    print("Gate checks passed")
```

### Assignment
1. Design an eight-case test table for a capacity-five bag function.
2. Label cases lower bound, upper bound, threshold or ordinary.

### Acceptance checks
- The set includes overflow and unchanged-state cases.
- Each case has a predicted numeric result.

**Submit:** Boundary-test table and optional assertion lab.

**Question:** When is exhaustive testing practical in this course?

**Support:** Use C-TESTS and one ordinary plus two boundary cases. Keep a known-good snapshot before injecting faults. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a property that should hold for many inputs and test a small finite range. 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-L138 — Read an error from the first useful cause

**Objective:** Distinguish parse, runtime and logic errors.

**Time:** 55 minutes

### Instruction
A parse error means the script could not be interpreted; a runtime error occurs while executing; a logic error runs but produces the wrong result. A misspelled variable may be caught before play, a missing $HUD/Score path may fail at runtime, and subtracting twice may silently produce the wrong score. Read the first relevant error and its file/line, then inspect the named context. Fixing a downstream symptom before the initial cause often wastes time. Use a duplicate lab for deliberate faults.

### Worked example
A capacity function tested at below-minimum, middle and above-maximum inputs is easier to trust than one demonstrated only at a normal value.

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. Create one safe example of each error category in a duplicate.
2. Record symptom, diagnostic clue and repair.

### Acceptance checks
- Each example is classified correctly.
- Restored lab has no remaining errors.

**Submit:** Three-category debugging report and clean lab.

**Question:** Is a game that runs without debugger errors necessarily correct?

**Support:** Use C-TESTS and one ordinary plus two boundary cases. Keep a known-good snapshot before injecting faults. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a property that should hold for many inputs and test a small finite range. 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-L139 — Refactor without breaking a tested contract

**Objective:** Improve naming and organization while preserving outputs.

**Time:** 55 minutes

### Instruction
Choose a working function with tests, such as plant_bed, gate_open or bounded_score. Rename vague parameters, extract a repeated calculation or remove a duplicated branch. Do not change its promised inputs and outputs unless you intentionally revise the contract. Run the same test suite before and after. A refactor is successful when behavior remains correct and another reader can follow the code more easily. Fewer lines are not the only measure of readability.

### Worked example
A capacity function tested at below-minimum, middle and above-maximum inputs is easier to trust than one demonstrated only at a normal value.

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. Make one focused readability refactor.
2. Show before/after function and unchanged test results.

### Acceptance checks
- All original cases still pass.
- The change has a clear readability or maintenance benefit.

**Submit:** Refactor comparison and passing test log.

**Question:** Why keep the same tests across a behavior-preserving refactor?

**Support:** Use C-TESTS and one ordinary plus two boundary cases. Keep a known-good snapshot before injecting faults. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a property that should hold for many inputs and test a small finite range. 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-L140 — Restore a version and bisect a regression manually

**Objective:** Use local checkpoints to locate when a defect appeared.

**Time:** 55 minutes

### Instruction
Version backups can be simple dated ZIP snapshots with short change notes. Create three small versions of a lab, deliberately break the third, then restore earlier versions into separate folders. Compare which version first fails the same reproduction. This is a manual form of narrowing the change interval. Never overwrite the only good copy. The course does not require a public repository, online account or command-line version-control tool. A backup must be restore-tested to be credible.

### Worked example
A capacity function tested at below-minimum, middle and above-maximum inputs is easier to trust than one demonstrated only at a normal value.

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. Create three labeled lab snapshots with one change each.
2. Identify the first failing snapshot and the responsible change.

### Acceptance checks
- The same reproduction passes in one version and fails in the next.
- A restored known-good artifact remains available.

**Submit:** Version log, three checkpoint names and regression diagnosis.

**Question:** Why is a change note useful even with a backup filename?

**Support:** Use C-TESTS and one ordinary plus two boundary cases. Keep a known-good snapshot before injecting faults. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a property that should hold for many inputs and test a small finite range. 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)

---