Week 22 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 22 — Inventory and transactions
## VGC-L106 — Model a tiny resource adventure
**Objective:** Define items, costs and an achievable objective.
**Time:** 55 minutes
### Instruction
Seed Station is a one-room resource puzzle. The player collects three seeds and two water units, spends one seed and one water to plant a bed, and wins after two beds are planted. Provide enough resources for the minimum solution and a way to restart. Store item types as stable IDs, not decorative labels. Sketch the data flow before building the room: collection changes inventory, planting validates both costs, success changes bed state, and the HUD displays counts.
### Worked example
With two tickets and one token, a purchase costing one of each succeeds once. A second request must preserve the remaining ticket if no token is available.
### Assignment
1. Draw the room and resource/cost table.
2. Trace a complete two-bed solution and one insufficient-resource attempt.
### Acceptance checks
- The planned resource supply can satisfy both beds.
- Failed planting changes no inventory or bed state.
**Submit:** Seed Station design and resource trace.
**Question:** Why must both costs be validated before subtracting either?
**Support:** Use C-INVENTORY in a blank lab and test add/spend before connecting any button or pickup. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.
**Optional extension:** Add one new allowed item ID and its tests without changing the transaction algorithm. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.
**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html), [Saving games: FileAccess and JSON](https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html)
---
## VGC-L107 — Use a dictionary as an inventory
**Objective:** Store quantities by stable item ID.
**Time:** 55 minutes
### Instruction
A Dictionary maps keys to values. C-INVENTORY maps seed and water to integer counts. The same add_item function can handle either allowed ID, reducing duplicate code. Reject unknown IDs and nonpositive amounts before changing state. Use int when reading a Variant value as a quantity. A display label such as “Water can” may change while the internal water key stays fixed. Start with the standalone assertions before connecting pickups so you know arithmetic is correct.
### Worked example
With two tickets and one token, a purchase costing one of each succeeds once. A second request must preserve the remaining ticket if no token is available.
Scaffold C-INVENTORY — Dictionary inventory with guarded spending
Setup: Attach to Node InventoryLab for standalone testing. In a real level put the same functions on its inventory node and update UI after successful calls. Item IDs are short fixed strings, never a player’s name. A key represents one item type and its integer value is the quantity.
```gdscript
extends Node
var items: Dictionary = {"seed": 0, "water": 0}
func add_item(id: String, amount: int = 1) -> bool:
if not items.has(id) or amount <= 0:
return false
items[id] = int(items[id]) + amount
return true
func spend(id: String, amount: int = 1) -> bool:
if not items.has(id) or amount <= 0 or int(items[id]) < amount:
return false
items[id] = int(items[id]) - amount
return true
func _ready() -> void:
assert(add_item("seed", 2))
assert(spend("seed", 1))
assert(not spend("seed", 2))
assert(items["seed"] == 1)
```
### Assignment
1. Run C-INVENTORY and add water tests.
2. Test unknown ID, zero amount and negative amount.
### Acceptance checks
- Allowed positive additions work.
- Invalid IDs and amounts leave all counts unchanged.
**Submit:** Inventory lab source and six-case table.
**Question:** Why not create a new arbitrary inventory key from every input string?
**Support:** Use C-INVENTORY in a blank lab and test add/spend before connecting any button or pickup. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.
**Optional extension:** Add one new allowed item ID and its tests without changing the transaction algorithm. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.
**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html), [Saving games: FileAccess and JSON](https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html)
---
## VGC-L108 — Make a two-resource purchase atomic
**Objective:** Validate the whole transaction before applying effects.
**Time:** 55 minutes
### Instruction
Atomic here means the planting action either completes fully or leaves the prior state unchanged. Check bed not already planted, seed at least one and water at least one. Only after all checks succeed should you subtract both and mark planted. Do not call spend(seed) and then discover spend(water) failed without rollback. Return a Boolean or clear result code so the caller can choose success or failure feedback. Write the three invalid cases before wiring the UI.
### Worked example
With two tickets and one token, a purchase costing one of each succeeds once. A second request must preserve the remaining ticket if no token is available.
Scaffold C-INVENTORY — Dictionary inventory with guarded spending
Setup: Attach to Node InventoryLab for standalone testing. In a real level put the same functions on its inventory node and update UI after successful calls. Item IDs are short fixed strings, never a player’s name. A key represents one item type and its integer value is the quantity.
```gdscript
extends Node
var items: Dictionary = {"seed": 0, "water": 0}
func add_item(id: String, amount: int = 1) -> bool:
if not items.has(id) or amount <= 0:
return false
items[id] = int(items[id]) + amount
return true
func spend(id: String, amount: int = 1) -> bool:
if not items.has(id) or amount <= 0 or int(items[id]) < amount:
return false
items[id] = int(items[id]) - amount
return true
func _ready() -> void:
assert(add_item("seed", 2))
assert(spend("seed", 1))
assert(not spend("seed", 2))
assert(items["seed"] == 1)
```
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. Implement plant_bed with both-cost validation and a planted flag.
2. Test missing seed, missing water, already planted and valid purchase.
### Acceptance checks
- Every failed case preserves both counts and bed state.
- Success subtracts exactly one of each and marks one bed.
**Submit:** Atomic planting function and before/after tables.
**Question:** Why are two individually safe spend calls not automatically one safe transaction?
**Support:** Use C-INVENTORY in a blank lab and test add/spend before connecting any button or pickup. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.
**Optional extension:** Add one new allowed item ID and its tests without changing the transaction algorithm. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.
**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html), [Saving games: FileAccess and JSON](https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html)
---
## VGC-L109 — Render inventory from data
**Objective:** Refresh a view after accepted mutations.
**Time:** 55 minutes
### Instruction
The HUD should be a view of inventory, not a second inventory. Write refresh_inventory to format seed and water counts, then call it after successful collection, planting and reset. Disable or explain an unavailable Plant action, but still validate in the handler because a disabled-looking button alone is not a rule. A stale label can mislead even when arithmetic is correct. Test changes in both directions and compare the screen with the actual dictionary values.
### Worked example
With two tickets and one token, a purchase costing one of each succeeds once. A second request must preserve the remaining ticket if no token is available.
Scaffold C-INVENTORY — Dictionary inventory with guarded spending
Setup: Attach to Node InventoryLab for standalone testing. In a real level put the same functions on its inventory node and update UI after successful calls. Item IDs are short fixed strings, never a player’s name. A key represents one item type and its integer value is the quantity.
```gdscript
extends Node
var items: Dictionary = {"seed": 0, "water": 0}
func add_item(id: String, amount: int = 1) -> bool:
if not items.has(id) or amount <= 0:
return false
items[id] = int(items[id]) + amount
return true
func spend(id: String, amount: int = 1) -> bool:
if not items.has(id) or amount <= 0 or int(items[id]) < amount:
return false
items[id] = int(items[id]) - amount
return true
func _ready() -> void:
assert(add_item("seed", 2))
assert(spend("seed", 1))
assert(not spend("seed", 2))
assert(items["seed"] == 1)
```
### Assignment
1. Add seed/water labels and refresh after every successful mutation.
2. Test collect, plant, failed plant and restart.
### Acceptance checks
- Displayed counts match dictionary after each event.
- Failed actions show an explanation without changing counts.
**Submit:** Inventory UI checkpoint and four-event comparison.
**Question:** Why validate in the handler even if the button is disabled?
**Support:** Use C-INVENTORY in a blank lab and test add/spend before connecting any button or pickup. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.
**Optional extension:** Add one new allowed item ID and its tests without changing the transaction algorithm. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.
**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html), [Saving games: FileAccess and JSON](https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html)
---
## VGC-L110 — Integrate Seed Station without saving yet
**Objective:** Prove the local game loop before adding file I/O.
**Time:** 55 minutes
### Instruction
Add pickups, two bed interactions, progress text, completion and Restart. Keep the inventory in memory for this checkpoint. A clean transient game is easier to debug than one that mixes faulty rules with a faulty save. Define whether touching a bed automatically plants or whether a nearby prompt requests an action, then make feedback match. Test collecting resources in two different orders. The same valid final quantities should produce the same ability to plant.
### Worked example
With two tickets and one token, a purchase costing one of each succeeds once. A second request must preserve the remaining ticket if no token is available.
Scaffold C-INVENTORY — Dictionary inventory with guarded spending
Setup: Attach to Node InventoryLab for standalone testing. In a real level put the same functions on its inventory node and update UI after successful calls. Item IDs are short fixed strings, never a player’s name. A key represents one item type and its integer value is the quantity.
```gdscript
extends Node
var items: Dictionary = {"seed": 0, "water": 0}
func add_item(id: String, amount: int = 1) -> bool:
if not items.has(id) or amount <= 0:
return false
items[id] = int(items[id]) + amount
return true
func spend(id: String, amount: int = 1) -> bool:
if not items.has(id) or amount <= 0 or int(items[id]) < amount:
return false
items[id] = int(items[id]) - amount
return true
func _ready() -> void:
assert(add_item("seed", 2))
assert(spend("seed", 1))
assert(not spend("seed", 2))
assert(items["seed"] == 1)
```
Scaffold C-STATES — Explicit game states and gated scoring
Setup: Attach to Node root StateLab with child Label Status. This is a standalone demonstrator; call start_round, add_points, finish_round from buttons or Remote Inspector. In a real game the manager owns this state. Additional Player and spawner scripts must also honor the manager’s state, or be disabled when finishing.
```gdscript
extends Node
enum Phase { TITLE, PLAY, WIN, LOSE }
var phase: Phase = Phase.TITLE
var score: int = 0
func _ready() -> void:
_show()
func start_round() -> void:
score = 0
phase = Phase.PLAY
_show()
func add_points(amount: int) -> void:
if phase != Phase.PLAY:
return
score += maxi(amount, 0)
_show()
func finish_round(won: bool) -> void:
if phase != Phase.PLAY:
return
phase = Phase.WIN if won else Phase.LOSE
_show()
func _show() -> void:
$Status.text = "State: %s | Score: %d" % [Phase.keys()[phase], score]
```
### Assignment
1. Build the two-bed game loop with enough resources and clear interactions.
2. Complete in two collection orders and test a failed planting attempt.
### Acceptance checks
- Both valid orders can finish.
- Restart restores resources, beds and progress.
**Submit:** Seed Station in-memory source checkpoint and loop tests.
**Question:** Why delay saving until the in-memory rules work?
**Support:** Use C-INVENTORY in a blank lab and test add/spend before connecting any button or pickup. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.
**Optional extension:** Add one new allowed item ID and its tests without changing the transaction algorithm. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.
**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html), [Saving games: FileAccess and JSON](https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html)
---
