GCSE Revision

Programming projects

How these projects work

Each project here is taken through the four stages the exam's Section B asks about:

  1. Design: the inputs, processes and outputs, and a structure diagram that splits the problem into parts.
  2. Write: the program, built and run a stage at a time.
  3. Test: a test plan with normal, boundary, invalid and erroneous data.
  4. 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.

InputsProcessesOutputs
The player's nameCompare each answer with the right one, ignoring capitals"Correct" or "Wrong" after each question
An answer to each questionAdd 1 to the score for each right answerThe score
Save the name and score; find the best score in the fileThe 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

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 dataTypeExpected result
2NormalPlays 2 questions
1 and 3BoundaryPlays 1 or 3 questions
0 and 4Invalid"Enter a number from 1 to 3", then asks again
three, or nothingErroneous"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.

InputsProcessesOutputs
A new password, up to three timesCheck its lengthWhat'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

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 dataTypeExpected result
Sunflower7NormalStrong
Abcdefg1 (8 characters), and 20 charactersBoundaryStrong
Ab1 (3 characters), and 21 charactersInvalidToo short; too long
sunflower7InvalidAdd 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.

InputsProcessesOutputs
The number of playersStore each name and score as a recordThe top three, numbered
Each player's name and scoreSort 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":

ijCompareSwap?Scores after
005 < 9Yes9, 5, 7
015 < 7Yes9, 7, 5
109 < 7No9, 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 dataTypeExpectedActual
4NormalTop three shownAs expected
1 and 20BoundaryAll shown, or the top threeCrashed with 1
0 and 21InvalidRejectedNot rejected
fourErroneousRejectedCrashed

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.

Take the quiz