17. 联机骰子游戏设计与实现

17.1. 功能描述

  • 两个设备同时运行骰子游戏并发送双方的结果给对方,比较大小显示出输赢结果。

17.2. 设计思路

  • 定义发送数据的规则区分出自己和对方的数据。比如玩家一发送11-16的数字,玩家二发送21-26的数字代表筛子对应的个数。
  • 连接到mqtt服务器
  • 在玩家操作完后等待网络回调获取对方的个数并判断结果。
  • 在回调获取到对方的个数后等待用户按键操作并判断结果。

17.3. 代码实现

玩家一代码:

导入库文件

from machine import Pin,UART
import random
import time
import screen
import ubitmap
import text
import network
import utime
from random import randint
from umqtt import MQTTClient

定义类并初始化

class Game():
  def __init__(self, playerName, computerName):#初始化
        #网络设定
        #WiFi名称和密码
        self.wifi_name = "NEUI"
        self.wifi_SSID = "NEUI3187"
        #MQTT服务端信息
        SERVER = "112.125.89.85"   #MQTT服务器地址
        SERVER_PORT = 3881         #MQTT服务器端口
        DEVICE_ID = "wc001"        #设备ID
        TOPIC1 = b"/cloud-skids/online/dev/" + DEVICE_ID
        self.TOPIC2 = b"/cloud-skids/message/server/" + DEVICE_ID
        CLIENT_ID = "f25410646a8348f8a1726a3890ad8f81"
        #设备状态
        ON = "1"
        OFF = "0"
        #对方的选择
        self.d=["1","2","3"]
        self.choose=0
        self.mark=0
        #启动网络连接
        self.do_connect()
        gc.collect()
        self.client = MQTTClient(CLIENT_ID, SERVER, SERVER_PORT)
        self.client.set_callback(self.sub_cb)    #设置回调
        self.client.connect()
        print("连接到服务器:%s" % SERVER)
        self.client.publish(TOPIC1, ON)     #发布“1”到TOPIC1
        self.client.subscribe(self.TOPIC2)       #订阅TOPIC
        #游戏设定
        self.gameStart = False
        self.playerName = playerName
        self.computerName = computerName
        self.playerScore = 0
        self.computerScore = 0
        self.equalNum = 0
        self.playerStatus = 0;
        self.computerStatus = 0
        for p in pins:
          keys.append(Pin(p,Pin.IN))
        self.displayInit()

连接wifi网络接口,使用STA模式

def do_connect(self):
      sta_if = network.WLAN(network.STA_IF)    #STA模式
      ap_if = network.WLAN(network.AP_IF)      #AP模式
      if ap_if.active():
              ap_if.active(False)                  #关闭AP
      if not sta_if.isconnected():
              print('Connecting to network...')
      sta_if.active(True)                      #激活STA
      sta_if.connect(self.wifi_name, self.wifi_SSID)     #WiFi的SSID和密码
      while not sta_if.isconnected():
              pass
      print('Network config:', sta_if.ifconfig())

连接到mqtt服务器接口

def esp(self):
        self.c.set_callback(self.sub_cb)    #设置回调
        self.c.connect()
        print("连接到服务器:%s" % self.SERVER)
        self.c.publish(self.TOPIC1, self.ON)     #发布“1”到TOPIC1
        self.c.subscribe(self.TOPIC2)       #订阅TOPIC
        #display.text("从微信取得信息", 20, 20, 0xf000, 0xffff)

服务器回调接口,其中mark变量代表计数,每次玩家或者对手做出一次选择则+1,此处为对手已经选择完成并发送了数据 玩家1发送的信息为11-16,其中第一位的1代表一号玩家所发,第二位代表选择的个数

def sub_cb(self,topic, message):
      message = message.decode()
      print("mark is :",self.mark)
      #赋值给choose
      self.choose=int(message)

      if (self.choose<20 ):#来自1号自己的信息,不判断
        print ("对方尚未选择")
        return
      elif(self.choose>20):
        self.mark=self.mark+1#修改标志,代表一方已完成选择
        print("player1 get choose is :",self.choose)
        #处理所得信息
        self.computerStatus = self.choose-20
        #显示对方结果
        text.draw(str(self.computerStatus), 172, 152, 0x000000, 0xffffff)
      #判断胜负并显示结果
      if self.mark%2==0:#如果双方都完成选择
        resultMessage = " 平局 "
        if self.computerStatus==0:
              return
        elif(self.playerStatus==self.computerStatus):
              self.equalNum+=1
        elif(self.playerStatus>self.computerStatus):
              self.playerScore+=1
              resultMessage = "%s胜出"%self.playerName
        elif(self.playerStatus<self.computerStatus):
              self.computerScore+=1
              resultMessage = "%s胜出"%self.computerName
        text.draw(resultMessage, 90, 210, 0x000000, 0xffffff)
        self.updateTotolArea()

显示初始化界面包括顶部的游戏规则说明与下方的结果显示,并将gameStart变量置位True,代表游戏开始。

def displayInit(self, x=10, y=10, w=222, h=303):
      #显示游戏规则信息
      mentionStr1 = "游戏规则:"
      mentionStr2 = "双方随机生成1-6的数"
      mentionStr3 = "按键1-3选择 按键4停止"
      text.draw(mentionStr1, 20, 20, 0x000000, 0xffffff)
      text.draw(mentionStr2, 20, 36, 0x000000, 0xffffff)
      text.draw(mentionStr3, 20, 52, 0x000000, 0xffffff)
      text.draw("-------------", 20, 68, 0x000000, 0xffffff)
      self.updateTotolArea()
      #设置游戏运行状态
      self.gameStart = True

按键事件处理,在按下一个键后,mark变量+1,调用roll接口获取1到6的随机数

def roll(self):
      #获得随机数并更新显示
      result1=randint(1,6)
      text.draw(str(result1), 52, 152, 0x000000, 0xffffff)
      self.playerStatus=result1

def pressKeyboardEvent(self, key):
      keymatch=["Key1","Key2","Key3","Key4"]
      #游戏还未开始,不必处理键盘输入
      if(self.gameStart == False):
        return
      #处理按键
      print(keymatch[key])
      self.mark=self.mark+1
      if(keymatch[key] == "Key1"):
        self.roll()
        self.client.publish(self.TOPIC2,"1"+str(self.playerStatus))
        text.draw(str(self.playerStatus), 52, 168, 0x000000, 0xffffff)
      elif(keymatch[key] == "Key2"):
        self.roll()
        self.client.publish(self.TOPIC2,"1"+str(self.playerStatus))
        text.draw(str(self.playerStatus), 52, 168, 0x000000, 0xffffff)
      elif(keymatch[key] == "Key3"):
        self.roll()
        self.client.publish(self.TOPIC2,"1"+str(self.playerStatus))
        text.draw(str(self.playerStatus), 52, 168, 0x000000, 0xffffff)
      else:
        text.draw("游戏结束", 90, 210, 0x000000, 0xffffff)
        #设置游戏运行状态
        self.gameStart = False
        return

      #对方玩家选择

      #一号玩家收到21-26

      print("choose is :",self.choose)
      if (self.choose<20 ):#来自1号自己的信息,不判断
        print ("对方尚未选择")
        return
      elif(self.choose>20):
        print("player1 get choose is :",self.choose)
        #处理所得信息
        self.computerStatus = self.choose-20
        #显示对方结果
        text.draw(str(self.computerStatus), 172, 152, 0x000000, 0xffffff)

      #判断胜负并显示结果
      if self.mark%2==0:#如果双方都完成选择
        resultMessage = " 平局 "
        if self.computerStatus==0:
              return
        elif(self.playerStatus==self.computerStatus):
              self.equalNum+=1
        elif(self.playerStatus>self.computerStatus):
              self.playerScore+=1
              resultMessage = "%s胜出"%self.playerName
        elif(self.playerStatus<self.computerStatus):
              self.computerScore+=1
              resultMessage = "%s胜出"%self.computerName
        text.draw(resultMessage, 90, 210, 0x000000, 0xffffff)
        self.updateTotolArea()

游戏开始与更新得分函数,游戏开始函数每次循环都调用client.check_msg()函数,接受到信息以后调用sub_cb函数

  def startGame(self):

        print("-------骰子游戏开始-------")
        while True:
          self.client.check_msg()
          self.roll()
          #print(gc.mem_free())
          i = 0
          j = -1
          for k in keys:
                if(k.value() == 0):
                  if i!=j:
                        j = i
                        self.pressKeyboardEvent(i)
                        self.client.check_msg()
                i = i+1;
                if(i > 3):
                  i = 0
          time.sleep_ms(100)#按键防抖

  def updateTotolArea(self):
        #汇总区域用于显示电脑和玩家的胜平负次数
        print("-------更新汇总区域-------")
        playerTotal = "%s赢了%d局" % (self.playerName, self.playerScore)
        computerTotal = "%s赢了%d局" % (self.computerName, self.computerScore)
        equalTotal = "平局%d次" % self.equalNum
        text.draw("-------------", 20, 240, 0x000000, 0xffffff)
        text.draw(playerTotal, 20, 256, 0x000000, 0xffffff)
        text.draw(computerTotal, 20, 272, 0x000000, 0xffffff)
        text.draw(equalTotal, 20, 288, 0x000000, 0xffffff)

if __name__ == '__main__':

        newGame = Game("玩家1", "玩家2")
        newGame.startGame()

玩家二的代码和玩家一不一样的地方是发送的数据(玩家1发送11-16,玩家2发送21-26)和CLIENT_ID不一样