Exploding Kittens Card Game - React, Nodejs and Redis
Tags: webdev, javascript, react, tutorial
Published: 2023-05-08 16:08:04

#Introduction
If your familiar with **Redis** you might know it is primarily used as a caching database along with a few other use cases. 

What this basically means is that, a Redis database acts as a high-speed data store that sits between the client and the main database, providing fast data retrieval compared to other primary databases such as MySQL, MongoDB, etc. This makes getting query results very fast.

Enough about what it is, you can read up more about Redis from their docs. 

##What this article covers
What this tutorial is about is **How to build a Card game using React and Redux Toolkit on the frontend, Nodejs on the backend and Redis as the primary database**.
Now, you might be thinking why use Redis as a primary database, and for that question my friend I only have one answer.

![meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ibqkev90qf3q1vfolq02.jpg)
On a more serious note, Redis provides many modules such as `RedisSearch`, `RedisJson` etc which allows us to extend the core Redis functionality, thus allowing us to use it more like a primary database. 

This article is part 1 of a two part series, in this part I'll go over the frontend implementation of the game. So if you are only concerned about the backend implementation you can visit just the second blog.

---

### Prerequisites 🖥️

 > - React (Basics)
 > - Nodejs (Basics)
 > - Typescript (Basics)

---

###Let's get started

If your familiar with game **"Exploding Kittens😼💣"** that's great, but my version of the game is different. So, even if you're not familiar with the game it doesn't matter. Here's how my version works.
 
This will be an online single-player card game that consists of 4 different types of cards

- Cat card 😼
- Defuse card 🙅‍♂️
- Shuffle card 🔀
- Exploding kitten card 💣

There will be a button to start the game. When the game is started there will be a deck of 5 cards ordered randomly. Each time user clicks on the deck a card is revealed and that card is removed from the deck. A player wins the game once he draws all 5 cards from the deck and there is no card left to draw.

**Rules**

- If the card drawn from the deck is a cat card, then the card is removed from the deck.
- If the card is exploding kitten (bomb) then the player loses the game.
- If the card is a defusing card, then the card is removed from the deck. This card can be used to defuse one bomb that may come in subsequent cards drawn from the deck.
- If the card is a shuffle card, then the game is restarted and the deck is filled with 5 cards again.

**What our game offers**

- Our game will allow a player to draw a random card from the deck once the game is started.
- Allow users to create a username to enter the game and create a leaderboard to record how many games they won.
- We will use Redis as a database to store the points of all the users and NodeJs using typescript for the backend. One game won is equal to one point.

>This is part **1** of a **2** part series, this article will cover the frontend implementation of the game and the second part will cover the backend implementation using Redis as the primary database.

Here's the projects folder structure

```
-
├── src
│   ├── components
|   │   ├── Navbar.jsx
|   |   ├── Login.jsx
|   |   ├── Signup.jsx
|   |   ├── Highscore.jsx
|   │   └── componentStyle.css
|   ├── redux
|        └──slices
|           └── userSlice.js
|   ├── App.jsx
|   ├── Board.jsx
|   ├── Board.css
│   └── main.jsx
├── package.json
└── package-lock.json
```

The project is setup using vite
After initial setup run

```
npm run dev
```
to start the frontend

Before we begin I just want to state that there's a lot of room for optimization and removal of redundant code, I just wanted to get through the frontend as soon as possible.

**Let's Begin**
So our main game logic lies inside Board.jsx


```javascript
function Board() {
const [deck, setDeck] = useState([])

const initializeDeck = () => {

const cards = [
 { cardName: 'Cat card', cardTitle: 'first title' },
 { cardName: 'Defuse card', cardTitle: 'second title' },
 { cardName: 'Shuffle card', cardTitle: 'third title' },
 { cardName: 'Exploding kitten card', cardTitle: 'forth title' }
]
const tempDeck = [];
      
const getRandomInt = (min, max) => {
    return Math.floor(Math.random() * (max - min + 1) + min);
}
      
for (let i = 0; i < 5; i++) {
   tempDeck.push(cards[getRandomInt(0, cards.length - 1)]);
}    
 return tempDeck;
}

useEffect(() => {
        const tempDeck = initializeDeck();
        setDeck(tempDeck);
}, [])

return (
  <div>
   Board
  <div>
 )
}

export default Board
```

We create a state variable `deck`, which will hold the 5 cards for the game. And we create the `initializeDeck` function which will fill the deck with 5 random cards when the game starts. We extract it out into a function because we will need to reuse it multiple times.
We then call the initializeDeck function inside useEffect and set the deck on initial render.


```javascript
const [diffuseCardCount, setDiffuseCardCount] = useState(0)
const [currentCard, setCurrentCard] = useState(null)
const [cardIsShowing, setCardIsShowing] = useState(false);
const [gameOver, setGameOver] = useState(false);
const [gameWon, setGameWon] = useState(false);

const handleCardShow = () => {
 const tempDeck = [...deck];
 const currCard = tempDeck[tempDeck.length-1];
 setCurrentCard(currCard)
 setCardIsShowing(true)
 setTimeout(() => {
   if(tempDeck.length == 1 && currCard.cardName != "Shuffle card" 
 && currCard.cardName != "Exploding kitten card"){
    setGameWon(true)
    dispatch(updateScore())
 }
 if(currCard.cardName == "Cat card"){
   //remove card from deck
   tempDeck.pop();
   setDeck(tempDeck);
 }
 else if(currCard.cardName == "Defuse card"){
 setDiffuseCardCount(prev => prev + 1)
 tempDeck.pop();
 setDeck(tempDeck);
 }
 else if(currCard.cardName == "Shuffle card"){
 restartGame() //Restart Game 
}
 else if(currCard.cardName == "Exploding kitten card"){ //exploding kitten card
  if(diffuseCardCount > 0 ){ //if player has any diffuse cards
   setExplodeAction(true);   //Game over
  }
  else{
   setGameOver(true)
 }
}
 setCurrentCard(null); //set currentCard to null after 2.5 secs
 setCardIsShowing(false)
 }, 2500)
        
}

<div className='board' >
   <div className="container">
     <div className='card-cont' >
      {
       deck && deck.map((card, ind) => (
        <div key={ind} className={`card card-${ind+1}`}>card {ind} 
        </div>
     ))
      }
    </div>

    {currentCard && ( 
              <div className='card active-card'> 
                    {  currentCard.cardName  }
              </div>
      )}

     { !cardIsShowing && <button className='show-btn' onClick= 
        {handleCardShow} >show card</button>
     }  

            <h2>Diffuse Cards Available - {diffuseCardCount}</h2>
        </div>
</div>
```
Then we introduce 5 more state variables `diffuseCardCount` to keep track of total diffuse cards drawn ingame(in order to counter the exploding kitten card). And the other 4 are what their names suggest.
Inside the component we map over the deck to display the 5 cards, display the current card on show card button press by running the `handleCardShow` function. We display the card for 2.5 secs before any action takes place.


```javascript
const restartGame = () => {
    const tempDeck = initializeDeck();
    setDeck(tempDeck);
    setDiffuseCardCount(0);
    dispatch(fetchHighscore())
    setGameOver(false)
    setGameWon(false)
}

return (
<>
 <Navbar/>
 {
   gameWon ? (
     <div>
       <h1>You Won</h1>
       <button onClick={ restartGame } >Restart</button>
      </div>
        ) : (
    gameOver ? (
       <div>
       <h1>Game Over</h1>
         <button onClick={ restartGame } >Restart</button>
       </div>
        ) : (
   <div className='board' >
        
    <div className="container">
       (...._Same code as before_)
     </div>

    <Highscore highscore={highscore} />
   </div>
        )
      )
    }
</>
```

Then, if user wins set `gameWon` to true and if user losses set `gameOver` to true and display their respective div's. On click restart button the restartGame function gets called thus reinitializing the deck and resetting all state variables to initial condition.
The `Highscore` component gets the top 10 user high scores from the Redis database and displays it on the screen.

I won't go over the Redux code you can view it from the repo which I'll link below as it contains a very basic code, there was no need to use Redux for this project. I was just playing around and felt like using Redux.

Here's the link to the codebase - [code](https://github.com/NabajitS/game-frontend)