Time left2:30:00 Answered 0 of 44 140 marks
OCR H446/02 2 hours 30 minutes 140 marks Full length paper

A Level Computer Science Paper 2 B: Algorithms and Programming

A full length H446/02 paper. Section A: computational thinking, recursion, parameter passing, OOP, Big O, insertion sort, Dijkstra's algorithm and A*, tree traversals, and programs for bracket matching, a recursive binary search and a circular queue. Section B: a word game to program in Python, with a 9 mark evaluation of object-oriented design.

Instructions

Time allowed
2 hours 30 minutes
Total marks
140
Sections
Section A, 90 marks; Section B, 50 marks
Calculator
You must not use a calculator.
  • Answer all the questions.
  • The number of marks for each question is shown in brackets. Use it as a guide to how much to write.
  • The clock at the top counts down from the real time allowed. When it reaches zero the paper is locked and marked, as it would be collected in the exam hall.
  • Write program code in Python, so it can be run and marked here. In the real exam you may answer in OCR Exam Reference Language or a high-level language.
  • The 9 mark question is an extended response. In the real exam it is marked in levels; here every relevant point you make earns a mark, up to the total.
  • Run every program on the examples in the question, and on the edge cases: an empty list, a single item, the first and the last. In Section B each function builds on the last, so read the whole scenario first.

Questions

Answer every question, then finish the paper to have it marked. Marking as you go teaches you far less.

140 marks

Section A 90 marks

Answer all the questions in this section.

1(a)A satnav app shows a map of the roads to help a driver plan a route. Explain how abstraction has been used to produce the map.2 marks
Explain

Mark scheme0 / 2
  • LDetails that are not needed, such as buildings, trees or the exact shape of the land, are removed
  • LOnly what the driver needs, such as the roads, junctions and distances, is kept, so the problem is simpler to solve
Model answer

Everything that does not help plan a route, such as buildings, trees and the shape of the land, has been removed. Only the roads, the junctions and the distances between them are kept, which turns the real world into a much simpler problem the app can solve.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
1(b)A function binarySearch(items, target) is to be written. Identify two preconditions for the function.2 marks
Identify

Mark scheme0 / 2
  • LThe list items must be sorted
  • LThe target and the items must be of the same type, so they can be compared (or the list must not be empty)
Model answer

The list must already be sorted, and the target must be the same type as the items in the list, so that they can be compared.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
1(c)A web browser caches the pages it has recently loaded. Explain one benefit and one drawback of caching.3 marks
Explain

Mark scheme0 / 3
  • LA cached page is loaded from local storage instead of being downloaded again
  • LSo it loads faster and uses less bandwidth
  • LDrawback: the cached copy may be out of date, or the cache uses storage space
Model answer

A page in the cache is loaded from the computer's own storage rather than downloaded again, so it appears faster and less bandwidth is used. But if the page has changed since it was cached, the browser may show an out of date version.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
1(d)Explain how thinking concurrently could reduce the time taken to display a web page that contains many images.3 marks
Explain

Mark scheme0 / 3
  • LIdentify the parts of the task that can be carried out at the same time
  • LThe images can be downloaded at the same time as each other, while the text is laid out
  • LBecause these tasks do not depend on each other, the total time is reduced
Model answer

Thinking concurrently means identifying the parts of the task that can happen at the same time. Each image can be requested and downloaded at the same time as the others, while the text of the page is being laid out. None of these tasks depends on the others, so overlapping them makes the page appear much sooner than doing them one after another.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
1(e)Explain two reasons why programmers use reusable components, such as subroutines from a library.2 marks
Explain

Mark scheme0 / 2
  • LThey save development time, because the code does not need to be written again
  • LThey have already been tested, so they are reliable
Model answer

Using a library subroutine saves the time it would take to write and test the code again, and because it has already been tested and used by many programmers it is likely to be reliable.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
1(f)Tick (✓) one box in each row to show the aspect of computational thinking described.2 marks
Thinking aheadThinking procedurallyThinking logicallyThinking concurrently
Identifying the inputs and outputs a program needs before writing it
Breaking a problem into sub-procedures that can each be solved separately
Identifying the decision points in an algorithm and the conditions that decide them
Identifying the parts of a problem that can be tackled at the same time

Tick one box in each row.

Mark scheme0 / 2
  • L1 mark for both: Identifying the inputs and outputs a program needs before writing it: Thinking ahead; Breaking a problem into sub-procedures that can each be solved separately: Thinking procedurally
  • L1 mark for both: Identifying the decision points in an algorithm and the conditions that decide them: Thinking logically; Identifying the parts of a problem that can be tackled at the same time: Thinking concurrently
Model answer

Inputs and outputs are thinking ahead, sub-procedures are thinking procedurally, decisions are thinking logically and simultaneous parts are thinking concurrently.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
2(a)(i)State the value returned by mystery(472), for the function in Figure 1.1 mark

Figure 1

Python code
def mystery(n):
    if n == 0:
        return 0
    else:
        return n % 10 + mystery(n // 10)

Mark scheme0 / 1
  • L13
Model answer

2 + mystery(47) = 2 + 7 + mystery(4) = 2 + 7 + 4 + mystery(0) = 13. The function adds up the digits.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
2(a)(ii)State how many times mystery is called, in total, when mystery(472) is called.1 mark

Mark scheme0 / 1
  • L4
Model answer

mystery(472), mystery(47), mystery(4) and mystery(0): four calls.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
2(a)(iii)Rewrite the function mystery so that it uses iteration instead of recursion. It must return the same values.4 marks
mystery.py

Mark scheme0 / 4
  • LReturns 13 for 472
  • LReturns 0 for 0
  • LWorks for other numbers: 36 for 9999 and 1 for 1000000
  • LUses iteration, and the function no longer calls itself
A program that scores full marks

Take the last digit with % 10, add it to a running total, and remove it with // 10, until no digits are left.

Python
def mystery(n):
    total = 0
    while n > 0:
        total = total + n % 10
        n = n // 10
    return total
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
2(b)Explain one advantage and one disadvantage of using recursion rather than iteration.4 marks
Explain

Mark scheme0 / 4
  • LAdvantage: recursive code is often shorter and more natural for problems defined in terms of themselves, such as traversing a tree
  • LBecause the code mirrors the structure of the problem, it is easier to follow
  • LDisadvantage: every call adds a stack frame, using more memory
  • LSo deep recursion can cause a stack overflow, and the extra calls make it slower
Model answer

For a problem that is defined in terms of smaller versions of itself, such as visiting every node of a tree, a recursive solution is short and mirrors the structure of the data, so it is easier to write and follow. But every recursive call puts a new frame on the call stack, holding its parameters, local variables and return address, so recursion uses more memory and time than a loop, and very deep recursion can overflow the stack.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
2(c)State the two values printed when this program is run.2 marks
OCR reference language
procedure change(byVal a, byRef b)
    a = a + 1
    b = b + 1
endprocedure

x = 5
y = 5
change(x, y)
print(x)
print(y)

Mark scheme0 / 2
  • L5
  • L6
Model answer

x is passed by value, so the procedure changes only its own copy and x stays 5. y is passed by reference, so the change to b changes y, which becomes 6.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
2(d)Tick (✓) one box in each row to show the feature of an IDE described.2 marks
BreakpointWatch windowSteppingSyntax highlighting
Pauses the program when it reaches a chosen line
Displays the values of chosen variables as the program runs
Runs the program one line at a time
Shows keywords, strings and comments in different colours

Tick one box in each row.

Mark scheme0 / 2
  • L1 mark for both: Pauses the program when it reaches a chosen line: Breakpoint; Displays the values of chosen variables as the program runs: Watch window
  • L1 mark for both: Runs the program one line at a time: Stepping; Shows keywords, strings and comments in different colours: Syntax highlighting
Model answer

Breakpoints pause, watches show values, stepping runs line by line, and highlighting colours the code.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
2(e)(i)Which one object-oriented concept is shown by the method speak in the class Dog, in Figure 2?1 mark

Figure 2

Python code
class Animal:
    def __init__(self, name):
        self.__name = name

    def getName(self):
        return self.__name

    def speak(self):
        return "..."


class Dog(Animal):
    def speak(self):
        return "Woof"

Tick one box.

Mark scheme0 / 1
  • LB Polymorphism
Model answer

Dog overrides the speak method it inherits, so the same call gives different behaviour depending on the object's class: polymorphism.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
2(e)(ii)Explain how the class Animal in Figure 2 uses encapsulation.2 marks
Explain

Mark scheme0 / 2
  • LThe attribute name is private (__name), so it cannot be accessed directly from outside the class
  • LIt can only be read through the public method getName
Model answer

The attribute __name is private, so code outside the class cannot read or change it directly. The only way to get the name is through the public method getName, so the class controls how its data is used.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
2(e)(iii)Tick (✓) one box in each row to show the object-oriented term described.2 marks
ClassObjectMethodInheritance
A template that defines the attributes and methods of a type of object
An instance of a class
A subroutine that belongs to a class
Creating a new class from an existing one, so it gains its attributes and methods

Tick one box in each row.

Mark scheme0 / 2
  • L1 mark for both: A template that defines the attributes and methods of a type of object: Class; An instance of a class: Object
  • L1 mark for both: A subroutine that belongs to a class: Method; Creating a new class from an existing one, so it gains its attributes and methods: Inheritance
Model answer

A class is the template, an object an instance of it, a method a subroutine in it, and inheritance builds a class on another.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
2(f)Explain why programmers avoid using global variables where possible.2 marks
Explain

Mark scheme0 / 2
  • LA global variable can be changed by any part of the program, so errors caused by unexpected changes are hard to trace
  • LSubroutines that use globals are harder to reuse and test separately, and the variable uses memory for the whole run
Model answer

Because any subroutine can change a global variable, an unexpected change is hard to trace back to its cause. Subroutines that depend on globals are also harder to test separately and reuse in other programs.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
2(g)A binary tree is stored either as None, if it is empty, or as a tuple (left, value, right), where left and right are themselves trees. For example, the tree in Figure 3 is stored as((None, 2, None), 4, ((None, 5, None), 7, (None, 9, None)))Write a recursive function countNodes(tree) that returns the number of nodes in a tree.4 marks

Figure 3

  4
 / \
2   7
   / \
  5   9
tree.py

Mark scheme0 / 4
  • LReturns 0 for an empty tree, None
  • LReturns 1 for a tree of one node
  • LCounts the nodes in both subtrees: 5 for the tree in Figure 3
  • LIs recursive: countNodes calls itself
A program that scores full marks

The base case is the empty tree, with 0 nodes. Any other tree has 1 node, its root, plus the nodes in its left and right subtrees.

Python
def countNodes(tree):
    if tree is None:
        return 0
    left, value, right = tree
    return 1 + countNodes(left) + countNodes(right)
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(a)Tick (✓) one box in each row to show the time complexity.4 marks
O(1)O(log n)O(n)O(n log n)O(n²)
A binary search
A bubble sort, in the worst case
A merge sort
Reading an item from an array by its index

Tick one box in each row.

Mark scheme0 / 4
  • LA binary search: O(log n)
  • LA bubble sort, in the worst case: O(n²)
  • LA merge sort: O(n log n)
  • LReading an item from an array by its index: O(1)
Model answer

Binary search halves the list each time, bubble sort compares every pair, merge sort does n work on each of log n levels, and indexing is a single step.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(b)Explain why an O(n log n) sorting algorithm is preferred to an O(n²) one for a large data set.2 marks
Explain

Mark scheme0 / 2
  • LThe time an O(n²) algorithm takes grows with the square of the number of items, much faster than n log n
  • LSo for large n the O(n log n) algorithm needs far fewer steps: for a million items, about 20 million against a million million
Model answer

The time taken by an O(n²) algorithm grows with the square of the number of items, so doubling the data quadruples the time. For a million items, n² is a million million steps while n log n is about 20 million, so the O(n log n) algorithm is many thousands of times faster.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
3(c)An insertion sort is used to sort the list 7, 3, 9, 1, 5 into ascending order. Complete the table to show the list after each pass of the outer loop.4 marks
Position 0Position 1Position 2Position 3Position 4
After pass 1
After pass 2
After pass 3
After pass 4

Mark scheme0 / 4
  • LRow 1: 3, 7, 9, 1, 5
  • LRow 2: 3, 7, 9, 1, 5
  • LRow 3: 1, 3, 7, 9, 5
  • LRow 4: 1, 3, 5, 7, 9
Model answer

Pass 1 inserts 3 before 7. Pass 2 leaves 9 where it is. Pass 3 moves 1 to the front. Pass 4 inserts 5 between 3 and 7.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(d)Which one is the worst case time complexity of a quick sort?1 mark

Tick one box.

Mark scheme0 / 1
  • LA O(n²)
Model answer

If every pivot is the largest or smallest item, each partition removes only one item, giving O(n²).

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(e)(i)Figure 4 shows the weights of the edges of an undirected graph. Use Dijkstra's algorithm to find the length of the shortest path from A to each node.3 marks

Figure 4

EdgeA-BA-CB-CB-DC-DC-ED-ED-FE-F
Weight4215810262

Mark scheme0 / 3
  • LB and C: 3, 2
  • LD and E: 8, 10
  • LF: 12
Model answer

C is 2. B is 3, through C (2 + 1), which beats the direct 4. D is 8, through B (3 + 5). E is 10, through D (8 + 2), which beats 12 through C. F is 12, through E (10 + 2), which beats 14 through D.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(e)(ii)State the shortest path from A to F, as the list of nodes visited.1 mark

Mark scheme0 / 1
  • LA, C, B, D, E, F
Model answer

A, C, B, D, E, F, with a length of 12.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(f)Explain how the A* algorithm differs from Dijkstra's algorithm.3 marks
Explain

Mark scheme0 / 3
  • LA* uses a heuristic: an estimate of the distance from each node to the goal
  • LIt visits next the node with the lowest f = g + h: the distance so far plus the estimate
  • LSo it heads towards the goal and usually visits fewer nodes, finding one route faster; Dijkstra finds the shortest path to every node
Model answer

Dijkstra's algorithm always visits the unvisited node with the smallest distance from the start, so it spreads out in every direction and finds the shortest path to every node. A* adds a heuristic, an estimate of the remaining distance to the goal, and visits the node with the smallest total of distance so far plus estimate. That steers the search towards the goal, so it usually visits far fewer nodes to find a route to one destination.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
3(g)Complete the table to show the order in which the nodes of the tree in Figure 5 are visited by a breadth-first traversal and by a depth-first (pre-order) traversal, starting at A and visiting left children first.2 marks

Figure 5

    A
   / \
  B   C
 / \   \
D   E   F
Traversal1st2nd3rd4th5th6th
Breadth-first
Depth-first

Mark scheme0 / 2
  • LRow 1: A, B, C, D, E, F
  • LRow 2: A, B, D, E, C, F
Model answer

Breadth-first visits each level in turn: A, then B and C, then D, E and F. Depth-first goes as deep as it can down the left first: A, B, D, E, then C and F.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(h)Explain why a binary search cannot be carried out efficiently on a linked list.2 marks
Explain

Mark scheme0 / 2
  • LA binary search must go straight to the middle item, which needs direct access by index
  • LIn a linked list an item can only be reached by following the pointers from the start, so finding the middle takes linear time
Model answer

A binary search works by jumping straight to the middle item, which needs direct access by position, as an array gives. A linked list can only be followed node by node from its start, so just reaching the middle takes as long as a linear search.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
3(i)Write a function isBalanced(text) that uses a stack to check whether the brackets ( ), [ ] and { } in a string are balanced. It returns True if every opening bracket is closed by the matching bracket, in the right order, and False otherwise. Other characters are ignored.6 marks
brackets.py

Mark scheme0 / 6
  • LReturns True for balanced brackets: (a[b]{c})
  • LReturns True for a string with no brackets, and for an empty string
  • LReturns False when a bracket is closed by the wrong type: (]
  • LReturns False when brackets close in the wrong order: ([)]
  • LReturns False when a bracket is never closed: ((x)
  • LReturns False when a closing bracket has nothing to match: x)(
A program that scores full marks

Push each opening bracket. For each closing bracket, the stack must not be empty and the bracket popped must be its partner. At the end, the stack must be empty.

Python
def isBalanced(text):
    pairs = {")": "(", "]": "[", "}": "{"}
    stack = []
    for character in text:
        if character in "([{":
            stack.append(character)
        elif character in ")]}":
            if len(stack) == 0 or stack.pop() != pairs[character]:
                return False
    return len(stack) == 0
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(j)Write a recursive function binarySearch(items, target, low, high) that returns the index of target in the sorted list items, searching only between the indexes low and high inclusive, or -1 if target is not there. It is first called as binarySearch(items, target, 0, len(items) - 1).5 marks
search.py

Mark scheme0 / 5
  • LFinds an item in the middle: 25 is at index 4
  • LFinds the first and the last items
  • LFinds items in either half
  • LReturns -1 when the item is not there, including for an empty list
  • LIs recursive, and searches only between low and high
A program that scores full marks

The base case is low > high: nothing is left to search. Otherwise compare the middle item and recurse on the half that could hold the target, moving low or high past the middle.

Python
def binarySearch(items, target, low, high):
    if low > high:
        return -1
    mid = (low + high) // 2
    if items[mid] == target:
        return mid
    elif items[mid] < target:
        return binarySearch(items, target, mid + 1, high)
    else:
        return binarySearch(items, target, low, mid - 1)
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(k)Complete the procedure insertionSort(items), which sorts the list items into ascending order, in place, using an insertion sort.4 marks
sort.py

Mark scheme0 / 4
  • LSorts the list in place: 7, 3, 9, 1, 5
  • LSorts a list in reverse order, and one with repeated values
  • LCopes with an empty list and a list of one item
  • LKeeps the outer loop of the insertion sort, and uses no built-in sort
A program that scores full marks

Shift each larger item one place right, working back from position i - 1, then drop current into the gap.

Python
def insertionSort(items):
    for i in range(1, len(items)):
        current = items[i]
        position = i
        while position > 0 and items[position - 1] > current:
            items[position] = items[position - 1]
            position = position - 1
        items[position] = current
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(l)Explain why a merge sort needs more memory than an insertion sort.2 marks
Explain

Mark scheme0 / 2
  • LMerge sort creates new lists as it splits the list and merges the parts
  • LSo it needs extra memory in proportion to the size of the list, while insertion sort only moves items within the original list
Model answer

Merge sort builds new lists as it splits the data and merges the sorted halves, so it needs extra memory in proportion to the number of items. Insertion sort works in place, moving items within the original list, so it needs only one extra variable.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
3(m)(i)Which one is the time complexity of this code, in terms of n?1 mark
Python code
total = 0
for i in range(n):
    for j in range(n):
        total = total + i * j

Tick one box.

Mark scheme0 / 1
  • LC O(n²)
Model answer

The inner loop runs n times for each of the n passes of the outer loop: n x n steps.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(m)(ii)Which one is the time complexity of this code, in terms of n?1 mark
Python code
while n > 1:
    n = n // 2

Tick one box.

Mark scheme0 / 1
  • LD O(log n)
Model answer

n is halved each time round the loop, so it repeats about log2 n times.

Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
3(n)Explain why A* may not find the shortest path if its heuristic overestimates the distance to the goal.2 marks
Explain

Mark scheme0 / 2
  • LAn overestimate can make a node on the true shortest path look worse than it is
  • LSo the algorithm may reach the goal by another route first, and stop before exploring the shortest path
Model answer

A* explores the nodes with the smallest estimated total first. If the heuristic overestimates, a node on the genuinely shortest path can look more expensive than it is and be left unexplored, so the algorithm may reach the goal along a longer route first and stop there.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
4(a)The class Queue holds a circular queue in a list of fixed size. Complete the methods enqueue and dequeue, following the comments in the code.6 marks
queue.py

Mark scheme0 / 6
  • Lenqueue and dequeue give items back in first in, first out order
  • Lenqueue returns True when the item is added
  • Lenqueue returns False when the queue is full, and does not add the item
  • Ldequeue returns None when the queue is empty
  • LWraps round: when the end of the list is reached, the next item goes in position 0
  • LThe front pointer wraps round, so items keep coming out in order
A program that scores full marks

Check for a full or empty queue first. Move the pointer on with (pointer + 1) % size, which wraps from the last position back to 0, and keep count up to date.

Python
class Queue:
    def __init__(self, size):
        self.items = [None] * size
        self.size = size
        self.front = 0
        self.rear = -1
        self.count = 0

    def isEmpty(self):
        return self.count == 0

    def isFull(self):
        return self.count == self.size

    def enqueue(self, item):
        if self.isFull():
            return False
        self.rear = (self.rear + 1) % self.size
        self.items[self.rear] = item
        self.count = self.count + 1
        return True

    def dequeue(self):
        if self.isEmpty():
            return None
        item = self.items[self.front]
        self.front = (self.front + 1) % self.size
        self.count = self.count - 1
        return item
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
4(b)Explain why a circular queue is used, rather than a linear queue, when a queue is stored in an array of fixed size.2 marks
Explain

Mark scheme0 / 2
  • LIn a linear queue, positions freed at the front by dequeuing cannot be used again
  • LA circular queue wraps its pointers round to the start of the array, so every position is reused
Model answer

In a linear queue the front pointer moves along the array as items are removed, so the positions behind it are never used again and the queue soon reports itself full. A circular queue wraps both pointers back to position 0, so the freed positions are reused.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.

Section B 50 marks

Answer all the questions in this section. They are about a word game.

A company is writing a word game for two or more players. Each player has a rack of seven letter tiles. On their turn, a player makes a word from the tiles on their rack and scores the total of the letter values in the word. A word of seven or more letters scores double.

The letter values are stored in a dictionary called VALUES:

Python code
VALUES = {"A": 1, "B": 3, "C": 3, "D": 2, "E": 1, "F": 4, "G": 2, "H": 4, "I": 1,
          "J": 8, "K": 5, "L": 1, "M": 3, "N": 1, "O": 1, "P": 3, "Q": 10, "R": 1,
          "S": 1, "T": 1, "U": 1, "V": 4, "W": 4, "X": 8, "Y": 4, "Z": 10}
5(a)Identify two items of data that the game must store about each player.2 marks
Identify

Mark scheme0 / 2
  • LThe player's name
  • LThe player's score
  • LThe tiles on the player's rack
Model answer

Each player's name, their score and the tiles on their rack.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
5(b)Write a function wordScore(word) that returns the score for a word: the total of its letter values, doubled if the word has 7 or more letters. Lower case letters are worth the same as upper case letters.5 marks
game.py

Mark scheme0 / 5
  • LReturns the total of the letter values: CAT scores 5
  • LCounts every letter, including repeats: QUIZ scores 22 and BOOK 10
  • LScores lower case letters as upper case: cat scores 5
  • LDoubles a word of 7 or more letters: EXAMPLE scores 36 and QUESTION 34
  • LDoes not double a word of 6 letters: PUZZLE scores 26
A program that scores full marks

Convert the word to upper case, add up VALUES[letter] for each letter, then double the total if len(word) >= 7.

Python
VALUES = {"A": 1, "B": 3, "C": 3, "D": 2, "E": 1, "F": 4, "G": 2, "H": 4, "I": 1,
          "J": 8, "K": 5, "L": 1, "M": 3, "N": 1, "O": 1, "P": 3, "Q": 10, "R": 1,
          "S": 1, "T": 1, "U": 1, "V": 4, "W": 4, "X": 8, "Y": 4, "Z": 10}


def wordScore(word):
    total = 0
    for letter in word.upper():
        total = total + VALUES[letter]
    if len(word) >= 7:
        total = total * 2
    return total
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
5(c)Write a function canMake(word, rack) that returns True if word can be made from the tiles in rack, a string of seven upper case letters, using each tile at most once, and False otherwise. The word may be in upper or lower case.6 marks
game.py

Mark scheme0 / 6
  • LReturns True when every letter is on the rack: CAT from TACKLES
  • LReturns False when a letter is not on the rack: DOG from TACKLES
  • LUses each tile only once: BOOK cannot be made from BOKERST
  • LAllows a repeated letter when there are enough tiles: BOOK from BOOKEND
  • LCan use every tile on the rack: TACKLES from SLACKET
  • LAccepts a word in lower case: cat from TACKLES
A program that scores full marks

Copy the rack into a list of tiles and remove each letter of the word as it is used, so that a tile cannot be used twice. If a letter is not there, the word cannot be made.

Python
VALUES = {"A": 1, "B": 3, "C": 3, "D": 2, "E": 1, "F": 4, "G": 2, "H": 4, "I": 1,
          "J": 8, "K": 5, "L": 1, "M": 3, "N": 1, "O": 1, "P": 3, "Q": 10, "R": 1,
          "S": 1, "T": 1, "U": 1, "V": 4, "W": 4, "X": 8, "Y": 4, "Z": 10}


def canMake(word, rack):
    tiles = list(rack)
    for letter in word.upper():
        if letter in tiles:
            tiles.remove(letter)
        else:
            return False
    return True
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
5(d)Write a function bestWord(words, rack) that returns the highest scoring word in the list words that can be made from the tiles in rack. It returns an empty string if no word can be made. If two words have the same score, it returns the one that comes first in the list. Use the functions wordScore and canMake, which are given.6 marks
game.py

Mark scheme0 / 6
  • LReturns the highest scoring word that can be made
  • LIgnores a higher scoring word that cannot be made
  • LReturns an empty string when no word can be made
  • LReturns the first of two words with the same score
  • LReturns an empty string for an empty list of words
  • LUses the functions canMake and wordScore
A program that scores full marks

Keep the best word so far and its score, starting from an empty string and 0. Replace them only when a word can be made and scores strictly more, so the first of two equal words is kept.

Python
VALUES = {"A": 1, "B": 3, "C": 3, "D": 2, "E": 1, "F": 4, "G": 2, "H": 4, "I": 1,
          "J": 8, "K": 5, "L": 1, "M": 3, "N": 1, "O": 1, "P": 3, "Q": 10, "R": 1,
          "S": 1, "T": 1, "U": 1, "V": 4, "W": 4, "X": 8, "Y": 4, "Z": 10}


def wordScore(word):
    total = 0
    for letter in word.upper():
        total = total + VALUES[letter]
    if len(word) >= 7:
        total = total * 2
    return total


def canMake(word, rack):
    tiles = list(rack)
    for letter in word.upper():
        if letter in tiles:
            tiles.remove(letter)
        else:
            return False
    return True


def bestWord(words, rack):
    best = ""
    bestScore = 0
    for word in words:
        if canMake(word, rack) and wordScore(word) > bestScore:
            best = word
            bestScore = wordScore(word)
    return best
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
5(e)The game checks that every word played is in a dictionary of 270,000 valid words. Explain why the words should be stored in a hash table rather than in a list that is searched with a linear search.3 marks
Explain

Mark scheme0 / 3
  • LA hash table finds a word in constant time, O(1), by calculating its position from the word
  • LA linear search is O(n): it may compare the word with all 270,000 words
  • LSo checking a word is much faster, which matters because a check is made on every turn
Model answer

A hash table calculates where a word would be stored from the word itself, so a lookup takes constant time, O(1), however many words there are. A linear search is O(n): a word near the end, or one that is not valid, means comparing it with all 270,000 words. Words are checked on every turn, so the hash table keeps the game responsive.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
5(f)Write a class Player. It must have:private attributes for the player's name and score, with the score starting at 0a constructor that takes the player's namemethods getName() and getScore()a method addWord(word) that adds the score for the word to the player's score, and returns the score for that word, using wordScore.8 marks
game.py

Mark scheme0 / 8
  • LA new player's score is 0
  • LgetName returns the name given to the constructor
  • LaddWord returns the score for the word: 5 for CAT
  • LaddWord adds the word's score to the player's score
  • LThe score is a running total over several words
  • LEach player has a score of their own
  • LThe name and the score are private attributes
  • LaddWord uses the function wordScore
A program that scores full marks

Two underscores make an attribute private in Python. addWord works out the word's score once, adds it to the running total and returns it.

Python
VALUES = {"A": 1, "B": 3, "C": 3, "D": 2, "E": 1, "F": 4, "G": 2, "H": 4, "I": 1,
          "J": 8, "K": 5, "L": 1, "M": 3, "N": 1, "O": 1, "P": 3, "Q": 10, "R": 1,
          "S": 1, "T": 1, "U": 1, "V": 4, "W": 4, "X": 8, "Y": 4, "Z": 10}


def wordScore(word):
    total = 0
    for letter in word.upper():
        total = total + VALUES[letter]
    if len(word) >= 7:
        total = total * 2
    return total


class Player:
    def __init__(self, name):
        self.__name = name
        self.__score = 0

    def getName(self):
        return self.__name

    def getScore(self):
        return self.__score

    def addWord(self, word):
        points = wordScore(word)
        self.__score = self.__score + points
        return points
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
5(g)Write a class Game that keeps the players in a queue, in the order they take turns. Complete the constructor and the methods nextPlayer and winner, following the comments. The function wordScore and the class Player are given.7 marks
game.py

Mark scheme0 / 7
  • LnextPlayer returns the first player in the list first
  • LThe players then take turns in order
  • LAfter the last player, the first player has a turn again
  • LWorks with two players
  • Lwinner returns the name of the player with the highest score
  • Lwinner works when the first player has the highest score
  • Lwinner is still right after turns have moved the queue round
A program that scores full marks

A list used as a queue: nextPlayer removes the player at the front and appends them to the back, so the order cycles. winner looks at every player in the queue, whatever order it is in.

Python
VALUES = {"A": 1, "B": 3, "C": 3, "D": 2, "E": 1, "F": 4, "G": 2, "H": 4, "I": 1,
          "J": 8, "K": 5, "L": 1, "M": 3, "N": 1, "O": 1, "P": 3, "Q": 10, "R": 1,
          "S": 1, "T": 1, "U": 1, "V": 4, "W": 4, "X": 8, "Y": 4, "Z": 10}


def wordScore(word):
    total = 0
    for letter in word.upper():
        total = total + VALUES[letter]
    if len(word) >= 7:
        total = total * 2
    return total


class Player:
    def __init__(self, name):
        self.__name = name
        self.__score = 0

    def getName(self):
        return self.__name

    def getScore(self):
        return self.__score

    def addWord(self, word):
        points = wordScore(word)
        self.__score = self.__score + points
        return points


class Game:
    def __init__(self, players):
        self.__queue = list(players)

    def nextPlayer(self):
        player = self.__queue.pop(0)
        self.__queue.append(player)
        return player

    def winner(self):
        best = self.__queue[0]
        for player in self.__queue:
            if player.getScore() > best.getScore():
                best = player
        return best.getName()
Marked exactly against the mark scheme. Change it only if an examiner would accept your answer.
5(h)Describe how the game could use a stack to let a player undo the words they have played.4 marks
Describe

Mark scheme0 / 4
  • LEach word played, with its score, is pushed onto a stack
  • LTo undo, the top item is popped: the most recent word
  • LIts score is taken off the player's score, and its tiles are returned to the rack
  • LBecause a stack is last in, first out, repeated undos remove the words in the reverse of the order they were played
Model answer

Every time a word is played it is pushed onto a stack along with its score. Undo pops the top item, which is always the most recent word, subtracts its score from the player's total and puts its tiles back on the rack. Because a stack is last in, first out, pressing undo again removes the word before that, so the words are undone in the reverse order to the one they were played in.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
5(i)Evaluate the use of object-oriented programming, compared with a procedural approach, for developing the word game.9 marks
Evaluate

Mark scheme0 / 9
  • LClasses such as Player and Game model the things in the game, so the design matches the problem
  • LEncapsulation protects data such as a player's score from being changed incorrectly
  • LAny number of players can be created as instances of one class, each holding its own data
  • LInheritance would let a ComputerPlayer class reuse Player's code
  • LPolymorphism: a computer player could override a method, such as choosing its word, and be used in the same way
  • LClasses can be written and tested separately, by different programmers, which helps maintenance
  • LBut object-oriented design needs more planning, and adds code and complexity
  • LA procedural approach is simpler for small parts, such as wordScore, which need no class
  • LA justified conclusion: object-oriented programming suits this game
Model answer

Object-oriented programming suits the game because its design can mirror the problem: there are players, a game and a queue of turns, and each becomes a class. Encapsulation keeps each player's score private, so it can only change through addWord, which stops other parts of the program corrupting it. Any number of players can be created as instances of Player, each with its own name and score, without any extra code. The design is also easy to extend. A ComputerPlayer class could inherit everything from Player and override a method to choose its own word, and through polymorphism the Game class would treat it exactly like a human player. Because each class is self-contained it can be written and tested separately, so a team can share the work and later changes are less likely to break other parts. There are costs. The classes and their relationships need careful design before coding starts, and for a small program the classes add code and complexity. Parts of the game, such as wordScore and canMake, are simple calculations that are clearer as ordinary functions, as a procedural approach would write them. Overall an object-oriented approach is the better choice for the game as a whole, because it has several kinds of object with their own data and behaviour and is likely to grow, while small stateless calculations can stay as plain functions.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
Machine mark 0 / 140
About this paper

This is an original paper modelled on the real OCR H446/02 past papers: the same structure, the same balance of topics, the same command words and mark tariffs, 140 marks in 2 hours 30 minutes, exactly as the real paper allows. It is not a past paper. Real past papers are the copyright of the exam board and cannot be republished here, so links to the official papers on the board's own site are on the exam papers page.