4

Computational Thinking Gamified and Unplugged

In this chapter, we’ll look at some of the more discrete elements of computer programs. This information includes coding concepts, such as variables; control structures; functions; and computational thinking concepts, such as algorithms, modeling, and abstraction.

A common lament among K–8 teachers now tasked with teaching computer science concepts in addition to their other subjects is that they don’t know how to teach these seemingly technical concepts. But as we’ll see, they’re concepts we’re already using in our daily lives. Recognizing that fact turns each concept into a tool we can integrate with existing classroom topics to enhance our teaching practice.

Throughout this chapter, you’ll learn about ways to teach these concepts in class without the use of computers. Computers are powerful tools for learning, but they’re also isolating: students quietly turn away from each other while they’re plugged into screens that put them through rote learning exercises. There is a time and place for such deep, sustained focus. But here you’ll learn about group activities, games, and creative projects that will exercise your students’ computational thinking muscles without screen time.

Variables

Let’s begin with the most elementary programming unit, the variable, a label—preferably one intuitive to other humans—that identifies a piece of data. As we learned in the previous chapter, in low-level programming languages, variables are coupled to specific storage locations in the computer. In high-level programming languages, we don’t need to worry about memory allocation; instead, we only need to understand a variable as a name identifier given to a piece of information known as its value. Variables can point to different kinds of data as values, such as integers, strings, Booleans, and lists; because variables don’t actually hold data, but rather point to a location in the computer’s memory, we can reuse a single variable throughout a software application.

A popular way to explain variables to students with a real-world example is to have them think of each variable in a program as a container in a series of named containers, such as labeled folders in a filing cabinet. To see this concept in action, try taking your students to the library. The shelves are like memory in a computer, and each one can only hold so many books or so much data. The labels are like the memory addresses in the computer. What is the book variable value for the address 343.73099 Les? 793.93 Coo? With a little forethought, you can construct a fun adventure using the Dewey Decimal System classifications, pretending they’re memory addresses in a huge computer that is the library.

As their name suggests, a key feature of variables is that their contents can vary. For example, a string variable named currentPresidentOfTheUnitedStates might at one time contain the value George Washington and later contain the value Abraham Lincoln. An integer variable named currentYear might increment by one each time the date equals the first second of January 1. A Boolean variable named stopLightGreen will switch between true and false as the light changes.

Variables can also point to lists of values. Many programming languages allow you to store an array, or list of items, in a variable. Think of an array as a list of variables where each variable is identified by its location in the array. For example, the array of foodGroups[] might contain ['vegetables', 'fruits', 'grains', 'meats', 'dairy']. The first element in an array is identified with a 0 instead of a 1—a fact that often trips up novice programmers and leads to many software bugs. So when we reference foodGroups[0], we get vegetables, and foodGroups[4] would return dairy. We can also have arrays of arrays, making them more complex. Table 4-1 shows a possible two-dimensional array of this food groups example.

Table 4-1: Two-Dimensional Food Groups Array

Vegetables

Fruits

Grains

Meats

Dairy

Celery

Apples

Barley

Beef

Cheese

Peas

Bananas

Oats

Chicken

Milk

Spinach

Oranges

Wheat

Pork

Yogurt

Not including the table headers as part of this array of arrays, foodGroups[0][0] would return Celery, foodGroups[2][1] Oats, and foodGroups[4][2] Yogurt. Returning to the library, you can use the bookshelves to explain arrays and arrays of arrays. For example, the reference shelves might include labels like ['001-372', '373–803', '803–979'], and each of these labels references an array of books within these addresses.

Changing a single variable can have dramatic effects. A single character, or char data type variable, labeled currentVowel used to represent the second letter in the string 'b_t' could generate words with very different meanings, such as 'bat', 'bet', 'bit', 'bot', or 'but'. Here’s another example: as the floating-point variable representing parts per million (PPM) of carbon dioxide (CO2) molecules in the atmosphere—named cO2PPM—increases, solar radiation is captured as thermal radiation on planet Earth, and the average planetary temperature increases. When we model these subjects into programming code, we find that changing a single variable in a computational process can produce wildly different outputs.

As you might have realized, and as your students will hopefully recognize, we’re already working with variables in our day-to-day academic lives. Identifying the variables in a range of academic contexts can formalize how we think about them. In the next section, we’ll explore how to formally manipulate variables using logical controls.

Control Structures

Variables are the most basic elements of our computational world. Once we define these elements, we can then start making decisions based on their values through control structures. Control structures are conditional statements that evaluate variables and decide what to do based on the values of those variables. There are also control structures that iteratively perform computational tasks, looping over conditional statements and evaluations.

As noted in “Programming is Communication” on page 19 communicating with a computer is like communicating with a very literal person. The control structures explained in the following subsections are formal, whereas natural language is messy. If we hold students’ use of natural language to formal standards, we can cultivate in them a more precise use of language.

Boolean Conditions

Programming control structures rely heavily on Boolean conditions to direct their flow. These statements evaluate to true or false and chain together with any number of and and or operators. When Boolean conditions are connected with and, all the conditions must evaluate to true to satisfy the if statement. For example, if an organism eats other organisms and it has a spinal cord and it feeds its young with milk and it lays eggs and it has a beak, then it’s a platypus. When Boolean conditions are connected with or, any condition evaluating to true will satisfy the if statement. For example, if an organism nurses its young or it possesses a neocortex or it has fur or it has three middle ear bones, then it’s a mammal.

if-then and else

A clerk evaluating a voter registration form in the United States must consider two variables: the applicant’s birth date and citizenship. If the birth date is equal to or greater than today’s date minus 18 years and they’re a US citizen, then the applicant will be registered to vote. This is an example of an if-then control statement, which works like this: if the Boolean conditions are true, then take this action. Likewise, there is also the if-then-else statement: if the conditions are true, then take this action; else take a different action. Although unstated in the voter registration example, the else flow path would be to deny the application, and the applicant would remain unregistered to vote.

Between the if and else statements can come any number of else if conditions. Each evaluates a condition before moving on to the else statement. For example, if the birthDate check failed but the citizenship check passed, we might follow the if with else if USCitizen == true then deny the application and ask the applicant to reapply later when they’re old enough. Listing 4-3 shows an example of checking these conditions in JavaScript, a programming language for adding dynamic features to websites (you’ll learn to execute these features from your web browser in the next chapter).

var birthDate = new Date("01/01/2000");
var USCitizen = true;
var today = new Date();
var diffDays = parseInt(today - birthDate); 
if (USCitizen == true && diffDays > 567648000000)
  console.log("You are registered to vote!");
else if (USCitizen == true)
  console.log("Come back later.");
else
  console.log("You are not eligible to vote.")

Listing 4-3: Checking whether an applicant can register to vote

In this very basic example that doesn’t account for leap years, time zones, or daylight saving time, the birthDate is set to the first day of the year 2000 and USCitizen is set to true. We then calculate the difference between the birthDate and today into diffDays, and check whether the user is older than 567,648,000,000 milliseconds in the first if statement, which is the number of milliseconds in 18 years. If they’re not, we check whether USCitizen is true and tell them to come back later in the else if statement; finally, we tell them they’re ineligible if neither old enough or a US citizen in the else statement.

Once students understand Boolean conditions and if-then control structures in programming flow, you can add the next powerful level of control: iteration through loops.

Loops

One of the central concepts in computer science is iteration, where a set of programming instructions are executed repeatedly for a specified number of times or until a condition is met. We iterate over various things in our daily lives often without realizing it. We iterate over the days of the week to keep track of our schedules. We iterate over our future bills and paychecks to manage our finances. In programming, we emulate these iterative processes in controls called loops.

In a while loop, we iterate over a process until a condition is met. For example, we might say that while a student hasn’t completed their in-class assignment, they don’t get screen time. In this case, we check for the condition (have they not completed their in-class assignment) and then perform the action (deny screen time). In a do-while loop, we perform an action and then check for the condition; for example, do add one milligram to the balance scale while the object we’re measuring is on the lower plate. In this situation, we first perform the action and then check for the condition before continuing, because we want to see how our action affects the condition.

The while loop performs an action until a condition is met. But sometimes, we need to iterate a specific number of times. In that case, we use a for loop. In some programming languages, a for loop takes three arguments, or inputs: a starting value, a condition, and an operation to perform on the starting value on each iteration. We can use these three arguments to iterate a set number of times. The second line of code in Listing 4-4 shows what this looks like in JavaScript.

var x = 0; // set x equal to 0
for (var i=0; i<10; i=i+1) {
   x = x + 1; // add one to x
}
// x will now equal 10.
console.log(x);

Listing 4-4: A for loop counting to 10

Note the line that reads for (var i=0; i<10; i=i+1). The var i=0 portion sets the variable i equal to 0. The second part, i<10, has the loop execute while i is less than 10. Finally, the i=i+1 part instructs the computer to add one to i each iteration so this loop executes 10 times. Between the opening and closing curly brackets are the instructions we want to execute. In this case, we’re adding one to x each iteration so x will equal 10 when the loop finishes processing.

Alternatively, the foreach control will iterate over each element in a list or array. As an example, foreach color in ["Red","Orange","Yellow","Green","Blue","Indigo","Violet"], append the first letter to a string. This statement will iterate over the sequence and output the mnemonic “ROYGBIV.” Listing 4-5 shows executable JavaScript code for this operation.

var mnem = ""; // set to an empty string
//define the color array
var colors = ["Red","Orange","Yellow","Green","Blue","Indigo","Violet"];
colors.forEach(function(color) {
    mnem += color.charAt(0);
});
console.log(mnem);

Listing 4-5: A foreach loop that constructs “ROYGBIV”

Loops can lead to interesting errors. Consider a sheet of paper that reads “What’s an infinite loop? (Answer on back)” on one side and “What’s an infinite loop? (Answer on back)” on the other. A human gets the joke instantly, but a computer will flip the sheet of paper over and over indefinitely. In programming, we must make sure our loops end at some point. We must make certain our while condition will be met and that the list in our foreach doesn’t grow as we iterate over it. For example, if we set x equal to 1, and while x doesn’t equal 4, add two to x, then x will iterate from one to three to five without ever satisfying the condition—meaning it will run forever. If we have a foreach loop that runs foreach egg in our basket, and in each iteration we add one egg to the basket, the eggs in our basket will grow indefinitely and the loop will never terminate, as in Listing 4-6.

var basket = ["egg"];
for (var i = 0; i < basket.length; i++) {
  basket.push("egg");
}
console.log(basket.length);

Listing 4-6: A foreach loop that will never terminate

In this code is an array basket that contains a single "egg" string. The for loop starts at zero and iterates as long as i is less than basket.length, which is a count of the number of values stored in basket. With one string stored, this value is 1. Inside the loop, the basket.push() function adds another "egg" string variable to the array, making the length 2, 3, 4, and so on, meaning i will always be less than basket.length. This code will execute indefinitely or at least until the computer runs out of memory locations in which to store all these "egg" strings and crashes.

With variables, conditional statements, and loops now under our belt, we can start combining these elements into more complex, formal processes.

Algorithms and Functions

Familiar with the concept of variables, your students can start identifying the basic units of information they want to work with. Armed with programming controls, students can direct the flows of logic based on those variables. Now they’re ready to begin learning how to intentionally combine these concepts into computational constructs that will achieve their goals.

Algorithms

As we noted in Chapter 1, an algorithm is a set of rules specifying how to solve a problem. The word “algorithm” can be intimidating. It sounds highly technical but is actually a simple concept. Like with variables and logical controls, your students are already working with algorithms when they follow any kind of instructions. For example, following this algorithm will produce the first line of stitches needed to crochet a cute Yoda doll: Rnd 1: ch2, work 6 sc into first ch—6st

This instruction is cryptic, just like a line from a computer program if you’re not familiar with the notation. It instructs the crafter to execute two chain stitches and then work six double crochets into it.

Following the algorithm in Figure 4-3 will produce the distinctive opening notes of Beethoven’s Symphony No. 5.

Figure 4-3: The opening of Beethoven’s Symphony No. 5

If you know how to read music, you might hear the notes in your head when you read this notation. But unless you’re a true virtuoso, you’d have a hard time playing these notes on every kind of musical instrument. The algorithms in the Yoda crochet instructions and the opening notes of Beethoven's Symphony No. 5 require certain levels of expertise and knowledge to understand and implement. Without understanding crochet notation or being able to read sheet music, you couldn’t understand these commands, let alone execute them. Similarly, your students’ lives are filled with algorithms that require some expertise to execute. The schedules they follow, the tests they take, and the rules of conduct they emulate are all algorithms for which they should find pride in being able to follow. Hopefully, appreciating the many complex algorithms your students are already successfully navigating in their daily lives will demystify the challenge of constructing new algorithms.

Simple algorithms can provide us with powerful solutions. We can solve mazes that have all their walls connected to an outer boundary with a simple algorithm of following either the right or left wall. If your students can memorize 10 short algorithms then with a little practice they can solve a Rubik’s Cube in less than five minutes. Students familiar with various sorting algorithms and their relative efficiencies will have an easier time organizing index cards, file folders, books, and other collections.

Algorithms are powerful tools for documenting solutions to complex problems so that others may solve them, and in computing they become even more powerful when we can automate and abstract them away into functions that are more convenient and easier to work with.

Functions

As we learned in the previous chapter, a computer system is filled with computational solutions that have been layered on top of one another over decades, like geological strata over eons of time. If we had to expose ourselves to all these algorithms every time we wanted to write a program, we would be overwhelmed. Even working in a single program, if we had to read past the detailed algorithms needed to generate random numbers, round decimals, concatenate strings, or sort arrays, we would spend all our time sifting through code rather than writing it.

For these reasons, in software development we use functions to hold algorithms. A function encapsulates the algorithm. In other words, it conceptually wraps up many lines of instructions into a process you can simply invoke by name—like clicking an icon that represents a program. For example, the algorithm to generate pseudorandom numbers might be 5 to 30 lines of mathematically dense code, but in a function, we only need to call random(). Encapsulating the code also makes it reusable. We can call random()repeatedly in our code, and if we find a bug in the function or a way to enhance it, we need only edit the one function to improve it everywhere.

The function also abstracts away the complexity of the algorithm. We don’t need to know what’s inside the function. We only need to know what arguments to give it and what outputs we should expect. If I can set a start and end range for my random number as random(1,20), I can expect to get the same results as a 20-sided dice roll without needing to know anything about how that number was produced. The function is a black box that takes inputs and gives us outputs.

Once your students are comfortable with algorithms and functions, they’re ready to take everything we’ve covered in this chapter and blend it together in a complex ecosystem of interacting variables, conditional logic, and encapsulated functions, also known as games!

Bringing It All Together with Game-Based Learning

Many activities in this chapter introduced computational concepts to your students. Exercises like the Parts of Speech Games, Gamebooks, the PB&J Algorithm Game, and the Guess the Function Game are all meant to be playful ways of engaging complex subjects. These are also examples of game-based learning, a kind of instruction growing in popularity that combines naturally engaging play with serious instruction to encourage students to more fully explore the subject matter.

It’s important to note that game-based learning is different from gamified learning, which uses points, badges, and rewards in the classroom to motivate students the same way an addictive video game keeps players clicking buttons for little rewards. The former is intended to facilitate deep engagement with a complex subject; the latter is associated with Pavlovian conditioning. An example of gamified learning would be giving a student a badge for doing well on a timed test. An example of game-based learning would be having students play a game about the Electoral College where they work as two teams competing to get 538 votes across a map of the United States. The former rewards students for exhibiting a desired behavior, whereas the latter immerses them in the subject matter for a deeper understanding.

Games are valuable learning tools because of the way they engage their players. Game designers often refer to the magic circle, the state of mind a player enters when playing a game where they forget about the outside world and are totally immersed in the game’s microworld. In education and in programming, there is a related psychological concept of flow where the student or programmer forgets themselves and completely engages the task at hand. The magic circle is a safe space where students can play and experiment within an artificial world without consequences. It’s also a space that promotes flow.

Games are also especially effective in engaging students in computational thinking because games are computational artifacts. Games are filled with variables. They have conditional logic in player choices. Each game round is a loop of players executing their turns. The rule book is an algorithm where we conceptualize many of the rules into functions. Board games are like software but run on human brainpower.

Consider the following example. Figure 4-6 shows a 7 × 9 version of the ancient Indian board game Snakes and Ladders, also known as Chutes and Ladders in the West. In this game, players take turns rolling a six-sided die and moving that many squares. If they land on a square at the bottom of ladder, they slide up to the top of that ladder. If they land on a square with the head of a snake, they slide down to the square at the end of the snake’s tail.

Because the game is simple and we don’t need to factor in player choices, it’s very easy to model in programming code. Listing 4-7 shows all the JavaScript code needed to emulate a game of Snakes and Ladders.

Figure 4-6: Snakes and Ladders board game
//An array of arrays naming our players and their scores.
var players = [
   ["Ada",0]
   ,["Alan",0]
   ,["Claude",0]
   ,["Grace",0]
];
var winner = "nobody";
//Keep playing until we have a winnner.
while (winner == "nobody") {
  //Loop through Ada, Alan, Claude, and Grace
  players.forEach(function(player) {
    //Role a 6-sided die.
    var spin = Math.floor(Math.random() * 6) + 1;
    var value = player[1] + spin;
    console.log(player[0] + " " + player[1] + " -> " + value);

    //Check for Snakes and Ladders
    if (value == 3) value = 17;
    else if (value == 20) value = 33;
    else if (value == 22) value = 36;
    else if (value == 25) value = 11;
    else if (value == 32) value = 16;
    else if (value == 38) value = 46;
    else if (value == 47) value = 61;
    else if (value == 48) value = 34;
    else if (value == 58) value = 45;

    //Assign new value to player
    player[1] = value;
    console.log(player[0] + " " + player[1]);     

    //Check for winner
    if (player[1] >= 63 && winner == "nobody") {
        winner = player[0]; 
    }
  });
}
//Tell us who won.
console.log("The winner is " + winner + "!");

Listing 4-7: Snakes and Ladders code

This code example illustrates how the logical structure and algorithms of games can translate to programming logic. The players and their scores are stored in an array data type. The game executes as long as there is no winner. Each player takes a turn where a random number is assigned to their current score, which is then subjected to conditional logic that checks for snakes and ladders. Finally, once a player’s score is equal to or greater than 63, we set the winner variable, and the program tells us the winner.

The act of translating Snakes and Ladders into a formal algorithm reveals insights into the game. Seeing the game translated into just 35 lines of code illustrates its simplicity. The fact that we didn’t need to code any player input emphasizes the lack of player agency: the game outcome is entirely out of the players’ hands. In fact, this is by design. Snakes and Ladders was invented to teach children the concept of karma and accepting one’s fate. In the Western world, we use Chutes and Ladders as an early childhood exercise in following rules and learning basic arithmetic. By writing the game as code, we’re extending the game’s use in the classroom to older students and giving them a fresh look at a nostalgic activity. The code is an isomorph of the board game, a concept touched on in Chapter 2 where the same logic is presented in a different way.

Children tend to get bored with Chutes and Ladders quickly. They rapidly recognize the lack of agency the game provides and how they have no control over the outcome. The game becomes a tedious chore where players execute the algorithm again and again waiting to see the random outcome. That makes this coding exercise also perfect for illustrating the power of automation. Once translated into a syntactically correct program, a 10- to 20-minute board game would execute in milliseconds. In a sense, automating the game makes it obsolete, but there is also a new opportunity here. Students can now review the gameplay algorithm and find ways to make it more fun.

Refactor Your Gameplay

Playing games and analyzing them as computational artifacts helps us understand them more deeply. If you can explain the game to our dense, overly literal friend the computer, then you know it completely. Once you know it completely, you can take ownership of it, enhance it, and make it your own.

The same is true of software code. When a programmer encounters new code, they must take the time to get familiar with its organization and flow. After taking ownership of a codebase, a programmer might take time to refactor that code, which is the art—not science—of reorganizing the code to potentially make it more reusable, efficient, intuitive, or elegant.

Board games are great laboratories to experiment with refactoring. Snakes and Ladders loses its engagement as the players catch on to the fact that the game’s outcome is out of their hands. So let’s change that. Instead of having players roll a die to move each turn, they each roll 10 dice at the start of the game. Then they “program” their token with these values, lining up the dice in a stack of moves to most optimally navigate the snakes and ladders. The player who can reach the end in the fewest steps is the winner. Thus, with a minor rule change, we’ve transformed a game with a purely random output to one with much deeper strategy requiring computational thinking.

When questioning and modifying the rules of existing games, challenge your students to identify the variables, logical flows, and algorithms in the rules and game mechanics. When isolated and articulated, these elements become parts they can use to modify other games or even assemble their own. This is just like computer programming. Coders always start out working with artifacts others have created. They might include a high-level coding language, software framework, or existing application, so the coder must learn about the environment in which they’re developing, adapt to it, and make it their own. Once your students are comfortable experimenting with the rules of existing games, it’s time to see how they do crafting games from scratch.

Modeling with Games

Many popular board games are effective for the way their mechanics model real-world elements. From schooling and professional choices, to mortgages, raising children, and retirement, Hasbro’s Game of Life models the economic life span of an average US citizen. The cooperative game Pandemic models global disease outbreaks for specialists to mitigate. The board game Evolution models how species adapt to environmental changes through natural selection. With the proper variables and algorithms, games can serve as valuable computational models for students to play with.

Having students model topics from class in games provides an avenue for project-based learning. A student modeling the life of a piece of legislation in a game, where each player represents a branch of government, will have a more precise and intimate understanding of the process than a student who only reads the algorithm in a textbook. A student tracing the energy stored in gasoline back to its ancient origin in the sun through a conceptual maze of electrons, photons, and nuclear fusions in a game they design will have detailed knowledge beyond their peers who solely followed a diagram in a book.

Likewise, modeling aspects of the real world in a game, and highlighting the ways in which that modeling is computational thinking, will give students a strong grasp of computer science concepts. Seeing the board and playing pieces as tracking variables, the rules as the software algorithm, and the players as testing the execution of that algorithm as they process it in play will all solidify these concepts in the student’s head. The skills learned in modeling for games are directly transferable to computational tasks in computer science and will prepare students for working with the rules in computers and software development environments.

Summary

In this chapter, you learned about the basic logical elements that make up an algorithm. We covered the variables our algorithms process and their inputs and outputs. We covered the control structures that direct the flow of our algorithms, and we covered the functions that encapsulate complex algorithms and make them easier to work with.

Throughout this chapter, we explored sample exercises to engage in with each of these concepts. None of these exercises required us to get anywhere near a computer, and many focused on how we’re already using these concepts in our daily lives. Because we’re already using the elements of computational thinking in our routines, we only need to draw attention to these elements to make our students aware.

In board games, we explored a medium that brings together variables, controls, algorithms, and modeling into an experience where students can feel safe to experiment with topics in class while also engaging with them more formally. As students refactor the existing rules of games, they make these games their own—just as a computer programmer uses existing code and modifies it to meet their requirements.

In the next chapter, we’ll learn about a tool any student with a web browser can use to start instantly exploring and experimenting with existing code. As with board games, they’ll learn about web applications by editing the logic of existing applications in a safe and experimental environment.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.85.33