The worksheetDownload the PDF
Answers

F6.7 Project: the fail-safe controller

Robust programs · GCSE · about 25 min

BugBotLab

What this lesson is about

A command program that authenticates, validates every command, and is tested against a plan.

Questions 4 marks in all

  1. [1 mark]What does this program print?

    def parse(command):
        parts = command.strip().lower().split()
        if len(parts) != 2 or parts[0] not in ["forward", "back"]:
            return None
        if not parts[1].isdigit() or not 1 <= int(parts[1]) <= 50:
            return None
        return (parts[0], int(parts[1]))
    
    print(parse("Forward 20"), parse("forward 60"), parse(""))
    Answer:
    ('forward', 20) None None

    lower() accepts Forward; 60 is out of range; an empty command has no parts.

  2. [1 mark]Why put all the command checks in one function, parse?

    1. AEvery command goes through the same checks, and they can be tested on their own
    2. BFunctions run faster
    3. CPython requires validation in a function
    4. DIt avoids using a loop
    Answer: A. One place for validation means nothing reaches the robot unchecked.
  3. [1 mark]In the controller's test plan, an empty command is which kind of test data?

    1. AErroneous
    2. BNormal
    3. CBoundary
    4. DValid
    Answer: A. It is the wrong kind of input altogether; the controller must refuse it without crashing.
  4. [1 mark]Which comes first in the controller, and why?

    1. AAuthentication, so no commands are accepted from someone who is not allowed
    2. BValidation, so the password is sensible
    3. CTesting, so the robot is safe
    4. DThe main loop
    Answer: A. Check who is giving commands before checking any command.

The task: the fail-safe controller

Build the controller from the brief with operators = {"ada": "robot42"}. Print access denied for a wrong login, welcome <name> for a right one, invalid command for every command it refuses, and bye on quit. The task types: ada and oops, then ada and robot42, then the commands forward 20, fly 10, right 500, an empty line, right 15, beep and quit.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

operators = {"ada": "robot42"}
MOVES = ["forward", "back", "left", "right"]

The hint students can ask for: Three parts: a login that allows a limited number of tries, a parser that splits a command and refuses anything it does not recognise, and a main loop that acts on what the parser gives back. Refusing safely is the point.

A solution

from bugbot import *
connect()
operators = {"ada": "robot42"}
MOVES = ["forward", "back", "left", "right"]

def login(tries=3):
    for attempt in range(tries):
        name = input("Username? ")
        password = input("Password? ")
        if name in operators and operators[name] == password:
            return name
        print("access denied")
    return None

def parse(command):
    parts = command.strip().lower().split()
    if len(parts) == 1 and parts[0] in ["beep", "quit"]:
        return (parts[0], 0)
    if len(parts) != 2 or parts[0] not in MOVES:
        return None
    if not parts[1].isdigit() or not 1 <= int(parts[1]) <= 50:
        return None
    return (parts[0], int(parts[1]))

user = login()
if user:
    print("welcome", user)
    while True:
        result = parse(input("Command? "))
        if result is None:
            print("invalid command")
            tone(200, 0.3)
        elif result[0] == "quit":
            print("bye")
            break
        elif result[0] == "beep":
            tone(880, 0.3)
        elif result[0] == "forward":
            forward(50, distance=result[1])
        elif result[0] == "back":
            backward(50, distance=result[1])
        elif result[0] == "left":
            left(50, distance=result[1])
        elif result[0] == "right":
            right(50, distance=result[1])

Any program that meets the task's checks is marked correct in the simulator; this is one way, not the only way.