High Scores

In this lesson, we will add high scores to the Tetris game, and you will learn how to save and load high scores from local storage.

Now that we have made the points system, it would be nice to be able to save our best scores. If we only save the score in memory, it will be gone the next time we play. Instead, we can save the scores in the browser’s local storage.

Local storage

With local storage, we can store data in the browser in the form of key-value pairs, where both the key and the value are strings. The data is persisted across browser sessions and its scope is limited to the origin where the script that accesses local storage resides.

Different browsers use different storages, so data is not shared between different browsers on the same computer. Each browser has its own unique storage.

The following snippets show how we can use localStorage:

// Add data
localStorage.setItem('myCar', 'Tesla');

// Read data
const car = localStorage.getItem('myCar');

// Remove specific data
localStorage.removeItem('myCar');

// Remove all data
localStorage.clear();

With this knowledge, we can start implementing our solution.

Load high scores

Let’s start by defining a couple of constants:

const NO_OF_HIGH_SCORES = 10;
const HIGH_SCORES = 'highScores';

Since we want to store an array, we need to translate the array into a string before we save it and back to an array when getting it. For this, we can use the JSON object that contains methods for parsing JSON and converting values to JSON.

We can parse the string that we get from localStorage. If we don’t have any scores saved yet we can use the OR operator || to give a default value of empty array:

const highScoreString = localStorage.getItem(HIGH_SCORES);
const highScores = JSON.parse(highScoreString) || [];

We now have the high scores parsed into an array. Next, we need to check if a score is good enough to make the high score list.

Check for a high score

To get the lowest score on the list we can take the score from the last position in the array with the help of our constant NO_OF_HIGH_SCORES. If we don’t have a full list yet we can use optional chaining with the Elvis operator (?) and return a zero:

const lowestScore = highScores[NO_OF_HIGH_SCORES - 1]?.score ?? 0;

We have enough now to create a function checkHighScore():

function checkHighScore(score) {
  const highScores = JSON.parse(localStorage.getItem(HIGH_SCORES)) || [];

  const lowestScore = highScores[NO_OF_HIGH_SCORES - 1]?.score ?? 0;

  if (score > lowestScore) {
    saveHighScore(score, highScores); // TODO
    showHighScores(); // TODO
  }
}

And add it to the gameOver() function:

function gameOver() {
  // ...
  checkHighScore(account.score);
}

As you can see, we have a couple of remaining functions marked with TODO that we need to take care of. If we have a score good enough to make the high score list, we need to save it and refresh the list.

Save high score

Now, we need to save the scores when the game ends and the player gets a score that qualifies on the top list. In addition to the score we need a name so let’s ask for it:

const name = prompt('You got a high score! Enter name:');

When we have the name and the score, we can create an object to save in the list:

const newScore = { score, name };

And when we have the new score that qualifies into the list, we can:

  1. Add it to the list
  2. Sort the list
  3. Select the new high score list
  4. Save it back to local storage
function saveHighScore(score, highScores) {
  const name = prompt('You got a highscore! Enter name:');

  const newScore = { score, name };

  // 1. Add to list
  highScores.push(newScore);

  // 2. Sort the list
  highScores.sort((a, b) => b.score - a.score);

  // 3. Select new list
  highScores.splice(NO_OF_HIGH_SCORES);

  // 4. Save to local storage
  localStorage.setItem(HIGH_SCORES, JSON.stringify(highScores));
};

So, now that we have all this logic figured out, we need to show the scores on the screen.

Show high scores

To show the best scores on the left of the board, we can alter the HTML and CSS to add a left column:

<div class="left-column">
  <h2>HIGH SCORES</h2>
  <ol id="highScores"></ol>
</div>
.grid {
  display: grid;
  grid-template-columns: 300px 320px 200px;
}

.left-column {
  display: flex;
  flex-direction: column;
  margin-left: 20px;
}

Now, we need to retrieve the high scores from localStorage and show them on the screen. We can do this by using the array method map. It lets us go through the array and add an HTML ordered list item to each score in the array.

highScores.map((score) => `<li>${score.score} - ${score.name}`);

We can then get the ordered list element and add the list items to its innerHTML:

const highScoreList = document.getElementById(HIGH_SCORES);

highScoreList.innerHTML = highScores.map((score) => `<li>${score.score} - ${score.name}`);

We can run this when we initialize the game and every time there is a new high score.

function showHighScores() {
  const highScores = JSON.parse(localStorage.getItem(HIGH_SCORES)) || [];

  const highScoreList = document.getElementById(HIGH_SCORES);

  highScoreList.innerHTML = highScores
    .map((score) => `<li>${score.score} - ${score.name}`)
    .join('');
}

Now, we have a functioning high score list. Play the game to make a high score!

We will take a break from discussing our Tetris game to revisit some more advanced features of JavaScript. Then, we will use these features to improve our game execution."

Get hands-on with 1100+ tech skills courses.