Play computer

Explore how a program works by going line by line

What is “Playing Computer”?

Someone might have sent you here because they suggested you try “playing computer”. What does this mean?

A computer program is made up of a sequence of instructions, carried out one after the other. To understand what a program is doing, following these instructions one by one lets us see what is happening. This is playing computer.

Playing computer can be useful when:

  • You are faced with a brand new program and are trying to figure out what it does
  • You want to confirm that code you have written will work as you expect
  • You are debugging code to find out where a bug is happening

This page has an explanation of how to play computer, advice for trainees and volunteers. There are also some worked examples showing playing computer with two different functions.

How to Play Computer

The goal of playing computer is to pretend you are the computer, running each line of code, and following what changes. You can play computer individually or in pairs. You can use paper or, if you prefer an online visualisation, a tool like the Python Tutor.

When playing computer you need to keep track of 3 things, and it may help to use 3 separate pieces of paper:

  • One to track the “Global Frame” for the whole program
  • A new piece of paper each time you enter a “Local Frame”
  • A final piece of paper to print out the output from any console logging

The Global Frame is the state of the whole program, and you use it to keep track of:

  • Which line you are at in the program, remembering to step back here when you exit a function
  • The global variables declared in the program
  • Any defined functions

The Local Frame is the state when you step into a function, or a scoped block of code like a for loop, and keep track of:

  • Arguments defined for the function or scope
  • Local variables scoped specifically to that function or scope
  • Which line you are at within that function or scope

You start at the top of a script and run down line by line, evaluating what each line does. When you reach the end without any errors, your script has finished.

When you see a variable or function declaration, you record that in the appropriate frame. You can only use functions or variables that are available to you in your frames. Each time you step into a function, you get a new local frame. Each time you leave (or return from) a function, you get rid of that local frame. When you leave a frame, you return to the last line of execution you were at in the frame above.

If you ever come across a console.log() call, you write down what needs to be printed. You can assume that built-in functions, like those in console. and Math., are always available to use.

If you try to access a function or a variable that is not defined in any frame, that is an error. When you encounter an uncaught error, the program has crashed and you can’t continue further.

Advice for Trainees

The goal of playing computer is to help you improve your skill at understanding how a computer interacts with code. You might want to play computer if you find you:

  • Have difficulty explaining what your code is doing to others
  • Struggle to understand why code is acting in a particular way
  • Can’t find why an error or bug is happening
  • Don’t understand how code someone else wrote works

To get the most benefit from this, here are some suggestions:

  • Read and work through every line of code, without skipping any of them
  • Avoid making assumptions about what you think it should be doing. Perform calculations yourself to see what the code is really doing
  • You might be tempted to avoid using notes to keep track of frames in simple scripts. While you’re building up experience try to do the full exercise so you understand what is happening

After playing computer, you might want to ask yourself some questions as a final step. This helps you better understand the code you were working with. Here are some examples you might use:

  • Purpose: What was the purpose of this code? Does its behaviour match what you think it should be doing? Does the code do what you expected?
  • Problems: Are there any bugs or errors? Can you think of any problems this code might have if you changed the inputs? Does it need any input validation or error handling?
  • Improvements: After playing computer, could you make any changes to this code to improve it? Does it get stuck in lots of loops that could be made more efficient? Are there variables that could be better named?

Rules for working in Pairs

To get comfortable with playing computer, trying it out a few times in pairs may help. Use the following rules as a starting point.

  1. Only one person plays computer in a given frame at once, you can take turns
  2. You start at the top of a script or function and run down line by line.
  3. Evaluate what each line does and update the frame memory and output as you go, writing down on paper if that helps.
  4. When a function is called and you “step into” it, use a new piece of paper and swap in your pair.
  5. When you reach the end of a script or return from a function without any errors, your script has finished.

Advice for Volunteers

Playing computer helps trainees build up improved mental models about how code execution works. It practices skills that will be helpful when debugging or testing code.

To best help facilitate playing computer, here are some tips:

  • Make sure that trainees are going through each line of code and are not skipping ahead
  • Encourage trainees who are working in pairs to take turns. Switch between tracking the global frame and local frame(s) of different programs
  • Trainees may be tempted to guess what code is doing, or skip parts that seem obvious. Recommend that trainees do everything, otherwise they might miss key learning opportunities

Playing computer is available as a workshop you can use in-class.

Step-By-Step Example

Let’s work through an example function to see how we would play computer. We will try to figure out what this simple function is doing:

function multiply(x, y){
    let sum = 0;
    for(let i = 0; i < x; i++){
        sum = sum + y;
    }
    return sum;
}
let result = multiply(2, 3);
console.log(result);
  1. Execution always starts from the top of the code. We first see the declaration of a function multiply. We haven’t actually called it yet, it’s just being defined. Note it down on your global frame:
    Global Frame
    multiply(x, y)
  2. Because we are not running this function right now, we skip to the next line of code.
  3. The next line which does something is let result = multiply(2, 3). Here we are calling a function.
  4. Make sure the function multiply is either on a globally available function or is a built-in JavaScript function. It is on our global frame, so we can start executing it.
  5. We step into the function multiply using our arguments 2 and 3.
  6. As the values 2 and 3 have been passed as arguments, we need to initialise these variables on our “Local Frame”:
    Global FrameLocal Frame
    multiply(x, y)x: 2
    y: 3
  7. The first line inside the function initialises a variable sum. We add that to the function-scope local frame:
    Global FrameLocal Frame
    multiply(x, y)x: 2
    y: 3
    sum: 0

πŸ‘‰πŸΌ Next

  1. The next line sets up a for loop: for(let i = 0; i < x; i++). We need a new frame for this scope.
  2. Although it is written on one line, a few separate things are happening here. The first time we encounter the for loop, we initialise a variable i to 0.
    Global FrameLocal Frame (function)Local Frame (loop)
    multiply(x, y)x: 2i: 0
    y: 3
    sum: 0
  3. The next operation is a comparison, i < x. We look up the values for these variables in our local frame, and see this gives us 0 < 2. 0 is less than 2, so we proceed with the for loop.
  4. The line inside the for loop changes a variable using the value stored in another variable. We first have to look up sum (0) and y (3). We add them together, and then store this new value back into sum, updating our local frame:
    Global FrameLocal Frame (function)Local Frame (loop)
    multiply(x, y)x: 2i: 0
    y: 3
    sum: 3
  5. The final step of a for loop is to execute the third instruction: i++. This tells us to look up the value i (0) and to “increment”, or “add one”. We then update our local frame:
    Global FrameLocal Frame (function)Local Frame (loop)
    multiply(x, y)x: 2i: 1
    y: 3
    sum: 3

πŸ‘‰πŸΌ Next

  1. We now repeat the steps for our for loop, starting with the comparison, i < x. We look up the values for these variables in our memory note, and see this gives us 1 < 2. 1 is less than 2, so we proceed with the for loop.
    Global FrameLocal Frame (function)Local Frame (loop)
    multiply(x, y)x: 2i: 1
    y: 3
    sum: 3
  2. Again, we look up sum (3) and y (3). We add them together, and then store this new value back into sum, updating our local frame:
    Global FrameLocal Frame (function)Local Frame (loop)
    multiply(x, y)x: 2i: 1
    y: 3
    sum: 6
  3. And finally we increment again: i++. This tells us to look up the value i (0) and add one to it. We then update our local frame:
    Global FrameLocal Frame (function)Local Frame (loop)
    multiply(x, y)x: 2i: 2
    y: 3
    sum: 6
  4. This time when we check whether our for loop should run, we look up i and x so we can check 2 < 2. 2 is not less than 2, so we stop going through the for loop.
  5. As we are leaving the scope of the loop, we get rid of the loop’s frame.

πŸ‘‰πŸΌ Next

  1. Exiting the for loop, the next and final line of the function is to return the value in the variable sum. We look this up and return 6 from our function.
    Global FrameLocal Frame
    multiply(x, y)x: 2
    y: 3
    sum: 6
    Return: 6
  2. We now have to go back to where we were before we stepped into the function. You can now erase or throw away the function scope memory now as we have left it. According to where we left off, the line was let result = multiply(2, 3). This is creating a new variable in the global frame, and assigning it the returned value. We now need to update the global frame. Notice how none of the variables used within the function (the local frame) exist any more:
    Global Frame
    multiply(x, y)
    result: 6
  3. Lastly, we are writing this out to the console. On your console page, look up the value stored in the result variable (6), and print that out.
    Global Frameoutput
    multiply(x, y)6
    result: 6
  4. Congratulations! You’ve successfully played computer and worked through this small program. Some extra activities:
    • Ask yourself, did the program do what you expected it to? Given the inputs 2 and 3, it returned the value 6. That sounds like the function multiply is working as expected!
    • Are there any improvements that could be made to the code? What if the inputs were 50 and 60? How long would it take you to get to the correct answer of 3000? Is there a faster way of achieving the same thing?

Example running through buggy code

The above example shows how you would play computer with some code that works well. But what about code that isn’t working right, which has bugs in it? This is where playing computer can really be helpful. It will help you spot where bugs are, and understand and why it is causing a problem.

We will play computer using this example code:

function double(numbers) {
    return number*2;
}

let number = 2;
let otherNumber = 3;
console.log(double(number));
console.log(double(otherNumber));
  1. We’ll use the same method, tracking the global frame, the local frame, and the outputs.
  2. We read past the function definition, recording it in our global frame:
    Global Frame
    double(numbers)
  3. We now have a couple of variable declarations we need to add to the frame:
    Global Frame
    number:2
    otherNumber:3
    double(numbers)
  4. At the next line, we have two functions being called. console.log and double. We need to evaluate double first. We look it up in our global frame, see it is defined, so we start that function. We have to look up the number variable as this is going to be passed as an argument.
  5. We pass the value 2 to our double function.

πŸ‘‰πŸΌ Next

  1. We have now entered a new scope, so we need to build a new local frame. First record the value 2 that was passed as a parameter to the function.
    Global FrameLocal Frame
    number:2numbers:2
    otherNumber:3
    double(numbers)
  2. Moving to the next line of our function, we are immediately returning an expression. The expression number*2 needs to be evaluated first before we can return it.
  3. We need to look up the value used in the variable number. We look this up in the local frame.
  4. Notice number is not in the local frame!. We have to then see if it exists in our global frame instead.
  5. Now we can remember it has the value 2 in it. We evaluate the expression 2*2, which in JavaScript is the equivalent of 2 times 2, giving us an answer of 4
  6. Lastly, we return this value 4 back out of our function. We don’t need the local frame any more, and we can get rid of it, leaving us only the global frame.
  7. We pass the value 4 that was returned from the function to the final console.log(). We look up console.log() in our global frame. It is not there, but we know that it is a built-in function so we can still use it. This gives us our output, and we can move on:
    Global FrameOutput
    number:24
    otherNumber:3
    double(numbers)

πŸ‘‰πŸΌ Next

  1. The next line works in much the same way as the last one. From our global frame, we find the function double and the value we want to pass to it, otherNumber, which has the value 3.
  2. We have now entered a new scope, so we need to build up a new local frame, recording the value 3 that was passed as a parameter to the function.
    Global FrameLocal FrameOutput
    number:2numbers:34
    otherNumber:3
    double(numbers)
  3. Moving to the next line of our function, we are immediately returning an expression. The expression number*2 needs to be evaluated first before we can return it.
  4. We need to look up the value used in the variable number. We look this up in the local frame.
  5. Notice number is not in the local frame!. We have to then see if it exists in our global frame instead.
  6. Now we can remember it has the value 2 in it. We evaluate the expression 2*2, which in JavaScript is the equivalent of 2 times 2, giving us an answer of 4
  7. Lastly, we return this value 4 back out of our function. We don’t need the local frame any more, and we can get rid of it, leaving us only the global frame.
  8. We pass the value 4 that was returned from the function to the final console.log(). We look up console.log() in our global frame. It is not there, but we know that it is a built-in function so we can still use it. This gives us our output, and we can move on:
    Global FrameOutput
    number:24
    otherNumber:34
    double(numbers)

πŸ‘‰πŸΌ Next

  1. We’ve now finished playing computer with our script. Let’s review where we ended.
    Global FrameOutput
    number:24
    otherNumber:34
    double(numbers)
  2. First, did the JavaScript behave as expected? Our function was called double, so we expect it to multiply a number by 2. Is this what happened?
    • We gave it an input 2, and it printed the value 4. That’s correct.
    • We gave it an input 3, and it printed the value 4. If we double 3 we should get the answer 6, so something has gone wrong.
  3. Our goal with playing computer in buggy code is to identify the source of bugs. We do this by doing exactly what the computer does. Let’s think about the places where a bug might have happened.
    • Remember when we tried to look up the variable number and found it did not exist in the local frame? That might be a cause of the problem.
    • We could investigate further by changing some of the values.
    • What happens if we changed the number variable to 3? It would then always give us the output 6. That also does not look right.
  4. How could we fix the problems?
    • We saw we never used the numbers parameter. This is probably where the bug is.
    • First, the parameter numbers has a similar name to other variables like number. A more obvious name like inputNumber makes more sense and prevents easy mistakes.
    • Then, we should fix the first variable in the expression number*2 so it does not use the global any more: inputNumber*2

If you change this and try playing computer again, you’ll see that it works as expected, logging the values 4 and 6 to the output. Much better!