Computer Science 9618/42 — October/November 2024
Cambridge A-Level · Practical · worked solutions for every part, with the mark scheme
Topics Programming Paradigms (Procedural and Object-oriented) · Algorithms and Abstract Data Types · File Processing and Exception Handling
Open the evidence document, evidence.doc
Make sure that your name, centre number and candidate number appear on every page of this document. This document must contain your answers to each question.
Save this evidence document in your work area as:
evidence_ followed by your centre number_candidate number, for example: evidence_zz999_9999
A class declaration can be used to declare a record. If the programming language used does not support arrays, a list can be used instead.
A computer game is designed for users to select characters. Each character can take part in a group of events. Each group has five events. There are four types of event: jump, swim, run, drive.
The program is written using object-oriented programming.
The class EventItem stores data about the events.
| EventItem | |
|---|---|
EventName : STRING | stores the name of the event |
Type : STRING | stores the type of event, either: jump, swim, run or drive |
Difficulty : INTEGER | stores the difficulty of the event from 1 (easiest) to 5 (hardest) |
Constructor() | initialises EventName, Type and Difficulty to its parameter values |
GetName() | returns the name of the event |
GetDifficulty() | returns the difficulty of the event |
GetEventType() | returns the type of event |
Write program code to declare the class EventItem and its constructor.
Do not declare the other methods.
Use your programming language’s appropriate constructor.
All attributes must be private.
If you are writing in Python, include attribute declarations, using comments.
Save your program as Question1_N24.
Copy and paste the program code into part 1(a)(i) in the evidence document.
Answer
class EventItem:
# __EventName: str
# __Type: str
# __Difficulty: int
def __init__(self, EventName, Type, Difficulty):
self.__EventName = EventName
self.__Type = Type
self.__Difficulty = Difficulty
See program code
Background Concept
In object-oriented programming, a class is a template used to create objects. The class defines the attributes each object stores and the methods that operate on that data. A constructor is the method that runs when a new object is created, and its job is usually to receive parameter values and store them in the object's attributes.
This question also requires private attributes. In Python, privacy is typically shown using a double underscore prefix, such as __EventName. This is how we show encapsulation: the data is stored inside the object and should normally be accessed through methods such as getters.
Understanding the Question
You are asked to declare the EventItem class and write only its constructor. The class must store three pieces of data for each event: the event name, the event type and the difficulty. The question specifically says not to declare the other methods here, so this part should contain just the class and constructor.
Because this is Python, the constructor must be __init__. The question also says to include attribute declarations using comments, because Python does not require formal type declarations in the same way some other languages do.
Approach
The simplest approach is:
- Write the class header
class EventItem:. - Add comment lines showing the three private attributes.
- Write the constructor with parameters for the three values.
- Store each parameter into a private attribute using
self.
That exactly matches what the class description requires.
Step-by-Step Reasoning
class EventItem: starts the class definition.
The three comment lines are included because the question asks Python candidates to show attribute declarations using comments. They document that the object will contain:
__EventNameas a string__Typeas a string__Difficultyas an integer
The constructor is written as:
def __init__(self, EventName, Type, Difficulty):
In Python, self refers to the object being created. The other three values are passed in when the object is instantiated.
Each assignment stores one parameter in the corresponding private attribute:
self.__EventName = EventNameself.__Type = Typeself.__Difficulty = Difficulty
That is all that is needed for this part. No getters are included yet because they are asked for in the next part.
Key Takeaways
- A constructor initialises an object's attributes.
- Private attributes in Python are typically written with a double underscore.
- In Python exam answers, comment lines can be used to show attribute declarations.
- Only include the methods asked for in that part.
Common Mistakes
- Making the attributes public, for example
self.EventName, when the question says all attributes must be private. - Using the wrong constructor name instead of
__init__. - Forgetting
selfin the constructor parameter list. - Adding the getter methods here even though this part says not to declare them.
Things to Be Careful About
- Use the exact class name
EventItem. - Keep
Difficultyas an integer value, not a string. - Match each parameter to the correct attribute.
- In Python, indentation is part of the syntax, so the constructor body must be indented inside the class.
The get methods GetName(), GetDifficulty() and GetEventType() each return the relevant attribute.
Write program code for the three get methods.
Save your program.
Copy and paste the program code into part 1(a)(ii) in the evidence document.
Answer
def GetName(self):
return self.__EventName
def GetDifficulty(self):
return self.__Difficulty
def GetEventType(self):
return self.__Type
See program code
Background Concept
Getter methods are used in object-oriented programming to return private data from an object. They support encapsulation: the attributes remain hidden inside the class, but other parts of the program can still read them safely through methods.
A getter usually contains only one job: return the relevant attribute.
Understanding the Question
This part tells you that GetName(), GetDifficulty() and GetEventType() each return the relevant attribute from an EventItem object. So the task is not to redesign anything, but simply to write the three methods so that each one returns the correct private value.
Approach
For each method:
- Use the exact method name given in the question.
- Include
selfbecause the method belongs to the object. - Return the corresponding private attribute.
Each method is very short, but the important point is that the correct attribute must be matched to the correct getter.
Step-by-Step Reasoning
GetName(self) should return the stored event name, so it returns self.__EventName.
GetDifficulty(self) should return the stored difficulty, so it returns self.__Difficulty.
GetEventType(self) should return the stored event type, so it returns self.__Type.
These methods are written as part of the EventItem class, so they must be indented inside that class when added to the full program.
Key Takeaways
- A getter returns one private attribute.
- Getter names and returned attributes must match exactly.
- Encapsulation means outside code reads data through methods rather than accessing private attributes directly.
Common Mistakes
- Returning the wrong attribute, for example
GetName()returningself.__Type. - Forgetting
selfin the method definition. - Writing output statements instead of returning a value.
- Using public attribute names instead of the private ones defined earlier.
Things to Be Careful About
- These methods belong inside the class, so keep the indentation correct.
- Use
return, notprint. - The method names must match the question exactly:
GetName,GetDifficulty,GetEventType.
The array Group stores five objects of type EventItem.
Write program code to declare Group local to the main program.
Save your program.
Copy and paste the program code into part 1(b)(i) in the evidence document.
Answer
Group = [None] * 5
See program code
Background Concept
A program often stores multiple objects of the same type in an array or list. In Python, a list is commonly used. When the required size is known in advance, the list can be initialised with placeholder values and filled later.
For object storage, the list holds references to objects rather than the objects' internal data directly.
Understanding the Question
The question says that Group stores five objects of type EventItem and asks you to declare it local to the main program. In Python, the natural choice is a list with five positions, ready to store five EventItem objects.
Approach
Create a list of length 5 using placeholder values. None is the standard placeholder in Python when an object reference has not yet been assigned.
Step-by-Step Reasoning
Group = [None] * 5 creates a list with five elements:
- index 0
- index 1
- index 2
- index 3
- index 4
Each element initially contains None. Later, each position will be replaced with an EventItem object. This makes Group local to the main program if it is written in the main section of the code rather than inside a class definition.
Key Takeaways
- A Python list can be used where the paper refers to an array.
Noneis a suitable placeholder before real objects are stored.- A fixed-size list is useful when the number of items is known.
Common Mistakes
- Declaring an empty list and forgetting to size it for five items.
- Using strings or numbers as placeholders instead of object references or
None. - Declaring
Groupinside the class instead of in the main program.
Things to Be Careful About
- Python indexing starts at 0, so the five valid positions will be
0to4. - This line only declares the storage; it does not create any
EventItemobjects yet.
One group has the following events:
| Event name | Event type | Event difficulty |
|---|---|---|
| Bridge | jump | 3 |
| Water wade | swim | 4 |
| 100 mile run | run | 5 |
| Gridlock | drive | 2 |
| Wall on wall | jump | 4 |
Write program code to create an instance of EventItem for each of the five events, and store them in Group.
Save your program.
Copy and paste the program code into part 1(b)(ii) in the evidence document.
Answer
Group[0] = EventItem("Bridge", "jump", 3)
Group[1] = EventItem("Water wade", "swim", 4)
Group[2] = EventItem("100 mile run", "run", 5)
Group[3] = EventItem("Gridlock", "drive", 2)
Group[4] = EventItem("Wall on wall", "jump", 4)
See program code
Background Concept
Once a class has been declared, objects are created by calling its constructor. Each object stores its own copy of the attribute values passed in. When several objects are needed, they can be stored in a list so the program can process them later.
Understanding the Question
You are given a table of five events. For each row, you must create an EventItem object and store it in Group. That means each constructor call must use the values in the correct order:
- event name
- event type
- difficulty
Approach
Go through the table row by row. For each row:
- call
EventItem(...) - pass the name, type and difficulty
- store the new object in the next list position
Because Python uses 0-based indexing, the five positions are Group[0] to Group[4].
Step-by-Step Reasoning
The first event is Bridge, of type jump, difficulty 3, so:
Group[0] = EventItem("Bridge", "jump", 3)
The second is Water wade, type swim, difficulty 4, so:
Group[1] = EventItem("Water wade", "swim", 4)
The third is 100 mile run, type run, difficulty 5, so:
Group[2] = EventItem("100 mile run", "run", 5)
The fourth is Gridlock, type drive, difficulty 2, so:
Group[3] = EventItem("Gridlock", "drive", 2)
The fifth is Wall on wall, type jump, difficulty 4, so:
Group[4] = EventItem("Wall on wall", "jump", 4)
After these assignments, Group contains five ready-to-use EventItem objects.
Key Takeaways
- Constructor parameters must be supplied in the correct order.
- Lists can store object references.
- In Python, list positions for five items are usually
0to4.
Common Mistakes
- Putting the constructor arguments in the wrong order.
- Forgetting the quotation marks around string values.
- Starting at index 1 instead of index 0 in Python.
- Storing the data directly instead of creating
EventItemobjects.
Things to Be Careful About
Difficultyis an integer, so do not put quotation marks around it.- Event type values must match the required spellings such as
"jump"and"drive". - Make sure all five list positions are filled.
The class Character stores data about the characters in the game.
Each character has a skill level for each type of event. The skill level is an integer between 1 and 5 inclusive. Skill level 1 is the lowest skill level, and skill level 5 is the highest skill level.
| Character | |
|---|---|
CharacterName : STRING | stores the name of the character |
Jump : INTEGER | stores the character’s skill level at events of type jump |
Swim : INTEGER | stores the character’s skill level at events of type swim |
Run : INTEGER | stores the character’s skill level at events of type run |
Drive : INTEGER | stores the character’s skill level at events of type drive |
Constructor() | initialises CharacterName, Jump, Swim, Run and Drive to its parameter values |
GetName() | returns the name of the character |
CalculateScore() | takes the type of event and difficulty as parameters. Calculates and returns the chance of the character completing the event |
Write program code to declare the class Character, its constructor and get method.
Use your programming language’s appropriate constructor.
All attributes must be private.
If you are writing in Python, include attribute declarations, using comments.
Save your program.
Copy and paste the program code into part 1(c) in the evidence document.
Answer
class Character:
# __CharacterName: str
# __Jump: int
# __Swim: int
# __Run: int
# __Drive: int
def __init__(self, CharacterName, Jump, Swim, Run, Drive):
self.__CharacterName = CharacterName
self.__Jump = Jump
self.__Swim = Swim
self.__Run = Run
self.__Drive = Drive
def GetName(self):
return self.__CharacterName
See program code
Background Concept
A class can store several related attributes that describe one entity. Here, one Character object represents one game character, and it stores a name plus four skill levels. The constructor initialises those attributes when the object is created, and a getter allows controlled access to the private name.
Encapsulation is important again here: the attributes are private, and the rest of the program interacts with the object through methods.
Understanding the Question
You must write the Character class declaration, its constructor and its GetName() method. The question lists five attributes that must be stored:
CharacterNameJumpSwimRunDrive
You are not asked to write CalculateScore() in this part, so it should not be included here.
Approach
The pattern is the same as in EventItem but with more attributes:
- Declare the class.
- Add comment lines for the private attributes.
- Write the constructor with five parameters.
- Store each parameter in a private attribute.
- Write
GetName()to return the character name.
Step-by-Step Reasoning
class Character: starts the class.
The comment lines show the intended private attributes and their data types. This is the Python equivalent of the attribute declarations requested by the question.
The constructor:
def __init__(self, CharacterName, Jump, Swim, Run, Drive):
receives the name and the four skill values.
Each of the constructor assignments stores one piece of information in the object:
self.__CharacterName = CharacterNameself.__Jump = Jumpself.__Swim = Swimself.__Run = Runself.__Drive = Drive
The getter method:
def GetName(self):
returns self.__CharacterName.
That gives the program a way to retrieve the character's name without directly accessing a private attribute.
Key Takeaways
- A class can group several related values into one object.
- Constructors should initialise every required attribute.
- Private attributes are accessed from outside the class through methods such as getters.
Common Mistakes
- Forgetting one of the four skill attributes.
- Making the attributes public instead of private.
- Writing
CalculateScore()in this part when it is asked for separately. - Returning the wrong attribute in
GetName().
Things to Be Careful About
- Use the exact class and method names from the question.
- Keep the parameter order consistent with the constructor calls that will be written later.
- The skill values are integers from 1 to 5, so they should be stored as integers.
The method CalculateScore() in the Character class calculates and returns the percentage chance of a character completing an event.
When a character’s skill level is greater than or equal to the difficulty of that event, the percentage chance of completing the event is 100%.
When a character’s skill level is less than the difficulty of that event, the character’s skill level is subtracted from the difficulty of that event. This difference is used to identify the percentage chance of success using this table:
| Difference | Percentage chance of success |
|---|---|
| 1 | 80 |
| 2 | 60 |
| 3 | 40 |
| 4 | 20 |
For example:
- A character has a skill level of 3 for events of type run.
- An event of type run has a difficulty level of 5
- The character’s skill level is less than the difficulty, therefore the difference is calculated.
- The difference is the character’s skill level subtracted from the event difficulty,
- The difference is 2, therefore the percentage chance of succeeding is 60%
Write program code for the method CalculateScore() to:
- take the type of event and difficulty as parameters
- calculate the percentage chance of the character completing the event
- return the percentage chance of completing the event as an integer number, for example 60
Save your program.
Copy and paste the program code into part 1(d) in the evidence document.
Answer
def CalculateScore(self, EventType, Difficulty):
if EventType == "jump":
Skill = self.__Jump
elif EventType == "swim":
Skill = self.__Swim
elif EventType == "run":
Skill = self.__Run
else:
Skill = self.__Drive
if Skill >= Difficulty:
return 100
else:
Difference = Difficulty - Skill
return 100 - (Difference * 20)
See program code
Background Concept
This is a class method that must use parameters, selection and a return value. A method is similar to a function, but it belongs to an object. Here, the method uses the object's stored skill levels and the parameters passed in for one event.
The logic has two stages:
- Find the correct skill value for the event type.
- Compare that skill against the event difficulty and return the correct percentage.
Understanding the Question
CalculateScore() takes two parameters:
- the event type
- the event difficulty
It must then work out the character's chance of completing that event. If the skill is at least as high as the difficulty, the result is 100. Otherwise, the difference Difficulty - Skill determines the percentage:
- difference 1 gives 80
- difference 2 gives 60
- difference 3 gives 40
- difference 4 gives 20
So the method must return an integer percentage.
Approach
First identify which skill attribute should be used:
jumpuses__Jumpswimuses__Swimrunuses__Rundriveuses__Drive
Then compare that skill value to the Difficulty parameter. If the skill is high enough, return 100. Otherwise calculate the difference and convert it into the required percentage.
Step-by-Step Reasoning
The first if / elif / else section chooses the correct skill field based on the EventType string.
For example, if EventType == "run", then the skill to use is self.__Run.
After that, the second decision checks whether the skill is greater than or equal to the difficulty:
- if yes, the character always has a
100percent chance, soreturn 100 - if no, the event is harder than the character's skill level, so the difference must be calculated
The difference is calculated as:
Difference = Difficulty - Skill
That subtraction order matters. The difficulty is larger in this case, so the result will be 1, 2, 3 or 4.
The percentages decrease by 20 each time the difference increases by 1, so the return value can be calculated with:
100 - (Difference * 20)
This produces:
- difference 1 -> 80
- difference 2 -> 60
- difference 3 -> 40
- difference 4 -> 20
So the method returns the required integer values exactly.
Key Takeaways
- A method can use both object attributes and parameters.
- Selection is useful when one of several stored values must be chosen.
- Always check the rule for when to return immediately and when to calculate further.
Common Mistakes
- Using the wrong skill attribute for an event type.
- Subtracting in the wrong order, for example
Skill - Difficulty. - Forgetting to return
100when skill is equal to difficulty. - Returning text such as
"60%"instead of the integer60.
Things to Be Careful About
- The event type strings must match the values used elsewhere in the program, such as
"jump"and"drive". - This code belongs inside the
Characterclass, so it must be indented accordingly. - The method should return a value in every possible case.
Two characters are attempting each event in the group you created in part 1(b).
One character has the name Tarz and the skill levels:
Jump 5
Swim 3
Run 5
Drive 1
The second character has the name Geni and the skill levels:
Jump 2
Swim 2
Run 3
Drive 4
Each Character object is stored in a variable.
Amend the main program to declare and create an instance of a Character object for Tarz and for Geni.
Save your program.
Copy and paste the program code into part 1(e)(i) in the evidence document.
Answer
Tarz = Character("Tarz", 5, 3, 5, 1)
Geni = Character("Geni", 2, 2, 3, 4)
See program code
Background Concept
Creating an object means calling its constructor and supplying the required parameter values. The object is then stored in a variable, so the rest of the program can call its methods later.
Understanding the Question
You are told that there are two characters, Tarz and Geni, each with a name and four skill levels. The task is to declare variables and create one Character object for each of them.
The constructor for Character takes five values in this order:
- name
- jump skill
- swim skill
- run skill
- drive skill
Approach
Write one constructor call for Tarz and one for Geni, storing each new object in a variable with the same name as the character.
Step-by-Step Reasoning
For Tarz, the values are:
- name:
"Tarz" - jump:
5 - swim:
3 - run:
5 - drive:
1
So the constructor call is:
Tarz = Character("Tarz", 5, 3, 5, 1)
For Geni, the values are:
- name:
"Geni" - jump:
2 - swim:
2 - run:
3 - drive:
4
So the constructor call is:
Geni = Character("Geni", 2, 2, 3, 4)
After these lines, both variables refer to Character objects that can be used in the main program.
Key Takeaways
- Object creation uses the constructor.
- The order of arguments matters.
- Storing the object in a variable lets the program reuse it later.
Common Mistakes
- Putting the skill values in the wrong order.
- Forgetting quotation marks around the names.
- Using the class name correctly but storing the result in the wrong variable.
Things to Be Careful About
- The order must match the constructor definition exactly.
- Use integers for the skill values, not strings.
Both characters Tarz and Geni take part in each event in the group you created in part 1(b).
These steps are repeated for all five events:
- The percentage chance of each character completing the event is calculated and compared.
- The character with the highest percentage chance of completing the event gets 1 point. Their character name is output together with a message telling them they have won that event.
- If both characters have the same percentage chance of completing the event, the scores do not change, and a message is output stating that the event is a draw.
When the total score for each character has been calculated, the name of the character with the highest score is output stating they have won and the number of points they have. If both characters have the same number of points, a message is output telling them the group is a draw.
Amend the main program to:
- calculate the score for each character in each event
- output the name of the character that wins each event or that the event is a draw
- output the name of the character that has the most points for the group or that the group is a draw.
Save your program.
Copy and paste the program code into part 1(e)(ii) in the evidence document.
Answer
TarzPoints = 0
GeniPoints = 0
for Index in range(5):
TarzScore = Tarz.CalculateScore(Group[Index].GetEventType(), Group[Index].GetDifficulty())
GeniScore = Geni.CalculateScore(Group[Index].GetEventType(), Group[Index].GetDifficulty())
if TarzScore > GeniScore:
TarzPoints += 1
print(Tarz.GetName(), "has won", Group[Index].GetName())
elif GeniScore > TarzScore:
GeniPoints += 1
print(Geni.GetName(), "has won", Group[Index].GetName())
else:
print(Group[Index].GetName(), "is a draw")
if TarzPoints > GeniPoints:
print(Tarz.GetName(), "has won the group with", TarzPoints, "points")
elif GeniPoints > TarzPoints:
print(Geni.GetName(), "has won the group with", GeniPoints, "points")
else:
print("The group is a draw")
See program code
Background Concept
This is a typical main-program processing task using objects. The program must loop through a collection, call methods on each object, compare returned values, update totals and then produce a final summary.
There are two important programming patterns here:
- an accumulator pattern for the total points
- a loop over all items in a collection
Understanding the Question
For each of the five events in Group, both Tarz and Geni attempt the event. The program must:
- calculate Tarz's percentage chance
- calculate Geni's percentage chance
- compare the two percentages
- award 1 point to the higher one
- output the event winner, or output that the event is a draw
After all five events, it must compare the point totals and output the overall winner and points, or say the group is a draw.
Approach
The clean structure is:
- Set both point totals to 0.
- Loop through the five events in
Group. - For each event, call
CalculateScore()for Tarz and Geni. - Compare the returned percentages.
- Update the correct total or leave totals unchanged for a draw.
- Output the result for that event.
- After the loop, compare the totals and output the group result.
This matches the task exactly and avoids repeated code.
Step-by-Step Reasoning
First, two accumulators are needed:
TarzPoints = 0GeniPoints = 0
These store how many events each character has won.
The loop for Index in range(5): runs once for each event in the group. In Python, range(5) gives 0, 1, 2, 3, 4, which are the valid list positions.
Inside the loop, the program gets the event type and difficulty from Group[Index] and uses them as the parameters for both characters' CalculateScore() calls. That makes the comparison fair because both characters are being scored against the same event.
Then the program compares the two returned percentages.
If Tarz's score is higher:
- increase
TarzPointsby 1 - print that Tarz has won that event
If Geni's score is higher:
- increase
GeniPointsby 1 - print that Geni has won that event
Otherwise the percentages are equal:
- do not change either total
- print that the event is a draw
After all five events are processed, the totals are compared again.
- If Tarz has more points, output that Tarz won the group and how many points he has.
- If Geni has more points, output that Geni won the group and how many points she has.
- Otherwise output that the group is a draw.
This gives both per-event output and final overall output, exactly as required.
Key Takeaways
- Use a loop when the same processing must be repeated for every item in a list.
- Use running totals to keep scores across multiple iterations.
- Method calls on objects make the main program cleaner and more modular.
- Always handle all three comparison outcomes: greater than, less than and equal.
Common Mistakes
- Forgetting to initialise the point totals before the loop.
- Awarding points on a draw when the question says scores do not change.
- Calling
CalculateScore()with the wrong arguments. - Comparing the characters' raw skill values instead of the calculated percentages.
- Forgetting the final comparison after the loop.
Things to Be Careful About
- In Python, the list indexes are
0to4, sorange(5)is correct. - Use the same event's type and difficulty for both characters in each iteration.
- The output wording can vary, but it must clearly identify the event winner or say it is a draw, and must also give the final group result.
Test your program.
Take a screenshot of the output.
Save your program.
Copy and paste the screenshot into part 1(e)(iii) in the evidence document.
Answer
Run the program with the Group, Tarz and Geni objects already created.
Tarz has won Bridge
Tarz has won Water wade
Tarz has won 100 mile run
Geni has won Gridlock
Tarz has won Wall on wall
Tarz has won the group with 4 points
See expected console output
Background Concept
When a program has no user input and all the data is fixed in the code, its output is deterministic. That means the same objects and the same logic will always produce the same console output. To predict that output, you trace the program event by event.
Understanding the Question
This part asks for a screenshot of the output after testing the program. Since the event data and both characters' skills are already given, the result can be worked out exactly by tracing the scoring logic from earlier parts.
Approach
For each event:
- identify the event type and difficulty
- calculate Tarz's percentage chance
- calculate Geni's percentage chance
- compare them and decide the winner
- update the points
Then compare the total points at the end.
Step-by-Step Reasoning
There are five events.
Bridge, typejump, difficulty3
- Tarz jump skill = 5, so score = 100
- Geni jump skill = 2, difference = 1, so score = 80
- Tarz wins
Water wade, typeswim, difficulty4
- Tarz swim skill = 3, difference = 1, so score = 80
- Geni swim skill = 2, difference = 2, so score = 60
- Tarz wins
100 mile run, typerun, difficulty5
- Tarz run skill = 5, so score = 100
- Geni run skill = 3, difference = 2, so score = 60
- Tarz wins
Gridlock, typedrive, difficulty2
- Tarz drive skill = 1, difference = 1, so score = 80
- Geni drive skill = 4, so score = 100
- Geni wins
Wall on wall, typejump, difficulty4
- Tarz jump skill = 5, so score = 100
- Geni jump skill = 2, difference = 2, so score = 60
- Tarz wins
Final totals:
- Tarz = 4 points
- Geni = 1 point
So the final output states Tarz as the winner for four events, Geni as the winner for one event, and Tarz as the winner of the group with 4 points.
Key Takeaways
- Testing a program often means tracing the data all the way through the logic.
- Deterministic programs produce predictable output.
- Final totals should always be checked after all iterations, not during just one event.
Common Mistakes
- Miscalculating one of the percentage scores, especially when the skill is lower than the difficulty.
- Forgetting that
Gridlockis the only event Geni wins. - Giving the wrong final points total.
- Assuming draws occur when the percentages are different.
Things to Be Careful About
- The exact wording of the screenshot depends on the print statements used in the program. The output shown here matches the solution code given in part
1(e)(ii). - If a candidate used different but equivalent message wording, their screenshot could still be valid as long as the logic and winners are correct.
A linear queue data structure is designed using a record structure.
The record structure Queue has the following fields:
QueueArray, a 1D array of up to 100 integer valuesHeadpointer, a variable that stores the index of the first data item inQueueArrayTailpointer, a variable that stores the index of the next free location inQueueArray.
Write program code to declare the record structure Queue and its fields.
If your programming language does not support record structures, a class can be declared instead.
If you are writing in Python, use comments to declare the appropriate data types.
Save your program as Question2_N24.
Copy and paste the program code into part 2(a) in the evidence document.
Answer
class Queue:
def __init__(self):
self.QueueArray = [0 for x in range(100)] # list of INTEGER
self.Headpointer = 0 # INTEGER
self.Tailpointer = 0 # INTEGER
See program code
Background Concept
A queue is an abstract data type where items are added at one end and removed from the other. To implement it in code, we need a data structure that stores both the data and the information needed to manage it. In this question, that structure contains:
QueueArrayto hold the integer valuesHeadpointerto identify the first item currently in the queueTailpointerto identify the next free position
In languages with records, you would declare a record type. Python does not have records in the same way, so a class is used instead. In this exam, that is acceptable, and comments are used to show the intended data types.
Understanding the Question
This part only asks for the declaration of the Queue structure itself. It is not yet asking you to create the actual queue object used by the program, and it is not yet asking you to initialise the queue to its empty state. You just need a structure with the three required fields.
The important clues are:
- the queue holds up to 100 integers
- Python may use a class instead of a record
- Python answers should show the intended data types in comments
Approach
The simplest valid Python answer is:
- declare a class called
Queue - give it a constructor with three attributes
- make
QueueArraya list with 100 positions - include
HeadpointerandTailpointeras integer fields - add type comments because the question specifically asks for them
Step-by-Step Reasoning
class Queue: declares the structure that will represent one queue.
def __init__(self): defines the constructor. This runs when a new queue object is created.
self.QueueArray = [0 for x in range(100)] creates a list of 100 integer positions. The exact starting value here is not the key point for this part; the important thing is that the field exists and represents the array.
self.Headpointer = 0 creates the head pointer field.
self.Tailpointer = 0 creates the tail pointer field.
The comments such as # list of INTEGER and # INTEGER make the intended types clear, which is exactly what the question asks for in Python.
Key Takeaways
- In Python, a class can be used where another language might use a record.
- A queue implementation needs both the data storage and pointer fields.
- In Paper 4, type comments are often used in Python when the question asks for declared types.
Common Mistakes
- Declaring only the array and forgetting one or both pointers.
- Using the wrong class name instead of
Queue. - Forgetting that the array must allow up to 100 integer values.
- Writing pseudocode instead of real Python code for Paper 4.
Things to Be Careful About
- Use the field names exactly as given:
QueueArray,Headpointer,Tailpointer. - Keep this part as a declaration of the structure, not the full program.
- In Python, comments are important here because the question explicitly asks for the appropriate data types.
The main program creates a new Queue record with the identifier TheQueue. The head pointer is initialised to –1. The tail pointer is initialised to 0. Each element in the array is initialised with –1.
Write program code for the main program.
Save your program.
Copy and paste the program code into part 2(b) in the evidence document.
Answer
TheQueue = Queue()
TheQueue.Headpointer = -1
TheQueue.Tailpointer = 0
TheQueue.QueueArray = [-1 for x in range(100)]
See program code
Background Concept
An empty linear queue is usually represented by special pointer values. In this question:
Headpointer = -1means the queue is emptyTailpointer = 0means the next free position is the first array element
The array is also filled with -1. That value is acting as a placeholder or sentinel to show unused positions. The queue logic does not depend on searching the array for -1; it depends on the pointers. The initial array values are simply part of the required starting state.
Understanding the Question
This part moves from the declaration of the structure to creating the actual queue used by the program. The question names it TheQueue and tells you exactly how each field must be initialised.
So you need to do three things:
- create a new queue object called
TheQueue - set the pointers to the empty-queue state
- set every array element to
-1
Approach
The correct approach is to instantiate the class from part (a), then assign the required starting values. A list comprehension is a clean Python way to create 100 identical values.
Step-by-Step Reasoning
TheQueue = Queue() creates one queue object from the Queue class.
TheQueue.Headpointer = -1 sets the empty flag for the queue. This is important because later Enqueue() checks whether the queue is empty by testing the head pointer.
TheQueue.Tailpointer = 0 sets the next free location to the start of the array.
TheQueue.QueueArray = [-1 for x in range(100)] creates a list with 100 elements, each equal to -1. That means every position is in its required initial state.
This combination gives a consistent empty queue:
- no valid item exists yet, because
Headpointer = -1 - the first inserted item will go to index
0
Key Takeaways
- Queue initialisation is not just creating the object; the pointers must also be set correctly.
Headpointer = -1is the key empty-queue condition used by later functions.- The array contents and pointer values must match the specification exactly.
Common Mistakes
- Setting
Headpointerto0instead of-1, which would make the queue appear non-empty. - Forgetting to initialise all 100 array elements.
- Using the wrong variable name instead of
TheQueue. - Creating the object but not assigning the required pointer values afterwards.
Things to Be Careful About
Tailpointerstores the next free location, not the last used location.- The array length must be 100.
- Even though the array is filled with
-1, the queue's empty/full logic should still rely on the pointers, not on scanning the array.
The pseudocode function Enqueue() inserts an integer value into the queue.
The function is incomplete. There are three incomplete statements.
FUNCTION Enqueue(BYREF AQueue : Queue, BYVAL TheData : INTEGER)
RETURNS INTEGER
IF AQueue.Headpointer = -1 THEN
AQueue.QueueArray[AQueue.Tailpointer] ← ........................................
AQueue.Headpointer ← 0
AQueue.Tailpointer ← AQueue.Tailpointer + 1
RETURN 1
ELSE
IF AQueue.Tailpointer > ........................................ THEN
RETURN -1
ELSE
AQueue.QueueArray[AQueue.Tailpointer] ← TheData
AQueue.Tailpointer ← AQueue.Tailpointer ........................................
RETURN 1
ENDIF
ENDIF
ENDFUNCTION
Write program code for Enqueue().
Save your program.
Copy and paste the program code into part 2(c) in the evidence document.
Answer
def Enqueue(AQueue, TheData):
if AQueue.Headpointer == -1:
AQueue.QueueArray[AQueue.Tailpointer] = TheData
AQueue.Headpointer = 0
AQueue.Tailpointer = AQueue.Tailpointer + 1
return 1
else:
if AQueue.Tailpointer > 99:
return -1
else:
AQueue.QueueArray[AQueue.Tailpointer] = TheData
AQueue.Tailpointer = AQueue.Tailpointer + 1
return 1
See program code
Background Concept
Enqueue() adds an item to the rear of a queue. In an array-based linear queue, the rear is tracked by the tail pointer. This question defines Tailpointer as the index of the next free location, not the index of the last item already stored.
That means:
- when an item is inserted, it is stored at
Tailpointer - after insertion,
Tailpointerincreases by 1
The queue is empty when Headpointer = -1.
Because this is a linear queue, not a circular queue, once Tailpointer has moved past the last valid index, no further insertions are possible even if some earlier elements were dequeued.
Understanding the Question
The pseudocode already gives the logic and shows that three blanks must be completed. Your task is to convert that exact logic into program code.
The missing ideas are:
- store
TheDatain the first blank - check the last valid array index in the full-queue test
- increase the tail pointer in the final blank
The array can hold 100 values, so the valid indexes are 0 to 99.
Approach
Follow the pseudocode directly:
- if the queue is empty, insert the first item, set the head, move the tail, return success
- otherwise, check whether the queue is already full
- if not full, insert at the tail and move the tail
- return
1for success or-1for failure
In Python, that becomes a function returning an integer status code.
Step-by-Step Reasoning
def Enqueue(AQueue, TheData): declares the function. It takes the queue object and the integer to add.
if AQueue.Headpointer == -1: checks for the empty-queue state.
If the queue is empty:
AQueue.QueueArray[AQueue.Tailpointer] = TheDatastores the value in the first free positionAQueue.Headpointer = 0sets the head to the first valid itemAQueue.Tailpointer = AQueue.Tailpointer + 1moves the next free position on by onereturn 1signals success
If the queue is not empty:
if AQueue.Tailpointer > 99:checks whether the next free position has moved beyond the last valid index- if it has,
return -1means the queue is full - otherwise, store the new item at
Tailpointer, then incrementTailpointer, then return1
The important detail is why the full check uses 99: the array has 100 elements, so the highest valid index is 99.
Key Takeaways
Tailpointerpoints to the next free space, not the current last item.- In a linear queue, insertion happens at the tail and removal happens at the head.
- Status codes such as
1and-1are a common way to report success or failure.
Common Mistakes
- Checking
Headpointerinstead ofTailpointerto detect a full queue. - Writing to
Tailpointer + 1instead of writing toTailpointerfirst. - Forgetting to increment
Tailpointerafter insertion. - Using
100as an array index even though the last valid index is99.
Things to Be Careful About
- The queue is zero-indexed, so
0to99are the only valid positions. - The first insertion is a special case because the head pointer must change from
-1to0. - This is Paper 4, so the answer must be real Python code, not pseudocode.
The function ReturnAllData() accesses TheQueue. It concatenates all the integer values that have been inserted into the queue’s array, starting from the value stored at HeadPointer, with a space between each integer value. The string of concatenated values is returned.
None of the integer values are removed from the queue.
Write program code for ReturnAllData().
Save your program.
Copy and paste the program code into part 2(d) in the evidence document.
Answer
def ReturnAllData():
DataValues = []
if TheQueue.Headpointer != -1:
for Index in range(TheQueue.Headpointer, TheQueue.Tailpointer):
DataValues.append(str(TheQueue.QueueArray[Index]))
return " ".join(DataValues)
See program code
Background Concept
To read all current items in an array-based queue, you do not scan the whole array. Instead, you use the pointers to identify the active section.
In this design:
- the first item is at
Headpointer - the next free location is at
Tailpointer - therefore the stored items are from
Headpointerup toTailpointer - 1
ReturnAllData() does not remove anything. It simply reads those elements and produces a string version of them.
Understanding the Question
You are asked to write a function that accesses the already-existing TheQueue, reads every queued integer in order, and returns one string containing those values separated by spaces.
The question gives two crucial restrictions:
- start from the value at
HeadPointer - do not remove any values from the queue
So this is not a dequeue operation. It is just a read-only traversal.
Approach
A clean Python solution is:
- create an empty list for the string versions of the numbers
- check whether the queue is empty
- if not empty, loop from
HeadpointertoTailpointer - 1 - convert each integer to a string and store it
- use
" ".join(...)to place one space between values
Step-by-Step Reasoning
def ReturnAllData(): declares a function with no parameter because the question says it accesses TheQueue directly.
DataValues = [] creates an empty list that will hold each number as text.
if TheQueue.Headpointer != -1: prevents an empty queue from being processed as if it had data.
for Index in range(TheQueue.Headpointer, TheQueue.Tailpointer): loops over the occupied section only. In Python, range(start, stop) includes start but excludes stop, which is perfect here because Tailpointer is the next free position.
DataValues.append(str(TheQueue.QueueArray[Index])) reads each integer and converts it to a string.
return " ".join(DataValues) combines the strings with one space between them. This produces output like 10 9 8 7 without an unwanted trailing space.
Key Takeaways
- Queue traversal should use the head and tail pointers, not the whole array.
Tailpointeris exclusive here because it points to the next free slot.- When building textual output, converting numbers to strings is essential.
Common Mistakes
- Looping to
Tailpointer - 1incorrectly in Python and accidentally missing the last item. - Looping from
0instead of fromHeadpointer. - Forgetting to convert integers to strings before concatenation.
- Deleting or changing queue contents even though the function should only read them.
Things to Be Careful About
- If the queue is empty, return an empty string rather than trying to loop from
-1. - Make sure you access
TheQueue, because the question states that this function uses that queue directly. - Remember that
range(a, b)stops beforeb, which matches the meaning ofTailpointerin this design.
The main program asks the user to enter 10 integers with values of 0 or greater. It reads each input repeatedly until a valid number is entered.
All 10 valid inputs are added to the queue, using Enqueue().
If the value returned from Enqueue() is –1, a message is output to state that the queue is full, otherwise a message is output to state that the item has been added to the queue.
The function ReturnAllData() is called once all 10 integers have been entered and the return value from the function call is output.
Amend the main program to perform these actions.
Save your program.
Copy and paste the program code into part 2(e)(i) in the evidence document.
Answer
TheQueue = Queue()
TheQueue.Headpointer = -1
TheQueue.Tailpointer = 0
TheQueue.QueueArray = [-1 for x in range(100)]
for Count in range(10):
Valid = False
while not Valid:
try:
Data = int(input())
if Data >= 0:
Valid = True
else:
print("Invalid input")
except ValueError:
print("Invalid input")
Result = Enqueue(TheQueue, Data)
if Result == -1:
print("Queue full")
else:
print("Item added to queue")
print(ReturnAllData())
See program code
Background Concept
A robust input routine does two jobs:
- it checks that the data is of the correct type
- it checks that the value is within the allowed range
Here the program needs 10 integers, each with a value of 0 or greater. In Python, converting user input with int(...) can cause a run-time error if the input is not a valid integer, so exception handling with try and except is a standard way to protect the program.
This part also uses the queue ADT. Each valid input is passed to Enqueue(), which returns:
1if the item was added-1if the queue was full
Understanding the Question
You are not writing a completely new program from scratch. You are amending the main program so that it:
- accepts 10 valid non-negative integers
- repeats an input until it is valid
- adds each valid value to the queue
- outputs a message depending on the return value from
Enqueue() - outputs all queued values at the end using
ReturnAllData()
The phrase "reads each input repeatedly until a valid number is entered" is the key clue that you need a nested validation loop.
Approach
The best structure is:
- initialise
TheQueue - use a
forloop to process exactly 10 valid values - inside that loop, use a
whileloop to keep asking until one input is valid - use
tryandexceptto catch non-integer input - also test that the integer is
>= 0 - once valid, call
Enqueue()and print the appropriate message - after all 10 values, print
ReturnAllData()
Step-by-Step Reasoning
The first four lines create and initialise the queue exactly as required in part (b).
for Count in range(10): makes sure the program processes 10 accepted values.
Valid = False sets up a flag for the validation loop.
while not Valid: repeats until one acceptable input has been entered.
Inside the loop:
Data = int(input())tries to convert the user input to an integer- if that succeeds and
Data >= 0, the input is valid, soValid = True - if the number is negative, it is rejected and
Invalid inputis printed - if the conversion fails,
except ValueError:catches the error and also printsInvalid input
After a valid value is obtained, Result = Enqueue(TheQueue, Data) attempts to add it to the queue.
Then:
- if
Result == -1, printQueue full - otherwise, print
Item added to queue
Finally, print(ReturnAllData()) outputs all current queue contents in order.
Although the queue can hold 100 values and this test only enters 10, the program still needs the full-queue branch because the question explicitly asks for it.
Key Takeaways
- Validation often needs both a type check and a range check.
- A nested loop is a common pattern when a fixed number of valid inputs is required.
- Return values from functions should be used to control the program's messages and decisions.
Common Mistakes
- Accepting 10 inputs total instead of 10 valid inputs.
- Checking for non-negative values but not handling non-integer input.
- Calling
Enqueue()before validation is complete. - Forgetting the final call to
ReturnAllData(). - Printing the wrong message for the return value from
Enqueue().
Things to Be Careful About
range(10)gives 10 iterations, which is correct.- The validation loop must continue after a negative value as well as after invalid text input.
ReturnAllData()accessesTheQueue, soTheQueuemust already exist before it is called.- Even if the queue cannot actually fill during this test, the required full-queue output branch still needs to be present.
Test your program with the following inputs in the order given:
10 9 –1 8 7 6 5 4 3 2 1
Take a screenshot of the output.
Save your program.
Copy and paste the screenshot into part 2(e)(ii) in the evidence document.
Answer
Using inputs 10 9 -1 8 7 6 5 4 3 2 1, the console output is:
Item added to queue
Item added to queue
Invalid input
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
10 9 8 7 6 5 4 3 2 1
See expected output
Background Concept
Testing a program means following the program logic with specific data and checking that the output matches what the code should do. For a queue program with validation, you need to consider both the queue operations and the input-checking behaviour.
In this question, negative values are not valid, so they are rejected before any enqueue takes place.
Understanding the Question
You are given the exact input sequence:
10 9 -1 8 7 6 5 4 3 2 1
The important point is that the program needs 10 valid integers. The value -1 is not valid because the allowed values are 0 or greater, so it is rejected and the program asks again. That is why there are 11 numbers in the test sequence even though only 10 valid items are finally stored.
Approach
To work out the output:
- process each input in order
- reject
-1and produce the invalid-input message - enqueue each valid non-negative integer
- print the success message after each successful enqueue
- after 10 valid items have been stored, print the queue contents
Step-by-Step Reasoning
10is valid, so it is enqueued andItem added to queueis printed.9is valid, so it is enqueued and the same success message is printed.-1is invalid because it is less than0, soInvalid inputis printed and nothing is added to the queue.8is then read as the replacement input for that same position. It is valid, so it is enqueued.7,6,5,4,3,2, and1are all valid and are each enqueued successfully.
So the 10 valid queued values are:
10 9 8 7 6 5 4 3 2 1
Because ReturnAllData() prints the current contents from head to tail, that exact sequence appears on the final output line.
The exact prompt text may vary between implementations, but the key credited output is the invalid-input message, the success messages, and the final queue contents in the correct order.
Key Takeaways
- Test data must be traced in the order entered.
- Invalid inputs are rejected and do not enter the queue.
- The final queue contents depend only on the accepted values, not on rejected ones.
Common Mistakes
- Treating
-1as if it were added to the queue. - Forgetting that the program needs 10 valid values, so the next input after
-1is still part of the same count. - Printing only 9 success messages because one input was invalid.
- Reversing the final queue order.
Things to Be Careful About
-1causes validation failure, not queue-underflow behaviour.- The output shown assumes the messages used in the program code from part (e)(i).
- If a candidate uses different prompt wording, the essential behaviour is still the same: one rejection, ten successful additions, then the final queue contents.
The function Dequeue() accesses TheQueue. The function returns –1 if the queue is empty. If the queue is not empty, the function returns the next item in the queue and updates the relevant pointer(s).
The data is not replaced or deleted from the queue.
Write program code for Dequeue().
Save your program.
Copy and paste the program code into part 2(f) in the evidence document.
Answer
def Dequeue():
if TheQueue.Headpointer == -1:
return -1
else:
ReturnValue = TheQueue.QueueArray[TheQueue.Headpointer]
if TheQueue.Headpointer == TheQueue.Tailpointer - 1:
TheQueue.Headpointer = -1
TheQueue.Tailpointer = 0
else:
TheQueue.Headpointer = TheQueue.Headpointer + 1
return ReturnValue
See program code
Background Concept
Dequeue() removes the next item from the front of a queue. In an array-based implementation, the front item is the one at Headpointer.
For this queue design:
- if
Headpointer = -1, the queue is empty - otherwise, the item at
Headpointeris the one to return - after removing an item, the head pointer must move forward
If the item removed was the last remaining item, the queue becomes empty again, so the pointers should be reset to the empty state.
Understanding the Question
This function accesses TheQueue directly. It must:
- return
-1if there is no data - otherwise return the next queued value
- update the pointer or pointers correctly
- leave the array values unchanged
That final point matters: the question says the data is not replaced or deleted from the array. So the function changes pointers, not stored values.
Approach
The standard dequeue algorithm here is:
- test for empty queue
- if empty, return
-1 - otherwise store the current head item in a temporary variable
- decide whether that item is the only remaining item
- if it is the last one, reset the queue to empty
- otherwise move
Headpointerforward by one - return the saved item
Step-by-Step Reasoning
def Dequeue(): declares the function.
if TheQueue.Headpointer == -1: checks for an empty queue. If true, return -1 immediately.
If the queue is not empty:
ReturnValue = TheQueue.QueueArray[TheQueue.Headpointer] saves the item currently at the front.
Then the code checks:
if TheQueue.Headpointer == TheQueue.Tailpointer - 1:
This means the head is pointing to the last occupied element. In other words, there is only one item left in the queue.
If that is true, after removing it the queue should become empty again, so:
TheQueue.Headpointer = -1TheQueue.Tailpointer = 0
Otherwise there were at least two items, so only the head pointer moves on:
TheQueue.Headpointer = TheQueue.Headpointer + 1
Finally, return ReturnValue returns the dequeued item.
Key Takeaways
- Dequeue returns the item at the head, not the tail.
- Removing an item usually means moving the head pointer, not deleting array contents.
- The last-item case needs special handling so the queue returns to a valid empty state.
Common Mistakes
- Returning the value at
Tailpointerinstead ofHeadpointer. - Incrementing
Tailpointerduring dequeue. - Overwriting the data in the array even though the question says not to.
- Forgetting to reset the queue when the last item is removed.
Things to Be Careful About
Tailpointeris the next free location, so the last valid item is atTailpointer - 1.- The function accesses
TheQueuedirectly, so it does not need a queue parameter in this version. - Make sure the empty test happens before trying to read from the array.
The main program calls Dequeue() twice, and each time it either outputs ‘Queue empty’ if there is no data in the queue or outputs the return value.
The main program then calls ReturnAllData() a second time.
Amend the main program.
Save your program.
Copy and paste the program code into part 2(g)(i) in the evidence document.
Answer
Result = Dequeue()
if Result == -1:
print("Queue empty")
else:
print(Result)
Result = Dequeue()
if Result == -1:
print("Queue empty")
else:
print(Result)
print(ReturnAllData())
See program code
Background Concept
Once queue operations such as Dequeue() exist, the main program uses their return values to decide what to display. This is a common pattern in procedural programming:
- call a function
- store its return value
- test that value
- output the appropriate message
Because Dequeue() returns -1 for an empty queue, the main program can use that as the condition for printing Queue empty.
Understanding the Question
This part does not ask you to rewrite the queue functions. It only asks you to amend the main program so that:
Dequeue()is called twice- after each call, the program either prints
Queue emptyor prints the returned value - then
ReturnAllData()is called again to show the remaining contents
So the task is mainly about correct function calls and correct use of the return value.
Approach
The simplest solution is to repeat the same pattern twice:
- call
Dequeue()and store the result - check whether the result is
-1 - print either the message or the value
Then finish with one more print(ReturnAllData()).
Step-by-Step Reasoning
Result = Dequeue() performs the first removal attempt.
if Result == -1: checks whether the queue was empty.
- if true, print
Queue empty - otherwise, print the dequeued value
The same three-step pattern is repeated for the second call.
Finally, print(ReturnAllData()) shows the queue contents after those two removals. This works because Dequeue() changes the queue pointers, so the front of the queue has moved on.
Even though the old values are still physically present in the array, ReturnAllData() starts at the updated Headpointer, so only the remaining queued values are shown.
Key Takeaways
- Return values from functions often control the next decision in the main program.
- Queue-removal code and output code should be kept separate: the function returns data, the main program decides what to print.
- After dequeuing, pointer-based traversal shows only the remaining logical contents of the queue.
Common Mistakes
- Calling
Dequeue()twice inside oneifstatement and accidentally removing two items when checking one result. - Printing
Queue emptyfor the wrong condition. - Forgetting the second call to
ReturnAllData(). - Assuming the array itself must be rewritten before the remaining values can be displayed.
Things to Be Careful About
- Store each dequeue result before testing it.
- Use the exact message
Queue emptyif that is what your program is meant to output. ReturnAllData()will only be correct ifDequeue()updated the pointers properly.
Test your program with the following inputs in the order given:
10 9 8 7 6 5 4 3 2 1
Take a screenshot of the output.
Save your program.
Copy and paste the screenshot into part 2(g)(ii) in the evidence document.
Answer
Using inputs 10 9 8 7 6 5 4 3 2 1, the console output is:
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
Item added to queue
10 9 8 7 6 5 4 3 2 1
10
9
8 7 6 5 4 3 2 1
See expected output
Background Concept
When testing queue operations, the key idea is that dequeue removes items logically from the front by moving the head pointer. The stored values may still remain in the array, but they are no longer part of the queue once the head has moved past them.
Understanding the Question
This test uses 10 valid inputs:
10 9 8 7 6 5 4 3 2 1
Unlike part (e)(ii), there is no invalid input here. So every entered value is accepted and enqueued. After that, the main program performs two dequeues and then shows the remaining contents.
Approach
Work through the program in three stages:
- process the 10 inputs and the enqueue messages
- output the full queue using
ReturnAllData() - apply two dequeues and then output the remaining queue
Step-by-Step Reasoning
All 10 inputs are valid non-negative integers, so the program prints Item added to queue 10 times.
After the 10 insertions, the queue contains:
10 9 8 7 6 5 4 3 2 1
So the first ReturnAllData() prints that full sequence.
Now the program calls Dequeue() twice.
First dequeue:
- the head item is
10 10is returned and printed- the head pointer moves to the next item
Second dequeue:
- the new head item is
9 9is returned and printed- the head pointer moves again
At this point, the remaining logical queue contents are:
8 7 6 5 4 3 2 1
So the second ReturnAllData() prints that sequence.
As in the earlier screenshot part, exact prompt wording can vary, but these are the essential output lines from the program logic.
Key Takeaways
- After two dequeues, the first two inserted values are no longer in the logical queue.
- Queue contents are determined by the pointers, not by whether old values still exist in the array.
- Testing output is often easiest if you separate the run into stages and trace the queue state after each stage.
Common Mistakes
- Forgetting the first
ReturnAllData()from the earlier part of the main program. - Assuming dequeued values disappear from the array and therefore misunderstanding how
ReturnAllData()works. - Printing
9and10in the wrong order. - Starting the remaining queue at
7instead of8after two dequeues.
Things to Be Careful About
- This output assumes the program from earlier parts is still present, so the enqueue messages and first queue display happen before the dequeue outputs.
- The remaining queue starts at the updated
Headpointerafter two removals. - With this test data, the queue never becomes empty, so
Queue emptyis not printed.
The text file HighScoreTable.txt stores the top seven player scores for a game.
The data in the file is stored in the order:
Player ID
Game level
Score
Each item of data is stored on a new line. For example, the first set of data in the file is:
Player ID: GHEH
Game level: 3
Score: 10
One source file is used to answer Question 3. The file is called HighScoreTable.txt
The data about the players and their scores is stored in a 2D array of strings with the identifier HighScores.
The first dimension of the array has seven elements: one for each player. The second dimension of the array has three elements: one for the player ID, one for the game level and one for the score. All data is stored in the array as strings.
HighScores is declared local to the main program, and all elements are initialised to an empty string, for example "".
Write program code to declare and initialise HighScores.
Save your program as Question3_N24.
Copy and paste the program code into part 3(a) in the evidence document.
Answer
HighScores = [["" for Column in range(3)] for Row in range(7)]
See program code
Background Concept
A 2D array stores data in rows and columns. In this question, each row represents one player, and each column represents one item of data about that player. The first dimension therefore needs 7 elements because there are seven players, and the second dimension needs 3 elements because each player has three fields: player ID, level and score.
In Python, a 2D array is usually represented by a list of lists. Because every element must start as an empty string, each inner list needs three empty-string values, and there must be seven of those inner lists.
Understanding the Question
You are being asked only to declare and initialise HighScores. You are not reading the file yet and you are not outputting anything yet. The important details are:
- 7 rows are needed
- 3 columns are needed
- every item is stored as a string
- all values must start as
""
So the finished structure must be a 7 by 3 2D array of strings.
Approach
The quickest correct Python approach is to build a list containing 7 inner lists, where each inner list contains 3 empty strings. That gives the exact shape required by the question.
Step-by-Step Reasoning
HighScores = [["" for Column in range(3)] for Row in range(7)]
range(3)creates three positions for the second dimension."" for Column in range(3)makes those three positions all empty strings.- Wrapping that in another list comprehension with
range(7)creates seven such rows. - The result is a 2D array like this in structure:
- row 0: 3 empty strings
- row 1: 3 empty strings
- ...
- row 6: 3 empty strings
So HighScores is ready to store all seven records from the file.
Key Takeaways
- A 2D array can model rows of records and fields within each record.
- You must match the dimensions exactly to the data description in the question.
- In Python, a list of lists is the normal way to represent a 2D array.
Common Mistakes
- Using the wrong dimensions, such as 3 rows and 7 columns.
- Forgetting that all items are strings and initialising with
0instead of"". - Creating only one row instead of seven.
Things to Be Careful About
Be careful not to use [[""] * 3] * 7 in teaching or practice without understanding it, because it creates repeated references to the same inner list. The list-comprehension version is safer and gives seven separate rows.
The function ReadData() reads the data from HighScoreTable.txt and stores the data in a 2D array. The function returns the 2D array.
The function uses exception handling when opening and reading data from the file.
Write program code for ReadData().
Save your program.
Copy and paste the program code into part 3(b) in the evidence document.
Answer
def ReadData():
HighScores = [["" for Column in range(3)] for Row in range(7)]
try:
with open("HighScoreTable.txt", "r") as FileHandle:
for Row in range(7):
for Column in range(3):
HighScores[Row][Column] = FileHandle.readline().strip()
except OSError:
print("File could not be opened or read")
return HighScores
See program code
Background Concept
Sequential file processing means reading data from a text file one item after another in order. This file stores 21 lines in total because there are 7 players and each player has 3 items: ID, level and score. Since each item is on a new line, the program can read one line at a time and place it into the next array position.
Exception handling is used so that if the file cannot be opened or read, the program does not crash unexpectedly. In Python, try and except are used for this.
Understanding the Question
The function ReadData() must:
- read data from
HighScoreTable.txt - store it into a 2D array
- return that 2D array
- use exception handling while opening and reading
A key detail is that all data is stored as strings, so this function should not convert the values to integers. It should simply read the text and store it.
Approach
Create a new 7 by 3 array inside the function, open the file, then use nested loops:
- outer loop for each player row
- inner loop for the 3 pieces of data in that row
Each call to readline() gets the next line from the file. strip() removes the newline character at the end so the stored string is clean.
Step-by-Step Reasoning
The function starts by creating:
- 7 rows, one per player
- 3 columns, one each for ID, level and score
Then the try block attempts to open the file:
with open("HighScoreTable.txt", "r") as FileHandle:opens the file for readingwithis useful because it automatically closes the file afterwards
The nested loops then fill the array in row order:
for Row in range(7):goes through each playerfor Column in range(3):goes through ID, level and scoreFileHandle.readline()reads the next line from the file.strip()removes the trailing newline- the cleaned string is stored in
HighScores[Row][Column]
So the first three lines:
GHEH310
become:
HighScores[0][0] = "GHEH"HighScores[0][1] = "3"HighScores[0][2] = "10"
This continues until all 21 lines are stored.
If there is a problem opening or reading the file, the except OSError: block runs and prints an error message instead.
Finally, the function returns the array, whether filled normally or left at its initial values if an error occurred.
Key Takeaways
- Sequential files are read in order, one line after another.
- A nested loop is a natural way to load a 2D array.
strip()is important when reading text lines into data structures.- Exception handling is used to deal safely with file errors.
Common Mistakes
- Forgetting to return the array.
- Reading only 7 lines instead of all 21 lines.
- Forgetting
.strip(), which leaves newline characters in the stored strings. - Converting the values to integers here even though the question says all data is stored as strings.
- Opening the file without any exception handling.
Things to Be Careful About
The file has a very regular structure: exactly 3 lines per player. That is why a 7 by 3 nested loop works well. Also be careful with the filename spelling and capital letters: HighScoreTable.txt must match the source file name exactly.
The procedure OutputHighScores() takes a 2D array as a parameter. It outputs each player’s ID, level and score in the order they are in the array. The outputs are in the format:
GHEH reached level 3 with a score of 10
Write program code for OutputHighScores().
Save your program.
Copy and paste the program code into part 3(c) in the evidence document.
Answer
def OutputHighScores(HighScores):
for Row in range(7):
print(f"{HighScores[Row][0]} reached level {HighScores[Row][1]} with a score of {HighScores[Row][2]}")
See program code
Background Concept
A procedure is used when a task performs an action but does not need to return a value. Here, the action is outputting each record in the correct format. The procedure receives the 2D array as a parameter, so it can work with the data that was already read from the file.
In a 2D array, each row is one record and each column is one field. That means:
- column 0 is player ID
- column 1 is game level
- column 2 is score
Understanding the Question
The procedure OutputHighScores() must take a 2D array parameter and output every row in its current order. The exact sentence format matters:
GHEH reached level 3 with a score of 10
So the task is not to sort or change anything, only to display each row correctly.
Approach
Use one loop through the 7 rows. On each iteration, take the three values from the current row and place them into the required sentence.
Step-by-Step Reasoning
def OutputHighScores(HighScores):
- defines a procedure-like function that receives the array as a parameter
for Row in range(7):
- loops through rows 0 to 6
- each row represents one player record
print(f"{HighScores[Row][0]} reached level {HighScores[Row][1]} with a score of {HighScores[Row][2]}")
HighScores[Row][0]is the player IDHighScores[Row][1]is the levelHighScores[Row][2]is the score- the f-string places them into exactly the required output sentence
For example, if the row contains "GHEH", "3", "10", the line printed is:
GHEH reached level 3 with a score of 10
The procedure repeats this for all 7 rows.
Key Takeaways
- Procedures can take arrays as parameters.
- A loop over the first dimension lets you process each record in order.
- Correct output formatting is part of the mark in Paper 4 questions.
Common Mistakes
- Outputting the fields in the wrong order.
- Missing words from the sentence, such as leaving out
with a score of. - Using the wrong column numbers.
- Printing the whole row list instead of formatting the individual items.
Things to Be Careful About
Keep the current order of the array. This procedure should not sort or edit the data. Also make sure the parameter name is actually used inside the procedure, rather than trying to access some different variable name by mistake.
The scores in HighScoreTable.txt are not in order.
The player scores are first sorted by the game level they reached. For example, all players that reached level 5 are higher in the high score table than players that reached level 4.
The players that reached the same level are then sorted in descending order of score.
An example sorted high score table is:
| Position | Player ID | Game level | Score |
|---|---|---|---|
| 1 | GFED | 5 | 25 |
| 2 | HJKM | 4 | 21 |
| 3 | RERR | 4 | 19 |
| 4 | TTYU | 4 | 15 |
| 5 | WSVG | 3 | 20 |
| 6 | PPTR | 3 | 15 |
| 7 | SNQT | 2 | 10 |
The function SortScores() uses a bubble sort to sort the data as described and returns the sorted array.
Write program code for SortScores().
Save your program.
Copy and paste the program code into part 3(d) in the evidence document.
Answer
def SortScores(HighScores):
for Pass in range(6):
for Row in range(6 - Pass):
Level1 = int(HighScores[Row][1])
Level2 = int(HighScores[Row + 1][1])
Score1 = int(HighScores[Row][2])
Score2 = int(HighScores[Row + 1][2])
if Level1 < Level2 or (Level1 == Level2 and Score1 < Score2):
Temp = HighScores[Row]
HighScores[Row] = HighScores[Row + 1]
HighScores[Row + 1] = Temp
return HighScores
See program code
Background Concept
Bubble sort repeatedly compares adjacent items and swaps them if they are in the wrong order. After each full pass, the largest or highest-priority remaining item has bubbled into the correct position. For n items, bubble sort usually needs up to n - 1 passes.
This question uses a compound comparison, meaning there are two sort keys:
- primary key: game level, descending
- secondary key: score, descending when levels are equal
Because the level and score are stored as strings in the array, they should be converted to integers before numeric comparison.
Understanding the Question
You must write SortScores() so that the records are ordered exactly like a high-score table:
- players with a higher level come first
- if two players reached the same level, the higher score comes first
The whole record must move during a swap, not just the score or just the level, because the player ID, level and score belong together.
Approach
Use a standard bubble sort structure:
- outer loop for the passes
- inner loop for adjacent comparisons
For each pair of neighbouring rows:
- compare their levels as integers
- if the first level is lower, swap the rows
- if levels are equal, compare scores as integers
- if the first score is lower, swap the rows
Step-by-Step Reasoning
def SortScores(HighScores):
- the function receives the 2D array to be sorted
for Pass in range(6):
- there are 7 rows, so bubble sort needs up to 6 passes
for Row in range(6 - Pass):
- compares neighbouring pairs
- the range shortens each pass because the end section is already sorted
For each adjacent pair, extract the numeric values:
Level1 = int(HighScores[Row][1])Level2 = int(HighScores[Row + 1][1])Score1 = int(HighScores[Row][2])Score2 = int(HighScores[Row + 1][2])
The swap condition is:
- if
Level1 < Level2, the first record should move down because it has a lower level - or if levels are equal and
Score1 < Score2, the first record should also move down because it has a lower score
That is exactly what this condition tests:
Level1 < Level2 or (Level1 == Level2 and Score1 < Score2)
If true, the rows are swapped:
- store the first row in
Temp - copy the second row into the first position
- copy
Tempinto the second position
Notice that each row is swapped as a whole record, so the ID stays matched with its level and score.
After all passes are complete, the array is returned.
Key Takeaways
- Bubble sort compares adjacent items and swaps them repeatedly.
- Multi-key sorting uses a primary comparison and then a secondary comparison when needed.
- When data is stored as strings, numeric fields often need converting before comparison.
- In a record-based sort, swap the entire row, not individual fields.
Common Mistakes
- Sorting the levels in ascending order instead of descending order.
- Comparing scores even when levels are different.
- Swapping only the score column and not the whole record.
- Forgetting to convert string values like
"10"and"3"to integers before comparing them. - Writing a loop that goes out of range when accessing
Row + 1.
Things to Be Careful About
The secondary sort is only used when the levels are equal. Also remember that string comparison is not reliable for numeric ordering in general, so converting with int() is the safe approach. Finally, keep the bubble sort bounds correct: with 7 rows, there are 6 passes and the inner loop must stop before the last valid adjacent comparison.
Amend the main program to implement these steps in the order given:
- call
ReadData()and store the returned array inHighScores - output "Before"
- call
OutputHighScores()withHighScoresas a parameter - call
SortScores()and store the returned array inHighScores - output "After"
- call
OutputHighScores()withHighScoresas a parameter.
Save your program.
Copy and paste the program code into part 3(e)(i) in the evidence document.
Answer
HighScores = ReadData()
print("Before")
OutputHighScores(HighScores)
HighScores = SortScores(HighScores)
print("After")
OutputHighScores(HighScores)
See program code
Background Concept
The main program coordinates the different procedures and functions. A function call is used when a value is returned, and a procedure call is used when the task just performs an action such as output.
In this question:
ReadData()returns a 2D arraySortScores()returns a sorted 2D arrayOutputHighScores()outputs the contents of a 2D array
Understanding the Question
This part is not asking you to write the functions again. It is asking you to amend the main program so the previously written routines are used in exactly the order given.
The sequence matters because the program needs to:
- read the file into
HighScores - show the unsorted data
- sort the data
- show the sorted data
Approach
Follow the bullet points in the question one by one. Whenever a function returns an array, store that returned value in HighScores. Whenever output is needed, call OutputHighScores(HighScores).
Step-by-Step Reasoning
HighScores = ReadData()
- calls the file-reading function
- stores the returned 2D array in
HighScores
print("Before")
- labels the first output block so the unsorted records are clearly identified
OutputHighScores(HighScores)
- displays the records in the order they were read from the file
HighScores = SortScores(HighScores)
- passes the current array into the sorting function
- stores the sorted returned array back into
HighScores
print("After")
- labels the second output block
OutputHighScores(HighScores)
- displays the sorted records
This gives a complete before-and-after test of the program.
Key Takeaways
- The main program often glues together smaller routines.
- Returned values must be assigned back to a variable if you want to keep the result.
- The order of function and procedure calls can be just as important as the code inside them.
Common Mistakes
- Calling
ReadData()but not storing the returned array. - Calling
SortScores()before displaying theBeforeoutput. - Forgetting to pass
HighScoresintoOutputHighScores(). - Forgetting to store the sorted array returned by
SortScores().
Things to Be Careful About
Read the required sequence carefully and follow it exactly. In Paper 4, marks are often awarded for correct integration as well as for the individual routines themselves.
Test your program
Take a screenshot of the output.
Save your program.
Copy and paste the screenshot into part 3(e)(ii) in the evidence document.
Answer
Using the data in HighScoreTable.txt, the expected output is:
Before
GHEH reached level 3 with a score of 10
KWQW reached level 4 with a score of 20
MMND reached level 4 with a score of 18
RFOO reached level 5 with a score of 20
XXHD reached level 3 with a score of 19
QWSD reached level 3 with a score of 15
JGHF reached level 5 with a score of 22
After
JGHF reached level 5 with a score of 22
RFOO reached level 5 with a score of 20
KWQW reached level 4 with a score of 20
MMND reached level 4 with a score of 18
XXHD reached level 3 with a score of 19
QWSD reached level 3 with a score of 15
GHEH reached level 3 with a score of 10
See expected console output
Background Concept
A practical test question like this checks whether the complete program behaves correctly with the supplied data file. To work out the expected output, you follow the same steps as the program:
- read the file records in order
- output them unchanged for the
Beforesection - sort them by the stated rules
- output them again for the
Aftersection
Understanding the Question
You are not being asked to write more code here. You are being asked to run the finished program and show its output. Since the file contents are given, the expected output can be worked out exactly.
The source file contains these seven records:
GHEH, level3, score10KWQW, level4, score20MMND, level4, score18RFOO, level5, score20XXHD, level3, score19QWSD, level3, score15JGHF, level5, score22
Approach
First list the Before output exactly in file order. Then apply the sort rule:
- highest level first
- if the level is the same, highest score first
Then write the After output using that new order.
Step-by-Step Reasoning
For Before, the program outputs the records in the same order they were read:
GHEHlevel 3 score 10KWQWlevel 4 score 20MMNDlevel 4 score 18RFOOlevel 5 score 20XXHDlevel 3 score 19QWSDlevel 3 score 15JGHFlevel 5 score 22
For After, sort by level descending:
- level 5 records first:
RFOO 20,JGHF 22 - level 4 records next:
KWQW 20,MMND 18 - level 3 records last:
GHEH 10,XXHD 19,QWSD 15
Now apply the secondary sort by score descending inside each level group:
- level 5 becomes:
JGHF 22,RFOO 20 - level 4 stays:
KWQW 20,MMND 18 - level 3 becomes:
XXHD 19,QWSD 15,GHEH 10
So the final sorted order is:
JGHFlevel 5 score 22RFOOlevel 5 score 20KWQWlevel 4 score 20MMNDlevel 4 score 18XXHDlevel 3 score 19QWSDlevel 3 score 15GHEHlevel 3 score 10
Those records are then printed using the output sentence format.
Key Takeaways
- Testing output often means tracing real data through the whole program.
- You must preserve the original file order for the unsorted output.
- Multi-key sorting means grouping by the first key and ordering within each group by the second key.
Common Mistakes
- Sorting the
Beforeoutput as well as theAfteroutput. - Forgetting that higher levels should come first, not last.
- Ordering equal-level scores from low to high instead of high to low.
- Changing the wording or spacing of the output sentence.
Things to Be Careful About
When producing the screenshot in the real exam, make sure both Before and After sections are visible. Also check that every line matches the required format exactly, because output-format marks are often strict.