DEV Community

Hemant Singh Parmar
Hemant Singh Parmar

Posted on

Know Backend of Stake’s Mine Game

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

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,
      };
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

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! 🚀

Top comments (0)