區塊鏈開發學習第七章:第一個Dapp-猜拳遊戲

請叫我胖子發表於2021-12-21

第一個簡單的Dapp-猜拳遊戲。本智慧合約的功能很簡單,就是使用者與電腦猜拳,使用者選擇出手後,電腦隨機一個選項,然後呼叫智慧合約方法把兩個選項值傳過去,在智慧合約上進行比較,並通過區塊鏈合約事件廣播結果,本地監聽事件拿到結果後展示猜拳結果。

 

先大體宣告下幾個環境跟工具:

1、沒有用truffle,直接MetaMask連結以太坊Ropsten測試鏈後,用Remix部署合約程式碼

2、前端用web3.js(1.6.1版本)

 

 

下面分成幾步詳細說明:

一、從Ropsten上獲取eth

因為呼叫合約需要消耗eth,所以要先在Ropsten測試網上獲取eth,可以複製自己的錢包地址後在這個網站上獲取:https://faucet.ropsten.be/  獲取過程可能會有延時,多點幾次就好。

 

二、編寫猜拳智慧合約

Remix上新建GuessGame.sol檔案,並寫入以下內容:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract GuessGame {
	event GuessResult(uint playerChoice,uint computerChoice,uint result);
	function play(uint playerChoice,uint computerChoice) public returns (bool){
		if(playerChoice > 0 && playerChoice <=3 && computerChoice > 0 && computerChoice <=3){
			if(playerChoice == computerChoice){
				//平手
				emit GuessResult(playerChoice,computerChoice,1);
			}else if(playerChoice == (computerChoice + 1) % 3){
				//電腦贏了
				emit GuessResult(playerChoice,computerChoice,2);
			}else{
				//其餘都算玩家贏了
				emit GuessResult(playerChoice,computerChoice,3);
			}
			return true;
		}else{
			return false;
		}
	}
}

編譯後部署到Ropsten上,當然首先到MetaMask連上Ropsten網路,並且賬戶上有足夠的eth

 

 部署成功後,這裡其實就顯示了智慧合約上的play方法了,可以直接在這裡傳值點選transact進行呼叫,呼叫成功後再控制檯會輸出結果,點選Debug可以檢視詳細資訊

 

 

三、前端用web3.js呼叫智慧合約

我後臺因為整合了專案,所以是用jsp寫的,大家簡單點可以直接寫Html,先貼下檔案目錄結構:

1、複製ABI檔案:在Remix上點選複製ABI檔案內容,並新建GuessGame.json檔案,將複製的abi內容貼進去

 

2、引入web3.js等其它js

3、編寫guessGame.jsp檔案:

<%--
  Created by IntelliJ IDEA.
  User: ****
  Date: 2021/12/13
  Time: 21:35
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="Access-Control-Allow-Origin" content="*" >
    <c:set var="ctx" value="${pageContext.request.contextPath}" />
    <script type="text/javascript" src="${ctx}/resource/js/jquery-1.9.1.min.js" charset="utf-8"></script>
    <script type="text/javascript" src="${ctx}/resource/js/web3.min-1.6.1.js"></script>
    <title>猜拳遊戲</title>
    <style>
        *{margin:0; padding: 0; font-weight: 200;}
        .player,.computer{
            width: 50%;
            float: left;
            padding-top: 30px;
            text-align: center
        }
        .player,.computer dt{
            font-size: 28px;
        }
        .player img,.computer img{
            margin-top: 30px;
            width: 30%;
        }
        .player img{
            transform:rotateY(180deg);
        }
        .select{
            text-align: center;
            font-size: 18px;
            max-width: 800px;
            margin: 0 auto;
            padding-top: 2%;
        }
        .select dt{
            width: 100%;
            overflow: hidden;
            line-height: 50px;
        }
        .select button{
            width: 20%;
            border:none;
            color: #fff;
            border-radius: 8px;
            line-height: 45px;
            margin: 0 5%;
            outline: none;
            font-size: 18px;
            cursor: pointer;
        }
        #info{
            width: 100%;
            text-align: center;
            overflow: hidden;
            font-size: 25px;
            line-height: 50px;
            color: red;
            padding-top: 2%;
            opacity: 0;
        }
    </style>
    <script type="text/javascript">
        var web3,guessGameABI,guessGameContract,refresh_timer;
        $(function(){
            web3Auth();
            jspFun();
            //var version = web3.version;
            //console.log(version);
            //web3.eth.net.getNetworkType().then(console.log);
            web3.eth.getAccounts(console.log);
            $.getJSON('/resource/abi/GuessGame.json',function(data){
                guessGameABI = data;
                //建立合約例項
                guessGameContract = new web3.eth.Contract(guessGameABI, '0x85daAd7dbB5Ba1B4020444Ab2f1D84c58d409edF', []);
                console.log(guessGameContract.defaultAccount);
                console.log(guessGameContract.defaultBlock);
                listenEvent();
            });

        })

        function listenEvent(){
            guessGameContract.events.GuessResult({
                //filter: {myIndexedParam: [20,23], myOtherIndexedParam: '0x123456789...'}, // 使用陣列表示 或:如 20 或 23。
                //fromBlock: 0
            }, function(error, event){
                console.log(event);
            }).on("connected", function(subscriptionId){
                    console.log(subscriptionId);
            }).on('data', function(event){
                    console.log(event); // 與上述可選的回撥結果相同
                    //uint playerChoice,uint computerChoice,uint result
                    resultDeal(event.returnValues.playerChoice,event.returnValues.computerChoice,event.returnValues.result);
            }).on('changed', function(event){
                    // 從本地資料庫中刪除事件
                    console.log("事件刪除");
            }).on('error', function(error, receipt) {
                    // 如果交易被網路拒絕並帶有交易收據,第二個引數將是交易收據。
                    console.log("事件被網路拒絕");
            });
        }

        function resultDeal(player_choice,computer_choice,r){
            //var r = result.args.result.toNumber();
            var info = "未知";
            if(r == 1){
                info = "平手";
            }else if(r == 2){
                info = "你輸了";
            }else if(r == 3){
                info = "你贏了";
            }
            update_page(player_choice, computer_choice, info);
        }

        function update_page(player,computer,result){
            console.log(player+"----"+computer+"-----"+result);
            var info = document.getElementById('info');
            var playerImg = document.getElementById('player');
            var comImg = document.getElementById('computer');
            info.style.opacity = '0';
            clearInterval(refresh_timer);
            playerImg.src = '/resource/images/'+player+'.png';
            comImg.src = '/resource/images/'+computer+'.png';
            info.style.opacity = 1;
            info.innerText = result;
        }


        function guess(player_choice){
            //web3.eth.getCoinbase().then(console.log);
            //1:剪刀  2:石頭   3:布
            var result;
            player_choice = parseInt(player_choice);
            computer_choice = parseInt(Math.random()*3)+1;
            document.getElementById('info').innerText = '';
            guessGameContract.methods.play(player_choice,computer_choice).send({
                from: '0x229Ea411D368C97b008c7bc19B01Fdd813163701'
            }).on('transactionHash', function(hash){
                console.log(hash);
                beginGame();
            }).on('confirmation', function(confirmationNumber, receipt){
                console.log(confirmationNumber);
                console.log(receipt);
            }).on('receipt', function(receipt) {
                // receipt 相關例子
                console.log(receipt);
            }).on('error', function(error, receipt) { // 如果交易被網路拒絕並帶有交易收據,則第二個引數將是交易收據。
                console.log(error);
                console.log(receipt);
            });

        }

        function beginGame(){
            var playerImg = document.getElementById('player');
            var comImg = document.getElementById('computer');
            refresh_timer = setInterval(function(){
                this.n?this.n:this.n=1;this.n++
                this.n>3?this.n=1:this.n;
                playerImg.src = '/resource/images/'+this.n+'.png';
                comImg.src = '/resource/images/'+this.n+'.png';
            },100);
        }

        function jspFun(){
            choices = $('button');
            for(var i=0; i<choices.length; i++){
                choices[i].onclick = function(){
                    guess(this.value);
                }
            }
        }

        function web3Auth(){
            //初始化過程
            //var Web3 = require('web3');
            if (typeof web3 !== 'undefined') {
                console.info("init web3");
                web3 = new Web3(web3.currentProvider);
            } else {
                console.info("init new web3");
                // set the provider you want from Web3.providers
                web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
            }

        }
    </script>
</head>
<body>
    <div class="computer">
        <dl>
            <dt>對手</dt>
            <dd><img src="${ctx}/resource/images/2.png" id="computer" alt=""></dd>
        </dl>
    </div>
    <div class="player">
        <dl>
            <dt>你</dt>
            <dd><img src="${ctx}/resource/images/2.png" id="player" alt=""></dd>
        </dl>
    </div>
    <div id="info">平手</div>
    <div class="select">
        <dl>
            <dt>點選下列圖示選擇要出的選項:</dt>
            <dd>
                <button value="1"><img src='${ctx}/resource/images/1.png' style="width:80px"></button>
                <button value="2"><img src='${ctx}/resource/images/2.png' style="width:80px"></button>
                <button value="3"><img src='${ctx}/resource/images/3.png' style="width:80px"></button>
            </dd>
        </dl>
    </div>
</body>
</html>

詳細內容參考web3文件看吧,我也是參考文件一點點寫的,文件地址:https://learnblockchain.cn/docs/web3.js/

四、執行結果示例:

 

如果有學習計劃的童鞋,可以加我QQ一起交流:

 

相關文章