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.
- Only one person plays computer in a given frame at once, you can take turns
- You start at the top of a script or function and run down line by line.
- Evaluate what each line does and update the frame memory and output as you go, writing down on paper if that helps.
- When a function is called and you “step into” it, use a new piece of paper and swap in your pair.
- 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);
- 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) - Because we are not running this function right now, we skip to the next line of code.
- The next line which does something is
let result = multiply(2, 3). Here we are calling a function. - Make sure the function
multiplyis 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. - We step into the function
multiplyusing our arguments2and3. - As the values 2 and 3 have been passed as arguments, we need to initialise these variables on our “Local Frame”:
Global Frame Local Frame multiply(x, y) x: 2 y: 3 - The first line inside the function initialises a variable
sum. We add that to the function-scope local frame:Global Frame Local Frame multiply(x, y) x: 2 y: 3 sum: 0
ππΌ Next
- The next line sets up a for loop:
for(let i = 0; i < x; i++). We need a new frame for this scope. - 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
ito 0.Global Frame Local Frame (function) Local Frame (loop) multiply(x, y) x: 2 i: 0 y: 3 sum: 0 - The next operation is a comparison,
i < x. We look up the values for these variables in our local frame, and see this gives us0 < 2. 0 is less than 2, so we proceed with the for loop. - The line inside the for loop changes a variable using the value stored in another variable. We first have to look up
sum(0) andy(3). We add them together, and then store this new value back intosum, updating our local frame:Global Frame Local Frame (function) Local Frame (loop) multiply(x, y) x: 2 i: 0 y: 3 sum: 3 - The final step of a for loop is to execute the third instruction:
i++. This tells us to look up the valuei(0) and to “increment”, or “add one”. We then update our local frame:Global Frame Local Frame (function) Local Frame (loop) multiply(x, y) x: 2 i: 1 y: 3 sum: 3
ππΌ Next
- 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 us1 < 2. 1 is less than 2, so we proceed with the for loop.Global Frame Local Frame (function) Local Frame (loop) multiply(x, y) x: 2 i: 1 y: 3 sum: 3 - Again, we look up
sum(3) andy(3). We add them together, and then store this new value back intosum, updating our local frame:Global Frame Local Frame (function) Local Frame (loop) multiply(x, y) x: 2 i: 1 y: 3 sum: 6 - And finally we increment again:
i++. This tells us to look up the valuei(0) and add one to it. We then update our local frame:Global Frame Local Frame (function) Local Frame (loop) multiply(x, y) x: 2 i: 2 y: 3 sum: 6 - This time when we check whether our for loop should run, we look up
iandxso we can check2 < 2. 2 is not less than 2, so we stop going through the for loop. - As we are leaving the scope of the loop, we get rid of the loop’s frame.
ππΌ Next
- 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 Frame Local Frame multiply(x, y) x: 2 y: 3 sum: 6 Return: 6 - 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 - Lastly, we are writing this out to the console. On your console page, look up the value stored in the
resultvariable (6), and print that out.Global Frame output multiply(x, y) 6 result: 6 - 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
multiplyis 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?
- 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
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));
- We’ll use the same method, tracking the global frame, the local frame, and the outputs.
- We read past the function definition, recording it in our global frame:
Global Frame double(numbers) - We now have a couple of variable declarations we need to add to the frame:
Global Frame number:2 otherNumber:3 double(numbers) - At the next line, we have two functions being called.
console.loganddouble. We need to evaluatedoublefirst. 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. - We pass the value 2 to our
doublefunction.
ππΌ Next
- 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 Frame Local Frame number:2 numbers:2 otherNumber:3 double(numbers) - Moving to the next line of our function, we are immediately returning an expression. The expression
number*2needs to be evaluated first before we can return it. - We need to look up the value used in the variable
number. We look this up in the local frame. - Notice
numberis not in the local frame!. We have to then see if it exists in our global frame instead. - 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 - 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.
- We pass the value 4 that was returned from the function to the final
console.log(). We look upconsole.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 Frame Output number:2 4 otherNumber:3 double(numbers)
ππΌ Next
- The next line works in much the same way as the last one. From our global frame, we find the function
doubleand the value we want to pass to it,otherNumber, which has the value 3. - 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 Frame Local Frame Output number:2 numbers:3 4 otherNumber:3 double(numbers) - Moving to the next line of our function, we are immediately returning an expression. The expression
number*2needs to be evaluated first before we can return it. - We need to look up the value used in the variable
number. We look this up in the local frame. - Notice
numberis not in the local frame!. We have to then see if it exists in our global frame instead. - 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 - 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.
- We pass the value 4 that was returned from the function to the final
console.log(). We look upconsole.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 Frame Output number:2 4 otherNumber:3 4 double(numbers)
ππΌ Next
- We’ve now finished playing computer with our script. Let’s review where we ended.
Global Frame Output number:2 4 otherNumber:3 4 double(numbers) - 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.
- 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
numberand 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
numbervariable to 3? It would then always give us the output 6. That also does not look right.
- Remember when we tried to look up the variable
- How could we fix the problems?
- We saw we never used the
numbersparameter. This is probably where the bug is. - First, the parameter
numbershas a similar name to other variables likenumber. A more obvious name likeinputNumbermakes more sense and prevents easy mistakes. - Then, we should fix the first variable in the expression
number*2so it does not use the global any more:inputNumber*2
- We saw we never used the
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!