Project 2

The Coffee Shop Conversation

Apologetic question: "Why does God allow suffering — and is faith just intellectual?"

Project 2 — The Coffee Shop Conversation

“Rejoice with those who rejoice, weep with those who weep.” — Romans 12:15

“A loop is a conditional that refuses to stop asking.” — §2.10

Chapter: 2 — Asking Questions and Doing Them Again Due: End of Week 2 Submit: A link to your code — an OnlineGDB project URL or a public GitHub repo URL — with p2_coffee_shop.cpp as the main source file. See Appendix A for the full workflow. Allowed tools: Everything through Chapter 2 — all of Chapter 1 (types, const, arithmetic, static_cast, cin/cout, getline, boolalpha, fixed/setprecision), plus if/else if/else, &&/||/!, switch, while, do…while, for, sentinels, accumulators, counters, break/continue, nested loops. Not yet allowed: Functions you define yourself, arrays, structs, classes. Estimated time: Normal 5–7 hrs · Medium 7–9 hrs · Hard 9–12 hrs


The Setup

You are at a coffee shop. A friend you have known for years — someone you respect, someone who respects you — sets their cup down and does not pick it back up.

“If God is good, why is there so much suffering?”

You can hear that it is not a debate move. Something happened. You do not know what yet, but you can tell it is real.

This is the conversation. Your program models it — turn after turn, not question-and-done. That is the whole difference between this project and a menu. Your friend does not ask one thing and leave. They stay at the table. You get several turns, each turn branches on what you say, and the program remembers everything you did and decides how the evening landed from the whole run rather than from your last click.

That structure is exactly this week’s two halves welded together. The loop keeps the scene alive. The conditionals choose what happens inside each turn. The accumulator is the memory that makes the ending earned instead of arbitrary. Take any one of the three out and the program stops being a conversation.

A direct word before you start, and it is a grading criterion rather than a suggestion: this project does not produce an ending where you win the debate or convert your friend. That is not how real conversations about real suffering go, and a program that pretends otherwise is a worse witness than no program at all. The endings you write are the honest ones:

  • The conversation goes deeper. You both leave thoughtful, and nothing is settled.
  • You stumbled, and you said so out loud.
  • You said “I don’t know” — and meant it — and your friend respected it.
  • You answered a question your friend had not finished asking. The moment closed.
  • Something more pressing interrupted, and that was okay.

At least one route through your program must end in an honest “I don’t know.” That is not a consolation ending. On this particular question it is frequently the most truthful thing a person can say, and a program that cannot say it is modeling a conversation nobody has ever actually had.

You are not building a debate flowchart. You are building a model of how this question gets engaged in a real room by two people who care about each other. Aim at that, and the conditionals will earn their keep.


Learning Targets

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

  • Drive a program with a loop that keeps running until the user ends it — a sentinel, a cap, and a guarded read, all three.
  • Branch with if/else if/else and dispatch with a switch inside a loop body.
  • Nest a conditional where the problem is genuinely nested — a follow-up that exists only because of an earlier answer.
  • Maintain counters, an accumulator, and boolean flags across iterations, and read them after the loop ends.
  • Validate input inside a loop with continue, so a typo costs nothing.
  • Use a nested loop to render structured output.
  • Avoid the integer-division trap and the divide-by-zero trap in a computed summary.
  • Write a final if/else if cascade whose branches are decided by accumulated state, including one guarded by an && combination.

Normal Tier

Goal: One scene, played out over several turns inside a loop. Each turn offers four ways to respond, each response changes what the program remembers, and when the scene ends the program prints a ledger of what you actually did and picks one honest ending from it.

Start from code/coffee_shop_starter.cpp in this chapter’s code/ folder. It compiles clean and runs, but it does not meet Normal tier — the loop is there, the branches are stubbed, and the TODOs mark what you owe. code/conversation_loop.cpp from §2.17 is the same idea in miniature; read it, then build yours bigger.

Required features

  1. Opening scene. Two to four sentences establishing the coffee shop, the friend, and the question, followed by a one- or two-line legend of the moves available each turn. Multi-line cout is fine; the words are yours, not the starter’s.

  2. The conversation loop. The scene runs turn after turn until it ends. It must have all three exits:

    • A sentinel. 0 means “let it rest.” The user can end the conversation at any turn.
    • A cap. A const int MAX_TURNS the loop respects, so the scene ends on its own if nobody ends it. (The barista calls last orders. Coffee shops close.)
    • A guarded read. if (!(cin >> choice)) { ... break; } so the program ends cleanly instead of hanging when input runs out — which is what happens the moment the grader runs it with an empty Stdin box. See §2.12’s sidebar for why an unguarded cin in a loop hangs forever.
  3. Four or more distinct moves per turn, numbered, dispatched with a switch or an if/else if cascade. At minimum: ask something, answer with what you have, stay quiet, and say you don’t know. Each move prints one to three sentences of what happens and adjusts what the program remembers.

  4. Input validation that does not punish a typo. An out-of-range number prints a short message and continues — the turn counter must not advance. Typing 9 costs the user nothing but a keystroke.

  5. One genuinely nested follow-up. Somewhere in the scene, a choice must open a second question that would make no sense otherwise — for example, asking what brought this up gets you an answer about a specific person, and then you decide whether to say something about it or move to the theology. Nest because the conversation is nested there. Do not nest to show off (§2.6).

  6. Memory: at least two bool flags, one integer accumulator, and one counter per move type. All of them declared before the loop — declare an accumulator inside a loop and it resets every pass (§2.13). Suggested names, because good names are half the grade: friend_shared_pain, you_acknowledged_it, cut_them_off, warmth, asked_count.

  7. The ledger. After the loop, print one row per move type showing a bar of * characters whose length equals that move’s count, drawn with a nested loop — outer over move types, inner over the count. Then print the number of turns taken and the accumulator’s total.

  8. One computed statistic, correctly. Print the accumulator per turn as a doublestatic_cast<double>(warmth) / turns — and guard it so that zero turns prints a message instead of dividing by zero. Both traps are graded (§2.13, §2.18).

  9. The ending cascade. After the ledger, an if/else if/else chain picks exactly one of at least four distinct endings, and it must read the accumulated state — not the last choice the user made. At least one ending must be gated on an && combination, e.g. (warmth >= DEEP_THRESHOLD) && friend_shared_pain && you_acknowledged_it. At least one route must reach an honest “I don’t know” ending, and at least one must be a believable stumble.

  10. Zero “you converted your friend” endings. No “your friend was persuaded by your reasoning and confessed Christ.” Theologically off, dramatically false, and the grader will mark it down.

  11. Compiles cleanly with -Wall -Wextra enabled in OnlineGDB’s compiler settings (or g++ -std=c++17 -Wall -Wextra if you build locally). No warnings, no errors.

Example run

Below is the reference transcript for this project, showing what a Normal-tier program looks like when it is driven with the inputs 1 1 3 9 4 1 0. You are not required to match the wording — the dialogue is yours to write. Match the shape: a loop, a nested follow-up, a rejected typo, a ledger, one ending chosen from accumulated state. The arithmetic in it is checkable, and you should check it: the ledger counts sum to the turn count, and warmth per turn is warmth divided by turns taken printed as a double. If your own program cannot pass that same check against its own numbers, requirement 10 is not done yet.

Prompts and answers share a line because that run’s input was piped in rather than typed. Typed by hand in OnlineGDB, your answers appear after each prompt.

*** The Coffee Shop ***

Your friend sets the cup down and does not pick it back up.
"If God is good, why is there so much suffering?"
It is not a debate move. Something happened, and you do not know what yet.

Each turn: 1 = ask  2 = answer  3 = stay quiet  4 = say you do not know
           0 = let it rest

Turn 1 of 6 >   You ask what brought it up.
  Your friend looks at the table. "My cousin died in March.
  She was nineteen. Nobody will say anything true about it."
  1 = say you are sorry and let them keep going
  2 = move to the question about God
  >   You say you are sorry. You do not add anything to it.
  Your friend keeps talking, longer than you expected.

Turn 2 of 6 >   You stay quiet. The silence is not awkward; your friend fills it.

Turn 3 of 6 >   That was not one of the options. Try again.

Turn 3 of 6 >   "I don't know," you say. "I really don't."
  Your friend looks up. It is the first thing anyone has not dressed up.

Turn 4 of 6 >   You ask another question instead of answering one.
  Your friend tells you the part they had not planned to say.

Turn 5 of 6 > 
You let it rest. Neither of you fills the silence.

=== The ledger ===
asked        **  (2)
answered       (0)
stayed quiet *  (1)
said unknown *  (1)

turns taken: 4   warmth: 7
warmth per turn: 1.75

=== How it landed ===
You both went deeper. Quieter, less debate-shaped.
Nothing was settled. You make plans to keep talking.

Three things in that transcript are worth more than the dialogue.

Turn 3 of 6 appears twice. The 9 was rejected and continue sent the loop back around without touching the counter. That is requirement 4, visible in the output.

The ending does not match the last move. The last real move was ask, but the ending was decided by warmth, friend_shared_pain, and you_acknowledged_it together — everything that happened across the whole run. That is requirement 9, and it is the design lesson of the week.

The same program, fed 1 2 2 2 0, ends completely differently. Verbatim tail of that run:

=== The ledger ===
asked        *  (1)
answered     **  (2)
stayed quiet   (0)
said unknown   (0)

turns taken: 3   warmth: -3
warmth per turn: -1.00

=== How it landed ===
You answered a question your friend had not finished asking.
They let you finish. The moment is gone. You notice it later, in the car.

And run with no input at all — an empty Stdin box — it does not hang. The guarded read fires on the first turn, prints (the conversation is interrupted), and falls through to the zero-turns ending. Test that case before you submit; it is the first thing the grader will see.

Grading rubric — Normal (out of 100)

CriterionPoints
Compiles cleanly with -Wall -Wextra8
Opening scene in 2–4 sentences, plus a legend of the moves5
Conversation loop with all three exits: sentinel 0, MAX_TURNS cap, guarded failed read10
Four or more distinct moves per turn, dispatched with switch or if/else if8
Out-of-range input rejected with a message and continue — no turn spent on a typo8
One nested follow-up that exists only because of an earlier choice8
Two or more bool flags set inside the loop and read after it6
Accumulator plus one counter per move type, all declared outside the loop8
The ledger: one row per move type, bar drawn with a nested loop, turns and total reported8
Per-turn statistic printed as a double — no integer-division bug, guarded when turns is 06
Ending cascade over accumulated state: 4+ distinct endings, exactly one fires, at least one gated by &&12
An honest “I don’t know” ending and a believable stumble ending are both reachable; zero “you converted your friend” endings8
Reflection comment block at the top of p2_coffee_shop.cpp5

Medium Tier (+up to 25% extra credit)

Pick any one for partial credit, all three for full Medium.

M1. Input that survives a human

Right now, typing a word at your prompt kills the scene (guarded read, clean exit — correct, but blunt). Upgrade it: when the read fails, recover instead of quitting.

cin.clear();                                           // reset the failure flag
cin.ignore(numeric_limits<streamsize>::max(), '\n');   // discard the bad line

numeric_limits needs #include <limits>. Print something in character — “You say something neither of you can make out over the espresso machine.” — and continue so the turn is not spent. Then wrap your nested follow-up read from Normal requirement 5 in a do…while that re-prompts until the answer is in range. This is §2.12’s third defensive move plus §2.11’s home turf for do…while, in the one place the program actually needs both.

Careful: keep a real exit. Ctrl+D (or an empty Stdin box) must still end the program — recover from bad values, not from end of input, or you have written an infinite loop. Test with an empty Stdin box before you claim this one.

M2. Two more moves, six endings

Add at least two more moves — good candidates: quote a verse, and tell them about something hard that happened to you. Both are real moves people make, and both can land either way.

With six moves you can support six distinct endings, and their reachability must depend on flag combinations rather than single choices. Two of the six must require &&-joined conditions across the whole run. Include at least one ending where the verse was exactly what the moment called for, and one where it was not. Both happen. Both are honest.

M3. The streak

Track the longest run of consecutive turns in which you did not answer — that is, turns where you asked, stayed quiet, or said you did not know. This is the streak pattern: a running counter that increments on a qualifying turn, resets to 0 the moment it does not qualify, and a second variable holding the longest run seen so far.

Report it in the ledger (longest stretch of listening: 3 turns), and gate one ending on it — an ending reachable only with a stretch of three or more. You cannot walk back through the conversation to compute this afterward; you have no arrays and no way to store the history. It must be computed inside the loop, in one pass. That constraint is the rep.


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

The Hard tier asks for depth in the model of the conversation, not surface complexity in the code. A great Hard tier is one where a reader looks at your endings and thinks: “Yeah. That’s a real outcome. I’ve seen that one.”

H1. Endings that only combinations can reach

At least eight distinct endings, of which at least three require compound conditions across the whole run — not (warmth >= 6) alone, but (warmth >= 6) && friend_shared_pain && !cut_them_off. Beyond the honest set already listed, these all qualify:

  • You both ended up more confused, in the good way — the kind of confusion that means the question finally got taken seriously.
  • Your friend asked you a question you had never thought about, and the conversation turned around.
  • The conversation was interrupted — a phone call, a coworker walking in — and it was okay. Or it was not, and you know which.
  • You quoted a verse that was what the moment called for. Allowed. Also rare.
  • You quoted a verse that was not. Also allowed. Also honest.

Write the endings in English, on paper, before you write the conditions. Students who code first end up with eight endings that are secretly two.

H2. The honesty audit

After the ledger, compute what share of your turns were spent answering rather than asking, sitting quiet, or admitting you did not know. If that share exceeds 60%, print a single flag line:

Note: 4 of your 6 turns (67%) were spent answering.

Guard the division (turns can be zero), cast before dividing, and print the note only when the threshold is crossed. This is not your program passing judgment on you. It is your program making one fact visible that is otherwise easy to miss from inside the conversation — which is the entire reason anybody accumulates anything.

H3. The flex move

Find one C++ feature this book has not covered yet and use it deliberately. Strong candidates this week:

  • getline(cin, response) to let the user type a free-form sentence at one key moment — then acknowledge that they typed it without pretending to parse it. Watch the cin >> / getline newline trap from §1.10; you will need cin.ignore().
  • string(n, '*') to build a bar of n characters with no inner loop at all. It exists. Look it up. It is beautiful, and it makes you appreciate what the nested loop was doing.
  • setw from <iomanip> to align the ledger into real columns.
  • A second const threshold so no magic number appears twice in your ending cascade.

Name the feature and what it bought you in your reflection block. An undocumented flex move earns nothing — the grader cannot read your mind, and there is no class period in which to ask you about it.


Submission

Submit one URL via the course portal:

  • OnlineGDB project link (recommended). Create your project at onlinegdb.com, set the compiler flags to -Wall -Wextra in project settings, build your solution, and share the link. The full workflow — including where the flags and the Stdin box live — is in Appendix A.
  • GitHub repo link (optional). If you have set up local development on your own, push the source to a public repo and submit that URL. You are responsible for it compiling when the grader opens it.

What the linked project must contain

  1. One source file — p2_coffee_shop.cpp — containing your full solution. One file, one main, no exceptions — you do not have functions yet, so everything lives in main and that is correct for this week.

  2. A reflection comment block at the very top of that file:

/*
 * Tier targeted:    Normal / Medium / Hard
 * Features done:    list each feature you completed
 * Endings reachable: list them, one per line
 * 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. This program reads from stdin, so pre-fill OnlineGDB’s Stdin panel with a sequence of choices that walks the grader down an interesting path — one that reaches an ending you are proud of and trips your validation at least once. A grader who presses Run and gets an immediate (the conversation is interrupted) sees a working guarded read and nothing else you built.

No separate demo.txt. No screenshots. The grader opens your link, reads the comment block, runs the program, walks a second path by hand, and grades against the rubric.

Coach’s Note — You and the grader are looking at the exact same browser-hosted environment, which means there is no “works on my machine” defense available to either of you, by design. That cuts both ways and it is worth the trade: nobody here is going to lose a grade to a toolchain problem.

Hints

  • “The scene ends after one turn.” Almost always one of two things. Either your ending cascade is inside the loop — it belongs after the closing brace — or you wrote break where you meant continue in the validation branch. break leaves the loop; continue goes back around (§2.12).

  • “A typo eats a turn.” You wrote turns++ above the range check. The order inside the body must be: read, check for the sentinel, check the range and continue, then turns++, then act.

  • “The program hangs and prints nothing.” You typed a word at a numeric prompt. cin went into a failed state, and every later read returns instantly without consuming anything, so your sentinel can never match and your loop spins forever (§2.12 sidebar). The Normal-tier fix is the guarded read; the Medium-tier fix recovers with cin.clear().

  • “I always get the same ending.” Three suspects, in order. (a) Your cascade is ordered wrong — the first true branch wins, so a loose condition placed early swallows everything after it (§2.4). (b) You wrote if (warmth = 6) with one =, which assigns and is always true (§2.8) — compile with -Wall and read the -Wparentheses warning. (c) Your endings read choice instead of your accumulated state, which makes the whole loop decorative.

  • “Two endings print at once.” You wrote a stack of separate if statements. They are independent; every true one fires. Chain them with else if and finish with a bare else.

  • “My bars are empty.” Either the count really is zero, or you are drawing the chart inside the conversation loop where the counts are not final yet. The ledger runs once, after the loop.

  • A worked numeric check you can run right now. Set your move values to exactly these while testing: first ask +2, acknowledging what your friend shared +2, stay quiet +1, say you don’t know +1, a later ask +1. Play those five moves across four turns, with one out-of-range typo somewhere in the middle. Your ledger must then read turns taken: 4, warmth: 7, and warmth per turn: 1.75, and your turn header must print the same turn number twice.

    • If warmth is not 7, an adjustment is in the wrong branch or is running twice.
    • If turns is 5, the typo spent a turn — see the second hint.
    • If warmth per turn prints 1.00, you have the integer-division bug. 7 / 4 in int arithmetic is 1, and storing it in a double afterward is too late; it prints 1.00 and looks deliberate. static_cast<double>(warmth) / turns prints 1.75. That one is worth verifying before anything else, because it is the bug that looks most like a working program.
  • “How do I make the endings feel real?” Read them out loud. If a sentence sounds like something a person would actually say at a table at 9 p.m., ship it. If it sounds like a tract or a fortune cookie, rewrite it. The test is believability, not cleverness — and you already know the difference, because you have been on the receiving end of both.

  • “How long should this take me?” Normal: 5–7 hours, and most of it is writing dialogue and endings rather than code. Medium: 7–9. Hard: 9–12, with the time going into designing eight endings that are genuinely distinct. If Normal is running past eight hours, the problem is nearly always design rather than syntax — stop coding, write your endings and their conditions in plain English on paper, and then come back.


What Mastery Looks Like

A great Project 2 could not have been written without the loop. Remove the loop and it collapses, because the ending depends on things that accumulated across turns. A program where the last choice determines the ending is a menu with extra steps, and it will read that way to the grader within ten seconds.

A great Project 2 has clean code under the dialogue. The cascades are ordered most-restrictive first. The flags are named positively and read like English at the point of use. The switch is break-disciplined with a default. A reader could scan the file and follow the conversation without running it.

A great Project 2 takes the weight of the question seriously. It does not trivialize suffering. It does not treat the friend as a debate dummy. It does not claim more for the apologetic toolkit than the toolkit can deliver. Somewhere in it, the program is willing to say “I don’t know” and let that be the ending.

A great Project 2 survives a hostile grader. Empty input: ends cleanly. A 9: rejected, no turn spent. Ending the scene on turn one: a coherent ending, not a division by zero. All zeros in the ledger: bars still print, statistic still guarded. You are not being graded on theology. You are being graded on whether your conditionals and your loop do honest work in service of a hard conversation.


When You’re Done

  1. Write the endings out on paper first if you have not already — and if you built them last, go back now and read all four aloud in a row. If two of them are the same ending wearing different sentences, merge them and write a real one.
  2. Run it four times, down four different routes, and reach at least three different endings by hand. If you cannot reach a fourth ending, it is unreachable, and an unreachable ending is not an ending. Find the condition that blocks it.
  3. Run it once with an empty Stdin box. It must end cleanly and print an ending. This is the single most common way this project loses points.
  4. Recompile with -Wall -Wextra one last time and read every warning out loud before you silence it. -Wunused-variable on a counter usually means a missing ++, not a useless variable.
  5. Fill in the reflection block honestly, including the AI line. Then pre-fill the Stdin panel with a good path and submit the link.
  6. Take the §2.20 Checkpoint again if any part of this fought you harder than it should have. Then read Chapter 3 — functions and collections, where you stop repeating yourself.

Coach’s Note — This is the project where the apologetics frame of the course either earns its keep or does not. If you treat it as a serious exercise in modeling a hard conversation — not “what is the smart answer?” but “what is the honest answer for the person in front of me?” — you will learn something that does not fit on a rubric. That is the goal. The grade is downstream.

You are doing this one alone, at whatever hour you found. Nobody is going to walk you through it tomorrow morning, and that is the deal you signed for — but it also means that when the ledger prints and the ending is the right ending, the thing on your screen is unambiguously yours. Save the file. Close the laptop. Chapter 3 will still be there when you come back.