O propósito comercial deste tutorial é ensinar como a plataforma Reach pode ser usada no desenvolvimento da Blockchain Algorand. Ela permite que os desenvolvedores criem novas soluções Algorand em uma fração do tempo, sem a necessidade de se preocupar em construir o contrato inteligente e os endpoints do cliente.
O objetivo deste tutorial é ensinar como criar um aplicativo descentralizado utilizando a plataforma Reach. Por meio de uma rede de consenso de back-end como a Algorand, permitiremos que esse jogo seja transferido e que receba valores na forma de tokens específicos da rede, como ALGO.
A criação de “contratos” garantirá que todos os jogadores sigam as mesmas regras em todas as jogadas.
Requisitos
Link para o React JS https://create-react-app.dev
Ícones do React https://react-icons.github.io/react-icons/
Tailwind https://tailwindcss.com/docs/guides/create-react-app
Craco https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md
Docker https://www.docker.com/get-started
Arquivo executável do Reach https://docs.reach.sh/install.html
Biblioteca JavaScript padrão do Reach https://docs.reach.sh/ref-frontends-js.html
VS Code https://code.visualstudio.com
Contexto
O jogo “RAPA” foi um jogo bem tradicional em Portugal, de origem judaica. Este jogo tem regras simples, com um pequeno pião de quatro faces, onde as letras R, T, P e D estão impressas de cada lado e um punhado de feijões, dinheiro ou o que você quiser jogar. Depois é só rodar o “RAPA” e esperar pela sua sorte.
Os jogadores decidem a quantia que será colocada no meio da mesa a cada vez que rodarem o “RAPA”.
De acordo com as letras que saem, o jogador realiza a tarefa.
R (RAPA) significa coletar tudo da mesa.
T (TIRA) significa retirar apenas um feijão ou a quantidade previamente estipulada.
P (PÕE) significa que o jogador deve colocar um ou a quantidade previamente estipulada na pilha de feijões.
D (DEIXA) ou sair, fica tudo igual e a vez passa para o próximo jogador.
O jogador que iniciar o jogo precisará adicionar fundos à Mesa de Jogo. Então seguindo as regras irá rodar o “RAPA” e aguardar o resultado.
O jogador pode conectar a conta usando a carteira MyAlgo, adicionar os fundos e usar a conta para jogar. A Mesa de Jogo será fornecida pelo usuário. O usuário deve adicionar uma Carteira de Jogo (GameWallet) para simular a Mesa de Jogo.
Quando o jogador inicia o jogo pressionando o botão jogar, o compilador do Reach deriva automaticamente um contrato para a rede de consenso por meio de um conector que impõe essas regras.
Passos:
Configurando o Projeto
Instalações externas
Instalação do Reach
Carteira MyAlgo
Animação RAPA
Adicionar fundos
Front-end do Jogo
Navegador da Web
index.rsh
Executar e testar
Projeto no Github
1. Configurando o Projeto
Primeiro, vamos começar a configurar o projeto.
Este projeto será construído usando o Visual Studio Code. O jogo em React RAPA será criado com uma ferramenta chamada Create React App. Esta ferramenta exigirá o NPM, certifique-se de ter o NPM instalado com o Node.js 5.2 ou superior.
Vamos começar a criar nosso aplicativo com a ferramenta Create React App. Abra seu Terminal no Mac ou seu prompt de comando no PC. Altere sua pasta de diretório, execute os seguintes comandos e pressione enter.
Nota: Se você receber algum erro ao executar este comando, primeiro execute
npm cache clean --force
E então crie seu aplicativo.
npx create-react-app myrapagame
cd myRapaGame
npm start
Figura 1 - Criar React
2. Instalações externas
Agora precisamos instalar outras bibliotecas e precisamos fazer todo o trabalho nós mesmos.
Neste projeto, precisamos instalar as seguintes bibliotecas de componentes React. Não tenha medo das instalações que precisamos para realizar isso, pois é o que fazemos quando trabalhamos na web. Essa é a maneira que é.
Ícones do React
npm install react-icons
CSS Tailwind
Um framework para deixar seu aplicativo com ótima aparência.
npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9 @tailwindcss/forms
Figura 2.1 - Tailwind CSS
CRACO
Como a versão atual do Create React App ainda não suporta PostCSS 8, mais um utilitário deve ser instalado (CRACO).
npm install @craco/craco
Abra seu projeto no Visual Studio Code. Precisamos modificar nosso package.json e adicionar esses scripts para que possamos usar o CRACO.
"start": "craco start",
"build": "craco build"
"test": "craco test",
Figura 2.2 - CRACO
Em seu aplicativo no Visual Studio Code, crie um arquivo craco.config.js e adicione o seguinte.
module.exports = {
style: {
postcss: {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
],
},
},
Figura 2.3 - Configuração do CRACO
Outra instalação requisita o plugin Tailwind CSS e o Autoprefixer, que faziam parte da instalação que fizemos anteriormente. Estamos quase lá.
npx tailwindcss init
Figura 2.4 - Plugin Tailwind CSS
Agora que criamos o tailwind.config.js, vamos configurar e personalizar o Tailwind CSS.
purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
...
plugins: [ require('@tailwindcss/forms')]
Figura 2.5 - Configuração do Tailwind
Precisamos alterar nosso arquivo CSS porque o tailwind precisa de três tipos diferentes de subcomponentes. Abra o arquivo index.css e substitua tudo por essas três linhas de código.
@tailwind base;
@tailwind components;
@tailwind utilities;
Figura 2.6 - index.css
3. Instalação do Reach
Em seguida, precisamos instalar a biblioteca padrão Reach JavaScript, stdlib, usando o terminal com este comando.
Observação: primeiro, certifique-se de instalar o Docker no Mac ou Windows com o Docker Desktop.
Primeiro, e na pasta certa “cd my-app”, instale o Reach executando no terminal:
curl https://docs.reach.sh/reach -o reach ; chmod +x reach
Figura 3.1 - Reach
Em segundo lugar, e na pasta certa “cd my-app”, instale a biblioteca padrão Reach JavaScript executando no terminal:
npm install @reach-sh/stdlib@latest
Figura 3.2 - Reach stdlib
Para executar seu aplicativo com o Reach, você precisa verificar se tudo está alinhado. Se a biblioteca stdlib do Reach não corresponder, você precisará executar os seguintes comandos nesta ordem:
docker system prune > ./reach docker-reset > ./reach upgrade > ./reach
Executar um comando após o outro deve alinhar tudo.
Figura 3.3 - Reach versão stdlib
O passo final e, acredite, o mais importante.
Primeiro, verifique se você está na sua pasta /src e execute
curl https://docs.reach.sh/reach -o reach ; chmod +x reach
Para executar seu aplicativo em um navegador da Web, precisamos compilar seu programa Reach index.rsh para o back-end e construir o artefato build/index.main.mjs executando ./reach compile. Não se esqueça que, para compilar, você precisa do Docker em execução. Depois que a compilação foi gerada, você pode fechar o Docker.
./reach compile
4. Carteira MyAlgo
A equipe do Reach adiciona novos recursos com frequência, portanto, verifique se você está executando a última versão.
Atualmente no Reach, existe a possibilidade do fornecimento de uma carteira que se conecta diretamente à rede Algorand.
Comece a importar a biblioteca Reach e o MyAlgoConnect para a Algorand em nosso arquivo App.js.
import * as backend from './build/index.main.mjs';
import MyAlgoConnect from "@reach-sh/stdlib/ALGO_MyAlgoConnect";
import { loadStdlib } from '@reach-sh/stdlib';
Em segundo lugar, é necessário especificar a conexão de rede e a chave para definir a função de fallback para a carteira como My Algo, usada com a Algorand TestNet.
const stdlib = loadStdlib("ALGO");
stdlib.setWalletFallback(
stdlib.walletFallback({
providerEnv: "TestNet",
MyAlgoConnect,
})
);
Figura 4 - Carteira MyAlgo
5. Animação do jogo RAPA
Em nosso arquivo App.js do jogo RAPA, começaremos a importar todos os ativos necessários para o jogo.
Na pasta /src, crio uma pasta de imagens para adicionar minhas imagens.
Em seguida, no App.js, importo todos os ativos.
import rapalogo from "../src/imgs/rapalogo.png";
import deixa from "../src/imgs/deixa.png";
import poemimg from "../src/imgs/poem.png";
import rapaimg from "../src/imgs/rapa.png";
import tiraimg from "../src/imgs/tira.png";
import rapas from "../src/imgs/rapas.jpg";
Figura 5.1 - Ativos
Os componentes do React serão renderizados novamente, então vamos incorporar os ganchos useState e useEffect da biblioteca React. Um gancho é uma função que permite adicionar funcionalidade a um componente. Ele simplesmente renderiza um estado do jogo.
import React, { useState, useEffect } from 'react';
Adicione as variáveis do jogo.
var addr = "Oi";
var addrGameWallet = "Oi";
var balAtomic = "saldo";
var balAtomicGameWallet = "saldo";
var roll = 0;
var hand = 0;
var WagerValue = 0;
var playerpaytoplay = false;
var poem = 2;
var tira = 0;
var rapa = 0;
var gameWalletBalance = '';
var fmtGameWallet = '';
var fmt = '';
Gancho useState e useEffect
Um gancho é uma função que permite adicionar alguma funcionalidade ao componente. E useState é um gancho embutido que podemos usar para lidar com mudanças de estado em nosso aplicativo. O useEffect nos permitirá realizar efeitos secundários dentro de nossos componentes de função. Isso será adicionado dentro de nossa função “ComponentDidMount1”.
const [Aliceplayer, setAliceplayer] = useState('');
useEffect(() => {
}, [Aliceplayer]);
const [alicePublicKey, setalicePublicKey] = useState('');
useEffect(() => {
}, [alicePublicKey]);
const [walletG, setWalletG] = useState('');
useEffect(() => {
}, [walletG]);
const [rapalist, setRapaValue] = useState();
useEffect(() => {
}, [rapalist]);
const [rapaRound, setrapaRound] = useState();
useEffect(() => {
}, [rapaRound]);
const [tablegamevalue, settableGameValue] = useState();
useEffect(() => {
}, [tablegamevalue]);
const [input, setInput] = useState('');
Animação do Rapa
Vou criar uma função Rapa aqui, declarando props, ou properties, que é um objeto em React que contém propriedades sobre o componente.
A propriedade agora pode ser aceita dinamicamente sempre que o valor mudar, e pode ser exibida na tela.
function Rapa(props) {
if (props.list === 0) {
return <img src={poemimg} height={200} alt="Algorand Blockchain" />;
} else if (props.list === 1) {
return <img src={tiraimg} height={200} alt="Algorand Blockchain" />;
} if (props.list === 2) {
return <img src={rapaimg} height={200} alt="Algorand Blockchain" />;
} if (props.list === 3) {
return <img src={deixa} height={200} alt="Algorand Blockchain" />;
} else {
return <img src={rapas} height={200} alt="Algorand Blockchain" />;
}
}
Figura 5.2 - Faces do Rapa
Dentro da nossa função “ComponentDidMount1”, declararei a função “rapaAnimation”. Nesta etapa, o jogo irá simular a rotação do RAPA e gerar uma das possíveis probabilidades “P”, “T”, “R” e “D”.
Um setTimeout será adicionado para que o jogador possa ver o resultado antes que a função “getGameResult” seja chamada.
function rapaAnimation() {
if (playerpaytoplay === true) {
(async () => {
const accgameWalletUpdate = await stdlib.newAccountFromMnemonic(input);
const balAtomicGameWalletUpdate = await stdlib.balanceOf(
accgameWalletUpdate
);
const gameWalletBalanceUpdate = stdlib.formatCurrency(
balAtomicGameWalletUpdate,
4
);
setWalletG(gameWalletBalanceUpdate);
})();
setTimeout(getGameResult, 2000);
let counter = 0;
const interval = setInterval(() => {
counter += 1;
if (counter >= 15) clearInterval(interval);
roll = Math.floor(Math.random() * 4);
setRapaValue(roll);
//console.log(roll);
playerpaytoplay = false;
}, 100);
} else {
console.log("adicione fundos para jogar!");
playerpaytoplay = false;
alert("Adicione fundos para jogar!");
}
}
Figura 5.3 - Animação do jogo Rapa
6. Adicione fundos
Primeiro, no arquivo App.js do jogo RAPA, começaremos a importar todos os ativos necessários para o jogo. Antes que qualquer jogador possa começar a rodar o RAPA, ele deve seguir as regras. A primeira regra, cada jogador precisa estar na Mesa de Jogo com 10 Algos e assim o jogador pode rodar o RAPA.
Agora, conecte a Carteira MyAlgo.
Precisamos definir a conta padrão para a carteira MyAlgo. Para a proposta deste tutorial, todos os jogadores escolhem e concordam em adicionar uma carteira como a Mesa de Jogo, para que todos os jogadores possam adicionar e obter os fundos. Um campo de input é adicionado na tela do aplicativo para que seja inserida a conta mnemônica para a Mesa de Jogo.
O Reach realizará a transação com a expressão de transferência. O “stdlib.transfer” realizará uma transferência de tokens da conta padrão “accAlice” para a conta receptora “accgameWallet” com o valor estabelecido “MICROALGOS”.
async function foundGameTableAccount() {
if (input !== "") {
const MICROALGOS = '10000000';
const accAlice = await stdlib.getDefaultAccount();
const accgameWallet = await stdlib.newAccountFromMnemonic(input);
const balAtomic = await stdlib.balanceOf(accAlice);
const balAtomicGameWallet = await stdlib.balanceOf(accgameWallet);
console.log(balAtomicGameWallet);
await stdlib.transfer(accAlice, accgameWallet, MICROALGOS);
console.log(balAtomic);
gameWalletBalance = stdlib.formatCurrency(balAtomicGameWallet, 4);
setWalletG(gameWalletBalance);
playerpaytoplay = true;
alert("Mesa de jogo financiada!");
} else {
playerpaytoplay = false;
alert("O input mnemônico está vazio!");
}
}
Figura 6 - Adicionar fundos
7. Front-end do jogo
Agora definimos uma função assíncrona e criamos o corpo do nosso jogo. O jogador “accAlice” e a Mesa de Jogo como “accgameWallet” são as contas que serão utilizadas neste jogo.
O saldo será definido para que possa ser renderizado na tela como a chave pública do jogador no jogo.
Figura 7.1 - O jogador
Contrato
Cada participante tentará implantar um contrato para uma aplicação. O Reach define que para interagir com um contrato implantado, você deve construir um identificador de contrato a partir de uma conta.
As probabilidades
Para as constantes definidas aqui para os arrays que guardam o significado dos resultados do jogo, mantenho os nomes das variáveis conforme o tutorial da documentação do Reach.
Você pode verificar esse conteúdo neste link.
As variáveis estão agora implementadas.
Figura 7.2 - As probabilidades
Promessa
Uma promessa é um objeto que representa o resultado final de uma operação assíncrona.
Nesta etapa, o aplicativo aguardará a conclusão da operação e acompanhará seu estado.
Figura 7.3 - Promessa
import rapalogo from "../src/imgs/rapalogo.png";
import deixa from "../src/imgs/deixa.png";
import poemimg from "../src/imgs/poem.png";
import rapaimg from "../src/imgs/rapa.png";
import tiraimg from "../src/imgs/tira.png";
import rapas from "../src/imgs/rapas.jpg";
import React, { useState, useEffect } from "react";
import * as backend from "./build/index.main.mjs";
import MyAlgoConnect from "@reach-sh/stdlib/ALGO_MyAlgoConnect";
import { loadStdlib } from "@reach-sh/stdlib";
const stdlib = loadStdlib("ALGO");
stdlib.setWalletFallback(
stdlib.walletFallback({
providerEnv: "TestNet",
MyAlgoConnect,
})
);
var addr = "Oi";
var addrGameWallet = "Oi";
var balAtomic = "saldo";
var balAtomicGameWallet = "saldo";
var roll = 0;
var hand = 0;
var WagerValue = 0;
var playerpaytoplay = false;
var poem = 2;
var tira = 0;
var rapa = 0;
var gameWalletBalance = "";
var fmtGameWallet = "";
var fmt = "";
function Rapa(props) {
if (props.list === 0) {
return <img src={poemimg} height={200} alt="Algorand Blockchain" />;
} else if (props.list === 1) {
return <img src={tiraimg} height={200} alt="Algorand Blockchain" />;
}
if (props.list === 2) {
return <img src={rapaimg} height={200} alt="Algorand Blockchain" />;
}
if (props.list === 3) {
return <img src={deixa} height={200} alt="Algorand Blockchain" />;
} else {
return <img src={rapas} height={200} alt="Algorand Blockchain" />;
}
}
function ComponentDidMount1(props) {
async function foundGameTableAccount() {
if (input !== "") {
const MICROALGOS = "10000000";
const accAlice = await stdlib.getDefaultAccount();
const accgameWallet = await stdlib.newAccountFromMnemonic(input);
const balAtomic = await stdlib.balanceOf(accAlice);
const balAtomicGameWallet = await stdlib.balanceOf(accgameWallet);
console.log(balAtomicGameWallet);
await stdlib.transfer(accAlice, accgameWallet, MICROALGOS);
console.log(balAtomic);
gameWalletBalance = stdlib.formatCurrency(balAtomicGameWallet, 4);
setWalletG(gameWalletBalance);
playerpaytoplay = true;
console.log(gameWalletBalance);
alert("Mesa de Jogo financiada!");
} else {
playerpaytoplay = false;
alert("O input mnemônico está vazio!");
}
}
function rapaAnimation() {
if (playerpaytoplay === true) {
(async () => {
const accgameWalletUpdate = await stdlib.newAccountFromMnemonic(input);
const balAtomicGameWalletUpdate = await stdlib.balanceOf(
accgameWalletUpdate
);
const gameWalletBalanceUpdate = stdlib.formatCurrency(
balAtomicGameWalletUpdate,
4
);
setWalletG(gameWalletBalanceUpdate);
})();
setTimeout(getGameResult, 2000);
let counter = 0;
const interval = setInterval(() => {
counter += 1;
if (counter >= 15) clearInterval(interval);
roll = Math.floor(Math.random() * 4);
setRapaValue(roll);
//console.log(roll);
playerpaytoplay = false;
}, 100);
} else {
console.log("adicione fundos para jogar!");
playerpaytoplay = false;
alert("Adicione fundos para jogar!");
}
}
function getGameResult() {
tira = Math.round(gameWalletBalance * 0.1);
rapa = Math.round(gameWalletBalance * 0.8);
console.log("O valor de põe é " + poem);
console.log("O valor de tira é " + tira);
console.log("O valor de rapa é " + rapa);
hand = roll;
if (roll === 0) {
WagerValue = poem;
settableGameValue(WagerValue);
} else if (roll === 1) {
WagerValue = tira;
settableGameValue(WagerValue);
} else if (roll === 2) {
WagerValue = rapa;
settableGameValue(WagerValue);
} else if (roll === 3) {
WagerValue = 0;
settableGameValue(WagerValue);
} else {
WagerValue = 0;
settableGameValue(WagerValue);
}
//console.log(WagerValue);
console.log("!!!!O valor de aposta é " + WagerValue);
console.log("!!!!O valor da mão é " + hand);
console.log("pague para jogar");
console.log(playerpaytoplay);
(async () => {
const accAlice = await stdlib.getDefaultAccount();
const accgameWallet = await stdlib.newAccountFromMnemonic(input);
addr = stdlib.formatAddress(accAlice.getAddress());
addrGameWallet = stdlib.formatAddress(accgameWallet.getAddress());
console.log(addr);
console.log(addrGameWallet);
balAtomic = await stdlib.balanceOf(accAlice);
fmt = (x) => stdlib.formatCurrency(balAtomic, 4);
balAtomicGameWallet = await stdlib.balanceOf(accgameWallet);
fmtGameWallet = (x) => stdlib.formatCurrency(balAtomicGameWallet, 4);
setAliceplayer(fmt);
setalicePublicKey(addr);
setWalletG(fmtGameWallet);
const getBalance = async (who) => fmt(await stdlib.balanceOf(who));
console.log(getBalance);
const getBalanceGameWallet = async (who) =>
fmtGameWallet(await stdlib.balanceOf(who));
console.log(getBalanceGameWallet);
const beforeAlice = await getBalance(accAlice);
const beforeGameWallet = await getBalanceGameWallet(accgameWallet);
console.log(beforeAlice);
console.log(beforeGameWallet);
const ctcAlice = accAlice.contract(backend);
console.log(ctcAlice);
const ctcGameWallet = accgameWallet.contract(backend, ctcAlice.getInfo());
console.log(ctcGameWallet);
const HAND = ["Põe", "Tira", "Rapa", "Deixa"];
const OUTCOME = ["Alice põe", "Alice tira", "Alice rapa", "Alice deixa"];
const Player = (Who) => ({
...stdlib.hasRandom, // <--- new!
getHand: () => {
const hand = roll;
console.log(`${Who} played ${HAND[hand]}`);
setrapaRound(`${HAND[hand]}`);
return hand;
},
seeOutcome: (outcome) => {
console.log(`${Who} saw outcome ${OUTCOME[outcome]}`);
},
informTimeout: () => {
console.log(`${Who} observed a timeout`);
},
});
await Promise.all([
backend.Alice(ctcAlice, {
...Player("Alice"),
wager: stdlib.parseCurrency(WagerValue),
deadline: 10,
}),
backend.GameWallet(ctcGameWallet, {
...Player("Carteira do Jogo"),
acceptWager: (amt) => {
console.log(
`A Mesa de Jogo aceita o resultado do jogo de Alice de ${fmtGameWallet(amt)}.`
);
},
}),
]);
const afterAlice = await getBalance(accAlice);
const afterGameWallet = await getBalanceGameWallet(accgameWallet);
console.log(`Alice foi de ${beforeAlice} para ${afterAlice}.`);
console.log(
`A Carteira do Jogo foi de ${beforeGameWallet} para ${afterGameWallet}.`
);
(async () => {
const accgameWalletUpdate = await stdlib.newAccountFromMnemonic(input);
const balAtomicGameWalletUpdate = await stdlib.balanceOf(
accgameWalletUpdate
);
const gameWalletBalanceUpdate = stdlib.formatCurrency(
balAtomicGameWalletUpdate,
4
);
const accAliceUpdate = await stdlib.connectAccount({
addr: addr,
});
const balAtomicAliceWalletUpdate = await stdlib.balanceOf(
accAliceUpdate
);
const aliceBalanceUpdate = stdlib.formatCurrency(
balAtomicAliceWalletUpdate,
4
);
setWalletG(gameWalletBalanceUpdate);
setAliceplayer(aliceBalanceUpdate);
})();
playerpaytoplay = false;
})(); // <-- Não se esqueça disso
} //comound2
const [Aliceplayer, setAliceplayer] = useState("");
useEffect(() => {}, [Aliceplayer]);
const [alicePublicKey, setalicePublicKey] = useState("");
useEffect(() => {}, [alicePublicKey]);
const [walletG, setWalletG] = useState("");
useEffect(() => {
gameWalletBalance = walletG;
}, [walletG]);
const [rapalist, setRapaValue] = useState("");
useEffect(() => {}, [rapalist]);
const [rapaRound, setrapaRound] = useState("");
useEffect(() => {}, [rapaRound]);
const [tablegamevalue, settableGameValue] = useState("");
useEffect(() => {}, [tablegamevalue]);
const [input, setInput] = useState("");
8. Navegador da Web
Agora precisamos implementar todos os estilos necessários para exibir nosso jogo na tela.
Para personalizar e entender todos os estilos utilizados, você pode verificar este link aqui.
Figura 8 - A Div
return (
<>
<div >
<div className=" App container mx-auto mt-3 mb-8 font-thin">
<img className="w-1920 h-800 md:w-1920 md:h-auto md:rounded-none rounded-full mx-auto" src={rapalogo} height={200} alt="Algorand Blockchain" />
<div className="flex justify-center mt-6 ">
<Rapa list={rapalist} />
</div>
<div className="App container mx-auto mt-8 mb-8 font-thin">
<ul className="grid grid-cols-1 gap-4 h-24">
<li>
<div className="flex justify-center shadow bg-white rounded-lg whitespace-nowrap px-2 py-1 ml-1 h-full ">
<button type="button" onClick={() => rapaAnimation()} className="transition duration-500 ease-in-out justify-center px-4 py-2 bg-blue-400 hover:bg-blue-600 text-sm text-white transform hover:-translate-y-1 hover:scale-110 ...">
Jogar RAPA
</button>
</div>
</li>
</ul>
</div>
<ul className="grid grid-cols-2 gap-4 h-48">
<li>
<div className="shadow bg-white rounded-lg whitespace-nowrap px-2 py-1 ml-1 h-full ">
<div className="font-light text-4xl align-middle mb-2">Por favor, especifique o Mnemônico para a Mesa do jogo</div>
<div className="py-5">
<input value={input} onInput={e => setInput(e.target.value)} className=" focus:border-normal-blue-700 focus:ring-2 focus:ring-light-blue-700 focus:outline-none w-full text-sm text-black placeholder-gray-500 border border-gray-400 rounded-md py-2 pl-10" type="password" aria-label="Filter projects" placeholder="Insert Mnemonic as Table Game " />
</div>
<div className="font-light text-xl align-middle mb-2">Adicione 10 Algos na Mesa de Jogo para jogar</div>
<div className="py-2">
<button type="button" onClick={() => foundGameTableAccount()} className="transition duration-500 ease-in-out justify-center px-4 py-2 bg-blue-400 hover:bg-blue-600 text-sm text-white transform hover:-translate-y-1 hover:scale-110 ...">
Adicione fundos
</button>
</div>
</div>
</li>
<li>
<div className="shadow bg-white rounded-lg whitespace-nowrap px-2 py-1 ml-1 h-full">
<div className="font-light text-4xl align-middle mb-2">Estado do jogo</div>
<div className="ring-blue-500 ring-4 text-center rounded-md p-2 my-4">Resultado from game round: {tablegamevalue} Algos</div>
<div className="ring-blue-500 ring-4 text-center rounded-md p-2 my-4">Valor Total na Mesa de Jogo: {walletG} Algos</div>
<div className="text-left">
<div className="font-light text-1xl align-middle mb-2">Resultado do jogador</div>
<div className="ring-blue-500 ring-4 text-center rounded-md p-2 my-4">Valor total na carteira do jogador: {Aliceplayer} Algos</div>
</div>
</div>
</li>
<li>
<div className="shadow bg-white rounded-lg whitespace-nowrap px-2 py-1 ml-1 h-full">
<div className="text-xl font-semibold font-mono whitespace-nowrap px-1 py-1 ml-1 rounded text-white bg-red-700 rounded-2">Regras do jogo</div>
<ul className="list-inside list-disc">
<li>Cada jogador adiciona 10 Algos à Carteira de Jogo</li>
<li>Ao obter <span className="font-bold">P</span>, o jogador põe 2 Algos na Carteira de Jogos</li>
<li>Ao obter <span className="font-bold">T</span>, o jogador recebe 10% de Algos da Carteira de Jogos</li>
<li>Ao obter <span className="font-bold">R</span>, o jogador recebe 80% de Algos da Carteira de Jogos</li>
<li>Ao obter <span className="font-bold">D</span>, passe sua vez para o próximo jogador</li>
</ul>
</div>
</li>
<li>
<div className="shadow bg-white rounded-lg whitespace-nowrap px-2 py-1 ml-1 h-full">
<div className="font-light text-xl align-middle mb-2">Outras Informações</div>
<h1> Jogador em jogo: <span className="font-bold">{alicePublicKey}</span> </h1>
<h1>Jogador jogou <span className="font-bold">{rapaRound}</span></h1>
</div>
</li>
<li>
<div className="col-span-1 bg-white rounded-lg whitespace-nowrap px-2 py-1 ml-1 mt-4 mb-8 h-full">
<p className="text-md leading-tight">Mario Fernandes 2021 </p>
</div>
</li>
</ul>
</div>
</div>
</>
);
9. index.rsh
Comece a criar um arquivo chamado index.rsh. Como você pode verificar, o arquivo foi criado dentro da pasta /src.
Primeiro, precisamos definir no topo do arquivo que é um programa do Reach.
'reach 0.1';
Em segundo lugar, a exportação principal que o compilador Reach analisará.
export const main = Reach.App(() => {}
Asserção
Para garantir que computamos o valor correto, precisamos implementar asserções como as seguintes.
const [ isHand, Poem, Tira, Rapa, Deixa ] = makeEnum(4);
const [ isOutcome, Alice_poem, Alice_tira, Alice_rapa, Alice_deixa ] = makeEnum(4);
const winner = (handAlice) =>
handAlice;
Defina as regras do Jogo RAPA.
Verifique a resposta conforme o esperado.
assert(winner(Poem) == Alice_poem);
assert(winner(Tira) == Alice_tira);
assert(winner(Rapa) == Alice_rapa);
assert(winner(Deixa) == Alice_deixa);
Nesta etapa, obtemos os números declarados no front-end.
const Player = {
getHand: Fun([], UInt),
seeOutcome: Fun([UInt], Null),
informTimeout: Fun([], Null),
};
Agora especificamos os jogadores. Alice como primeira participante e a Carteira do Jogo como participante da mesa de jogo.
Alice fornecerá o valor como a “aposta”, que será compartilhada entre os dois jogadores.
GameWallet é um método chamado por acceptWager, que pode obter o valor da aposta.
Agora podemos implantar e começar a escrever nosso programa.
const Alice = Participant('Alice', {
...Player,
// Especifique a interface de interação de Alice aqui
wager: UInt,
// tempo delta (blocos/rodadas)
deadline: UInt,
});
// Alice joga e recebe a aposta ou paga a aposta
const GameWallet = Participant('GameWallet', {
...Player,
// Especifique a interface de interação de Alice aqui
// Mesa de Jogo recebe a aposta ou paga a aposta
acceptWager: Fun([UInt], Null),
});
deploy();
informTimeout
O método informTimeout é uma cláusula de timeout que garante a devolução de uma aposta a todos os participantes se algum dos participantes não aceitar ou jogar dentro de um determinado período de tempo.
const informTimeout = () => {
each([Alice, GameWallet], () => {
interact.informTimeout();
});
};
O próximo bloco de código define algo que apenas cada um dos participantes executa.
Alice.only()
Temos duas variáveis “aposta” como o valor jogado no front-end. O “handAlice” é um valor que Alice obteve ao rodar o RAPA.
Alice agora ingressa no aplicativo publicando o valor na rede de consenso.
Em seguida, Alice compartilha e pode transferir o valor como parte da publicação e confirmar o estado da rede de consenso.
Alice.only(() => {
const wager = declassify(interact.wager);
const deadline = declassify(interact.deadline);
});
Alice.publish(wager,handAlice, deadline)
.pay(wager);
commit();
O participante da Carteira de Jogo mostra e aceita a aposta. A Carteira de Jogo não joga, mas o participante pode aceitar e transferir seus fundos.
O tempo limite (timeout) especifica que, se a Carteira de Jogo não realizar essa ação no prazo (tempo delta do prazo final), todos os fundos serão transferidos de volta para Alice.
GameWallet.only(() => {
interact.acceptWager(wager);
});
GameWallet.pay(wager)
.timeout(relativeTime(deadline), () => closeTo(Alice, informTimeout));
O DApp será executado em uma etapa de consenso e o próprio contrato agora calculará o resultado e confirmará o estado.
Ele será executado duas vezes e transferirá os valores correspondentes com base no resultado final. Essa transferência ocorre do contrato para os participantes, não dos participantes entre si, porque todos os fundos residem dentro do contrato.
const outcome = winner(handAlice);
if (outcome === 0) {
transfer(wager).to(GameWallet);
} else if (outcome === 1) {
transfer(wager).to(Alice);
}else if (outcome === 2) {
transfer(wager).to(Alice);
}else if (outcome === 3) {
transfer(wager).to(Alice);
}else {
transfer(wager).to(Alice);
}
if (outcome === 0) {
transfer(wager).to(GameWallet);
} else if (outcome === 1) {
transfer(wager).to(Alice);
}else if (outcome === 2) {
transfer(wager).to(Alice);
}else if (outcome === 3) {
transfer(wager).to(Alice);
}else {
transfer(wager).to(Alice);
}
commit();
each([Alice, GameWallet, ], () => {
interact.seeOutcome(outcome);
});
10. Executar e testar
Para executar seu aplicativo em um navegador da Web, precisamos compilar seu programa Reach index.rsh para o back-end e criar o artefato build/index.main.mjs com:
./reach compile
Nota: verifique se você está na sua pasta /src.
Em seguida, clique em executar com o script de inicialização CRACO.
Figura 10.1 - Execução do Craco
Observação: Adicione fundos à sua conta usando o Dispenser - link para o Dispenser da TestNet
Para utilizar a TestNet com o Reach, precisamos adicionar fundos na conta que você deseja usar como remetente (“getDefaultAccount()”).
Agora estamos prontos para começar a depurar nosso projeto.
Figura 10.1 - Aplicativo da Web
Figura 10.2 - Resultado
11. Projeto Github
Acesse o link a seguir e experimente o projeto completo.
https://github.com/3dlifee/rapagame.git
Obrigado.
Este tutorial destina-se apenas para fins de aprendizado. Não cobre a verificação de erros e outros casos extremos. Portanto, não deve ser usado como um aplicativo de implantação.
Este tutorial foi publicado por Mario Fernandes. Tradução por Paulinho Giovannini
Oldest comments (0)