Pilgrim's Journey Engine (FINAL)
Apologetic question: "The shape of the Christian journey"
Project 14 — FINAL: Pilgrim’s Journey Engine
“And let us not grow weary of doing good, for in due season we will reap, if we do not give up.” — Galatians 6:9
“Now I saw in my dream, that the highway up which Christian was to go was fenced on either side with a wall, and that wall is called Salvation.” — John Bunyan, The Pilgrim’s Progress
Chapter: 16 — Final Review
Time: 75 minutes recommended (your instructor may use 60–90)
Submit: A link to your code — an OnlineGDB project URL or a public GitHub repo URL — with JourneyEngine.java + supporting class files as the main source file. See Appendix D for the full workflow.
Allowed during the exam:
- The textbook itself (printed or non-interactive PDF).
- Your own past project files, in printout or USB form.
- The compiler, the JVM, and a basic editor.
NOT allowed:
- Internet.
- AI assistants of any kind.
- Anyone else’s code.
- Communication with another person during the exam window.
The Setup
John Bunyan’s The Pilgrim’s Progress (1678) is the most-read Christian allegory in English. A pilgrim named Christian travels from the City of Destruction to the Celestial City. He meets Trials (the Slough of Despond, the Hill of Difficulty, Apollyon, Doubting Castle). He meets Companions (Faithful, Hopeful, Evangelist). He meets Temptations (Vanity Fair, By-Path Meadow). Each encounter does something different to him. Some test his faith. Some strengthen it. Some both.
Your final project: build the engine for that journey. Not Bunyan’s specific plot — the shape of a journey where every encounter does something different, and polymorphism handles the dispatch.
This is everything you’ve learned in Phase 2, in one program. Classes, abstract bases, inheritance, polymorphism, interfaces, ArrayList. In Java. In one class session.
A direct word before you start, as with every project that touches the apologetics frame:
This engine does not take a doctrinal position on perseverance, assurance, or anything else theological. It is a polymorphic engine that models the shape of a journey allegory. Bunyan did the theology. Your job is the engine.
Coach’s Note (confessional Lutheran framing) — John Bunyan wrote from the Puritan tradition — a Particular Baptist whose theology of conversion, perseverance, and assurance differs in places from confessional Lutheranism. The journey shape — pilgrim, trials, companions, temptations — is shared across Christian traditions (think of Luther’s “the Christian life is daily repentance,” or the Augsburg Confession on the church under the cross). This engine models the shape; it does not endorse Bunyan’s specific theology any more than it endorses Dante’s. If you’d prefer an explicitly Lutheran allegorical frame for your encounters, consider Luther’s Bondage of the Will themes (the will in captivity, freed only by grace), or hymn-image encounters like A Mighty Fortress — both yield perfectly legal Hard-tier flex variations on this engine.
Learning Targets
By completing this project, you will demonstrate that you can:
- Design an abstract base class with one or more abstract methods.
- Build a hierarchy of concrete subclasses, each with
@Overrideimplementations. - Define and implement Java interfaces.
- Combine inheritance and interfaces (a class extends one and implements multiple).
- Use
ArrayListto manage a polymorphic collection. - Write a
mainthat drives the system. - Submit working code under exam conditions.
Normal Tier
Goal: A working journey engine with an abstract Encounter, two concrete subclasses, and a Pilgrim that journeys through them.
Required features
-
Abstract
Encounterclass:public abstract class Encounter { protected String name; protected int intensity; public Encounter(String name, int intensity) { this.name = name; this.intensity = intensity; } public abstract void engage(Pilgrim p); public String getName() { return name; } } -
Two concrete subclasses:
Trial extends Encounter— overridesengageto subtractintensityfrom the pilgrim’s faith.Companion extends Encounter— overridesengageto add some amount (e.g.,intensity * 2) to the pilgrim’s faith, clamped at 100.
-
Pilgrimclass:public class Pilgrim { private String name; private int faith; public Pilgrim(String name, int startingFaith) { ... } public void loseFaith(int amount) { ... } // clamp at 0 public void gainFaith(int amount) { ... } // clamp at 100 public int getFaith() { ... } public String getName() { ... } public boolean hasGivenUp() { return faith <= 0; } } -
A
runJourney(Pilgrim p, ArrayList<Encounter> route)method (static, in your main class — or on the Pilgrim — your call) that:- Loops through the route in order.
- For each Encounter, calls
engage(p). - Polymorphism does the dispatch — no
instanceofchecks needed, noswitchon type. - Stops early if
p.hasGivenUp(). - Prints whether the pilgrim arrived or gave up.
-
A
mainmethod that:- Constructs a Pilgrim with a name and starting faith.
- Constructs an
ArrayList<Encounter>with at least 5 mixed encounters (Trials and Companions). - Calls
runJourney. - Prints the final state.
-
Compiles cleanly with
javac *.java.
Normal-tier rubric (out of 100)
| Criterion | Points |
|---|---|
Compiles cleanly with javac | 10 |
| Runs cleanly | 10 |
Abstract Encounter class correctly declared | 15 |
Trial and Companion subclasses with @Override | 20 |
Pilgrim class with the required methods | 15 |
runJourney uses polymorphism (no instanceof in main loop) | 15 |
Early stop on hasGivenUp() | 5 |
main constructs a mixed route and runs the journey | 10 |
Medium Tier (+up to 25% extra credit)
Complete both M1 and M2 below for the full +25%. Each feature is itemized so partial credit is clean.
M1. Two interfaces (+12)
Add:
interface Strengthening { void strengthen(Pilgrim p); }interface Testing { int trialCost(); }
Implement them on appropriate concrete classes:
Companion implements Strengthening—strengthen()gives a small extra faith boost.Trial implements Testing—trialCost()returns the intensity.
Update your runJourney so that, after engage, it also:
- Calls
strengthen(p)if the encounter implements Strengthening. - Tracks the total trial cost across the journey, printed at the end.
Use instanceof for these capability checks (this is the right time for instanceof — when probing for an optional capability).
M2. A third encounter type with both interfaces (+13)
Add a Mentor extends Companion implements Strengthening (or Mentor extends Encounter implements Strengthening, Testing — either works). A Mentor strengthens and tests — they push you and they support you.
Update your route to include at least one Mentor. Confirm the dispatch handles all three encounter types correctly.
Medium-tier rubric (+25 total)
| Feature | Points |
|---|---|
M1 — both interfaces declared and used in runJourney | +12 |
| M2 — Mentor implementing both interfaces, used in route | +13 |
Hard Tier (+up to 25% additional extra credit)
Pick ONE of the following for the full Hard extra credit (+25). Each H feature stands alone — H1, H2, H3, and H4 each earn the full Hard credit on their own if completed cleanly. Each additional Hard feature you complete earns +5 bonus on top, if you have time. The 60–90 minute clock is real; do not try to ship all four.
H1. Party of pilgrims (full Hard credit if chosen: +25)
Add a Party class that holds an ArrayList<Pilgrim>. Add void runPartyJourney(ArrayList<Encounter> route) that, for each encounter, calls engage on every living pilgrim in the party.
The journey ends when:
- All pilgrims have given up (party total failure), OR
- The route is fully traversed (arrival of survivors).
Print a per-pilgrim final status at the end. Some may have arrived; some may have given up. Both outcomes are part of the story.
H2. Spellbook / counsel-book (full Hard credit if chosen: +25)
Add an abstract Counsel class with a apply(Pilgrim p) method. Three concrete subclasses:
EncouragementCounsel— boosts faith significantly.RebukeCounsel— costs faith short-term but clamps it from going below a threshold.PrayerCounsel— restores half the gap between current faith and 100.
Add a Pastor subclass:
public class Pastor extends Companion {
private ArrayList<Counsel> counselBook;
public Pastor(String name, ArrayList<Counsel> counsels) {
super(name, 0); // intensity is irrelevant for a Pastor
this.counselBook = counsels;
}
@Override
public void engage(Pilgrim p) {
// Pick the most appropriate counsel based on the pilgrim's state.
// E.g., if faith < 30, use Prayer. If 30 <= faith < 70, use Encouragement.
// If faith >= 70, use Rebuke. Apply it.
}
}
Pastors are themselves polymorphic; their engage selects from a polymorphic collection. Polymorphism nested in polymorphism.
H3. Journey result object (full Hard credit if chosen: +25)
Build a JourneyResult class — final int arrived, final int gaveUp, final String mvpName (the pilgrim who finished with the highest faith). The keyword final on an instance field makes it assign-once-in-the-constructor — Java’s equivalent of declaring a field that can’t be reassigned later. (Chapter 14 §14.6 covered final on classes and methods; the field form works the same way: set it once at construction, never again.)
Return a JourneyResult from runPartyJourney. Print its summary in main.
This is “the result is itself a structured value, not just text output.” Same pattern as battle results in real game engines.
H4. Hand-built linked route (Phase 1 callback) (full Hard credit if chosen: +25)
Replace the ArrayList<Encounter> route with a hand-built linked list of Encounters — no ArrayList, no java.util.LinkedList. Build a private static RouteNode class inside JourneyEngine:
private static class RouteNode {
Encounter encounter;
RouteNode next;
RouteNode(Encounter e) { this.encounter = e; this.next = null; }
}
Add helper methods addEncounter(Encounter e) (appends to the end) and runRoute(Pilgrim p) (walks head → next → next → null, calling engage on each). This is the Java version of Chapter 11’s Chain — same pattern, no new/delete ceremony, no Rule of Three to worry about because the GC handles it. Compare your Java implementation to your C++ Chain from Project 11; in your reflection comment, name two specific things that got easier and one thing that did not.
This Hard-tier feature closes the loop on the course’s hardest C++ chapter: the design of linked structures, in the language that hides the memory management. Worth more than it looks.
Hard-tier rubric
| Feature | Points |
|---|---|
| Pick ONE of H1–H4, completed and demonstrated | +25 |
| Each additional H feature also completed (max 3 extras) | +5 each (bonus) |
So: one Hard feature = +25. Two = +30. Three = +35. All four = +40. The point ceiling exists, but realistically nobody finishes all four under exam time — and trying to is the most common way to ship a non-compiling submission.
Submission
Submit one URL via the course portal:
- OnlineGDB project link (recommended for Coding 1 and Coding 2). Create your project at onlinegdb.com, choose Java as the project language, build your solution, and share the link. See Appendix D for the full workflow.
- GitHub repo link (optional). If you’ve set up local development on your own (see Appendix B), push the source to a public repo and submit that URL. The grader will compile with
javac *.javaand run withjava JourneyEngine.
What the linked project must contain
- Your Java source files — typically
JourneyEngine.java(withmain) plus supporting class files (Pilgrim.java,Encounter.java, the concrete encounter classes, etc.). Filenames must match public class names exactly. - A reflection comment block at the very top of
JourneyEngine.java:
/*
* Tier targeted: Normal / Medium / Hard
* Features done: list each feature you completed
* What I learned: one short paragraph (no bullets)
* What I'd change: one sentence
* AI usage: where and how, if any. Be honest.
*/
- The program left in a “demonstrable” state — when the grader presses Run, the features for your targeted tier should be exercised. Hard-code inputs at the top of
main()(or pre-fill OnlineGDB’s Stdin panel) so the grader doesn’t have to guess what to type.
That’s it. No separate demo.txt. No screenshots. The instructor will open your link, read the comment block, run the program, and grade against the rubric.
Coach’s Note — Coding 1 and Coding 2 focus on writing code, not managing development environments. If something behaves oddly, you and the grader are looking at the exact same browser-hosted environment — there are no “works on my machine” defenses by design. Coding 3 will introduce a local toolchain properly.
Hints (Read Before the Exam)
- Start with the Pilgrim class. It’s the simplest. Get it compiling and running with a tiny test in main.
- Then add the abstract Encounter and one concrete subclass. Verify the override works.
- Then add a second subclass. Verify polymorphic dispatch.
- Then add the journey loop. Verify it runs end-to-end with a small route.
- Only then start on Medium tier. Interfaces first, then the third encounter type.
- Hard tier — pick ONE of H1–H4, only if you have 15–20+ minutes left after a clean Medium. Each Hard sub-feature is a standalone +25; do not try to ship all four. Pick the one that fits the time you have remaining (H1 Party and H4 Linked Route are usually fastest; H2 Counsel-book is the longest because of the nested polymorphism). Each additional H feature you ship earns +5 bonus, but only if your Normal and Medium tier still compile and run cleanly.
- If a feature isn’t working with 5 minutes left, remove it and ship a clean lower-tier version. Better to lose a feature than to submit a non-compiling file.
Coach’s Note on Exam Day
The students who pass the final have done two things: the reps before the exam, and at least one full timed practice run.
If you haven’t done a timed practice run, do Drill 5 in the exercises right now, even if you’re reading this two hours before the exam. The discipline of writing the same shape of program under a clock changes how your fingers move.
In the exam room: start with the simplest piece. Build it. Compile it. Run it. Then add the next piece. Compile early, compile often. Java’s compiler is your friend — use it as a tight feedback loop.
The final is a project. Treat it like one.
A Theological Footnote
The journey allegory is not Christianity itself. Christianity has many genres of self-understanding — covenant, sacrament, communion, vocation, kingdom, exile, pilgrimage. Bunyan’s allegory captures one of them with extraordinary force, and it’s the one most amenable to a polymorphic engine. The other genres are not less true; they’re just shaped differently.
If you’ve taken this final seriously — the technical work, not the metaphor — you’ve done what a sixteen-week intro programming course can do. You can sit down to a blank file and build a small working system in an hour. That is a real skill. Cherish it.
If the apologetics theme has spoken to you, you’ve also done some honest thinking about questions that matter. The reps for that don’t end with this course. They never do.
Show up rested. Trust your training. Submit something honest.
See you on the other side.