Qué son los eventos del juego
Anteriormente, generamos valores flotantes aleatorios entre 0 y 1 mediante las funciones generateFloats y byteGenerator. Los eventos del juego convierten estos valores flotantes generados en resultados reales del juego. Esto sucede de distintas maneras según el juego. En Plinko, los valores flotantes ayudan a determinar la dirección de la caída de la bola. En Dice, los valores flotantes ayudan a determinar el resultado de la tirada de dados.
A continuación se incluye una explicación detallada de cómo convertimos los valores flotantes en eventos o resultados justos para cada juego original de nuestra plataforma.
Tower
En Tower, el objetivo del juego es llegar a la cima de la torre y salvar a la princesa encontrando las llaves y evitando las manzanas envenenadas. Cada fila de la torre contiene una cantidad predefinida de casillas seguras (llaves) y manzanas envenenadas, según la dificultad seleccionada.
El evento del juego en Tower consiste en determinar la posición de las casillas seguras (o llaves) en cada fila.
Hay un total de 9 filas en la torre. El número de columnas y la distribución de llaves y manzanas envenenadas varían según la dificultad elegida:
Fácil: 4 columnas - 3 llaves y 1 manzana envenenada por fila
Medio: 3 columnas - 2 llaves y 1 manzana envenenada por fila
Difícil: 2 columnas - 1 llave y 1 manzana envenenada por fila
Experto: 3 columnas: - 1 llave y 2 manzanas envenenadas por fila
Maestro: 4 columnas - 1 llave y 3 manzanas envenenadas por fila
El resultado de cada fila se determina generando posiciones aleatorias para las llaves (casillas seguras) dentro de las columnas disponibles según la configuración de dificultad.
const TOWER_DIFFICULTY_TO_CONFIG: Record<TowerDifficulty, DifficultyConfig> = {
[TowerDifficulty.EASY]: {
count: 3,
columns: 4,
},
[TowerDifficulty.MEDIUM]: {
count: 2,
columns: 3,
},
[TowerDifficulty.HARD]: {
count: 1,
columns: 2,
},
[TowerDifficulty.EXPERT]: {
count: 1,
columns: 3,
},
[TowerDifficulty.MASTER]: {
count: 1,
columns: 4,
},
};
// Generate floats to determine the positions of safe tiles (keys) for each row
const floats = generateFloats({
serverSeed,
clientSeed,
nonce,
cursor,
count,
});
// Get the tower configuration based on the selected difficulty
const towerConfig = TOWER_DIFFICULTY_TO_CONFIG[difficulty];
const results: number[][] = [];
const resultPerRow = towerConfig.count;
// Iterate through each row to determine the positions of safe tiles for that row
for (let i = 0; i < TOWER_TOTAL_ROWS.toNumber(); i++) {
const result: number[] = [];
const values = Array.from(Array(towerConfig.columns).keys());
// Get positions for safe tiles in the current row
for (let j = 0; j < resultPerRow; j++) {
const float = floats[i * resultPerRow + j];
if (float === undefined) {
break;
}
const position = Math.floor(float * (towerConfig.columns - j));
result.push(values.splice(position, 1)[0] ?? 0);
}
results.push(result);
}
return results;
// Example: when clientSeed='abc', serverSeed='def', nonce=1, difficulty='easy'
// results: [
// [0, 1, 2],
// [0, 2, 3],
// [0, 2, 3],
// [0, 1, 2],
// [0, 1, 3],
// [0, 1, 2],
// [1, 2, 3],
// [0, 1, 3],
// [0, 1, 3]
// ]
// Each sub-array represents the column indexes of safe tiles for that row
// (e.g., for the first row, safe tiles are in columns 0, 1, and 2)
Plinko
En Plinko, los jugadores pueden elegir entre una pirámide con una altura de 8 a 16. A medida que la bola se libera desde la parte superior, caerá a la izquierda o a la derecha entre 8 y 16 veces cada vez que golpee una clavija en el tablero de Plinko. El evento del juego en Plinko consiste en determinar si la bola cae a la izquierda o a la derecha en cada nivel, representado por 0 y 1.
// Crear varios valores flotantes entre 0 y 1
// Ejemplo: floats = [0.8320531346835196, 0.8531235849950463, 0.18227359023876488, 0.774284637067467, 0.18627392360940576, 0.6012540922965854, 0.608081541955471, 0.46018488449044526] cuando clientSeed='abc', serverSeed='def', nonce=1, count=8
// count se refiere al número de filas de Plinko
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor, count });
// Ejemplo: [1, 1, 0, 1, 0, 1, 1, 0] Cada 1 representa una caída hacia la derecha, mientras que 0 representa una caída hacia la izquierda
const dropDirections = floats.map(val => Math.floor(val * 2));
// Esto suma todos los valores de la matriz para obtener la posición final de la bola desde la izquierda.
// Ejemplo: finalPositionFromLeft = 5 (la bola caerá en la 6.ª ranura desde la izquierda)
const finalPositionFromLeft = dropDirections.reduce((cur, acc) => cur + acc, 0)
Dice
En Dice, una tirada de dados puede tener valores entre 0.00 y 100.00. El evento del juego en Dice consiste en determinar este valor. Lo hacemos mediante (floats * 10,001) / 100). Como incluimos valores con hasta 2 decimales y el número 0, hay un total de 10,000 + 1 = 10,001 resultados posibles (incluidos los decimales).
// Creates an array of random numbers
// Example: With clientSeed = 'abc', serverSeed = 'def', nonce = 1, floats = [0.43053962965495884]
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor: 0, count: 1 });
// Converts the value in the array to a number between 0.00 to 100.00 and return the value
// Example: resultValue = [43.05]
const resultValue = floats.map(val => Math.floor(floats * 10001) / 100);
return resultValue[0];
// Example: 43.05 is returned as the result of the game
Limbo
En Limbo, el objetivo del juego es adivinar un multiplicador >1. Si el resultado del juego es mayor que el multiplicador, el jugador gana el juego. El evento del juego consiste en generar un valor flotante superior a 1 y que siga una distribución de curva hiperbólica.
// Generate float between 0 and 1
// Example: floats = [0.43053962965495884], float = 0.43053962965495884,
// when clientSeed='abc', serverSeed='def', nonce=1
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor: 0, count: 1 });
const float = floats[0]
// generate limbo game event results with house edge (1%)
// using a hyperbolic 1/x curve
// multiplying by this and dividing by (MAX_VALUE + 1) prevents division by 0 error
const MAX_VALUE = 2 ** 24;
const rtp = 0.99;
// Example: rawValue = 2.298905
const rawValue = (rtp * MAX_VALUE) / (MAX_VALUE * float + 1)
// resultValue = 2.29
const resultValue = (0.99 * MAX_VALUE / (MAX_VALUE * float +1)).toFixed(2,ROUND_DOWN);
return resultValue;
Keno
En Keno, el evento del juego requiere identificar 10 ubicaciones únicas de gemas en el tablero de Keno. Para lograrlo, generamos 10 valores flotantes diferentes entre 1 y 40, que representan cada ubicación en el tablero de Keno. Luego se utiliza un método para manejar los valores flotantes duplicados y devolver las 10 ubicaciones finales de las gemas.
// Generate 10 different floats as there are 10 positions that are selected when the game is played
// Example: floats = [18, 26, 7, 22, 31, 10, 23, 8, 24, 13] when clientSeed='abc', serverSeed='def', nonce=1
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor: 0, count: 10 }) .map((val, index) => Math.ceil(val * (40 - index)))
// array used to store the position of the gems const gemLocations: number[] = [];
// this loop helps to prevent duplicate gem positions by skipping positions that already contain a gem
for (const val of floats) { let currentGem = val;
let i = 0;
while (i <= currentGem) {
if (gemLocations.includes(i)) {
currentGem++;
}
i++;
}
gemLocations.push(currentGem);
}
// returns the location of the gem on the board
// Example: [18, 27, 7, 24, 35, 11, 28, 9, 31, 16] return gemLocations;
Mines
En Mines, el evento del juego requiere identificar la ubicación de las minas en el tablero de Mines de 25 casillas. Según la selección del jugador de una cantidad de minas, generamos una lista de enteros del 0 al 24 para representar los 25 espacios del tablero.
// Generate 10 different floats
// Example: [20, 20, 4] when clientSeed='abc', serverSeed='def', nonce=1, numberOfBombs=3
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor: 0, count: numberOfBombs })
.map((val, index) => Math.floor(val * (25 - index)));
// array used to store the position of the bombs
const mineLocations: number[] = [];
// this loop helps to prevent duplicate bomb positions by skipping positions that already contain a mine
for (const val of floats) {
let currentMine = val;
let i = 0;
while (i <= currentMine) {
if (mineLocations.includes(i)) {
currentMine++;
} i++; } mineLocations.push(currentMine); }
// returns the location of the bombs on the board
// Example: [20, 21, 4]
return mineLocations;
Roulette
Nuestra Roulette se basa en la ruleta europea de un solo «0», que tiene 37 casillas diferentes del 0 al 36. El evento del juego en Roulette consiste en calcular en cuál de las 37 casillas caerá la bola.
// Generate a float between 0 and 1
// Example: floats = [0.43053962965495884]
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor: 0, count: 1 });
// generate an integer from 0 to 37 to represent the pocket to land on
// Example: resultValue = 15
const resultValue = floats.map(val => Math.floor(val * 37))[0];
return resultValue
Wheel
En Wheel, el jugador seleccionará la cantidad de segmentos que tiene la rueda. Según el segmento en el que se detenga el indicador, el jugador ganará el multiplicador de ese segmento. El evento del juego consiste en determinar el segmento.
// Generate a float between 0 and 1
// Example: floats = [0.43053962965495884]
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:1 });
// Get an integer representing the number of segments set in the game
// Example: resultSegment = 8
const resultSegment = floats.map(val => Math.floor(val * numberOfSegments))[0];
return resultSegment
Blackjack
En Blackjack, el juego se juega con una baraja ilimitada. Por lo tanto, cada turno siempre tendrá la misma probabilidad. El evento del juego en Blackjack consiste en determinar la carta que se roba si el jugador o la banca roba una carta de la baraja.
// Generate a float between 0 and 1
// Example: floats = [0.43053962965495884, 0.6645898742135614, 0.17454026662744582, 0.5729992990382016, 0.850033157505095] (52 elements are generated but only 5 are shown here for brevity)
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:52 });
const CARDS = [ ♦2, ♥2, ♠2, ♣2, ♦3, ♥3, ♠3, ♣3, ♦4, ♥4, ♠4, ♣4, ♦5, ♥5, ♠5, ♣5, ♦6, ♥6, ♠6, ♣6, ♦7, ♥7, ♠7, ♣7, ♦8, ♥8, ♠8, ♣8, ♦9, ♥9, ♠9, ♣9, ♦10, ♥10, ♠10, ♣10, ♦J, ♥J, ♠J, ♣J, ♦Q, ♥Q, ♠Q, ♣Q, ♦K, ♥K, ♠K, ♣K, ♦A, ♥A, ♠A, ♣A
];
// Get an integer 0 to 51, representing the index of the cards
// Example: cardFloats = [22, 34, 9, 29, 44]
const cardFloats = floats.map(val => Math.floor(val * 52));
// Example: cards = [♠7, ♠10, ♥4, ♥9, ♦K]
const cards = cardFloats.map(val => CARDS[val])
return cards;
Hilo
En Hilo, el juego se juega con una baraja ilimitada. Por lo tanto, cada turno siempre tendrá la misma probabilidad. El objetivo del juego es adivinar si la siguiente carta tiene un valor mayor o menor que la carta actual. El evento del juego en Hilo consiste en determinar qué carta roba el jugador a continuación (si roba una).
// Generate a float between 0 and 1
// Example: floats = [0.43053962965495884, 0.6645898742135614, 0.17454026662744582, 0.5729992990382016, 0.850033157505095] (an unlimited number of cards can be drawn but only 5 are shown here for brevity)
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:52 });
const CARDS = [
♦2, ♥2, ♠2, ♣2, ♦3, ♥3, ♠3, ♣3, ♦4, ♥4, ♠4, ♣4, ♦5, ♥5, ♠5, ♣5, ♦6, ♥6, ♠6, ♣6, ♦7, ♥7, ♠7, ♣7, ♦8, ♥8, ♠8, ♣8, ♦9, ♥9, ♠9, ♣9, ♦10, ♥10, ♠10, ♣10, ♦J, ♥J, ♠J, ♣J, ♦Q, ♥Q, ♠Q, ♣Q, ♦K, ♥K, ♠K, ♣K, ♦A, ♥A, ♠A, ♣A
];
// Get an integer 0 to 51, representing the index of the cards
// Example: cardFloats = [22, 34, 9, 29, 44]
const cardFloats = floats.map(val => Math.floor(val * 52));
// Example: cards = [♠7, ♠10, ♥4, ♥9, ♦K]
const cards = cardFloats.map(val => CARDS[val])
return cards;
Baccarat
En Baccarat, el juego se juega con una baraja ilimitada. Por lo tanto, cada turno siempre tendrá la misma probabilidad. El evento del juego en Baccarat consiste en determinar las cartas repartidas a las manos del Jugador y de la Banca durante el reparto inicial y cualquier robo adicional conforme a las reglas estándar de robo de Baccarat.
Las cartas se reparten en el siguiente orden a partir de la secuencia de valores flotantes generada:
Primera carta del Jugador - índice de valor flotante 0
Primera carta de la Banca - índice de valor flotante 1
Segunda carta del Jugador - índice de valor flotante 2
Segunda carta de la Banca - índice de valor flotante 3
Tercera carta del Jugador (si se roba) - índice de valor flotante 4
Tercera carta de la Banca (si se roba)
índice de valor flotante 4 - si el Jugador no robó la tercera carta
índice de valor flotante 5 - si el Jugador roba la tercera carta
// Generate a float between 0 and 1
// Example: floats = [0.40353962965495884, 0.6645898742135614, 0.17454026662744582, 0.5729992990382016, 0.850033157505095] (an unlimited number of cards can be drawn but only 5 are shown here for brevity)
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:52 });
const CARDS = [
♠2, ♥2, ♦2, ♣2, ♠3, ♥3, ♦3, ♣3, ♠4, ♥4,
♦4, ♣4, ♠5, ♥5, ♦5, ♣5, ♠6, ♥6, ♦6, ♣6,
♠7, ♥7, ♦7, ♣7, ♠8, ♥8, ♦8, ♣8, ♠9, ♥9,
♦9, ♣9, ♠10, ♥10, ♦10, ♣10, ♠J, ♥J, ♦J,
♣J, ♠Q, ♥Q, ♦Q, ♣Q, ♠K, ♥K, ♦K, ♣K, ♠A,
♥A, ♦A, ♣A
];
// Get an integer 0 to 51, representing the index of the cards
// Example: cardFloats = [22, 34, 9, 29, 44]
const cardFloats = floats.map(val => Math.floor(val * 52));
// Example: cards = [♠7, ♠10, ♥4, ♠9, ♦K]
const cards = cardFloats.map(val => CARDS[val])
return cards;
Blitz
En Blitz, el juego se juega con una baraja ilimitada. Por lo tanto, cada turno siempre tendrá la misma probabilidad. El evento del juego en Blitz consiste en determinar las cartas repartidas al jugador según la cantidad de cartas que haya elegido robar y evaluar si todas las cartas robadas son únicas.
// Generate a float between 0 and 1
// Example: floats = [0.43053962965495884, 0.6645898742135614, 0.17454026662744582, 0.5729992990382016, 0.850033157505095] (an unlimited number of cards can be drawn but only 5 are shown here for brevity)
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:52 });
const CARDS = [
♠2, ♥2, ♣2, ♦2, ♠3, ♥3, ♣3, ♦3, ♠4, ♥4,
♣4, ♦4, ♠5, ♥5, ♣5, ♦5, ♠6, ♥6, ♣6, ♦6,
♠7, ♥7, ♣7, ♦7, ♠8, ♥8, ♣8, ♦8, ♠9, ♥9,
♣9, ♦9, ♠10, ♥10, ♣10, ♦10, ♠J, ♥J, ♣J, ♦J,
♠Q, ♥Q, ♣Q, ♦Q, ♠K, ♥K, ♣K, ♦K, ♠A,
♥A, ♣A, ♦A
];
// Get an integer 0 to 51, representing the index of the cards
// Example: cardFloats = [22, 34, 9, 29, 44]
const cardFloats = floats.map(val => Math.floor(val * 52));
// Example: cards = [♠7, ♠10, ♥4, ♠9, ♦K]
const cards = cardFloats.map(val => CARDS[val])
return cards;
Crash
Hay 2 partes en el cálculo del resultado de un juego de Crash
Hash de bloque de Bitcoin
Semilla
Hash de Bitcoin
El hash del bloque 949350 se puede encontrar en el explorador de bloques de BTC (https://btcscan.org/block/000000000000000000018aa25655f289ef0ee08c91a66500adab6ba1ff4a9790), y coincide con el hash que se muestra en el tuit.
Semilla
El proceso para generar hashes para 10 millones de juegos de Crash está diseñado para garantizar la imparcialidad y la transparencia, evitando cualquier posibilidad de manipulación por parte de Shuffle. Así es como se generan y verifican:
Generación de la semilla inicial: La semilla para el juego número 10 millones es elegida aleatoriamente por Shuffle .
Hash secuencial para la generación de semillas: Para generar el hash de todos los demás juegos, utilizamos un método de hash inverso:
El hash de cada juego es el resultado de aplicar hash al hash del juego posterior. Específicamente, el hash del juego número 9,999,999 es el hash SHA256 del hash del juego número 10 millones.
Este encadenamiento continúa de modo que el hash de cada juego se deriva de aplicar hash al hash del juego siguiente, culminando con el hash del primer juego, que se deriva del hash del segundo juego.
Verificación de la integridad de la semilla:
Al publicar el valor con hash de la semilla utilizada en el primer juego, los jugadores pueden verificar de forma independiente la integridad del hash de todos los juegos hasta el juego número 10 millones.
Con este método, si conocen el hash de cualquier juego específico, pueden verificar el hash de todos los juegos anteriores. Por ejemplo, si conocen la semilla del juego 1323185, pueden aplicarle hash para verificar que coincida con el hash del juego 1323184, y así sucesivamente, hasta llegar al primer juego.
Por ejemplo, en el juego 55447 (https://shuffle.us/games/originals/crash?md-id=55447&modal=crashGame), el hash es c4b0c1394a9212cb3fc53674e756abba00973a970a4371c322463e8311e6e3e0
En el juego 55446 (https://shuffle.us/games/originals/crash?md-id=55446&modal=crashGame), el hash es ea98f461df194103890d8778487fc88390026bf1d1046dea6c278f10c98be2c0
El valor hash del juego 55446 es el hash SHA256 de 55447. Pueden usar una calculadora SHA256 en línea como https://xorbin.com/tools/sha256-hash-calculator para verificarlo
Transparencia y seguridad:
Al utilizar un hash futuro de un bloque de BTC que está fuera del control de Shuffle y publicar la versión con hash de la semilla del primer juego, los usuarios pueden comprobar que los resultados de los juegos de Crash son justos y no han sido manipulados.
Slide
Slide utiliza el mismo proceso de siembra y de imparcialidad verificable que Crash. La siembra se realizó públicamente en x.com, y la publicación se puede encontrar aquí.
El hash del bloque 952294 se puede encontrar en el explorador de bloques de BTC (https://btcscan.org/block/00000000000000000000a382760ff19cdb7f9f22147b9123f3e471fb3c6d129a) y coincide con el hash mostrado en el tuit.


