Building a Number Guessing Game App with Next.js

Day 4: Number Guessing Game — 30 Days of 30 Projects Challenge

Asharib Ali
10 min readSep 5, 2024

Hey everyone, I hope you’re all doing well and enjoying your coding journey, Am I right? By the way, today marks the 4th day of the 30-day of 30-projects challenge. Our 4th project will be creating a mini Next.js application — a number-guessing game. Please make sure to clap and comment on this blog post. It motivates me to create more amazing content like this, Let’s get started straight away.

Overview of the Mini Next.js Application

Our Weather Widget application allows users to:

  • Start, pause, and guess the number
  • Try again, and see the number of guess attempts

Tech-Stack Used:

  • Next.js: A React framework for building full-stack web applications.
  • React: A JavaScript library for building user interfaces.
  • Tailwind CSS: A utility-first CSS framework for styling.
  • Shadcn UI: Beautifully designed tailwindcss components that you can copy and paste into your application.
  • Vercel: For deploying the Nextjs web application.

Initialize a Nextjs project

Start by following this guide to set up the new project.

  • Go to the “components” folder.
  • Create a new file named “number-guessing.tsx”.
  • This file will manage the entire functionality of the project.

We will go through the code step-by-step to make it easy to understand.

Component Breakdown

Import Statements

"use client"; 
import { useState, useEffect, ChangeEvent } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
  • "use client"; Enables client-side rendering for this component.
  • useState, useEffect, ChangeEvent: React hooks for managing state, handling events, and running side effects.
  • Button, Input: Custom UI components from the Shadcn UI library.

State Definition

interface NumberGuessingState {
gameStarted: boolean;
gameOver: boolean;
paused: boolean;
targetNumber: number;
userGuess: number | string;
attempts: number;
}
  • NumberGuessingState Interface: Defines the structure of the state for a number guessing game.

State Properties:

  • gameStarted: A boolean indicating if the game has started.
  • gameOver: A boolean indicating if the game is over.
  • paused: A boolean indicating if the game is paused.
  • targetNumber: A number representing the target number to be guessed.
  • userGuess: A number or string representing the user's current guess.
  • attempts: A number representing the count of attempts made by the user.

Purpose: Ensures consistent and accurate handling of game state data throughout the application.

State and References

const [gameStarted, setGameStarted] = useState<boolean>(false);
const [gameOver, setGameOver] = useState<boolean>(false);
const [paused, setPaused] = useState<boolean>(false);
const [targetNumber, setTargetNumber] = useState<number>(0);
const [userGuess, setUserGuess] = useState<number | string>("");
const [attempts, setAttempts] = useState<number>(0);
  • gameStarted: Indicates if the game has started, storing a boolean value.
  • gameOver: Indicates if the game is over, storing a boolean value.
  • paused: Indicates if the game is paused, storing a boolean value.
  • targetNumber: Stores the target number that the user needs to guess, initialized as 0.
  • userGuess: Stores the user's current guess, which can be a number or an empty string.
  • attempts: Tracks the number of attempts made by the user to guess the target number, initialized as 0.

Effect Hook for Target Number

useEffect(() => {
if (gameStarted && !paused) {
const randomNumber: number = Math.floor(Math.random() * 10) + 1;
setTargetNumber(randomNumber);
}
}, [gameStarted, paused]);
  • Effect Hook: Executes when the game starts or resumes, indicated by changes to gameStarted or paused state.
  • Target Number Generation: Generates a random number between 1 and 10 using Math.random() and Math.floor(), then updates the targetNumber state with this value.
  • Conditional Execution: Ensures the new target number is only set if the game is started and not paused.

Start Game Function

const handleStartGame = (): void => {
setGameStarted(true);
setGameOver(false);
setAttempts(0);
setPaused(false);
};
  • handleStartGame Function: Handles the initialization of the game.
  • State Initialization: Sets gameStarted to true, gameOver to false, and paused to false, and resets attempts to 0.
  • Game Start: Prepares the game by resetting necessary states to their starting values, allowing the game to begin afresh.

Pause and Resume Game Functions

const handlePauseGame = (): void => {
setPaused(true);
};

const handleResumeGame = (): void => {
setPaused(false);
};
  • handlePauseGame Function: Pauses the game by setting the paused state to true.
  • handleResumeGame Function: Resumes the game by setting the paused state to false.
  • Purpose: These functions manage the game’s paused state, allowing the game to be paused and resumed as needed.

Guess Handling Function

const handleGuess = (): void => {
if (typeof userGuess === "number" && userGuess === targetNumber) {
setGameOver(true);
} else {
setAttempts(attempts + 1);
}
}
  • handleGuess Function: Manages the user's guess and updates the game state accordingly.
  • Correct Guess: If userGuess is a number and matches targetNumber, the game is marked as over by setting gameOver to true.
  • Incorrect Guess: If the guess is incorrect, the attempts state is incremented by 1 to track the number of attempts made.

Try Again Function

const handleTryAgain = (): void => {
setGameStarted(false);
setGameOver(false);
setUserGuess("");
setAttempts(0);
};
  • handleTryAgain Function: Resets the game state to allow the user to try again.
  • State Reset: Sets gameStarted and gameOver to false, clears the userGuess input, and resets attempts to 0.
  • Game Restart: Prepares the game for a fresh start by resetting necessary states.

User Guess Change Function

const handleUserGuessChange = (e: ChangeEvent<HTMLInputElement>): void => {
setUserGuess(parseInt(e.target.value));
};
  • handleUserGuessChange Function: Manages the change event for the user's guess input.
  • State Update: Updates the userGuess state with the parsed integer value from the input field.
  • Input Handling: Converts the input value to a number using parseInt and sets it as the new userGuess.

JSX Return Statement

return (
<div className="flex flex-col items-center justify-center h-screen bg-gradient-to-br from-gray-800 to-black">
<div className="bg-white rounded-lg shadow-lg p-8 w-full max-w-md">
<h1 className="text-3xl font-bold text-center mb-2 text-black">
Number Guessing Game
</h1>
<p className="text-center text-black mb-4">
Try to guess the number between 1 and 10!
</p>
{!gameStarted && (
<div className="flex justify-center mb-4">
<Button
onClick={handleStartGame}
className="bg-black hover:bg-gray-700 text-white font-bold py-2 px-4 rounded"
>
Start Game
</Button>
</div>
)}
{gameStarted && !gameOver && (
<div>
<div className="flex justify-center mb-4">
{paused ? (
<Button
onClick={handleResumeGame}
className="bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded"
>
Resume
</Button>
) : (
<Button
onClick={handlePauseGame}
className="bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded"
>
Pause
</Button>
)}
</div>
<div className="flex justify-center mb-4">
<Input
type="number"
value={userGuess}
onChange={handleUserGuessChange}
className="bg-gray-100 border border-gray-300 rounded-lg py-2 px-4 w-full max-w-xs"
placeholder="Enter your guess"
/>
<Button
onClick={handleGuess}
className="bg-gray-700 hover:bg-gray-800 text-white font-bold py-2 px-4 rounded ml-4"
>
Guess
</Button>
</div>
<div className="text-center text-black">
<p>Attempts: {attempts}</p>
</div>
</div>
)}
{gameOver && (
<div>
<div className="text-center mb-4 text-black">
<h2 className="text-2xl font-bold">Game Over!</h2>
<p>You guessed the number in {attempts} attempts.</p>
</div>
<div className="flex justify-center">
<Button
onClick={handleTryAgain}
className="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded"
>
Try Again
</Button>
</div>
</div>
)}
</div>
</div>
);
  • Container Div: Centers the game UI on the screen with flexbox and provides a gradient background.
  • Card Component: Houses the game content, including a title, description, and interactive elements for playing the game.
  • Conditional Rendering: Displays different UI components based on the game state:
  • Start Button: Shown if the game hasn’t started.
  • Game Controls: Includes input for the user’s guess, guess button, and pause/resume button when the game is ongoing and not over.
  • Game Over Message: Displays a message and “Try Again” button when the game is over.

(Bonus just for you): Full Code with Comments

"use client"; // Enables client-side rendering for this component

// Importing necessary hooks and components from React and custom components
import { useState, useEffect, ChangeEvent } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

// Type definition for the NumberGuessingComponent's state
interface NumberGuessingState {
gameStarted: boolean;
gameOver: boolean;
paused: boolean;
targetNumber: number;
userGuess: number | string;
attempts: number;
}

// Defining the NumberGuessingComponent function component
export default function NumberGuessing(): JSX.Element {
// State variables to manage the game state
const [gameStarted, setGameStarted] = useState<boolean>(false); // Indicates if the game has started
const [gameOver, setGameOver] = useState<boolean>(false); // Indicates if the game is over
const [paused, setPaused] = useState<boolean>(false); // Indicates if the game is paused
const [targetNumber, setTargetNumber] = useState<number>(0); // The number to be guessed
const [userGuess, setUserGuess] = useState<number | string>(""); // The user's guess (can be a number or an empty string)
const [attempts, setAttempts] = useState<number>(0); // Number of attempts made by the user

// useEffect to generate a new target number when the game starts or resumes
useEffect(() => {
if (gameStarted && !paused) {
const randomNumber: number = Math.floor(Math.random() * 10) + 1; // Generate a random number between 1 and 10
setTargetNumber(randomNumber); // Set the target number
}
}, [gameStarted, paused]); // Dependencies: gameStarted and paused

// Function to handle the start of the game
const handleStartGame = (): void => {
setGameStarted(true); // Start the game
setGameOver(false); // Reset the game over state
setAttempts(0); // Reset the attempts counter
setPaused(false); // Ensure the game is not paused
};

// Function to handle pausing the game
const handlePauseGame = (): void => {
setPaused(true); // Pause the game
};

// Function to handle resuming the game
const handleResumeGame = (): void => {
setPaused(false); // Resume the game
};

// Function to handle the user's guess
const handleGuess = (): void => {
if (typeof userGuess === "number" && userGuess === targetNumber) {
setGameOver(true); // If the guess is correct, end the game
} else {
setAttempts(attempts + 1); // Increment the attempts counter
}
};

// Function to handle restarting the game
const handleTryAgain = (): void => {
setGameStarted(false); // Reset the game state
setGameOver(false); // Reset the game over state
setUserGuess(""); // Clear the user's guess
setAttempts(0); // Reset the attempts counter
};

// Function to handle input change for user's guess
const handleUserGuessChange = (e: ChangeEvent<HTMLInputElement>): void => {
setUserGuess(parseInt(e.target.value));
};

// JSX to render the game UI
return (
<div className="flex flex-col items-center justify-center h-screen bg-gradient-to-br from-gray-800 to-black">
{/* Main container for the game */}
<div className="bg-white rounded-lg shadow-lg p-8 w-full max-w-md">
{/* Title of the game */}
<h1 className="text-3xl font-bold text-center mb-2 text-black">
Number Guessing Game
</h1>
{/* Description of the game */}
<p className="text-center text-black mb-4">
Try to guess the number between 1 and 10!
</p>
{/* Conditional rendering: show start button if game hasn't started */}
{!gameStarted && (
<div className="flex justify-center mb-4">
{/* Button to start the game */}
<Button
onClick={handleStartGame}
className="bg-black hover:bg-gray-700 text-white font-bold py-2 px-4 rounded"
>
Start Game
</Button>
</div>
)}
{/* Conditional rendering: show game controls if game started and not over */}
{gameStarted && !gameOver && (
<div>
<div className="flex justify-center mb-4">
{/* Button to resume the game if paused */}
{paused ? (
<Button
onClick={handleResumeGame}
className="bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded"
>
Resume
</Button>
) : (
/* Button to pause the game */
<Button
onClick={handlePauseGame}
className="bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded"
>
Pause
</Button>
)}
</div>

<div className="flex justify-center mb-4">
{/* Input field for user's guess */}
<Input
type="number"
value={userGuess}
onChange={handleUserGuessChange}
className="bg-gray-100 border border-gray-300 rounded-lg py-2 px-4 w-full max-w-xs"
placeholder="Enter your guess"
/>
{/* Button to submit the guess */}
<Button
onClick={handleGuess}
className="bg-gray-700 hover:bg-gray-800 text-white font-bold py-2 px-4 rounded ml-4"
>
Guess
</Button>
</div>
<div className="text-center text-black">
{/* Display number of attempts */}
<p>Attempts: {attempts}</p>
</div>
</div>
)}
{/* Conditional rendering: show game over message if game is over */}
{gameOver && (
<div>
<div className="text-center mb-4 text-black">
{/* Game over message */}
<h2 className="text-2xl font-bold">Game Over!</h2>
<p>You guessed the number in {attempts} attempts.</p>
</div>
<div className="flex justify-center">
{/* Button to try the game again */}
<Button
onClick={handleTryAgain}
className="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded"
>
Try Again
</Button>
</div>
</div>
)}
</div>
</div>
);
}

Okay, you’ve completed the main component with functional UI. Now, you need to import this component into the app directory to use it in the app/page.tsx file. Your final code should look like this:

import NumberGuessing from "@/components/number-guessing";

export default function Home() {
return (
<div>
<NumberGuessing />
</div>
);
}

Running the Project

To see the number-guessing game in action, follow these steps:

  1. Start the Development Server: Run npm run dev to start the development server.
  2. Open in Browser: Open http://localhost:3000 in your browser to view the application.

Make sure to test it properly (each and everything) so that we don’t have any errors in the production mode aka when we host on the internet.

Now, we want people to see our application on the internet. All you have to do is create a repository on GitHub and then push your code to it. After that, deploy the Number Guessing Game application using Vercel.

Once you’re done with the deployment, please share the application link with me by commenting on this blog post, on Linkedin, and (most importantly, on X a.k.a. Twitter. Tag me there, and I’ll reply and appreciate your efforts) 👀

(Optional): One thing you can do on your own is to add new functionalities, enhance the styling, and improve the overall application. This way, you’ll learn something new by making modifications.

✨ Star Github Repository of the project 👈

Conclusion

In this blog post, we explored the creation of a simple calculator using Next.js. We covered:

  • The purpose and main features of the application.
  • A detailed breakdown of the number-guessing.tsx component, explaining how each part of the code works together.

See you tomorrow with the latest project. Happy coding!

Stay updated with the latest in cutting-edge technology! Follow me:

Thanks for reading!

--

--

Asharib Ali
Asharib Ali

Written by Asharib Ali

✨ I build & teach about AI and Blockchain stuffs⚡

Responses (3)