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
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

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 private prevents 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
    }
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

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);
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

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);
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

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);
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

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
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

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);
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

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

  1. Create a notebook in _notebooks/homework with 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
---
  1. Add code cells for the Popcorn Hack and Homework Hack, and make sure each runs with visible output.
  2. 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.