Project 12

Argument Case File

Apologetic question: "What kinds of arguments for God are there?"

Project 12 — Argument Case File

“Have you not known? Have you not heard? The LORD is the everlasting God, the Creator of the ends of the earth.” — Isaiah 40:28

“He could have ended the discussion at any moment by means of a miracle. But I think he would not, for the same reason that a teacher cannot end the lesson by writing the answer on the board — what we need is not the answer, but the practice of getting to one.” — paraphrased from C.S. Lewis

Chapter: 12 — Inheritance in C++ Due: End of Week 12 Submit: A link to your code — an OnlineGDB project URL or a public GitHub repo URL — with case_file.cpp as the main source file. See Appendix D for the full workflow. Allowed tools: Everything through Chapter 12 — classes, inheritance, virtual, override, polymorphism. Not yet allowed: Java syntax (Chapter 13 starts the migration).


The Setup

There are several traditional types of arguments for the existence of God. They have been honed over centuries by some of the sharpest minds in history. Each one has a different starting point, makes different demands of its premises, and faces different objections. They are not interchangeable — a Cosmological Argument doesn’t do what a Moral Argument does, and neither does what a Teleological Argument does.

But here’s the move: they all share structure. Each argument:

  • States its premises clearly.
  • States its conclusion.
  • Makes a characteristic defensive move against the most common objection.

That shared structure is exactly what a base class is for. The differences — what each argument actually argues — are exactly what overridden methods are for. The set of arguments, held together, is a CaseFile. Polymorphism does the work.

A direct word before you start, repeated for emphasis: this project does not claim that any single argument settles the question of God’s existence. The classical apologetic case is several arguments working together, each strong against different objections, each shoring up the others. That’s the whole pedagogical point of the project. If your CaseFile had only one Argument* in it, you’d be doing exactly what apologists shouldn’t do. Build the file with several.

Coach’s Note (confessional Lutheran framing) — Luther was famously skeptical of natural theology — building up to God from reason alone, what he called a theologia gloriae (“theology of glory”). His own work centers on the theologia crucis (“theology of the cross”) — God revealed in Christ crucified, received by faith through Word and Sacrament, not deduced. The arguments in this project (Aquinas’s Five Ways, Anselm/Plantinga’s Ontological) belong to the broader Christian tradition; Lutheran apologetics has generally treated them as secondary witnesses to general revelation (cf. Romans 1) rather than as the foundation of faith. Use them as conversation tools. Don’t preach with them. The Lutheran apologist John Warwick Montgomery uses these arguments thoughtfully — read his work for an example of how.


Learning Targets

By completing this project, you will demonstrate that you can:

  • Design a class hierarchy with a meaningful base and concrete subclasses.
  • Use virtual methods correctly, with override on overrides.
  • Provide a virtual destructor.
  • Store derived objects polymorphically via base-class pointers.
  • Iterate a polymorphic collection and dispatch correctly to each.
  • Chain constructors to base classes via initializer lists.
  • (Hard tier) Use pure virtual methods and a deeper hierarchy.

Normal Tier

Goal: A base Argument class with at least three concrete subclasses (Cosmological, Moral, Ontological) plus a CaseFile that holds and iterates them polymorphically.

Required features

  1. Base class Argument:

    class Argument {
    protected:
        string label;        // e.g., "Cosmological Argument"
        string main_claim;   // a one-sentence summary
        string conclusion;   // a one-sentence conclusion
    public:
        Argument(string l, string c, string conc);
        virtual ~Argument() {}
        virtual void defend() const;
        string get_label() const { return label; }
    };

    The base defend() prints a generic “[Label]: claim → conclusion.” Each subclass will override it.

  2. Three concrete subclasses, each overriding defend():

    • Cosmological : public Argument — defends based on contingency: the universe is contingent (could have not existed); contingent things require a sufficient reason; therefore there is a necessary being. Override defend() to print this case in 2-4 lines, with a real attribution (Aquinas’s Third Way — the argument from contingency — is the canonical source; the Second Way is the related-but-distinct argument from efficient causation).

    • Moral : public Argument — defends based on objective moral values: objective moral values exist; they require a transcendent standard; therefore the standard exists. Override defend() accordingly. Lewis’s Mere Christianity opening is the canonical source.

    • Ontological : public Argument — defends based on the concept of God: the concept of a maximally great being, properly analyzed, entails its existence in at least one possible world; therefore (by the S5 axiom of modal logic, ◇□p → □p — necessary existence in some possible world entails necessary existence in all, including this one) it exists in this world. Anselm originated the move; Plantinga’s modal version is the modern form.

  3. A CaseFile class that holds an array of Argument* (size up to 10) and exposes:

    • void add(Argument* a) — appends to the array.
    • void present_all() const — iterates and calls defend() polymorphically on each.
    • Destructor that deletes every argument it owns.
  4. A main that:

    • Constructs a CaseFile.
    • Adds one of each of the three concrete arguments via new.
    • Calls present_all().
    • Lets the destructor handle cleanup.
  5. Real attributions inside defend(). Each argument should mention a real historical source (Aquinas, Lewis, Anselm/Plantinga, etc.) where the form of the argument was actually made.

  6. Compiles cleanly with -Wall -Wextra enabled. No warnings.

  7. Zero memory leaks, verified with -fsanitize=address in OnlineGDB (or valgrind on Linux). ASan’s leak detection does not work on macOS, so don’t rely on a clean local Mac run — see Chapter 11 §11.9.

Normal-tier rubric (out of 100)

CriterionPoints
Compiles cleanly with -Wall -Wextra10
Argument base class with virtual defend and virtual destructor10
Three concrete subclasses with override on defend()15
Each subclass mentions a real historical attribution10
Subclass constructors chain to base via initializer list10
CaseFile stores Argument*[] and iterates polymorphically15
CaseFile destructor deletes every owned argument10
present_all() produces 3 distinct outputs from one loop10
Zero memory leaks verified5
OnlineGDB/GitHub link + reflection comment block5

Medium Tier (+up to 25% extra credit)

M1. strength_against

Add a virtual int strength_against(string objection) const method to Argument. Each subclass overrides it to score how well it responds to a given objection on a 1–10 scale.

Suggested objections:

  • "the universe is uncaused" — Cosmological is strong (8), Moral is weak (3), Ontological is medium (5).
  • "morality is just preference" — Moral is strong (9), others weaker.
  • "the concept of God is incoherent" — Ontological should respond directly (7), others sidestep (4).
  • "this is all just emotional reasoning" — all are roughly equal (5–6).

These numbers are pedagogical, not authoritative. The point is the polymorphic dispatch.

M2. CaseFile::pick_best

Add Argument* CaseFile::pick_best(string objection) const that iterates the case file, calls strength_against(objection) on each, and returns a pointer to the strongest.

In main, demonstrate by picking the best argument against three different objections.

M3. Add a Teleological argument

Add Teleological : public Argument — the design / fine-tuning argument. Real attributions: Paley’s watchmaker (historically), and the fine-tuning literature (Collins, the McGrews, modern revival). Override defend(). Override strength_against() (Teleological is strong on “the universe looks engineered” objections; weaker on “ancient natural-theology has been replaced by science”).

Add it to the case file. Now present_all() prints four arguments.


Hard Tier (+up to 25% additional extra credit)

Pick ONE of H1–H4 as your Hard-tier feature. Completing any one of them cleanly earns the full Hard credit (+25). Each additional one you finish earns +5 bonus on top, if you genuinely have the time.

This tier was narrowed for the same reason Project 11’s was: the all-four version ran 14–25 hours and was eating freshmen alive in the same week they were also carrying four other courses. One Hard feature, done well and documented, demonstrates the skill better than four done badly.

If you want the natural pairing: H1 (deeper hierarchy) sets up H3 (slicing) nicely, since H3’s demo wants a concrete-derived-from-concrete pair. Doing H1 then H3 is the cheapest way to earn the +5 bonus. H2 is the most conceptually valuable on its own.

H1. Deeper hierarchy

Add FineTuningArgument : public Teleological. The Teleological argument has multiple sub-forms (Paley’s biological version, the cosmological fine-tuning version of Collins/the McGrews, the consciousness argument of Chalmers, the value-of-information arguments of more recent literature). FineTuningArgument specifically defends the cosmological-constant-and-physical-laws version.

Override defend() again at the deeper level. (Yes, a derived class can override an already-overridden virtual.) Make sure the deeper override prints the fine-tuning-specific case.

Demonstrate constructor chaining through three levels: FineTuningArgument constructor → Teleological constructor → Argument constructor.

H2. Pure virtual respond_to_skeptic

Add virtual void respond_to_skeptic() const = 0; to Argument. This makes Argument abstract — you can no longer instantiate it directly, only its concrete subclasses. Every concrete subclass must implement respond_to_skeptic() or it too becomes abstract.

Each subclass’s respond_to_skeptic() should print the most common objection to that argument and a sensible (humble) response. Not a triumphant “and that’s why the skeptic is wrong” — a real response.

This pure-virtual approach is the bridge to abstract classes (Chapter 15 will revisit in Java).

H3. Demonstrate slicing

Note — If you completed H2, Argument is abstract — you can’t take it by value. So this demo slices between two concrete types in the hierarchy instead. The lesson is identical: when you pass a derived object by value to a base-typed parameter, the derived parts get sliced off. (H1 builds the right hierarchy for this: FineTuningArgument derives from Teleological, which is concrete.)

Add a function void inspect_by_value(Teleological a) (note: by value, not by reference, and the parameter is the concrete Teleological, not the abstract Argument). Inside, call a.defend().

In main, construct a FineTuningArgument (from H1). Call inspect_by_value(*fine_tuning_arg). Observe what happens — the FineTuningArgument-specific parts get sliced off and Teleological::defend() runs.

Then add void inspect_by_reference(const Teleological& a). Call it the same way. Observe FineTuningArgument::defend() runs correctly.

If you didn’t do H1, you can still do H3: pick any concrete-derived-from-concrete pair in your design (for example, add a small two-level subclass purely for this demo).

Document this in your README. Slicing is one of the easiest C++ bugs to introduce by accident.

H4. Coverage score

Add a int CaseFile::coverage_score() const method that returns a single number representing how strong the case file is. The score should depend on the dynamic type of each argument (using strength_against on a set of standard objections, summing the maxes).

This is polymorphism doing real work — the method doesn’t know what argument types are inside; it just dispatches and aggregates.


Submission

Submit one URL via the course portal:

  • OnlineGDB project link (recommended for Coding 1 and Coding 2). Create your project at onlinegdb.com, set compiler flags to -Wall -Wextra in the project settings, 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, push the source to a public repo and submit that URL. You’re responsible for making sure the code compiles when the grader checks it out.

What the linked project must contain

  1. The main source filecase_file.cpp — containing your full solution.
  2. A reflection comment block at the very top of that file:
/*
 * 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.
 */
  1. 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

  • “My subclass’s defend() doesn’t run.” Missing virtual on the base, or missing override on the derived. Or both. With override enforced, the compiler tells you.
  • “My program crashes when I delete an Argument.”* Missing virtual destructor in the base class.
  • “I want to put Argument objects in a vector<Argument>.” You can’t — that would slice them. Use vector<Argument*> (pointers) instead, OR use vector<unique_ptr<Argument>> for smart-pointer cleanup (which we haven’t covered but you can look up for Hard tier).
  • “My objections feel artificial.” They are. The point of this project isn’t to defeat objections; it’s to make the polymorphic dispatch concrete. Acknowledge artificial scoring in your README.
  • “What’s a ‘real’ attribution?” A historical figure who actually made this form of the argument, plus a text where you (or anyone) could verify it. Aquinas’s Summa Theologica, Question 2, Article 3 (“The Five Ways”) is the canonical Cosmological source — specifically the Third Way (from contingency); the Second Way (from efficient causation) is closely related but distinct. Lewis’s Mere Christianity Book 1 is the canonical Moral source. Anselm’s Proslogion (chapters 2-3) is the original Ontological; Plantinga’s The Nature of Necessity is the modern modal form. Look them up.
  • “How long should this take me?” Normal: 5–8 hours. Medium: 8–12 hours. Hard: 10–14 hours (Normal + Medium + one H feature). Hard tier was intentionally narrowed — attempting all four H features runs 20+ hours and is not worth it.

What Mastery Looks Like

A great Project 12 has a clean class hierarchy. The base class declares what’s shared. Each subclass declares what’s specific. There’s no cut-and-paste duplication between subclasses.

A great Project 12 uses polymorphism naturally. The present_all() loop is identical no matter what arguments are in the case file. Adding a fifth argument type doesn’t require changing present_all() at all.

A great Project 12 has real attributions. The Cosmological argument’s defend() cites Aquinas, not “some philosopher.” The Moral argument cites Lewis. The Ontological cites Anselm and Plantinga. The grader can pick any quote in your output and find it in a real book.

A great Project 12 is humble about itself. The case file doesn’t conclude “and therefore God exists.” It presents the arguments. The reader does the rest of the work — which is exactly the rep the apologetic tradition has always done.

A great Project 12 makes the polymorphism feel inevitable. You’d struggle to imagine writing this in a non-OO way. The shape of the problem is the shape of the solution.


When You’re Done

  1. Read your case_file.cpp aloud. The hierarchy is clean. The overrides are short. The main is a few lines.
  2. Run under ASan. Confirm clean.
  3. Verify at least 2 attributions by hand.
  4. Update README.
  5. Submit.
  6. Read Chapter 13. Java is up.

Coach’s Note — This is the project where students often look back and say “OK, now I get OOP.” The shift from “I have a bunch of related classes” to “I have a polymorphic hierarchy that the language dispatches over automatically” is the shift. Java is going to feel like a small change after this — the OO ideas survive, the syntax shifts. The real work was here.

See you on Monday.