Web Development

New JavaScript Guessing Game 2024

The JavaScript guessing game tutorial is a simple beginner’s project. It features modern JavaScript syntax and runs without a Framework. In part one of the game the foundation is built. The second part adds suspense by only giving the user five attempts to guess. In this third part, we have added the ability for the user to set the level and change part way through if you have any attempts left.

Added Change Difficulty

Prerequisites

A modern browser and a text editor.  I recommend VS Code with the Live Server extension. Some knowledge of HTML and JavaScript would be beneficial but not required. Version 2.0 uses TypeScript and includes code from the previous two versions of the JavaScript Guessing Game. You can run the game from the dist folder. Have you missed the previous versions?

Easy JavaScript Guessing Game Part 1
Easy JavaScript Guessing Game Part 2

Prerequisites To Build

Suppose you want to make any changes, the source files are included. I have used Bun to manage the Bulma package. I have used Dart-Sass CLI to build the Bulma Stylesheet. The TypeScript CLI compiles the code to JavaScript.

Download

The latest JavaScript Guessing Game update has been released on 26th September 2024.

Also available on GitHub

Let’s Get Coding

Guessing Game Code

We have assigned constants for the minimum and maximum range. To manage the state between level selects we use Object.defineProperty. The data is synced with the browser’s local storage.

Source

Frontend Masters

const max: number = 100;
const min: number = 1;
const currentLevel: string | null = localStorage.getItem("level");
const level_arr: string[] = ["10", "5", "2"];

/**
 * Manages the state of the levels
 */let diff: string = currentLevel ?? "5";

const level = {
  _difficulty: diff,
};

Object.defineProperty(level, "difficulty", {
  get: function () {
    return this._difficulty;
  },
  set: function (value) {
    this._difficulty = value;
  },
});

One new helper function is a shortcut to grab the element by ID which is used multiple times throughout the codebase.

/**
 * Shorthand Get Element By ID
 */
const getID = (id: string) => {
  return document.getElementById(id);
};

When the browser loads the page it directs focus on the input directly to save having to click inside the input field. It calls two new functions to set the level and generate the computer guess.

/**
 * Executes on browser load. Saves computer guess to browser session if not set.
 */window.onload = () => {
  const guessInput = getID("guess");
  if (guessInput !== null) {
    guessInput.focus();
  }
  setLevelActive();
  generateComputerGuess();
};
/**
 * Manages the switching of the difficulty buttons, it's state and saves to local storage
 */const setLevelActive = () => {
  let btnLevel = document.querySelectorAll(".btn-level");

  if (currentLevel !== null) {
    let levelIndex = level_arr.findIndex((value) => value === currentLevel);
    btnLevel[levelIndex].classList.add("is-active");
  } else {
    btnLevel[1].classList.add("is-active");
    localStorage.setItem("level", level._difficulty);
  }
  btnLevel.forEach((btn) => {
    btn.addEventListener("click", (e) => {
      const totalGuesses = countGuesses();
      btnLevel?.forEach((btn) => btn.classList.remove("is-active"));
      (e.target as HTMLElement).classList.add("is-active");
      let value = (e.target as HTMLElement).textContent;

      switch (value) {
        case "Easy":
          level._difficulty = "10";
          break;
        case "Medium":
          level._difficulty = "5";
          break;
        case "Hard":
          level._difficulty = "2";
          break;
      }
      localStorage.setItem("level", level._difficulty);
      if (totalGuesses > level._difficulty) {
        notify(
          "You have already had more guesses than the difficulty chosen. Choose another level or your game will end!"
        );
      }
    });
  });
};
/**
 * Generates the computer guess and saves it to session storage
 */const generateComputerGuess = () => {
  let genGuess: number = Math.floor(Math.random() * (max - min) + min),
    arr: [string, string | number] = ["computer-guess", genGuess],
    session = getSession(arr);
  if (session === null) {
    addToSession(arr);
  }
};

One new feature is notifications. Whenever there is an issue or an error a message will be passed to the notify function then it will display in the browser.

/**
 * Create notifications
 */const notify = (msg: string | null) => {
  let notification = document.createElement("div");
  let deleteButton = document.createElement("button");
  deleteButton.addEventListener("click", removeNotification);
  deleteButton.className = "delete";
  notification.className = "notification";
  notification.textContent = msg;
  notification.prepend(deleteButton);
  const heroHead = document.querySelector(".notify");
  if (heroHead !== null) {
    heroHead.appendChild(notification);
  }
};

The subsequent removeNotifications() function removes all the notifications from the DOM.

/**
 * Removes notifications
 */const removeNotification = () => {
  let notification = document.querySelectorAll(".notification");
  notification.forEach((item) => {
    item.remove();
  });
};

The event listener on the submit button has had a few changes since the original version. It will display an error if the value is empty/null or the number submitted is not between 1 and 100. If the value has already been picked it will also issue a warning without incrementing the count.

/*
 * Adds event listener to the submit button
 */const btnElement = getID("btn");
if (btnElement !== null) {
  btnElement.addEventListener("click", (e) => {
    e.preventDefault();

    let userGuess: HTMLInputElement | null = getID("guess") as HTMLInputElement,
      usrArr: (string | number)[] = [],
      data = getSession(["user-guess", null]);

    if (parseInt(userGuess.value) < min || parseInt(userGuess.value) > max) {
      notify(`Please enter a number between ${min} and ${max}`);
      return;
    }

    if (userGuess !== null && userGuess.value === "") {
      notify("Please enter a number");
      return;
    }
    if (data?.includes(userGuess.value)) {
      notify("Number already picked");
      return;
    }
    usrArr = ["user-guess", userGuess.value];
    checkGuess(usrArr);
    displayGuesses();
    userGuess.value = "";
  });
}

The following 6 functions remain in Version 2.0. They are pretty self-explanatory.

getSession
addToSession
checkGuess
higherOrLower
countGuesses
clearSession

/**
 * Get data out of session storage
 */const getSession = (item: [string, string | number] | (string | null)[]) => {
  let store: string = item[0] ?? "";
  const storedItem = sessionStorage.getItem(store);
  return storedItem !== null ? JSON.parse(storedItem) : null;
};
/**
 * Stores data in sessionStorage. Merges data if already exists
 */const addToSession = (item: any[]) => {
  //Check if a session is set
  let session = getSession(item),
    data: (string | number)[] = [],
    store: string = item[0],
    saveData = item[1],
    sessionData: (string | number)[] = [];
  data.push(saveData);
  // If the session return nothing
  if (session === null) {
    //create for the first time
    sessionData = data;
  } else {
    //grab data and merge
    session.push(data);
    sessionData = session;
  }
  sessionStorage.setItem(store, JSON.stringify(sessionData.flat()));
};
/**
 * Checks the user submitted number against the generated one
 */const checkGuess = (guess: any[]) => {
  addToSession(guess);
  // Get user generated number
  let generatedNumber = getSession(["computer-guess", null]),
    message = getID("message"),
    total_guesses = countGuesses();

  if (total_guesses < parseInt(level._difficulty)) {
    if (parseInt(guess[1]) !== generatedNumber[0]) {
      higherOrLower(message, guess[1], generatedNumber[0]);
      return;
    } else if (message !== null) {
      message.innerText =
        "Jackpot, you won. You guessed it within " + total_guesses + " tries";
    }
  } else {
    notify("You lose, you have reached the maximum guesses.");
  }
  clearSession();
};
/**
 * Checks to see if your number matches the computer one
 */const higherOrLower = (
  message: HTMLElement | null,
  num: string,
  num2: number
) => {
  if (message !== null) {
    if (parseInt(num) > num2) {
      message.innerText = "You need to go lower 👇";
    } else {
      message.innerText = "You need to go higher ☝️";
    }
  }
};
/**
 * Counts the user guesses
 */const countGuesses = () => {
  let userGuess = getSession(["user-guess", null]),
    guesses = userGuess !== null ? userGuess.length : 0;
  return guesses;
};
/**
 * Starts a new game by clearing a session and reloading the page
 */const clearSession = () => {
  setTimeout(() => {
    sessionStorage.clear();
    location.reload();
  }, 5000);
};

Finally displayGuesses displays the guesses that you have made.

/**
 * Displays the user guesses in a tag format
 */const displayGuesses = () => {
  let guess = getSession(["user-guess", null]);
  let tagsElement = getID("tags");
  if (guess !== null && tagsElement !== null) {
    tagsElement.textContent = "";
    guess.forEach((item: string) => {
      let span = document.createElement("span"),
        text = document.createTextNode(item);
      span.classList.add("tag");
      span.appendChild(text);
      tagsElement.appendChild(span);
    });
  }
};

The JavaScript Guessing Game V2.0 Demo

[codepen_embed height=”300″ default_tab=”html,result” slug_hash=”qBedNrX” pen_title=”Untitled” user=”ridgey28″]See the Pen
Untitled
by Tracy Ridge (@ridgey28)
on CodePen.[/codepen_embed]

Conclusion

What started as a simple JavaScript Guessing Game has been transformed with new features including displaying notifications and selecting the difficulty level. Converting the project to TypeScript also alleviates issues with JavaScript to maximise performance. If you have any errors or want to suggest a new feature get in touch.

Share
Published by
Tracy Ridge

Recent Posts

New Svelte 5 Guessing Game 2024

In this tutorial, you’ll build a fun and interactive guessing game using Svelte 5, the… Read More

2 weeks ago

Hot New Web Dev – September 2024

Welcome to Hot Web Dev September 2024, featuring the latest technology and web development news.… Read More

2 weeks ago

Hot Web Dev – 12 Useful Top Tools of 2023

If you have been following the monthly Hot Web Dev magazine you will find the… Read More

1 month ago

Hot New Web Dev – August 2024

Welcome to Hot Web Dev August 2024, featuring the latest technology and web development news.… Read More

1 month ago

Build an Awesome Daisy UI WordPress Theme – Part 4

Welcome to Building an Awesome Daisy UI WordPress Theme - Part 4. Daisy UI is… Read More

2 months ago

Hot New Web Dev – July 2024

Welcome to Hot Web Dev July 2024, featuring the latest technology and web development news.… Read More

2 months ago