Programming projects
How these projects work
Each project here is taken through the four stages the exam's Section B asks about:
- Design: the inputs, processes and outputs, and a structure diagram that splits the problem into parts.
- Write: the program, built and run a stage at a time.
- Test: a test plan with normal, boundary, invalid and erroneous data.
- Refine: improve the program, often to fix something the testing found.
Testing each stage as you build it is iterative testing; testing the finished program is final testing.
Each stage has a Try it question in this sub-topic's quiz, where you write the code and run it against tests.
These are practice projects. They're separate from the programming tasks your school sets, which must be your own work.
Key terms
Quiz: design
The problem: a quiz asks three questions, says whether each answer is right, gives a score, and saves it to a file so the best score can be shown later.
| Inputs | Processes | Outputs |
|---|---|---|
| The player's name | Compare each answer with the right one, ignoring capitals | "Correct" or "Wrong" after each question |
| An answer to each question | Add 1 to the score for each right answer | The score |
| Save the name and score; find the best score in the file | The best score so far |
Structure diagram:
- Quiz
- Ask the questions
- Show a question
- Check the answer
- Keep the score
- Save scores
- Add the score to the file
- Find the best score
- Ask the questions
Each box at the bottom is small enough to write as one sub program.
Key terms
Quiz: write
Stage 1: a function that asks one question and returns true if the reply is right. Comparing both in lower case means "paris" matches "Paris".
function ask(question, answer)
reply = input(question)
return reply.lower == answer.lower
endfunction
Stage 2: the questions and answers go in two arrays, at the same positions, and a loop asks each one.
array questions = ["What is 7 x 6?",
"What does CPU stand for?",
"How many bits are in a byte?"]
array answers = ["42", "Central processing unit", "8"]
score = 0
for i = 0 to 2
if ask(questions[i], answers[i]) then
print("Correct")
score = score + 1
else
print("Wrong")
endif
next i
print("You scored " + str(score) + " out of 3")
Try it: 'Quiz project, stage 1' and 'Quiz project, stage 2' in this sub-topic's quiz.
Common mistake
Comparing "paris" with "Paris" gives false: string comparisons care about capital letters. Change both to the same case first.
Quiz: saving scores
Stage 3: a procedure adds a name and score to the end of scores.txt, one per line, and a function reads them all back to find the best. Keeping the name and score on separate lines means each can be read with one readLine.
procedure save_score(name, score)
file = open("scores.txt")
file.writeLine(name)
file.writeLine(str(score))
file.close()
endprocedure
function best_score()
file = open("scores.txt")
best = 0
while NOT file.endOfFile()
name = file.readLine()
score = int(file.readLine())
if score > best then
best = score
endif
endwhile
file.close()
return best
endfunction
In ERL, writeLine always adds to the end of the file, and the file must already exist: newFile("scores.txt") makes it the first time. In Python, opening with "a" (append) adds to the end, and makes the file if it isn't there.
Try it: 'Quiz project, stage 3'.
Quiz: test and refine
The finished quiz will ask how many questions to play, from 1 to 3. A test plan for that input:
| Test data | Type | Expected result |
|---|---|---|
| 2 | Normal | Plays 2 questions |
| 1 and 3 | Boundary | Plays 1 or 3 questions |
| 0 and 4 | Invalid | "Enter a number from 1 to 3", then asks again |
| three, or nothing | Erroneous | "Enter a number from 1 to 3", then asks again |
The first version accepted anything, and crashed on "three", as int() can't turn it into a number. Refined, it keeps asking until the input is one character from "1" to "3", and only then casts it to an integer:
count = input("How many questions (1-3)?")
while count.length != 1 OR count < "1" OR count > "3"
print("Enter a number from 1 to 3")
count = input("How many questions (1-3)?")
endwhile
count = int(count)
print("Questions to play: " + str(count))
Try it: 'Quiz project, refine'.
Key terms
Password checker: design
The problem: when someone makes an account, check their new password is strong enough: 8 to 20 characters, with at least one digit and one capital letter. They get three attempts.
| Inputs | Processes | Outputs |
|---|---|---|
| A new password, up to three times | Check its length | What's wrong with a weak password |
| Look at each character for a digit and a capital letter | "Password accepted" or "Too many attempts" | |
| Count the attempts |
Structure diagram:
- Password checker
- Check a password
- Check the length
- Look for a digit
- Look for a capital letter
- Count the attempts
- Check a password
Checking input like this is validation: it makes sure data is sensible before the program uses it. It's part of defensive design.
Key terms
Password checker: write
Stage 1 checks the length alone, with >= and <= so that 8 and 20 are both allowed.
function length_ok(password)
if password.length >= 8 AND password.length <= 20 then
return true
else
return false
endif
endfunction
Stage 2 looks at each character in turn. Characters are compared by their codes, which are in order, so a character from "0" to "9" is a digit, and one from "A" to "Z" is a capital letter.
function check(password)
if password.length < 8 then
return "Too short"
elseif password.length > 20 then
return "Too long"
endif
digit = false
capital = false
for i = 0 to password.length - 1
letter = password.substring(i, 1)
if letter >= "0" AND letter <= "9" then
digit = true
elseif letter >= "A" AND letter <= "Z" then
capital = true
endif
next i
if NOT digit then
return "Add a digit"
elseif NOT capital then
return "Add a capital letter"
else
return "Strong"
endif
endfunction
Try it: 'Password project, stage 1' and 'Password project, stage 2'.
Password checker: test and refine
| Test data | Type | Expected result |
|---|---|---|
| Sunflower7 | Normal | Strong |
| Abcdefg1 (8 characters), and 20 characters | Boundary | Strong |
| Ab1 (3 characters), and 21 characters | Invalid | Too short; too long |
| sunflower7 | Invalid | Add a capital letter |
There's no erroneous data here: anything typed is text, which is the right type for a password. For a number, "ten" would be erroneous.
Refined, the program gives three attempts, saying what's wrong each time:
attempts = 0
accepted = false
while attempts < 3 AND NOT accepted
password = input("New password:")
attempts = attempts + 1
message = check(password)
if message == "Strong" then
accepted = true
else
print(message)
endif
endwhile
if accepted then
print("Password accepted")
else
print("Too many attempts")
endif
Try it: 'Password project, refine'.
Leaderboard: design
The problem: enter each player's name and score, then show the top three, highest first.
| Inputs | Processes | Outputs |
|---|---|---|
| The number of players | Store each name and score as a record | The top three, numbered |
| Each player's name and score | Sort the records by score, highest first |
The records go in a 2D array: one row per player, with the name in column 0 and the score in column 1. An array holds one data type, so the scores are stored as strings and cast with int() when compared.
Structure diagram:
- Leaderboard
- Enter the players
- Sort the board (bubble sort)
- Show the top three
Leaderboard: write
Stage 1 is a bubble sort on a list of scores, highest first. Stage 2 sorts the board, moving each whole row, so names stay with their scores. Using < means equal scores stay in the order they were entered.
function sort_board(board, n)
for i = 0 to n - 2
for j = 0 to n - 2 - i
if int(board[j, 1]) < int(board[j + 1, 1]) then
for k = 0 to 1
temp = board[j, k]
board[j, k] = board[j + 1, k]
board[j + 1, k] = temp
next k
endif
next j
next i
return board
endfunction
Tracing a board's scores, "5", "9", "7":
| i | j | Compare | Swap? | Scores after |
|---|---|---|---|---|
| 0 | 0 | 5 < 9 | Yes | 9, 5, 7 |
| 0 | 1 | 5 < 7 | Yes | 9, 7, 5 |
| 1 | 0 | 9 < 7 | No | 9, 7, 5 |
Stage 3 reads the players, sorts them and shows the top three:
n = int(input("How many players?"))
array board[n, 2]
for i = 0 to n - 1
board[i, 0] = input("Name:")
board[i, 1] = input("Score:")
next i
board = sort_board(board, n)
for i = 0 to 2
print(str(i + 1) + ". " + board[i, 0] + " " + board[i, 1])
next i
Try it: 'Leaderboard project', stages 1 to 3.
Leaderboard: test and refine
A test plan for the number of players, which should be from 1 to 20. The last column records what actually happened:
| Test data | Type | Expected | Actual |
|---|---|---|---|
| 4 | Normal | Top three shown | As expected |
| 1 and 20 | Boundary | All shown, or the top three | Crashed with 1 |
| 0 and 21 | Invalid | Rejected | Not rejected |
| four | Erroneous | Rejected | Crashed |
With fewer than three players, the loop asks for a row that isn't there. Refined, it shows at most as many places as there are players:
places = 3
if n < 3 then
places = n
endif
for i = 0 to places - 1
print(str(i + 1) + ". " + board[i, 0] + " " + board[i, 1])
next i
The other failures need validation, like the quiz's: refining usually takes more than one round of testing.
Try it: 'Leaderboard project, refine'.
Exam tip
In a test plan, give the actual test data, such as 21, not just 'too big', and say what should happen.