Know Backend of Stake’s Mine Game
Tags: gamedev, webdev, redis, graphql
Published: 2024-07-13 09:32:46

Hey there, fellow game enthusiasts! 🎮

I’ve recently built a Mine Game, and I’m excited to share how the backend is designed. If you’re curious about how such games work behind the scenes, you’re in the right place!

![Mine Game for Online Betting and Casino](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8rc9uvwzgr6slgb964qi.png)

**What is a Mine Game?**
First things first, let’s quickly recap what a Mine Game is. Inspired by Stake’s popular version, it’s a betting game where players reveal tiles on a grid, hoping to avoid mines. Each safe tile increases their winnings, but hitting a mine means losing the bet.

**Tech Stack**
Frontend: Vite and React
Styling: Tailwind CSS
Backend: Node.js with Redis and GraphQL

**Designing the Backend**
When a user starts a game, a unique game ID is generated. The game data includes fields like mineCount, mineField, bettingAmount, multiplierArray, and isGameOver. The mineField is not sent in queries but rather in mutations to prevent cheating or malpractices.

**GraphQL Resolvers**
Here’s how I implemented the backend using GraphQL resolvers:

```
const resolvers = {
  Query: {
    getGameResults: async (_, { gameId }) => {
      const gameData = await redis.get(gameId);
      const game = JSON.parse(gameData);
      return {
        mineCount: game.mineCount,
        mineField: game.gameOver ? game.mineField : [],
        betAmount: game.betAmount,
        multiplier: game.multiplier,
        isWinner: game.betAmount * game.multiplier > 0 ? true : false,
        rounds: game.isWinner ? game.rounds : game.rounds + 1,
        winningAmount:
          game.multiplier == null ? 0 : game.betAmount * game.multiplier,
        updatedAt: game.updatedAt,
      };
    },
  },
  Mutation: {
    startGame: async (_, { betAmount, mineCount }) => {
      const gameId = uuidv4();
      const mineField = generateMineField(mineCount);
      const multiplierArray = generatePayoutMultipliers(25, mineCount, 0.98);
      const updatedAt = new Date().toISOString();
      const gameData = {
        mineField,
        betAmount,
        mineCount,
        gameOver: false,
        rounds: -1, 
        positionSelected: [],
        multipliers: multiplierArray,
        updatedAt,
      };

      await redis.set(gameId, JSON.stringify(gameData));

      return { gameId, betAmount, mineCount, updatedAt };
    },
    cashoutResult: async (_, { gameId }) => {
      const gameData = await redis.get(gameId);
      const game = JSON.parse(gameData);

      game.gameOver = true;
      await redis.set(gameId, JSON.stringify(game));
      return {
        mineCount: game.mineCount,
        mineField: game.gameOver ? game.mineField : [],
        betAmount: game.betAmount,
        multiplier: game.multiplier,
        isWinner: game.betAmount * game.multiplier > 0 ? true : false,
        rounds: game.isWinner ? game.rounds : game.rounds + 1,
        winningAmount:
          game.multiplier == null ? 0 : game.betAmount * game.multiplier,
        updatedAt: game.updatedAt,
      };
    },
    selectTile: async (_, { gameId, position }) => {
      const gameData = await redis.get(gameId);
      const game = JSON.parse(gameData);

      if (game.gameOver) {
        return {
          multiplier: game.multiplier,
        };
      }

      if (!Array.isArray(game.positionSelected)) {
        game.positionSelected = [];
      }

      if (!game.positionSelected.includes(position)) {
        game.positionSelected.push(position);
        game.rounds += 1;
      }

      const isMine = game.mineField[position] === "M";
      const isGem = game.mineField[position] === "G";
      const updatedAt = new Date().toISOString();
      game.isMine = isMine;
      game.updatedAt = updatedAt;

      if (isMine) {
        game.gameOver = true;
        game.isWinner = false;
        game.multiplier = 0;
        game.winningAmount = 0;
      } else if (
        isGem &&
        !game.gameOver &&
        game.rounds < game.multipliers.length
      ) {
        game.multiplier = game.multipliers[game.rounds].payoutMultiplier;
        game.winningAmount = game.betAmount * game.multiplier;
      } else if (game.gameOver) {
        throw new Error("Game over. You can't select any more tiles.");
      } else {
        throw new Error("Invalid rounds index for multipliers array.");
      }

      await redis.set(gameId, JSON.stringify(game));

      return {
        isMine,
        multiplier: game.multiplier,
        winningAmount: game.winningAmount,
        updatedAt,
      };
    },
  },
};
```


**Wrapping Up**
Building the backend for a Mine Game was a fantastic learning experience. If you’re interested in game development or just curious about how such games work, I hope this overview helps you get started!

Soon gonna add a payment gateway to manage wallet and deploying it on AWS EKS.

Try this out and feel free to comment down your queries! Stay tuned for more insights! 🚀