[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开发者一起学习交流!
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 2021-12-01 09:18
- 阅读 ( 216 )
- 学分 ( 8 )
- 分类:其他
- 专栏:Reach开发资料
评论