James Bachini

How To Shuffle A Deck Of Cards In Solidity

Solidity Shuffle

In this post I explore how to simulate the shuffling of a deck of cards, addressing the challenges of randomness and predictability in a solidity smart contract environment where every input and output is part of the public record.

Whether you’re looking to create an on-chain poker game, a collectible card game, or you’re simply curious about random processes on the blockchain, this guide will teach you the basics to ‘shuffle’ through your blockchain development journey effectively. So fire up remix, and lets deal the cards!

The Solidity Shuffle Code

The full code for this smart contract is available on the Solidity Snippets github repository: https://github.com/jamesbachini/Solidity-Snippets/blob/main/contracts/Shuffle.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract Shuffle {
    struct Card {
        uint8 suit; // 1-4
        uint8 rank; // 1-13
    }

    Card[] public deck;

    constructor() {
        // Create a deck of cards
        for (uint8 suit = 1; suit <= 4; suit++) {
            for (uint8 rank = 1; rank <= 13; rank++) {
                deck.push(Card(suit, rank));
            }
        }
    }

    function shuffle() public {
        uint256 deckSize = deck.length;
        for (uint256 i = 0; i < deckSize; i++) {
            uint256 j = uint256(keccak256(abi.encode(block.prevrandao, i))) % deckSize;
            Card memory tmpCard = deck[i];
            deck[i] = deck[j];
            deck[j] = tmpCard;
        }
    }
}

The Explainer

This contract is for a deck of cards, each with a suit (1-4) and a rank (1-13), and provides a method to shuffle those cards.

The contract starts with defining a Card struct that has two properties, suit and rank. These are each 8-bit unsigned integers, which means they can hold values from 0 to 255.

suit is expected to have the value from 1 to 4, representing the four suits in a deck of cards. Rank represents the 13 ranks in a deck, from 1 (Ace) to 13 (King).

The constructor for the contract, which is run once when the contract is deployed, initializes the deck array. It does this by looping over each possible combination of suit (1-4) and rank (1-13) and pushing a new Card with that suit and rank onto the deck.

At this point the cards are in order and we now need a shuffle function, which can be called by anyone to mix up the order within the array.

For each card in the deck (starting from the first card and going to the last one), it calculates a pseudorandom index j. Then it swaps the card at the current index with the card at the pseudorandom index. The pseudorandom index is calculated by taking the keccak256 hash of the block’s randao and the current index, converting that hash to an unsigned integer, and taking the modulus of that with the deck size.

This algorithm is known as the Fisher-Yates shuffle and ensures each permutation of the deck is equally likely if the used random number generator has a uniform distribution.

Note this simple implementation might be subject to validator manipulation because the randomness is somewhat dependent on the block builder. It could be improved by using a oracle as a source of randomness, something like Chainlink’s VRF, although this then adds a layer of centralization and trust to the protocol.

Another consideration is the predictiveness of the deck. In most card games the cards and order of the shuffle are concealed from the players but this difficult to do on a public blockchain network where all transactions and state are transparent.

Maybe there is a solution to a private shuffle mechanism using zero-knowledge tech but that is beyond the scope of this article and perhaps something to play with in the future.

Shuffle deck in solidity

Get The Blockchain Sector Newsletter, binge the YouTube channel and connect with me on Twitter

The Blockchain Sector newsletter goes out a few times a month when there is breaking news or interesting developments to discuss. All the content I produce is free, if you’d like to help please share this content on social media.

Thank you.

James Bachini

Disclaimer: Not a financial advisor, not financial advice. The content I create is to document my journey and for educational and entertainment purposes only. It is not under any circumstances investment advice. I am not an investment or trading professional and am learning myself while still making plenty of mistakes along the way. Any code published is experimental and not production ready to be used for financial transactions. Do your own research and do not play with funds you do not want to lose.