Building a Number Guessing Game App with Next.js
Day 4: Number Guessing Game — 30 Days of 30 Projects Challenge
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 as0
.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 as0
.
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
orpaused
state. - Target Number Generation: Generates a random number between 1 and 10 using
Math.random()
andMath.floor()
, then updates thetargetNumber
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
totrue
,gameOver
tofalse
, andpaused
tofalse
, and resetsattempts
to0
. - 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 thepaused
state totrue
.handleResumeGame
Function: Resumes the game by setting thepaused
state tofalse
.- 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 matchestargetNumber
, the game is marked as over by settinggameOver
totrue
. - 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
andgameOver
tofalse
, clears theuserGuess
input, and resetsattempts
to0
. - 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 newuserGuess
.
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:
- Start the Development Server: Run
npm run dev
to start the development server. - 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:
- Twitter: @0xAsharib
- LinkedIn: Asharib Ali
- GitHub: AsharibAli
- Website: asharib.xyz
Thanks for reading!