Lab 20: RealmKeeper
Integration — objects, arrays, file I/O, sorting in one program
After this lab, you will be able to:
- Design multiple classes with
Comparablefor a single application - Load structured data from files into typed arrays
- Build a menu-driven program with search, sort, and display operations
- Write modified data back to files for persistence
What You’re Building
RealmKeeper is a text-based world manager. You will define two classes — Block (terrain tiles with a type and elevation) and NPC (non-player characters with a name, role, and level). Both implement Comparable. The program loads blocks and NPCs from separate data files into arrays, then presents a menu: search for an NPC by name, sort blocks by elevation, sort NPCs by level, display all data, or save and quit. This capstone lab integrates every major concept from the quarter: classes, arrays, file I/O, sorting, and searching.
Concepts and Misconceptions
| Concept | Common Mistake | What the Test Catches |
|---|---|---|
| Multiple classes | Putting all code in one file instead of separate Block.java and NPC.java files |
Compilation error or missing class |
Comparable on two classes |
Implementing compareTo identically on both classes instead of using class-specific fields |
Blocks sorted by wrong field, NPCs sorted incorrectly |
| File loading | Assuming a fixed number of lines instead of reading the count from the file header | ArrayIndexOutOfBoundsException or unfilled array slots |
| Menu loop | Using == to compare the user’s menu choice string instead of .equals() |
Menu option never matches, infinite loop |
| Save to file | Overwriting the input file format (e.g., forgetting the header count line) | File cannot be reloaded on next run |
Checkpoints
Checkpoint 1: Define Block and NPC Classes with Comparable
What to do: Create a Block class with fields String type and int elevation. Implement Comparable<Block> with compareTo that sorts by elevation ascending. Create an NPC class with fields String name, String role, and int level. Implement Comparable<NPC> with compareTo that sorts by level descending, breaking ties alphabetically by name. Both classes need constructors, getters, and toString.
What the test checks: compareTo produces correct ordering for both classes. toString returns the expected format.
Debugging tip: If compareTo compiles but produces wrong results, double-check which direction each class sorts. Block sorts ascending (low elevation first): Integer.compare(this.elevation, other.elevation). NPC sorts descending (high level first): Integer.compare(other.level, this.level). Mixing these up is the most common error.
Checkpoint 2: Load Data from Files into Arrays
What to do: Write methods to read blocks.txt and npcs.txt. Each file’s first line is the count of records. Subsequent lines contain comma-separated fields. Parse each line, construct the appropriate object, and store it in an array. Handle FileNotFoundException gracefully.
What the test checks: Arrays contain the correct number of objects with correct field values after loading.
Debugging tip: If fields contain extra whitespace, call trim() on each token after splitting. If Integer.parseInt throws a NumberFormatException, print the raw token to see what you are actually parsing — a stray space or newline character is usually the cause. Test with the provided sample files before trying edge cases.
Checkpoint 3: Menu-Driven Operations
What to do: Present a numbered menu in a loop: (1) Search NPCs by name, (2) Sort blocks by elevation, (3) Sort NPCs by level, (4) Display all, (5) Save and quit. For search, use a linear scan comparing names with .equalsIgnoreCase(). For sort, call Arrays.sort. For display, loop through both arrays and print each element. For save, write both arrays back to their files in the original format (count on the first line, one record per line).
What the test checks: Each menu option produces the expected console output. The save operation writes files that can be reloaded correctly.
Debugging tip: If the menu runs once and exits, check your loop condition — it should continue until the user picks “save and quit.” If search never finds a match, verify you are using .equalsIgnoreCase(), not == or case-sensitive .equals(). If the saved file cannot be reloaded, compare its format line by line with the original input file — the header count and delimiter must match exactly.
How to Debug
-
Build incrementally. Get
BlockandNPCcompiling and tested before writing the file loader. Get the file loader working before building the menu. Each checkpoint builds on the previous one. -
Test each menu option independently. Before wiring up the menu loop, call each operation directly from
mainwith hardcoded data. Verify the output, then connect it to the menu. -
Round-trip test. Load the files, save them without modification, then reload and compare. If the data matches, your file format is correct. If it does not, print the saved file and compare it against the original.
Scoring
| Component | Points | Criteria |
|---|---|---|
| Checkpoints | 3 | 1 pt each. Binary: the checkpoint test passes or it does not. |
| Autograder | 5 | Correctness across all test cases. Partial credit by proportion of tests passed. |
| Timeliness | 2 | Full credit if submitted by the due date. 0 if late. |
| Total | 10 |