AI Pipeline in gaming and state persistence
lesson connecting AI Pipeline to our game.
Unit 3: AI Pipelines and State Persistence in Games
Store an NPC’s state in private instance variables, protect it with constructors and methods, and save it so it survives a restart.
1. Reference Guide
Key Topics
| Term | Definition | Example |
|---|---|---|
| Instance variable | Field storing one object’s state. | private int health; |
| Encapsulation | private data, controlled methods. |
private int health; |
| Persistence | Saving state so it survives a restart. | toSaveString() |
The AI Pipeline
Every game tick, each NPC runs this loop. Without instance variables, update state has nowhere to store what the NPC learned.
Code Runner Challenge
Run it, then make the guard take much more damage and predict the output
View IPYNB Source
flowchart LR
P["Perceive"] --> D["Decide"]
D --> A["Act"]
A --> U["Update state"]
U --> S[("Save file")]
U -.->|next tick| P
2. LxD Cycle Process
Empathize: Students meet classes as a list of rules instead of the answer to a real problem: keeping data valid over time. Novices also hold wrong mental models of memory (Kaczmarczyk et al., 2010).
Define:
- POV: CSA students need to see a class as the guardian of valid state, because Class Design free-response questions test it.
- Learning Goal: Students will write a class with private instance variables, a constructor, and a validated mutator.
Ideate:
- HMW Question: How might we show that
privateprevents impossible states instead of being a rule to memorize? - Activity: Break a game, fix it, then build a Shopkeeper.
Prototype:
- Diagrams, runnable Java, an MCQ, and a scaffolded rubric.
Test:
- Ask peers to complete the practice without help, then revise any unclear instruction.
3. College Board Requirements
AP CSA Unit 3: Class Creation, about 10-18% of the multiple-choice section (College Board, 2025, p. 18). Topics covered:
- 3.1-3.4 Program design, anatomy of a class, constructors (A, B, E)
- 3.5 Writing methods (B, D, E)
- 3.7 Class variables and methods (C)
- 3.8-3.9 Scope and access,
this(A, B)
Class Design is free-response question 2 (College Board, 2025, p. 176). Inheritance is outside AP CSA (p. 45), so this lesson skips it.
4. Lesson Plan
Learning Objective: Write a class that keeps an NPC’s state in private instance variables, sets it in a constructor, and changes it only through validated methods.
Success Criteria: You can explain why objects of one class hold different state, protect it with private, and turn it into a save string and back.
Tech Talk (5 minutes)
An NPC is a save file with legs. The AI reads its instance variables to decide and writes new state when the world changes, so a guard attacks you after you steal even though you have not moved. Only the class should change its own state, and every object must start valid.
5. Code Examples
A. An NPC’s memory. Each object keeps its own state
Code Runner Challenge
Run it, then add a heal(int amount) method that never goes above 100
View IPYNB Source
classDiagram
class NPC {
-String name
-int health
-String state
+NPC(String name)
+takeDamage(int amount) void
}
In UML, - means private and + means public. this.name = name; assigns the parameter to the instance variable.
Code Runner Challenge
Run it, then create one more TrackedNPC and predict the new count
View IPYNB Source
// CODE_RUNNER: Run it, then make the guard take much more damage and predict the output
public class NPC {
private String name;
private int health;
private String state;
public NPC(String name) {
this.name = name;
this.health = 100;
this.state = "WANDER";
}
public void takeDamage(int amount) {
health -= amount;
}
public void describe() {
System.out.println(name + ": " + health + " HP, state=" + state);
}
public static void main(String[] args) {
NPC guard = new NPC("Guard Bob");
NPC villager = new NPC("Villager Sam");
guard.takeDamage(40);
guard.describe();
villager.describe();
}
}
NPC.main(null);
Notice: damaging the guard does not touch the villager.
B. Encapsulation. Why private protects the save file
Java protects your state from impossible values. In any other class, boss.health = -50; will not compile, so the only legal way to change health is a method you control, like the clamp in takeDamage.
Code Runner Challenge
Run it, then change the witnessTheft() amount to 3 and predict what happens
View IPYNB Source
// CODE_RUNNER: Run it, then add a heal(int amount) method that never goes above 100
public class SafeNPC {
private int health;
public SafeNPC() {
this.health = 100;
}
public void takeDamage(int amount) {
health -= amount;
if (health < 0) {
health = 0; // clamp: no negative HP, ever
}
}
public int getHealth() {
return health;
}
public static void main(String[] args) {
SafeNPC boss = new SafeNPC();
boss.takeDamage(30);
boss.takeDamage(9999);
System.out.println("Health: " + boss.getHealth());
// In ANOTHER class, boss.health = -50; would be a COMPILE ERROR
}
}
SafeNPC.main(null);
C. Class variables. State shared by the whole class
npcCount is a class variable (one copy total) and id is an instance variable (one per object).
Code Runner Challenge
Run it, then change the tampered health and see what the constructor does
View IPYNB Source
// CODE_RUNNER: Run it, then create one more TrackedNPC and predict the new count
public class TrackedNPC {
private static int npcCount = 0; // shared by the class
private int id; // one per object
public TrackedNPC() {
npcCount++;
id = npcCount;
}
public static int getNpcCount() {
return npcCount;
}
public static void main(String[] args) {
new TrackedNPC();
new TrackedNPC();
new TrackedNPC();
System.out.println("NPCs: " + TrackedNPC.getNpcCount());
}
}
TrackedNPC.main(null);
D. The AI pipeline in code. State makes the same input give a different result
Same distances, different behavior after the theft. Only grudge changed.
Code Runner Challenge
Replace the sample values with your own, then run it
View IPYNB Source
stateDiagram-v2
[*] --> WANDER
WANDER --> SEARCH : grudge at least 5
SEARCH --> ATTACK : player within 3 tiles
Code Runner Challenge
Fill in the blanks, then run it
View IPYNB Source
// CODE_RUNNER: Run it, then change the witnessTheft() amount to 3 and predict what happens
public class GrudgeNPC {
private int grudge; // memory of the player
private String state;
public GrudgeNPC() {
this.grudge = 0;
this.state = "WANDER";
}
public void witnessTheft() {
grudge += 5;
}
public void tick(int distance) {
if (grudge < 5) {
state = "WANDER";
} else if (distance < 4) {
state = "ATTACK";
} else {
state = "SEARCH";
}
System.out.println("grudge=" + grudge + ", distance " + distance + " -> " + state);
}
public static void main(String[] args) {
GrudgeNPC bob = new GrudgeNPC();
bob.tick(2);
bob.witnessTheft();
System.out.println("--- player stole something ---");
bob.tick(8);
bob.tick(2);
}
}
GrudgeNPC.main(null);
E. Saving and loading. Turning state into text and back
flowchart LR
A["NPC object"] -->|toSaveString| B[("Save file")]
B -->|read values back| C["New NPC object"]
A real game writes this text to a file, later parses it back into values, and builds a new object. The constructor validates every value it receives, so a corrupted save cannot create an impossible NPC. (Parsing with split and Integer.parseInt is standard Java but not on the exam.)
// CODE_RUNNER: Run it, then change the tampered health and see what the constructor does
public class SavableNPC {
private int health;
private String state;
public SavableNPC(int health, String state) {
if (health > 100) {
health = 100;
}
this.health = health;
this.state = state;
}
public String toSaveString() {
return state + "," + health;
}
public static void main(String[] args) {
SavableNPC bob = new SavableNPC(62, "ATTACK");
System.out.println("SAVED: " + bob.toSaveString());
SavableNPC loaded = new SavableNPC(62, "ATTACK"); // values read back from the file
System.out.println("LOADED: " + loaded.toSaveString());
SavableNPC tampered = new SavableNPC(9999, "ATTACK");
System.out.println("TAMPERED: " + tampered.toSaveString());
}
}
SavableNPC.main(null);
6. Hacks & Practice Tasks
Prepare your submission IPYNB
- Create a notebook in
_notebooks/homeworkwith this raw frontmatter cell at the top:
---
layout: post
codemirror: true
title: State Persistence HW
categories: [Java]
lesson_language: Java
lesson_topic: State-Persistence HW
lesson_part: interactive
lesson_type: lesson
permalink: /csa/unit_03/state-persistence-hw
author: yourGithubID
---
- Add code cells for the Popcorn Hack and Homework Hack, and make sure each runs with visible output.
- Submit your published page link and paste this in the description box:
Lesson: CSA Unit 3 State Persistence
MCQ: <paste the copied result line, such as 3/4 | answers: B,C,D,C>
Homework: mood after changeMood(20) = <value>, then changeMood(-99) = <value>
Homework: save string printed = <value>
Submission Safety Rules (Read First)
- One class per cell, ending with
ClassName.main(null);, with output showing. - Use your own values, and include your MCQ score.
Popcorn Hack (In-Class)
2-minute challenge: design a Companion with a name, a loyalty from 0 to 10, and whether it is following you. Make them private, set them in a constructor, and use your own values.
// CODE_RUNNER: Replace the sample values with your own, then run it
// Practice #1 - Try it yourself first!
public class Companion {
// Sample answer:
private String name;
private int loyalty;
private boolean isFollowing;
public Companion(String name) {
this.name = name;
this.loyalty = 5;
this.isFollowing = true;
}
public void describe() {
System.out.println(name + " | loyalty=" + loyalty + " | following=" + isFollowing);
}
public static void main(String[] args) {
new Companion("Pixel").describe();
}
}
Companion.main(null);
MCQ Check
4 questions, one at a time. Copy the score line into your submission notes.
Homework Hack
Task: Build a Shopkeeper with private name, gold, and mood, starting at 100 gold and mood 5, with mood kept between 0 and 10, and a save string.
Solution Skeleton:
// CODE_RUNNER: Fill in the blanks, then run it
public class Shopkeeper {
// 1. Three PRIVATE variables: name, gold, mood
// 2. Constructor: gold starts at 100, mood at 5
public Shopkeeper(String name) {
}
// 3. Add to mood, but keep it between 0 and 10
public void changeMood(int amount) {
}
public int getMood() {
return 0;
}
// 4. Return name,gold,mood (example: Mira,100,5)
public String toSaveString() {
return "";
}
public static void main(String[] args) {
Shopkeeper mira = new Shopkeeper("Mira");
mira.changeMood(20);
System.out.println(mira.getMood());
mira.changeMood(-99);
System.out.println(mira.getMood());
System.out.println(mira.toSaveString());
}
}
Shopkeeper.main(null);
Grading Plan (1 Point Total)
| Part | Points | What earns the points |
|---|---|---|
| Popcorn | 0.2 | Three private variables and a constructor; runs. |
| MCQ | 0.2 | 4 correct. 0.15 for 3, 0.1 for 1 or 2, 0.05 if all answered. |
| Homework: fields | 0.2 | Fields private; gold starts at 100, mood at 5. |
| Homework: validation | 0.2 | Mood stays between 0 and 10 after +20 and -99. |
| Homework: output | 0.2 | getMood() works and the save string prints as name,gold,mood. |
| Total | 1.0 |
7. Lesson Revisions
Revision Made: Lesson authors: what you changed because of it.
8. Feedback Evidence
Feedback Received: Lesson authors: what your peers said in the practice run.
9. References
College Board. (2025). AP Computer Science A course and exam description [Effective fall 2025]. https://apcentral.collegeboard.org/media/pdf/ap-computer-science-a-course-and-exam-description-effective-fall-2025.pdf
Kaczmarczyk, L. C., Petrick, E. R., East, J. P., & Herman, G. L. (2010). Identifying student misconceptions of programming. In SIGCSE ‘10 (pp. 107-111). ACM.