14. 联机五子棋设计与实现

14.1. 设计思路

  • 画出棋盘
  • 连接到mqtt服务器
  • 时刻检查是否收到消息,监听按键动作,选择落棋位置并判断是否落子
  • 接收或发送消息时在屏幕上显示出来,判断胜负

14.2. 代码实现

玩家一代码:

导入库文件

import ubitmap
import screen
import time
import text
import network
from machine import UART,Pin
import utime
from umqtt.simple import MQTTClient
import framebuf

定义类并初始化棋盘,默认开始坐标,mqtt服务器信息等

class GOES():
        def __init__(self):
                self.screen_width = 200
                self.screen_height = 280
                self.keys = [Pin(p, Pin.IN) for p in [35, 36, 39, 34]]
                self.keymatch = ["Key1", "Key2", "Key3", "Key4"]
                self.board=[
                                        [[20,20,0],[40,20,0],[60,20,0],[80,20,0],[100,20,0],[120,20,0],[140,20,0],[160,20,0],[180,20,0],[200,20,0],[220,20,0]],
                                        [[20,40,0],[40,40,0],[60,40,0],[80,40,0],[100,40,0],[120,40,0],[140,40,0],[160,40,0],[180,40,0],[200,40,0],[220,40,0]],
                                        [[20,60,0],[40,60,0],[60,60,0],[80,60,0],[100,60,0],[120,60,0],[140,60,0],[160,60,0],[180,60,0],[200,60,0],[220,60,0]],
                                        [[20,80,0],[40,80,0],[60,80,0],[80,80,0],[100,80,0],[120,80,0],[140,80,0],[160,80,0],[180,80,0],[200,80,0],[220,80,0]],
                                        [[20,100,0],[40,100,0],[60,100,0],[80,100,0],[100,100,0],[120,100,0],[140,100,0],[160,100,0],[180,100,0],[200,100,0],[220,100,0]],
                                        [[20,120,0],[40,120,0],[60,120,0],[80,120,0],[100,120,0],[120,120,0],[140,120,0],[160,120,0],[180,120,0],[200,120,0],[220,120,0]],
                                        [[20,140,0],[40,140,0],[60,140,0],[80,140,0],[100,140,0],[120,140,0],[140,140,0],[160,140,0],[180,140,0],[200,140,0],[220,140,0]],
                                        [[20,160,0],[40,160,0],[60,160,0],[80,160,0],[100,160,0],[120,160,0],[140,160,0],[160,160,0],[180,160,0],[200,160,0],[220,160,0]],
                                        [[20,180,0],[40,180,0],[60,180,0],[80,180,0],[100,180,0],[120,180,0],[140,180,0],[160,180,0],[180,180,0],[200,180,0],[220,180,0]],
                                        [[20,200,0],[40,200,0],[60,200,0],[80,200,0],[100,200,0],[120,200,0],[140,200,0],[160,200,0],[180,200,0],[200,200,0],[220,200,0]],
                                        [[20,220,0],[40,220,0],[60,220,0],[80,220,0],[100,220,0],[120,220,0],[140,220,0],[160,220,0],[180,220,0],[200,220,0],[220,220,0]]
                                        ]#初始化棋子坐标(x坐标,y坐标,颜色)
                self.startX = 20
                self.startY = 20
                self.selectXi = 5
                self.selectYi = 5
                self.displayInit()
                self.color=0x000000#player1棋子颜色为黑色
                self.wifi_name = "NEUI"
                self.wifi_SSID = "NEUI3187"
                #MQTT服务端信息
                self.SERVER = "112.125.89.85"   #MQTT服务器地址
                self.SERVER_PORT = 3881         #MQTT服务器端口
                self.DEVICE_ID = "wc001"        #设备ID
                self.TOPIC1 = b"/cloud-skids/online/dev/" + self.DEVICE_ID
                self.TOPIC2 = b"/cloud-skids/message/server/" + self.DEVICE_ID
                self.CLIENT_ID = "f25410646a8348f8a1726a3890ad8f73"
                self.uart = UART(1, baudrate=115200, bits=8, parity=0, rx=18, tx=19, stop=1)
                #设备状态
                self.ON = "1"
                self.OFF = "0"
                self.d=" "
                self.c = MQTTClient(self.CLIENT_ID, self.SERVER, self.SERVER_PORT)

下棋焦点位置绘制接口

def drawcross(self, x, y, lineColor):# 画选择位置的框
        screen.drawline(x - 10, y-10, x - 5, y-10, 3, lineColor)
        screen.drawline(x - 10, y-10, x - 10, y-5, 3, lineColor)
        screen.drawline(x + 10, y-10, x + 5, y-10, 3, lineColor)
        screen.drawline(x + 10, y-10, x + 10, y-5, 3, lineColor)
        screen.drawline(x - 10, y+10, x - 5, y+10, 3, lineColor)
        screen.drawline(x - 10, y+10, x - 10, y+5, 3, lineColor)
        screen.drawline(x + 10, y+10, x + 5, y+10, 3, lineColor)
        screen.drawline(x + 10, y+10, x + 10, y+5, 3, lineColor)

画棋盘网络接口

#画棋盘网格
def grid(self):
        x=20
        y=20
        for i in range(11):
                screen.drawline(x, 20, x, 220, 3, 0x000000)
                x+=20
        for j in range(11):
                screen.drawline(20, y, 220, y, 3, 0x000000)
                y+=20

连接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())
        gc.collect()

连接到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)

服务器回调接口

def sub_cb(self,topic, message):#从服务器接受信息
        message = message.decode()

        print("服务器发来信息:%s" % message)
        #global count
        t=int(message)#根据接收到的信息解析棋子位置
        j=t//11
        i=t%11
        x = self.board[j][i][0]
        y = self.board[j][i][1]
        if self.board[j][i][2]==0:
                self.put_circle_back(x,y,10,0x00ff00)#在解析出的位置上画绿色棋子
                self.board[j][i][2]=1#将棋子标记为绿色
        self.is_win(i,j,self.board[j][i][2])#判断胜负

画棋子接口

def put_circle_back(self,x,y,r,color):#画圆形棋子
        a=0
        b=r
        di=3-(r<<1)
        while (a<=b):
                screen.drawline(x - b, y-a, x + b, y-a, 3, color)
                screen.drawline(x - a, y, x - a, y+b, 3, color)
                screen.drawline(x - b, y-a, x, y-a, 3, color)
                screen.drawline(x - a, y-b, x - a, y, 3, color)
                screen.drawline(x, y+a, x + b, y+a, 3, color)
                screen.drawline(x + a, y-b, x + a, y, 3, color)
                screen.drawline(x + a, y, x + a, y+b, 3, color)
                screen.drawline(x - b, y+a, x, y+a, 3, color)
                a+=1
                if(di<0):
                        di+=4*a+6
                else:
                        di+=10+4*(a-b)
                        b-=1
                screen.drawline(x + a, y, x + a, y+b, 3, color)

启始焦点绘制接口

def selectInit(self):#选择初始化
        # 变量初始化
        self.selectXi = 5
        self.selectYi = 5
        x = self.board[self.selectYi][self.selectXi][0]
        y = self.board[self.selectYi][self.selectXi][1]
        # 选择初始化
        self.drawcross(x, y, 0xff0000)

程序初始化接口

# 界面初始化
def displayInit(self):#开始游戏初始化
        screen.clear()
        self.grid()
        for self.selectYi in range(11):
                for self.selectXi in range(11):
                        self.board[self.selectYi][self.selectXi][2]=0
        self.selectInit()

判断胜负接口

def is_win(self,i,j,k):#判断胜负

        start_y=0
        end_y=10
        if j-4>=0:
                start_y=j-4
        if j+4<=10:
                end_y=j+4
        count=0
        for pos_y in range(start_y,end_y+1):#判断纵向胜负
                if self.board[pos_y][i][2]==k and k==1:
                        count+=1

                        if count>=5:
                                text.draw("绿色方胜",88,160,0xff0000)
                else:
                        count=0
        for pos_y in range(start_y,end_y+1):
                if self.board[pos_y][i][2]==k and k==2:
                        count+=1

                        if count>=5:
                                text.draw("黑色方胜",88,160,0xff0000)
                else:
                        count=0

        start_x=0
        end_x=10
        if i-4>=0:
                start_x=i-4
        if i+4<=10:
                end_x=i+4
        count=0
        for pos_x in range(start_x,end_x+1):#判断横向胜负
                if self.board[j][pos_x][2]==k and k==1:
                        count+=1

                        if count>=5:
                                text.draw("绿色方胜",88,160,0xff0000)
                else:
                        count=0
        for pos_x in range(start_x,end_x+1):
                if self.board[j][pos_x][2]==k and k==2:
                        count+=1

                        if count>=5:
                                text.draw("黑色方胜",88,160,0xff0000)
                else:
                        count=0

        count=0
        s=j-i
        start=start_y
        end=end_x+s
        if j>i:
                start=start_x+s
                end=end_y
        for index in range(start,end+1):#判断斜方向胜负(左上右下)
                if self.board[index][index-s][2]==k and k==1:
                        count+=1

                        if count>=5:
                                text.draw("绿色方胜",88,160,0xff0000)
                else:
                        count=0
        for index in range(start,end+1):
                if self.board[index][index-s][2]==k and k==2:
                        count+=1

                        if count>=5:
                                text.draw("黑色方胜",88,160,0xff0000)
                else:
                        count=0

        count=0
        s=j+i

        if j+i<=10:
                start=start_y
                end=s-start_x
        if j+i>10:
                start=s-10
                end=10
        if s>=4 and s<=16:

                for index in range(start,end+1):#判断斜方向胜负(左下右上)
                        if self.board[index][s-index][2]==k and k==1:
                                count+=1

                                if count>=5:
                                        text.draw("绿色方胜",88,160,0xff0000)
                        else:
                                count=0
                for index in range(start,end+1):
                        if self.board[index][s-index][2]==k and k==2:
                                count+=1

                                if count>=5:
                                        text.draw("黑色方胜",88,160,0xff0000)
                        else:
                                count=0

键盘监听接口

def keyboardEvent(self, key):
        # 右移选择键
        if self.keymatch[key] == "Key1":
                # 取消前一个选择
                x = self.board[self.selectYi][self.selectXi][0]
                y = self.board[self.selectYi][self.selectXi][1]
                self.drawcross(x, y, 0xffffff)
                # 选择右边一个
                self.selectXi=(self.selectXi+1)%11
                x = self.board[self.selectYi][self.selectXi][0]
                y = self.board[self.selectYi][self.selectXi][1]
                self.drawcross(x, y, 0xff0000)
        # 纵向移动键
        elif self.keymatch[key] == "Key2":
                # 取消前一个选择
                x = self.board[self.selectYi][self.selectXi][0]
                y = self.board[self.selectYi][self.selectXi][1]
                self.drawcross(x, y, 0xffffff)
                # 选择下边一个
                self.selectYi=(self.selectYi+1)%11
                x = self.board[self.selectYi][self.selectXi][0]
                y = self.board[self.selectYi][self.selectXi][1]
                self.drawcross(x, y, 0xff0000)
        # 确认键
        elif self.keymatch[key] == "Key3":
                x = self.board[self.selectYi][self.selectXi][0]
                y = self.board[self.selectYi][self.selectXi][1]
                if self.board[self.selectYi][self.selectXi][2]==0:
                        self.put_circle_back(x,y,10,self.color)#画黑色棋子
                        self.board[self.selectYi][self.selectXi][2]=2#将棋子标记为黑色
                        s=(self.selectYi)*11+(self.selectXi)
                        self.d=str(s)
                        self.c.publish(self.TOPIC2,self.d)#向服务器发送棋子位置信息
                self.is_win(self.selectXi,self.selectYi,self.board[self.selectYi][self.selectXi][2])
        elif self.keymatch[key] == "Key4":
                self.displayInit()

程序启动接口

def start(self):
        try:
                while True:
                        self.c.check_msg()#检查是否收到信息
                        i = 0#用来辅助判断那个按键被按下
                        j = -1
                        for k in self.keys:
                                if (k.value() == 0):#如果按键被按下
                                        if i != j:
                                                j = i
                                                self.keyboardEvent(i)#触发相应按键对应事件
                                i = i + 1
                                if (i > 3):
                                        i = 0
                        time.sleep_ms(200)  # 按键去抖
        finally:
                        self.c.disconnect()
                        print("MQTT连接断开")

程序主函数

if __name__ == '__main__':
        go = GOES()
        go.do_connect()
        go.esp()
        go.start()

玩家二的代码和玩家一不一样的地方是初始的颜色不一样,以及服务器回调回来后画棋子的颜色和标记不一样。

14.3. 效果展示

../../_images/goes1.png ../../_images/goes2.png