Case Study 2: Tomás Builds a Grade Calculator
The Scenario
Tomás Vieira is halfway through his sophomore year. He has four courses this semester, and he wants to know where he stands. His university uses a 4.0 GPA scale, and each course has a different number of credit hours. Tomás decides to write a Pascal program that:
- Asks him for each course name, credit hours, and current grade (as a percentage)
- Converts the percentage to a letter grade and grade points
- Calculates his semester GPA
- Displays everything in a formatted academic report
This is a realistic beginner scenario: the math is simple (weighted averages), the I/O is substantial (multiple prompts, formatted table output), and the result is genuinely useful.
Tomás's Design Sketch
Before he writes any code, Tomás sketches the output he wants to see:
==========================================
SEMESTER GPA CALCULATOR
Spring 2026
==========================================
Enter your courses:
Course 1:
Name: Intro to Computer Science
Credits: 3
Grade (%): 91
Course 2:
Name: Calculus II
Credits: 4
Grade (%): 78
Course 3:
Name: English Composition
Credits: 3
Grade (%): 85
Course 4:
Name: Chemistry I
Credits: 4
Grade (%): 72
==========================================
ACADEMIC REPORT - Spring 2026
==========================================
Course Credits Grade% Letter Points
---------------------------------------------------------
Intro to Computer Science 3 91.0 A- 3.70
Calculus II 4 78.0 C+ 2.30
English Composition 3 85.0 B 3.00
Chemistry I 4 72.0 C- 1.70
---------------------------------------------------------
TOTAL 14
Semester GPA: 2.62
==========================================
The Problem Breakdown
Grade Conversion Table
Tomás needs to convert percentage grades to letter grades and grade points. He writes out the conversion table:
| Percentage | Letter | Grade Points |
|---|---|---|
| 93-100 | A | 4.00 |
| 90-92 | A- | 3.70 |
| 87-89 | B+ | 3.30 |
| 83-86 | B | 3.00 |
| 80-82 | B- | 2.70 |
| 77-79 | C+ | 2.30 |
| 73-76 | C | 2.00 |
| 70-72 | C- | 1.70 |
| 67-69 | D+ | 1.30 |
| 63-66 | D | 1.00 |
| 60-62 | D- | 0.70 |
| 0-59 | F | 0.00 |
GPA Calculation
The GPA is a credit-weighted average:
GPA = (credits1 * points1 + credits2 * points2 + ...) / totalCredits
The Challenge: No if-then-else Yet
Tomás is working through this textbook in order. He has not yet reached Chapter 5 (decisions) or Chapter 6 (loops). So he cannot use if-then-else to convert percentages to letter grades programmatically.
His workaround: he will enter the letter grade and grade points manually for now, and focus on the I/O and formatting aspects. In Chapter 5, he will revisit this program and add automatic grade conversion.
This is a realistic development approach — build what you can now, and enhance it later.
The Implementation
Version 1: Manual Entry with Formatted Output
program GradeCalculator;
{ Tomás's Semester GPA Calculator }
{ Chapter 4 Case Study 2 }
var
{ Course 1 }
course1Name: String;
course1Credits: Integer;
course1Pct: Real;
course1Letter: String;
course1Points: Real;
{ Course 2 }
course2Name: String;
course2Credits: Integer;
course2Pct: Real;
course2Letter: String;
course2Points: Real;
{ Course 3 }
course3Name: String;
course3Credits: Integer;
course3Pct: Real;
course3Letter: String;
course3Points: Real;
{ Course 4 }
course4Name: String;
course4Credits: Integer;
course4Pct: Real;
course4Letter: String;
course4Points: Real;
{ Summary }
totalCredits: Integer;
totalQualityPoints: Real;
gpa: Real;
{ Input validation }
inputStr: String;
valCode: Integer;
begin
{ === Welcome Header === }
WriteLn('==========================================');
WriteLn(' SEMESTER GPA CALCULATOR');
WriteLn(' Spring 2026');
WriteLn('==========================================');
WriteLn;
WriteLn('For each course, enter the name, credits,');
WriteLn('grade percentage, letter grade, and grade');
WriteLn('points. (Use the grade table in your');
WriteLn('syllabus for conversion.)');
WriteLn;
{ === Course 1 === }
WriteLn('Course 1:');
Write(' Name: ');
ReadLn(course1Name);
repeat
Write(' Credits: ');
ReadLn(inputStr);
Val(inputStr, course1Credits, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid integer.');
until valCode = 0;
repeat
Write(' Grade (%): ');
ReadLn(inputStr);
Val(inputStr, course1Pct, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid number.');
until valCode = 0;
Write(' Letter grade: ');
ReadLn(course1Letter);
repeat
Write(' Grade points: ');
ReadLn(inputStr);
Val(inputStr, course1Points, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid number.');
until valCode = 0;
WriteLn;
{ === Course 2 === }
WriteLn('Course 2:');
Write(' Name: ');
ReadLn(course2Name);
repeat
Write(' Credits: ');
ReadLn(inputStr);
Val(inputStr, course2Credits, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid integer.');
until valCode = 0;
repeat
Write(' Grade (%): ');
ReadLn(inputStr);
Val(inputStr, course2Pct, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid number.');
until valCode = 0;
Write(' Letter grade: ');
ReadLn(course2Letter);
repeat
Write(' Grade points: ');
ReadLn(inputStr);
Val(inputStr, course2Points, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid number.');
until valCode = 0;
WriteLn;
{ === Course 3 === }
WriteLn('Course 3:');
Write(' Name: ');
ReadLn(course3Name);
repeat
Write(' Credits: ');
ReadLn(inputStr);
Val(inputStr, course3Credits, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid integer.');
until valCode = 0;
repeat
Write(' Grade (%): ');
ReadLn(inputStr);
Val(inputStr, course3Pct, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid number.');
until valCode = 0;
Write(' Letter grade: ');
ReadLn(course3Letter);
repeat
Write(' Grade points: ');
ReadLn(inputStr);
Val(inputStr, course3Points, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid number.');
until valCode = 0;
WriteLn;
{ === Course 4 === }
WriteLn('Course 4:');
Write(' Name: ');
ReadLn(course4Name);
repeat
Write(' Credits: ');
ReadLn(inputStr);
Val(inputStr, course4Credits, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid integer.');
until valCode = 0;
repeat
Write(' Grade (%): ');
ReadLn(inputStr);
Val(inputStr, course4Pct, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid number.');
until valCode = 0;
Write(' Letter grade: ');
ReadLn(course4Letter);
repeat
Write(' Grade points: ');
ReadLn(inputStr);
Val(inputStr, course4Points, valCode);
if valCode <> 0 then WriteLn(' Please enter a valid number.');
until valCode = 0;
WriteLn;
{ === Calculations === }
totalCredits := course1Credits + course2Credits
+ course3Credits + course4Credits;
totalQualityPoints := course1Credits * course1Points
+ course2Credits * course2Points
+ course3Credits * course3Points
+ course4Credits * course4Points;
gpa := totalQualityPoints / totalCredits;
{ === Formatted Report === }
WriteLn('==========================================');
WriteLn(' ACADEMIC REPORT - Spring 2026');
WriteLn('==========================================');
WriteLn;
{ Table header }
Write('Course');
Write('':19); { pad "Course" (6 chars) to 25 total }
WriteLn('Credits':8, 'Grade%':8, 'Letter':8, 'Points':8);
WriteLn('---------------------------------------------------------');
{ Course 1 }
Write(course1Name);
if Length(course1Name) < 25 then
Write('':25 - Length(course1Name));
WriteLn(course1Credits:8, course1Pct:8:1, course1Letter:8, course1Points:8:2);
{ Course 2 }
Write(course2Name);
if Length(course2Name) < 25 then
Write('':25 - Length(course2Name));
WriteLn(course2Credits:8, course2Pct:8:1, course2Letter:8, course2Points:8:2);
{ Course 3 }
Write(course3Name);
if Length(course3Name) < 25 then
Write('':25 - Length(course3Name));
WriteLn(course3Credits:8, course3Pct:8:1, course3Letter:8, course3Points:8:2);
{ Course 4 }
Write(course4Name);
if Length(course4Name) < 25 then
Write('':25 - Length(course4Name));
WriteLn(course4Credits:8, course4Pct:8:1, course4Letter:8, course4Points:8:2);
WriteLn('---------------------------------------------------------');
Write('TOTAL');
Write('':20);
WriteLn(totalCredits:8);
WriteLn;
WriteLn('Semester GPA: ', gpa:0:2);
WriteLn;
WriteLn('==========================================');
end.
What Tomás Learned
1. Input Prompts Must Be Aligned
Tomás initially wrote his prompts like this:
Write('Name: ');
Write('Credits: ');
Write('Grade (%): ');
The result was messy — each prompt had a different length, so the user's input started at different horizontal positions. He fixed this by padding all prompts to the same width:
Write(' Name: '); { 18 characters }
Write(' Credits: '); { 18 characters }
Write(' Grade (%): '); { 18 characters }
Small detail. Big difference in usability.
2. Validation Matters Even for Simple Programs
Tomás's first version did not use Val. When he accidentally typed three instead of 3 for a credit value, the program crashed. After adding validation, the program politely asked again. This took more lines of code but made the program usable by someone other than Tomás.
3. The Repetition Is Painful — But Temporary
Tomás notices that the code for entering each course is nearly identical, copied four times. This violates the programming principle of "Don't Repeat Yourself" (DRY). He makes a note:
"When I learn procedures (Chapter 7) and loops (Chapter 6), I will refactor this into a single block of code that handles any number of courses."
The ability to recognize that code should be refactored, even before you have the tools to refactor it, is itself a valuable skill. Tomás is developing a programmer's eye.
4. The Padding Trick Is Essential for Tables
The expression Write('':25 - Length(courseName)) pads a variable-length string to a fixed column width. Without this, the table columns would be ragged — short course names would leave the numbers shifted left, and long course names would push them right.
5. Separating Input from Output Creates Clean Structure
The program has three distinct phases: input, calculation, output. This separation makes the code easier to understand, debug, and modify. If the GPA formula is wrong, Tomás knows to look in the calculation section. If a column is misaligned, he looks in the output section. If a prompt is confusing, he looks in the input section.
Discussion Questions
-
Why does Tomás enter the letter grade manually? What programming concept from Chapter 5 would allow the program to determine the letter grade automatically?
-
What happens if
totalCreditsis zero? (This would occur if the user enters 0 credits for every course.) How should the program handle this? What kind of error would occur? -
Tomás's program handles exactly four courses. What if he has five courses? What if he has three? How would you modify the program to handle a variable number of courses without loops? Is that even practical?
-
The GPA is displayed with 2 decimal places (
gpa:0:2). Some universities report GPA to 3 decimal places. How would you change this? What other values in the program might need their formatting adjusted? -
Compare the effort of building this formatted report in Pascal versus doing it by hand in a spreadsheet. For four courses, the spreadsheet might be faster. At what point does the program become more efficient than manual calculation?
Extension Ideas
For students who have read ahead to Chapters 5 and 6:
-
Automatic letter grade conversion. Replace manual letter grade entry with an
if-then-elsechain orcasestatement that converts the percentage to a letter and grade points automatically. -
Loop-based entry. Replace the four copied code blocks with a loop that asks "How many courses?" and then iterates. (You will need arrays from Chapter 9 to store the data, or you can accumulate the totals as you go.)
-
Dean's List check. After calculating the GPA, display whether Tomás made the Dean's List (GPA >= 3.5) or is on Academic Probation (GPA < 2.0).
-
Input range validation. Ensure that credit hours are between 1 and 6, grade percentages are between 0 and 100, and grade points are between 0 and 4.0.