[Reach教程翻译] 2.8 玩家交互、自主运行游戏

不同于先前的自动化测试,这篇教程里,我们修改了前端,让用户可以将合约真正部署到共识网络上,并实现自主下注、出拳。 (而这一切都只需要修改前端,也就是说在上一篇教程里,我们后端的Reach合约就已经完整了!)

*[Reach教程翻译] Reach是安全简单的Dapp开发语言 让用户可以像开发传统App一样开发DApp 目前使用Reach开发的智能合约可以部署在以太坊、Conflux、Algorand [Reach官网](https://reach.sh/) [Reach官方文挡](https://docs.reach.sh/)* # 2.8 玩家交互、自主运行游戏 [原文链接](https://docs.reach.sh/tut-8.html) 在之前实现的程序中,我们的石头剪刀布会一直运行直到确定最终的赢家为止。在本节中,我们不会改变Reach程序本身。反之,我们会通过修改Javascript前端来打造一个可交互的版本,这个版本将能够在真正的共识网络上运行。 我们将从头开始,再次展示程序的每一行。您会发现这个版本与先前的版本非常相似,但是为了完整起见,我们会显示每一行。 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L1-L6) ```javascript 1 import { loadStdlib } from '@reach-sh/stdlib'; 2 import * as backend from './build/index.main.mjs'; 3 import { ask, yesno, done } from '@reach-sh/stdlib/ask.mjs'; 4 const stdlib = loadStdlib(process.env); 5 6 (async () => { .. // ... ``` * 第 1、2、4 行和之前一样 : 导入标准库和后端。 * 第 3 行是新加入的,导入了一个 Reach 标准库中的 ask.mjs 库。下面我们将看到如何使用这三个函数。 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L7-L12) ```javascript .. // ... 7 const isAlice = await ask( 8 `Are you Alice?`, 9 yesno 10 ); 11 const who = isAlice ? 'Alice' : 'Bob'; 12 .. // ... ``` * 第 7-10 行,询问用户是否以 Alice 的身份参与,期望得到“是”或“不是”的回答。`ask` 会反复显示问题并提示用户输入,直到得到的输入是 'y' 或 'n'为止。 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L13-L29) ```javascript .. // ... 13 console.log(`Starting Rock, Paper, Scissors! as ${who}`); 14 15 let acc = null; 16 const createAcc = await ask( 17 `Would you like to create an account? (only possible on devnet)`, 18 yesno 19 ); 20 if (createAcc) { 21 acc = await stdlib.newTestAccount(stdlib.parseCurrency(1000)); 22 } else { 23 const secret = await ask( 24 `What is your account secret?`, 25 (x => x) 26 ); 27 acc = await stdlib.newAccountFromSecret(secret); 28 } 29 .. // ... ``` * 第 16 - 19 行,用户可以选择新创建一个测试帐户,或者输入密钥来导入自己的帐户。 * 第 21 行像之前一样创建测试帐户。 * 第 27 行导入帐户。 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L30-L41) ```javascript .. // ... 30 let ctc = null; 31 if (isAlice) { 32 ctc = acc.contract(backend); 33 ctc.getInfo().then((info) => { 34 console.log(`The contract is deployed as = ${JSON.stringify(info)}`); }); 35 } else { 36 const info = await ask( 37 `Please paste the contract information:`, 38 JSON.parse 39 ); 40 ctc = acc.contract(backend, info); 41 } .. // ... ``` * 第 31 行确定以下分支中玩家扮演的角色是Alice-部署合约,还是Bob-加入Alice部署的合约。 * 第 32 - 34 行部署合约并打印合约公开信息(ctc.[getInfo](https://docs.reach.sh/ref-frontends-js-ctc.html#%28javascript._%28%28get.Info%29%29%29)),部署者可以在部署好后将这个信息发送其他要加入合约的参与者,用于让他们加入合约。 * 第 36 - 41 行请求、解析和处理这个信息。 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L43-L49) ```javascript .. // ... 43 const fmt = (x) => stdlib.formatCurrency(x, 4); 44 const getBalance = async () => fmt(await stdlib.balanceOf(acc)); 45 46 const before = await getBalance(); 47 console.log(`Your balance is ${before}`); 48 49 const interact = { ...stdlib.hasRandom }; .. // ... ``` * 定义几个辅助函数并声明参与者交互接口。 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L51-L54) ```javascript .. // ... 51 interact.informTimeout = () => { 52 console.log(`There was a timeout.`); 53 process.exit(1); 54 }; .. // ... ``` * 定义一个超时处理程序。 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L56-L73) ```javascript .. // ... 56 if (isAlice) { 57 const amt = await ask( 58 `How much do you want to wager?`, 59 stdlib.parseCurrency 60 ); 61 interact.wager = amt; 62 interact.deadline = { ETH: 100, ALGO: 100, CFX: 1000 }[stdlib.connector]; 63 } else { 64 interact.acceptWager = async (amt) => { 65 const accepted = await ask( 66 `Do you accept the wager of ${fmt(amt)}?`, 67 yesno 68 ); 69 if (!accepted) { 70 process.exit(0); 71 } 72 }; 73 } .. // ... ``` * 根据是 Alice 还是 Bob,决定请求赌注值 `wager` ,还是定义 `acceptWager` 方法 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L75-L92) ```javascript .. // ... 75 const HAND = ['Rock', 'Paper', 'Scissors']; 76 const HANDS = { 77 'Rock': 0, 'R': 0, 'r': 0, 78 'Paper': 1, 'P': 1, 'p': 1, 79 'Scissors': 2, 'S': 2, 's': 2, 80 }; 81 82 interact.getHand = async () => { 83 const hand = await ask(`What hand will you play?`, (x) => { 84 const hand = HANDS[x]; 85 if ( hand == null ) { 86 throw Error(`Not a valid hand ${hand}`); 87 } 88 return hand; 89 }); 90 console.log(`You played ${HAND[hand]}`); 91 return hand; 92 }; .. // ... ``` * 接着定义两人共同的 `getHand` 方法。 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L94-L97) ```javascript .. // ... 94 const OUTCOME = ['Bob wins', 'Draw', 'Alice wins']; 95 interact.seeOutcome = async (outcome) => { 96 console.log(`The outcome is: ${OUTCOME[outcome]}`); 97 }; .. // ... ``` * 最后是 `seeOutcome` 方法。 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs#L99-L106) ```javascript .. // ... 99 const part = isAlice ? ctc.p.Alice : ctc.p.Bob; 100 await part(interact); 101 102 const after = await getBalance(); 103 console.log(`Your balance is now ${after}`); 104 105 done(); 106 })(); ``` * 最后的最后,我们选择合适的后端并等待其完成。** --- 现在我们可以运行在两个终端分别运行`$ ./reach run`来扮演 Alice 和 Bob 。 下面是一组运行的例子: ``` $ ./reach run Are you Alice? y Starting Rock, Paper, Scissors as Alice Would you like to create an account? (only possible on devnet) y Do you want to deploy the contract? (y/n) y The contract is deployed as = "0x132b724e55AEb074C15A5CBb7b8EeE0dBEd45F7b" Your balance is 999.9999 How much do you want to wager? 10 What hand will you play? r You played Rock The outcome is: Bob wins Your balance is now 989.9999 ``` 另一边: ``` $ make run-bob Are you Alice? n Starting Rock, Paper, Scissors as Bob Would you like to create an account? (only possible on devnet) y Do you want to deploy the contract? (y/n) n Please paste the contract information: "0x132b724e55AEb074C15A5CBb7b8EeE0dBEd45F7b"` Your balance is 1000 Do you accept the wager of 10? y What hand will you play? p You played Paper The outcome is: Bob wins Your balance is now 1009.9999 ``` 当然,每次运行时的赌注可以不同,部署地址也会不一样。 如果你的版本不能正常运行,看看完整的版本: [rps-8-interact/index.rsh](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.rsh) 和 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs) ,并确保您完整地复制了所有内容! 如果我们在两个终端运行的是 `$ REACH_CONNECTOR_MODE=ALGO-devnet ./reach run` 则相当于在一个私有的 Algorand 测试网运行我们的程序。 连接真正的[共识网络](https://docs.reach.sh/ref-model.html#%28tech._consensus._network%29)也一样简单,例如运行: `$ REACH_CONNECTOR_MODE=ETH-live ETH_NODE_URI="http://some.node.fqdn:8545" ./reach run` 您知道了吗? : 是非题 : Reach 帮助你自动化测试DApp,但它不支持构建交互式用户界面 答案是: 否! Reach 不会对使用 Reach 应用程序的前端做任何限制。 欢迎关注Reach微信公众号 并在公众号目录 -> 加入群聊 选择加入官方开发者微信群与官方Discord群 与更多Reach开发者一起学习交流! ![_.png](https://img.learnblockchain.cn/attachments/2021/11/IslW4w8i6192082848c1a.png)

[Reach教程翻译] Reach是安全简单的Dapp开发语言 让用户可以像开发传统App一样开发DApp 目前使用Reach开发的智能合约可以部署在以太坊、Conflux、Algorand Reach官网 Reach官方文挡

2.8 玩家交互、自主运行游戏

原文链接

在之前实现的程序中,我们的石头剪刀布会一直运行直到确定最终的赢家为止。在本节中,我们不会改变Reach程序本身。反之,我们会通过修改Javascript前端来打造一个可交互的版本,这个版本将能够在真正的共识网络上运行。

我们将从头开始,再次展示程序的每一行。您会发现这个版本与先前的版本非常相似,但是为了完整起见,我们会显示每一行。

rps-8-interact/index.mjs

 1    import { loadStdlib } from '@reach-sh/stdlib';
 2    import * as backend from './build/index.main.mjs';
 3    import { ask, yesno, done } from '@reach-sh/stdlib/ask.mjs';
 4    const stdlib = loadStdlib(process.env);
 5    
 6    (async () => {
..    // ...
  • 第 1、2、4 行和之前一样 : 导入标准库和后端。
  • 第 3 行是新加入的,导入了一个 Reach 标准库中的 ask.mjs 库。下面我们将看到如何使用这三个函数。

rps-8-interact/index.mjs

..    // ...
 7    const isAlice = await ask(
 8      `Are you Alice?`,
 9      yesno
10    );
11    const who = isAlice ? 'Alice' : 'Bob';
12    
..    // ...
  • 第 7-10 行,询问用户是否以 Alice 的身份参与,期望得到“是”或“不是”的回答。ask 会反复显示问题并提示用户输入,直到得到的输入是 'y' 或 'n'为止。

rps-8-interact/index.mjs

..    // ...
13    console.log(`Starting Rock, Paper, Scissors! as ${who}`);
14    
15    let acc = null;
16    const createAcc = await ask(
17      `Would you like to create an account? (only possible on devnet)`,
18      yesno
19    );
20    if (createAcc) {
21      acc = await stdlib.newTestAccount(stdlib.parseCurrency(1000));
22    } else {
23      const secret = await ask(
24        `What is your account secret?`,
25        (x => x)
26      );
27      acc = await stdlib.newAccountFromSecret(secret);
28    }
29    
..    // ...
  • 第 16 - 19 行,用户可以选择新创建一个测试帐户,或者输入密钥来导入自己的帐户。
  • 第 21 行像之前一样创建测试帐户。
  • 第 27 行导入帐户。

rps-8-interact/index.mjs

..    // ...
30    let ctc = null;
31    if (isAlice) {
32      ctc = acc.contract(backend);
33      ctc.getInfo().then((info) => {
34        console.log(`The contract is deployed as = ${JSON.stringify(info)}`); });
35    } else {
36      const info = await ask(
37        `Please paste the contract information:`,
38        JSON.parse
39      );
40      ctc = acc.contract(backend, info);
41    }
..    // ...
  • 第 31 行确定以下分支中玩家扮演的角色是Alice-部署合约,还是Bob-加入Alice部署的合约。
  • 第 32 - 34 行部署合约并打印合约公开信息(ctc.getInfo),部署者可以在部署好后将这个信息发送其他要加入合约的参与者,用于让他们加入合约。
  • 第 36 - 41 行请求、解析和处理这个信息。

rps-8-interact/index.mjs

..    // ...
43    const fmt = (x) => stdlib.formatCurrency(x, 4);
44    const getBalance = async () => fmt(await stdlib.balanceOf(acc));
45    
46    const before = await getBalance();
47    console.log(`Your balance is ${before}`);
48    
49    const interact = { ...stdlib.hasRandom };
..    // ...
  • 定义几个辅助函数并声明参与者交互接口。

rps-8-interact/index.mjs

..    // ...
51    interact.informTimeout = () => {
52      console.log(`There was a timeout.`);
53      process.exit(1);
54    };
..    // ...
  • 定义一个超时处理程序。

rps-8-interact/index.mjs

..    // ...
56    if (isAlice) {
57      const amt = await ask(
58        `How much do you want to wager?`,
59        stdlib.parseCurrency
60      );
61      interact.wager = amt;
62      interact.deadline = { ETH: 100, ALGO: 100, CFX: 1000 }[stdlib.connector];
63    } else {
64      interact.acceptWager = async (amt) => {
65        const accepted = await ask(
66          `Do you accept the wager of ${fmt(amt)}?`,
67          yesno
68        );
69        if (!accepted) {
70          process.exit(0);
71        }
72      };
73    }
..    // ...
  • 根据是 Alice 还是 Bob,决定请求赌注值 wager ,还是定义 acceptWager 方法

rps-8-interact/index.mjs

..    // ...
75    const HAND = ['Rock', 'Paper', 'Scissors'];
76    const HANDS = {
77      'Rock': 0, 'R': 0, 'r': 0,
78      'Paper': 1, 'P': 1, 'p': 1,
79      'Scissors': 2, 'S': 2, 's': 2,
80    };
81    
82    interact.getHand = async () => {
83      const hand = await ask(`What hand will you play?`, (x) => {
84        const hand = HANDS[x];
85        if ( hand == null ) {
86          throw Error(`Not a valid hand ${hand}`);
87        }
88        return hand;
89      });
90      console.log(`You played ${HAND[hand]}`);
91      return hand;
92    };
..    // ...
  • 接着定义两人共同的 getHand 方法。

rps-8-interact/index.mjs

..    // ...
94    const OUTCOME = ['Bob wins', 'Draw', 'Alice wins'];
95    interact.seeOutcome = async (outcome) => {
96      console.log(`The outcome is: ${OUTCOME[outcome]}`);
97    };
..    // ...
  • 最后是 seeOutcome 方法。

rps-8-interact/index.mjs

..    // ...
99    const part = isAlice ? ctc.p.Alice : ctc.p.Bob;
100    await part(interact);
101    
102    const after = await getBalance();
103    console.log(`Your balance is now ${after}`);
104    
105    done();
106    })();
  • 最后的最后,我们选择合适的后端并等待其完成。**

现在我们可以运行在两个终端分别运行$ ./reach run来扮演 Alice 和 Bob 。

下面是一组运行的例子:

$ ./reach run
Are you Alice?
y
Starting Rock, Paper, Scissors as Alice
Would you like to create an account? (only possible on devnet)
y
Do you want to deploy the contract? (y/n)
y
The contract is deployed as = "0x132b724e55AEb074C15A5CBb7b8EeE0dBEd45F7b"
Your balance is 999.9999
How much do you want to wager?
10
What hand will you play?
r
You played Rock
The outcome is: Bob wins
Your balance is now 989.9999

另一边:

$ make run-bob
Are you Alice?
n
Starting Rock, Paper, Scissors as Bob
Would you like to create an account? (only possible on devnet)
y
Do you want to deploy the contract? (y/n)
n
Please paste the contract information:
"0x132b724e55AEb074C15A5CBb7b8EeE0dBEd45F7b"`
Your balance is 1000
Do you accept the wager of 10?
y
What hand will you play?
p
You played Paper
The outcome is: Bob wins
Your balance is now 1009.9999

当然,每次运行时的赌注可以不同,部署地址也会不一样。

如果你的版本不能正常运行,看看完整的版本: rps-8-interact/index.rsh 和 rps-8-interact/index.mjs ,并确保您完整地复制了所有内容!

如果我们在两个终端运行的是 $ REACH_CONNECTOR_MODE=ALGO-devnet ./reach run 则相当于在一个私有的 Algorand 测试网运行我们的程序。

连接真正的共识网络也一样简单,例如运行: $ REACH_CONNECTOR_MODE=ETH-live ETH_NODE_URI="http://some.node.fqdn:8545" ./reach run

您知道了吗? :

是非题 : Reach 帮助你自动化测试DApp,但它不支持构建交互式用户界面

答案是:

否! Reach 不会对使用 Reach 应用程序的前端做任何限制。

欢迎关注Reach微信公众号 并在公众号目录 -> 加入群聊 选择加入官方开发者微信群与官方Discord群 与更多Reach开发者一起学习交流!

[Reach教程翻译] 2.8 玩家交互、自主运行游戏插图

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 2021-12-01 09:18
  • 阅读 ( 216 )
  • 学分 ( 8 )
  • 分类:其他
  • 专栏:Reach开发资料

评论