WEB3DEV

Cover image for Jogo RAPA no Reach - Algorand
Paulo Gio
Paulo Gio

Posted on • Atualizado em

Jogo RAPA no Reach - Algorand

https://algorand-developer-portal.tryprism.com/static/Posts/2021/11/21%2012:42/algoReachLogorapa.jpg?w=2220&cmd=resize_then_crop&height=1018&quality=70

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:

  1. Configurando o Projeto

  2. Instalações externas

  3. Instalação do Reach

  4. Carteira MyAlgo

  5. Animação RAPA

  6. Adicionar fundos

  7. Front-end do Jogo

  8. Navegador da Web

  9. index.rsh

  10. Executar e testar

  11. 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
Enter fullscreen mode Exit fullscreen mode

E então crie seu aplicativo.

npx create-react-app myrapagame
cd myRapaGame
npm start
Enter fullscreen mode Exit fullscreen mode

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2012%3A53/s101.png

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

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

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2012%3A55/s201.png

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

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

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2012%3A59/s202.png

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'),
        ],
      },
    },
Enter fullscreen mode Exit fullscreen mode

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2012%3A59/s203.png

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

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2013%3A01/s204.png

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')]
Enter fullscreen mode Exit fullscreen mode

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2013%3A02/s205.png

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

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2013%3A03/s206.png

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

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2013%3A07/s301.png

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

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2013%3A07/s302.png

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.

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2013%3A11/s303.png

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

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

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

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

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2013%3A17/s401.png

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

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2013%3A19/s501.png

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

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 = '';
Enter fullscreen mode Exit fullscreen mode

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('');
Enter fullscreen mode Exit fullscreen mode

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" />;
  }

}
Enter fullscreen mode Exit fullscreen mode

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2013%3A25/s503.png

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!");
    }
  }
Enter fullscreen mode Exit fullscreen mode

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/12/10%2001%3A02/s503.png

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!");

    }
  }
Enter fullscreen mode Exit fullscreen mode

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2014%3A51/s601.png

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.

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2014%3A52/s701.png

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.

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/12/10%2001%3A17/s702.png

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.

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/12/10%2001%3A18/s703.png

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("");
Enter fullscreen mode Exit fullscreen mode

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.

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2014%3A58/s801.png

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>
    </>
  );
Enter fullscreen mode Exit fullscreen mode

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

Em segundo lugar, a exportação principal que o compilador Reach analisará.

export const main = Reach.App(() => {}
Enter fullscreen mode Exit fullscreen mode

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

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

Nesta etapa, obtemos os números declarados no front-end.

const Player = {
  getHand: Fun([], UInt),
  seeOutcome: Fun([UInt], Null),
  informTimeout: Fun([], Null),

};
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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

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();
Enter fullscreen mode Exit fullscreen mode

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

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);

});  
Enter fullscreen mode Exit fullscreen mode

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

Nota: verifique se você está na sua pasta /src.

Em seguida, clique em executar com o script de inicialização CRACO.

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2015%3A03/s1001.png

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.

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2016%3A05/s1002.png

Figura 10.1 - Aplicativo da Web

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2021/11/21%2016%3A06/s1003.png

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

Latest comments (0)