Skip to main content
All CollectionsFairness
Dice duel game - PvP
Dice duel game - PvP
Nootiih avatar
Written by Nootiih
Updated over a week ago

PvP uses a system of provable fairness whereby the public seed is not known to anyone until the bets have already been committed and the game has started. This ensures that nobody can know the result in advance, not even us, since a part of the three-part seed used to generate the final result is completely unknown until all the bets have been committed.

We offer verification which allows users to check the integrity of every game and confirm that the results were not manipulated. Every randomly generated result is calculated from a three-part seed based on a seed pair and a nonce. The seed pair is made up of a public seed and a server seed. This pair is "nonced" with a unique number to generate a random final result.

For Dice Duels, there is a different three-part seed for each player. A separate seed is required in order to generate a unique result for each participant. The public seed (EOS Hash)is the same for all players, but each user has their own separate server seed (and nonce) generated for each round.

NONCE

For Dice Duels, we use the numerical ID of the PvP bet ID as the nonce, this ID is a unique sequential number which is incremented each time a new PvP bet is made by anyone on the site. Each player in the round has their own unique nonce, which corresponds to their own PvP bet ID. The nonces used are also published and committed to before the EOS Hash (public seed) is known to us. The nonces are visible in the Provably Fair modal from the PvP Duel Page and can be seen before the round has been started or the public seed (EOS Hash) has been generated.

Since it is technically possible to have a tie in a Dice Duel (where more than one player throws the same total on their dice), we allow for re-rolls in this case. For each re-roll, each participant in the tie-breaker will have their same nonce incremented by 1 and a new result will be generated until a single winner is eventually found.

For example, if Player A's nonce is 100 and Player B's nonce is 200 and they tie, a re-roll will occur whereby the seeds remain the same but Player A's nonce is now 101 and Player B's nonce is now 201.

PUBLIC SEED (EOS HASH)

The public seed (EOS Hash) is known only when all of the players in the round have committed their bets. We achieve this by committing to an EOS Block that is not yet mined but will be in the near future, and we then use its hash as the public seed. This way, we can prove that we had no way of knowing what the seed would be in advance before the game starts.

SERVER SEED

The server seed is a random string which is generated for each player at the beginning of a new PvP Round. We commit to this seed as soon as each player joins a round, and we immediately display the SHA256 hash of this seed. As soon as the round has been played, we then reveal the un-hashed version of the players' seeds. You can confirm that the server seeds for each player were unmodified and no manipulation was done on our end by comparing the original SHA256 hash (the one that was displayed at the beginning) with the revealed seed shown after. This can be verified independently in a programming language such as JavaScript, by applying the SHA256 hashing function to the revealed seed and comparing the output with the hash which was shown earlier. It can also easily be confirmed online using tools such ashttps://xorbin.com/tools/sha256-hash-calculator

INDEPENDENT VERIFICATION

Each game result can be verified independently using the algorithmic formula that we use to generate the result. We have created an easy way for you to execute this code directly from your browser. It runs the exact same code which is shown below, without the hassle of having to set up Node.js on your home computer. Just pass in the three parts of the seed mentioned above (the EOS Hash, the server seeds and the nonces). These elements are all best retrieved from the Provably Fair popup dialog found on the PvP Duel page, as you can easily copy and paste each of them.

const crypto =require('crypto');// --- BEGIN: Fill these values// Hash (ID) of the mined EOS block in the future (assigned on the round start)const clientSeed ='0f2dd208fd2823c79c5db2560a99ae4a2795932c223ceef2e56740ef93db0be3';// Server seed created when new bet is createdconst yourServerSeed ='efc97365afe67db27c2a9a7aea09f4aef160ba6a5a512ecd096a5759bab5341c';const opponentServerSeed ='bb104caec1fe268aaf052da6d4e0937225545f85a906af7a229f2aed453755cc';// Numeric ID of the specific bet (used later in nonce)const yourBetId =10005;const opponentBetId =10007;// --- END: Fill these values// With example values ^ it will output `Your roll value: 14 --- Opponent's roll value: 26`// With example values ^ it will output `Your roll result: 3,3 --- Opponent's roll result: 5,3`// With example values ^ it will output `Your total of the roll: 6 --- Opponent's total of the roll: 8`// With example values ^ it will output `You lost`// Turn this on if you want to see all the messagesconst verboseMode =false;// Get possible dice rollsconst diceRolls =getDoubleDiceRolls();const diceRollsLength = diceRolls.length;log(`Number of possible dice rolls: ${diceRollsLength}`);log(`Possible dice rolls: ${JSON.stringify(diceRolls)}`);// Verify roll until single winner leftverifyRoll(1);/** * Below this line are algorithmic functions used for calculating a roll value * ============================================================================= */functionlog(message){if(verboseMode){ console.log(message);}}functiongetDoubleDiceRolls(numberOfSides =6){return Array.from({ length: numberOfSides * numberOfSides }).map((v, i)=>`${Math.floor(i / numberOfSides)+1},${(i % numberOfSides)+1}`,);}/** * @param rollNumber Which roll this is (used for nonce and only changes if roll was a draw) */functionverifyRoll(rollNumber){// Get nonceconst yourNonce = yourBetId + rollNumber;const opponentNonce = opponentBetId + rollNumber;log(`Roll number: ${rollNumber}`);log(`Your nonce value: ${yourNonce}`);log(`Opponent's nonce value: ${opponentNonce}`);// Get seedsconst game =undefined;const yourSeed =getCombinedSeed(game, yourServerSeed, clientSeed, yourNonce);const opponentSeed =getCombinedSeed(game, opponentServerSeed, clientSeed, opponentNonce);// Get random roll value using provided informationconst max = diceRollsLength;const yourRollValue =getRandomInt({ max, seed: yourSeed }); console.log(`Your roll value: ${yourRollValue}`);const opponentRollValue =getRandomInt({ max, seed: opponentSeed }); console.log(`Opponent's roll value: ${opponentRollValue}`);// Get resultconst yourRollResult = diceRolls[yourRollValue];const opponentRollResult = diceRolls[opponentRollValue];log(`Selecting element (for you) at index "${yourRollValue}" from zero-indexed array of possible dice rolls`); console.log(`Your roll result: ${yourRollResult}`);log(`Selecting element (for opponent) at index "${opponentRollValue}" from zero-indexed array of possible dice rolls`); console.log(`Opponent's roll result: ${opponentRollResult}`);if(verboseMode){ console.table(diceRolls);}// Get the total of rollconst yourRollTotal = yourRollResult.split(',').reduce((total, diceRoll)=> total ++diceRoll,0);const opponentRollTotal = opponentRollResult.split(',').reduce((total, diceRoll)=> total ++diceRoll,0); console.log(`Your total of the roll: ${yourRollTotal}`); console.log(`Opponent's total of the roll: ${opponentRollTotal}`);// Check who wonif(yourRollTotal === opponentRollTotal){ console.log(`It's a draw, check next roll to find the final winner`); console.log("\n\r");verifyRoll(rollNumber +1);}elseif(yourRollTotal > opponentRollTotal){ console.log(`You won`);}else{ console.log(`You lost`);}}functiongetRandomInt({ max, seed }){// Get hash from seedlog(`Seed value: ${seed}`);const hash = crypto.createHmac('sha256', seed).digest('hex');// Get value from hashconst subHash = hash.slice(0,13);const valueFromHash = Number.parseInt(subHash,16);// Get dynamic result for this rollconst e = Math.pow(2,52);const result = valueFromHash / e;return Math.floor(result * max);}functiongetCombinedSeed(game, serverSeed, clientSeed, nonce){// Add main parametersconst seedParameters =[serverSeed, clientSeed, nonce];// Add game parameter if neededif(game){ seedParameters.unshift(game);}// Combine parameters to get seed valuereturn seedParameters.join('-')}

Note:Our Random Number Generator algorithm was updated on 19th Oct 2022.
For all games playedbefore PvP Bet ID #201,649,533, please use this code for independent verification instead.

If you have any further questions about our provably fair system, please do not hesitate to contact our support team for more help and information.

Did this answer your question?