[Reach教程翻译] | 2.3 石头剪刀布
[Reach教程翻譯] Reach是安全簡單的Dapp開發語言
讓用戶可以像開發傳統App一樣開發DApp
目前使用Reach開發的智能合約可以部署在以太坊、Conflux、Algorand
Reach官網
Reach官方文擋
2.3 石頭剪刀布
原文鏈接
在這個部分中,Alice和Bob開始玩猜拳游戲!
首先,我們考慮如何表示出拳的手勢。一個簡單的方法是分別用數字0、1和2,代表“石頭”,“布”和“剪刀”。但是,Reach不支持兩位無符號整數,因此最好將它們表示為模數為3的整數等價類,即0和3是相等的,都表示石頭。
我們也用一樣的方法表示猜拳的三種結果:B贏,平手和A贏。
第一步要修改Reach程序,讓我們可以與Alice和Bob的前端交互,獲取他們要出的手勢,隨后通知他們游戲的結果。
tut-3/index.rsh
1 'reach 0.1'; 2 3 const Player = { 4 getHand: Fun([], UInt), 5 seeOutcome: Fun([UInt], Null), 6 }; 7 8 export const main = Reach.App(() => { 9 const Alice = Participant('Alice', { 10 ...Player, 11 }); 12 const Bob = Participant('Bob', { 13 ...Player, 14 }); 15 deploy(); 16 17 Alice.only(() => { .. // ...- 第3-6行定義了參與者的接口, 兩個參與者都會調用該接口。其中有兩個方法:getHand,它會返回一個數字;seeOutcome,它會接收一個數字。
- 第9-14行兩個參與者都利用了這個接口。有了這幾行代碼后,在剩余的部份里,interact都將被綁定到一個帶有這些方法的對象,連接對應參與者的前端。
在繼續編寫Reach程序之前,我們回到JavaScript中,在前端中實現這些方法。
tut-3/index.mjs*
.. // ... 13 const HAND = ['Rock', 'Paper', 'Scissors']; 14 const OUTCOME = ['Bob wins', 'Draw', 'Alice wins']; 15 const Player = (Who) => ({ 16 getHand: () => { 17 const hand = Math.floor(Math.random() * 3); 18 console.log(`${Who} played ${HAND[hand]}`); 19 return hand; 20 }, 21 seeOutcome: (outcome) => { 22 console.log(`${Who} saw outcome ${OUTCOME[outcome]}`); 23 }, 24 }); 25 26 await Promise.all([ 27 backend.Alice(ctcAlice, { 28 ...Player('Alice'), 29 }), 30 backend.Bob(ctcBob, { 31 ...Player('Bob'), 32 }), 33 ]); .. // ...- 第13-14行定義了數組,表示出拳手勢和猜拳結果的內容。
- 第15行定義了Player實現的構造函數。
- 第16-20行定義了getHand方法。
- 第21-23行定義了seeOutcome方法。
- 最后,第28-31行分別為Alice和Bob實例化對象。這些是Reach程序中interact綁定的實例化對象。
這部份代碼相當簡單,沒有特別之處;這就是Reach的優點:我們只需要編寫業務邏輯,而不必關心共識網絡和去中心化應用程序的細節。
讓我們回到Reach程序,研究Alice和Bob該怎么操作。
在現實生活中的“剪刀石頭布”中,Alice和Bob同時決定他們要出哪種手勢并同時出拳。“同時性”是一個復雜的概念,很難實踐。例如,跟小孩猜拳時,您可能會發現他們"慢出"—試圖在看到您出的手勢后才出拳,以獲得勝利。但是,在去中心化應用程序中,同時是不可能實現的。反之,必須讓一個參與者“先出”。在這個示例中,我們將讓Alice先出。
是Alice先出,還是我們將先出的玩家稱為Alice?似乎沒必要這樣區分,但是Reach巧妙地利用了這一點。在前端,我們明確區分了backend.Alice和backend.Bob。這樣我們把特定的JavaScript線程提交為Alice或Bob。在我們的游戲中,先運行Alice后端的人就是先出拳的人。在更后面的教程里,用戶可以選擇要扮演的角色,屆時這個概念將更加明確。
游戲分三步進行。
首先,Alice的后端與前端進行交互,獲取她的手勢,然后發布它。
.. // ... 17 Alice.only(() => { 18 const handAlice = declassify(interact.getHand()); 19 }); 20 Alice.publish(handAlice); 21 commit(); .. // ...- 第17行指出此代碼塊僅由A(即Alice)執行。
- 這意味著第18行上綁定的變量handA只對Alice可見。
- 第18行將Alice的手勢賦給變量handA。
- 第18行還對值進行了解密,因為在Reach中,來自前端的所有信息都是被加密了。
- 第20行Alice將值發布到共識網絡(這個步驟稱為"共識轉移"),從而可以判斷游戲的結果。一旦到了這一步,代碼將處于所有參與者共同參與的“共識步驟”。
- 第21行提交了共識網絡的狀態,并返回到“本地步驟”,此時每個參與者可以單獨運行。
下一步也類似,Bob發布了他的手勢。不同的是,此時我們不立即提交狀態,而是先計算猜拳的結果。
.. // ... 23 Bob.only(() => { 24 const handBob = declassify(interact.getHand()); 25 }); 26 Bob.publish(handBob); 27 28 const outcome = (handAlice + (4 - handBob)) % 3; 29 commit(); .. // ...- 第23-26行與Alice的步驟類似,通過共識轉移發布該應用程序。
- 第28行在提交前先計算游戲的結果。( (handA + (4-handB))%3 是一個巧妙的運算,用于使用公式化的算法計算猜拳的贏家。考慮當handA為0(即石頭)和handB為2(即剪刀)時,等式為 ((handA +(4-handB))%3)=((0 +(4-2))%3)=((0 + 2)%3)=(2%3)= 2,表示A獲勝,和我們預期的一樣。)
最后,我們調用each方法將每個參與者運行的結果發送到他們的前端。
.. // ... 31 each([Alice, Bob], () => { 32 interact.seeOutcome(outcome); 33 }); .. // ...- 第31行指出這是每個參與者都會執行的本地步驟。
此時,我們可以運行程序查看輸出結果 $ ./reach run
玩家的運行結果是隨機的,因此每次的結果都會有所不同。例如我們運行該程序三次,得到的結果如下:
$ ./reach run Alice played Scissors Bob played Paper Alice saw outcome Alice wins Bob saw outcome Alice wins $ ./reach run Alice played Scissors Bob played Paper Alice saw outcome Alice wins Bob saw outcome Alice wins $ ./reach run Alice played Paper Bob played Rock Alice saw outcome Alice wins Bob saw outcome Alice wins(Alice很會猜拳!!)
我們可以看到,共識網絡(特別是Reach)保證所有參與者都同意各自計算的結果。這就是共識網絡這個名稱的來源,因為它們使這些分散且不受信任的各方能夠就計算的中間狀態達成共識協議。如果他們同意中間狀態,那么他們也會同意輸出結果。這就是為什么每次運行./reach run時,Alice和Bob都會看到相同的結果!
如果您的代碼不能正確運行,請查看tut-3/index.rsh和tut-3/index.mjs的完整版本,并確保您正確復制了所有內容!
在下一步中,我們將增加下注機制,讓Alice可以利用自己的拳法變現!
您知道了嗎?:
Reach程序允許通過以下哪種方法與用戶界面進行交互
答案:2和3;Reach通過參與者交互界面實現前端與后端的雙向交互。
您知道了嗎?:
Reach應用程序中的參與者如何共享彼此信息,知道其他人共享了什么?
回答:2; 原始語句publish替你搞定了一切!
歡迎關注Reach微信公眾號
并在公眾號目錄 -> 加入群聊 選擇加入官方開發者微信群與官方Discord群
與更多Reach開發者一起學習交流!
總結
以上是生活随笔為你收集整理的[Reach教程翻译] | 2.3 石头剪刀布的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Layui 获取复选框的值
- 下一篇: 辐射光电流测试软件,电磁骚扰辐射发射的测