5.2: Program State for Game Modes

Learning Objectives

By the end of this lesson, you should:

  • Know how to use global variables to change the "state" of your program.

  • Be familiar with the concept and practice of "refactoring"

Introduction

We can use global state to expand the functionality of our apps. For games, we can use global state to keep track of game modes to treat inputs differently depending on the game mode.

Global State for App Modes

The following code outputs a different default quote depending on whether the app mode is green or blue. Note that we get the same output until we command the program to change modes. To change app modes, we can use the special input words greenmode and bluemode. Note that the initially-assigned value of mode is the default app mode.

var mode = 'green';

var main = function (input) {
  if (input == 'greenmode') {
    mode = 'green';
  } else if (input == 'bluemode') {
    mode = 'blue';
  }

  var myOutputValue =
    'A fool sees not the same tree that a wise man sees. -William Blake';

  if (mode == 'blue') {
    myOutputValue =
      'The sea, once it casts its spell, holds one in its net of wonder forever. -Jacques Cousteau';
  }

  return myOutputValue;
};

App Mode to Accept and Store User Name

In addition to keeping score, we may also want to persist data such as user name to personalise our app. In the following example, we persist user name when in a "waiting for user name" app mode. At the beginning of the game, the first input is saved as the user name. After that first input, the program changes the game mode to "dice game", and subsequent inputs are considered inputs to the dice game. Throughout the game, the app can refer to the userName variable to personalise output for the current user.

var currentGameMode = 'waiting for user name';
var bankRoll = 10;
var userName = '';

var rollDice = function () {
  var randomDecimal = Math.random() * 6;
  var randomInteger = Math.floor(randomDecimal);
  var diceNumber = randomInteger + 1;
  return diceNumber;
};

var main = function (input) {
  var myOutputValue = '';

  if (currentGameMode == 'waiting for user name') {
    // if the game mode is user name... set the name as the input
    userName = input;

    // now that we have the name, switch the mode
    currentGameMode = 'dice game';

    myOutputValue = 'Hello ' + userName;
  } else if (currentGameMode == 'dice game') {
    // if the game mode is dice game... define userGuess as the input
    var userGuess = input;
    // dice game logic
    var randomDiceRoll = rollDice();
    myOutputValue =
      userName +
      ' you lost! you guessed: ' +
      input +
      '. you rolled: ' +
      randomDiceRoll +
      '. current bank roll: ' +
      bankRoll;

    if (userGuess == randomDiceRoll) {
      bankRoll = bankRoll + 1;
      myOutputValue =
        userName +
        ' you won! you guessed: ' +
        input +
        '. you rolled: ' +
        randomDiceRoll +
        '. your current bank roll: ' +
        bankRoll;
    }
  }

  return myOutputValue;
};

Refactoring Code

As our programs become larger and more complicated we want to be able to "refactor" our programs to be more concise, understandable, and testable. We can use functions as subroutines and use parameters and return values to help us control the flow of data in our program. The following example is a refactored version of the code from the previous section, where we extract the dice game logic and put it into a new function playDiceGame. This is desirable because it makes it easier to skim the logic of our app by reading the main function. It also makes it easier to test logic in isolation, for example by running functions independently in the browser console.

var currentGameMode = 'waiting for user name';
var bankRoll = 10;
var userName = '';

var rollDice = function () {
  var randomDecimal = Math.random() * 6;
  var randomInteger = Math.floor(randomDecimal);
  var diceNumber = randomInteger + 1;
  return diceNumber;
};

var playDiceGame = function (userName, userGuess) {
  var message = '';

  // dice game logic
  var randomDiceRoll = rollDice();
  message =
    userName +
    ' you lost! you guessed: ' +
    userGuess +
    '. you rolled: ' +
    randomDiceRoll +
    '. current bank roll: ' +
    bankRoll;

  if (userGuess == randomDiceRoll) {
    bankRoll = bankRoll + 1;
    message =
      userName +
      ' you won! you guessed: ' +
      userGuess +
      '. you rolled: ' +
      randomDiceRoll +
      '. your current bank roll: ' +
      bankRoll;
  }

  return message;
};

var main = function (input) {
  var myOutputValue = '';

  if (currentGameMode == 'waiting for user name') {
    // set the name
    userName = input;

    // now that we have the name, switch the mode
    currentGameMode = 'dice game';

    myOutputValue = 'Hello ' + userName;
  } else if (currentGameMode == 'dice game') {
    myOutputValue = playDiceGame(userName, input);
  }

  return myOutputValue;
};

Exercises

Follow Along

Duplicate the Blue / Green app above.

Red Mode

Add a Red mode to the game. A reference solution can be found here.

Last updated