Simon says

When I first joined Google, I learned that a common interview question was “How would you code the game, Simon?” For those aren’t familiar with this 80s masterpiece of technological entertainment: Simon is a game in which you are shown a sequence of one of four colors. The game starts with a sequence of one color, and your job is to repeat the sequence. If you get the sequence right, the game adds a new color to the sequence. The game continues until you get the sequence wrong. I don’t know if reading this description makes the game seem fun, but it was fun.

At the time I joined Google, I had little experience writing code. The idea that I could have been asked that question terrified me. (I was not asked that question. The story of how I dodged the technical portion of my interview is a story for another time, however.) If I had been asked that question, I don’t know how I would have answered. So, you can imagine my delight when I came across a JavaScript code example that built a basic version of Simon. The code doesn’t go into a ton of detail about what it’s doing, so I thought it would be fun to try to document my interpretation here.

Note that I don’t have the experience to judge if this example is coded well. So I’ll just focus on what the code does, and leave discussions on better implementations to wiser heads than mine.

Before we get into the code, we should start by understanding how Simon actually works. I know that I explained the gameplay, but it’s helpful to think about what the game actually needs to do. I’m learning that how a user (in this case, the player) experiences an application doesn’t necessarily reflect what the application is doing. (I wonder how much of any application is smoke and mirrors, but again, that’s a topic for another time.) As I mentioned, in a Simon game you need to repeat a sequence of colors. The number of colors in each sequence increases by one each time you repeat a sequence correctly. (As long as the sequence is greater than 0, of course.) The next color in the sequence is randomly generated, but it’s limited to one of 4 colors.

With that in mind, we can start to build our application.

Accessing HTML elements

In this topic, I’m not going to dive deeply into HTML and CSS, as I’d rather focus on the JavaScript. You can look at the source code for the application if you’re interested in looking at those files further. But I have learned that, when I’m working with vanilla JavaScript–that is, JavaScript that isn’t used within a framework like React–it’s always good to get programmatic access to any of the elements I’m going to need. Here’s what that looks like for this application:

const scoreEl = document.getElementById("score");<br>const colorParts = document.querySelectorAll(".colors");<br>const containerEl = document.querySelector(".container");<br>const startBtn = document.querySelector("#start-btn");
const resultEl = document.querySelector("#score-result");
const wrapperEl = document.querySelector(".wrapper");

Each of these variables corresponds either to a specific element, like scoreEl, or a group of elements, like colorParts. I admit that I do wonder why some of these variables use document.querySelector to get an element by its class name, while others use document.getElementById to get an element by its id value. I would think that the latter method is better, but I’m still learning.

Setting up game colors

Now that we have variables representing the HTML elements in the game, the next task we can complete is to create some sort of collection that specifies which colors are available in the game. This code uses an object for this purpose:

const colorObj = {
    color1: { current: "#006400", new: "#00ff00" },
    color2: { current: "#800000", new: "#ff0000" },
    color3: { current: "#0000b8", new: "#0000ff" },
    color4: { current: "#808000", new: "#ffff00" },
};

This object has four properties, one for each color. Each property has another object as a value, and that object has two additional properties: a string representing the current color, and a string representing a new color. Later on, the code uses these properties to visual indicate to the player the colors in the sequence, or to indicate that the player clicked on a color.

Controlling gameflow with a flag

But we get into more of the game logic, I want to call out one thing that is the code that I hadn’t thought of when I was thinking through this code on my own: it creates a flag. Remember that, in a Simon game, the game displays a sequence of colors. The player then clicks on colors in an attemp to match the sequence. But it would get very messy if the player started clicking on colors while the game logic was creating or displaying a new sequence. To avoid that scenario, the application uses a flag–basically, a variable that it uses to control the flow of the game:

let isPathGenerating = false;

This flag will be useful in a little bit.

Creating a delay function

In addition to this flag, we need a way to control the speed at which the game displays the colors. Otherwise, the game will cycle through the sequence so fast that no human could hope to keep up. In this code, this control is implemented with a delay function.

const delay = async (time) => {
 return await new Promise((resolve) => setTimeout(resolve, time));
}; 

Basically, this function takes a parameter, time, and uses that value to delay the game by that amount of time. Now we have a way of controlling how long of a delay we want at different points in the application.

Keeping score and tracking clicks

One last thing before we get too far ahead of ourselves: the application also includes two additional variables: score, which is used to track the player’s score, and clickCount which is used to track how many times the player has clicked one of the colors.

let score = 0;
let clickCount = 0;

Nothing too fancy here, but I’m talking about them now because, like the isPathGenerating flag, they become important later.

Getting a random color

Okay! With the code framework in place, we can talk about how code stores the color sequence generated during a game. To start, the code creates an empty array:

let randomColors = [];

Notice that the code uses JavaScript’s let keyword, so we can change the contents of this array as the game progresses.

Moving on, the application adds a function to select a random color from the colorObj defined earlier.

const getRandomColor = (colorsObj) => {
 const colorKeys = Object.keys(colorsObj);
 return colorKeys[Math.floor(Math.random() * colorKeys.length)];
};

When the application calls this function, it passes in the colorObj object. The function then gets an array of keys from that object–in this case, the name of the object: color1, color2, and so on. Then the function uses two Math functions to randomly select a color object. With this code in place, the application now has a way to get a random color.

That’s great.

Generating a color sequence

Now that we have the ability to generate a random color, we can create a function that generates a color sequence. In this code, that capability is a function, generateRandomPath:

const generateRandomPath = async() => {
 randomColors.push(getRandomColor(colorObj));
 score = randomColors.length;
 isPathGenerating = true;
 await showPath(randomColors);
};

This function is an asynchronous function that performs a few tasks:

  1. It calls getRandomColor and adds that new color to our randomColors array.
  2. It updates the game score, which will always e the same value as the length of the randomColors array.
  3. It sets the isPathGenerating flag to true. That will be important later.
  4. It uses the await keyword to wait until the showPath function finsihes. We’ll define that function next.

Displaying the color sequence to the player

The function, generateRandomPath, asynchronously waits for a new function, showPath to complete. That function is responsible for showing the latest color sequence to the player.

const showPath = async(colors) => {
 scoreEl.innerText = score;<br> for (let color of colors) {
  const currentColor = document.querySelector(`.${color}`);
    await delay(500);
  currentColor.style.backgroundColor = colorObj[color].new;
  await delay(600);
  currentColor.style.backgroundColor = colorObj[color].current;
  await delay(600);
 }
 isPathGenerating = false;
}

Again, we have another async function. This time, the function takes an array of colors (our randomColors array, in fact). Then, the code loops through the array. For each color in the array, the code:

  1. Creates a variable, currentColor, and sets it to the appropriate HTML element.
  2. Waits 500 milliseconds.
  3. Sets the background color for the element to the colors new property. This change provides a visual cue to the player of what the next color in the sequence is.
  4. Waits again.
  5. Sets the color of the element back.
  6. Waits just a little more.

After the loop completes, the function sets the isPathGenerating flag to false.

With the generateRandomPath and showPath functions complete, we can turn our focus to handling the user inputs. Oh, and we’ll finally put that isPathGenerating flag to work.

Creating an event handler

We want each of the 4 colors in the game to handle click events the same way, so we should create a function. This time, let’s walk through the function bit by bit.

To start, we create (you guessed it) an async function, which we’ll call handleColorClick.

const handleColorClick = async (e) => {

We don’t want to handle clicks if the game is generating a new color sequence. So we’ll use our isPathGenerating flag:

if (isPathGenerating) {
  return false;
}

Now, if the application is generating a sequence, the game will ignore clicks (on those colors, of course).

It’s this next bt of code that I find fascinating–even if it’s pretty straightforward once you see it. It’s a conditional statement:

If (e.target.classList.contains(randomColors[clickCount])) {
   // ...more to come...
} else {
  endGame();
}

To understand this code, it helped me to think about what information was available. First, I know what the correct color is in the sequence. I know it, because I can use the clickCount value as an index in the randomColors array to find it. If clickCount is 5, then randomColors[clickCount] will return the fifth color in the sequence. Second, we know the color of the HTML element that the user clicked. That information is available through e.target.classList. These two pieces of information taken together, tell us if the user clicked on the right color. Each time the user clicks a color, the code uses the clickCount value to find what the correct color is, then sees if the HTML element that the user clicked has a class that matches that color. If there is a match, the user clicked the right color, and the game continues. If not, then the user clicked the wrong color, and the endGame() function executes.

I find this code interesting because, to be honest, I don’t know if I would have thought of it. I think it’s kind of a cool way of consistently tracking if the user is following the sequence correctly. Ask me in a few years if I think there’s a better way to do this. (Plot twist: There are a few other ways to do this for sure, but I couldn’t tell you if any of them are better.)

If the user makes the right selection, the code does a few other things:

  1. It changes the color of the element to it’s new value, waits, then changes it back. Just like when the game displays the current color sequence, this code gives a visual cue to the user that they clicked a specific color.
  2. It increments the clickCount value.
  3. It checks to see if the current clickCount value equals the current score. If it does, we know the user has completed the current sequence. The game then sets clickCount back to 0 and calls generateRandomPath(), continuing the game.

Here’s the entire handleColorClick function in its entirety.

const handleColorClick = async (e) => {
    if (isPathGenerating) {
        return false;
    }
    if (e.target.classList.contains(randomColors[clickCount])) {
        e.target.style.backgroundColor = colorObj[randomColors[clickCount]].new;
        await delay(500);
        e.target.style.backgroundColor = colorObj[randomColors[clickCount]].current;
        clickCount++;
        if (clickCount == score) {
            clickCount = 0;
            generateRandomPath();
        }
    } else {
        endGame();
    }
};

Adding the event handler

Of course, a function to handle events isn’t very useful unless you actually add it to the corresponding event listener. In this code, that’s handled like this:

colorParts.forEach((color) => color.addEventListener("click", handleColorClick));

This line uses the colorParts variable we created earlier. That variable is an array of HTML elements that represent the colors of our game. It then loops through each of these elements and adds the handleClickColor function to the element’s click event listener.

Final touches

There just a few more details to make the game functional. First, we need an endGame() function, to handle how each game ends.

const endGame = () => {
    resultEl.innerHTML = `<span> Your Score : </span> ${score}`;
    resultEl.classList.remove("hide");
    containerEl.classList.remove("hide");
    wrapperEl.classList.add("hide");
    startBtn.innerText = "Play Again";
    startBtn.classList.remove("hide");
};

Remember that the code calls this function if the user clicks the wrong color.

Next is a resetGame() function, which sets all of the application’s variables back to their original states.

const resetGame = () => {
    score = 0;
    clickCount = 0;
    randomColors = [];
    isPathGenerating = false;
    wrapperEl.classList.remove("hide");
    containerEl.classList.add("hide");
    generateRandomPath();
};

The game calls this function when the user clicks the Start button.

startBtn.addEventListener("click", resetGame);

With that, we have a working Simon game!

Dave’s thoughts

A few final thoughts I have about this code example:

  • I’m not sure why the code uses querySelector in some cases and getElementById in others. You’d think that getElementId would be better/easier. I understand why it uses querySelectorAll, though.
  • In the real Simon game, the sequence gets faster and faster as the sequence gets longer and longer. I bet this is way to guarantee that the user will fail before any mechanical limitations are met. That would be a nice addition to this code, and I might add that someday.
  • Another nice addition would be to play a sound with each color–that’s another aspect of the original Simon game that’s absent here.
  • I’d be really curious what more experienced developers think of this code. I wonder what improvements could be made to make the code more readable and maintainable.
  • This experience was really, really fun.

Finally, I’d like to call out ASMR programmer for the code. Check out their GitHub repository for this and other code examples. You’re welcome to check out my GitHub repository as well.

Until next time.

Tenkan

There is a particular warm up exercise that most aikido practitioners do before starting their training. This particular movement, however, also seems to encapsulate nearly everything there is to know about the art. And the principles that underpin this movement can, if you’re willing, have significant reverberations in other aspects of your life as well.

The movement is called “tenkan.” Literally translated into English, the word means “to convert or divert.” The physical movement is straightforward–at least in principle. You stand, one foot in front of the other, with the hand of your forward foot extended. (There are significant variations as to exactly how your feet should be placed, and exactly how your hand should be extended. But these variations mean little to you if you aren’t actively practicing the art.) In a fluid, controlled motion, you pivot from this initial position 180 degrees, then take a step backwards. You then reverse the movement, taking a step forwards (and switching to extend your other hand), pivoting in the opposite direction. To the uninitiated, the movement looks a little silly, almost dance-like.

Beneath the surface of this movement, however, is a torrent of activity and thought. Tenkan, as the name implies, is intended to do two things simultaneously: (1) divert an opponent’s movement around you, and (2) convert that movement into something that is no longer a threat. To successfully do a tenkan turn requires complete engagement throughout your body. Fail to extend your arm and it will collapse when your opponent enters your space. Fail to ground your legs and feet and you will only divert yourself–flailing around your opponent in an uncontrolled fashion. The movement becomes even more complicated when your working with a training partner. Now, you have the momentum and power of someone else to consider. This is where the “convert” part of the movement comes into play. What will you do with this energy? Can you simply dissipate it, and do so in such a way that you reduce your opponent’s ability to harm you to zero? Can you use it, powering your own movement to send your opponent flying away from you? And these choices are but two extremes–there are many other options that could be available to you, depending on your skill and your intention.

Over my 20+ years studying aikido, I have come to see many exercises and techniques, like tenkan, less as mere movements and more crucial decisions. This is because you should not let your opponent dictate your actions. Rather, your opponent is simply one set of data points that you should use to make a decision as to whether converting and diverting them is necessary. (Other options include, but are not limited to, your environment, your individual health at this moment, or your ultimate objectives.) When viewed as a choice, as opposed to a movement, an exercise, or–worst of all–a reaction, tenkan becomes an incredibly powerful statement to shape and accomplish your objectives.

It is at this intersection between physical action and mental commitment that the ideas of tenkan transforms beyond a simple martial art technique into something far more useful. For example, consider a typical day at work. Whether you are an individual contributor or a manager, nearly every day you will find yourself faced with the unexpected. Perhaps a team, in their enthusiasm to make their launch date, neglected to tell you of a crucial deadline, and now everyone is panicked that you’ll cause their release date to slip. Maybe a customer changed their acceptance requirements for a new feature. And maybe your CI/CD pipeline has just decided to fail without warning or explanation.

During these admittedly stressful situations, tenkan becomes incredibly useful. But before we get into how it is useful, there is an important point to remember. In all of these situations–and in the numerous other situations too long to list here–none of these events involve an actual opponent or enemy. That product team? They had the best of intentions. That customer? They have pressures of their own to deal with. And a CI/CD pipeline has all sorts of dependencies –sometimes, something just breaks. Part of understanding tenkan–any aikido movement, really–involves the eventual discovery that looking at a situation merely in the context of conflict is inaccurate at best. Only by being fully present in the moment, objectively observing what is happening, can you make the right decision for you or your team.

With that in mind, tenkan becomes very useful indeed. You can identify how to convert the situation into something beneficial to all involved. You can partner with the product team. You can use your customer’s changing demands as an opportunity to deepen your relationship with them. And you can use the downtime of your CI/CD pipeline to make your workflwos even more resilient in the future. And you can do these things without unnecessary confrontation or animosity.

True, tenkan is not easy. It takes practice focus, and many instances of failure before you find the implementation that works for you. More importantly, you also need to learn when you think tenkan is appropriate, and when a different tool or idea makes more sense. But, discovering how to employ tenkan in your every day life can help you increase productivity, decrease stress, and reduce the negative effects of confrontation.