基于UDP协议的五子棋

文章目录

需求

在这里插入代码片建立基于UDP协议的Socket编程方法来制作网络五子棋程序。
根据需求,该系统所应实现的功能有以下一些:
 游戏时服务器端首先启动,当客户端连接后,服务器端可以走棋。
 用户根据提示信息,轮到自己下棋才可以在棋盘上落子,同时下方标签会显对方的走棋信息,服务端和客户端用户均可以通过“退出游戏”按钮结束游戏。

代码

客户端

# 客户端启动程序
import sys
import tkinter as tk
from tkinter import messagebox as msgbox
import socket
import threading
import os
from lib import common
from conf import setting


# 发送消息
def send_message(pos):
    global s
    s.sendto(pos.encode(), (host, port))
    print('客户端发送信息', pos)


# 走棋函数
def callback(event):
    global turn
    global Myturn

    if Myturn == -1:  # 第一次确定自己的角色
        Myturn = turn
    else:
        if (Myturn != turn):
            msgbox.showinfo(title='提示', message='还没轮到自己走棋')
            return
    # print('clicked at ', event.x, event.y, turn)

    x = (event.x) // 40  # 610x610, 15x15
    y = (event.y) // 40    # 换算棋盘坐标
    print('clicked at ', x, y, turn)

    if map[x][y] != ' ':
        msgbox.showinfo(title='提示', message='已有棋子')
    else:
        img1 = imgs[turn]
        cv.create_image((x * 40 + 20, y * 40 + 20), image=img1)  # 画自己棋子
        cv.pack()

        map[x][y] = str(turn)

        pos = str(x) + ',' + str(y)
        send_message('move|' + pos)
        print('客户端走棋的位置', pos)
        label1['text'] = '客户端走棋的位置' + pos

        # 输赢信息
        if common.win_lose(turn, map) == True:
            if turn == 0:
                msgbox.showinfo(title='提示', message='黑方赢了')
                send_message('over|黑方赢了')
            else:
                msgbox.showinfo(title='提示', message='白方赢了')
                send_message('over|白方赢了')

        # 换对手走棋
        if turn == 0:
            turn = 1
        else:
            turn = 0

    return None


# 退出游戏
def callexit(event):
    pos = 'exit|'
    send_message(pos)
    os._exit(0)


# 显示当前棋子信息
def print_map():
    for j in range(0, 15):
        for i in range(0, 15):
            print(map[i][j], end=' ')
        print('w')


# 画对方棋子
def drawOtherChess(x, y):
    global turn
    img1 = imgs[turn]
    cv.create_image((x * 40 + 20, y * 40 + 20), image=img1)
    cv.pack()
    map[x][y] = str(turn)

    # 换对方走棋
    if turn == 0:
        turn = 1
    else:
        turn = 0


# 接受服务器消息
def receiveMessage():
    global s
    while True:
        global addr
        data, addr = s.recvfrom(1024)
        data = data.decode('utf-8')
        a = data.split('|')
        print('................客户端正在接收消息...')

        if not data:
            print('server has exited!')
            break
        # elif a[0] == 'join':
        #     print('client 连接服务器!')
        #     label1['text'] = 'client 连接服务器成功,请你走棋!'
        elif a[0] == 'exit':
            print('client 对方退出!')
            label1['text'] = 'server 对方退出,游戏结束!'
        elif a[0] == 'over':
            print_map()
            label1['text'] = data.split('|')[0]
            msgbox.showinfo(title='提示', message=data.split('|')[1])
        elif a[0] == 'move':
            print('received:  ', data, 'from ', addr)
            p = a[1].split(',')
            x = int(p[0])
            y = int(p[1])
            print(p[0], p[1])
            label1['text'] = '服务器走的位置' + p[0] + p[1]
            drawOtherChess(x, y)
    s.close()


# 开启新的线程,用于接受信息
def startNewThread():
    thread = threading.Thread(target=receiveMessage, args=())
    thread.setDaemon(True)
    thread.start()
    print('thread start')


if __name__ == '__main__':
    sys.path.append(setting.BASE_PATH)
    # print(sys.path)
    root = tk.Tk()  # 创建win窗口对象
    root.title('网络五子棋--客户端(By王训志)')
    imgs = [
        tk.PhotoImage(file=os.path.join(setting.BASE_PATH, 'pictures', 'stone_black.PNG')),
        tk.PhotoImage(file=os.path.join(setting.BASE_PATH, 'pictures', 'stone_white.PNG')),
    ]
    # 这里用0表示黑子、1表示白子
    turn = 0
    # 用于保存自己的角色 -1代表还未确定角色
    Myturn = -1
    # 记录游戏地图
    map = [[' ' for i in range(15)] for y in range(15)]

    cv = tk.Canvas(root, bg='green', width=610, height=610)  # 绑定画布
    common.draw(cv)
    cv.bind('<Button-1>', callback)  # 绑定点击按钮
    cv.pack()  # 将cv打包进主窗口

    label1 = tk.Label(root, text="客户端....")  # 绑定标签
    label1.pack()  # 同理打包

    button1 = tk.Button(root, text='退出游戏')
    button1.bind('<Button-1>', callexit)
    button1.pack()

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    print('................客户端正在尝试通信...')

    port = 8080
    host = '127.0.0.1'  # 回环地址

    pos = 'join|'  # 连接服务器
    send_message(pos)

    startNewThread()
    root.mainloop()  # 显示窗口

服务端

# 服务端启动程序
import sys
import tkinter as tk
from tkinter import messagebox as msgbox
import socket
import threading
import os
from conf import setting
from lib import common


# 不用扫描全部棋盘,以中心点判断
def checkWin(x, y):
    flag = False
    color = map[x][y]

    # 横向判断
    i = 1
    count = 1
    while color == map[x+i][y]:
        count += 1
        i += 1

    i = 1
    while color == map[x-i][y]:
        count += 1
        i += 1

    if count >= 5:
        flag = True

    # 纵向判断
    i2 = 1
    count2 = 1
    while color == map[x][y-i2]:
        count2 += 1
        i2 += 1

    i2 = 1
    while color == map[x][y+i2]:
        count2 += 1
        i2 += 1

    if count2 >= 5:
        flag = True

    # 斜方向 异向
    i3 = 1
    count3 = 1
    while color == map[x+i3][y-i3]:
        count3 += 1
        i3 += 1

    i3 = 1
    while color == map[x-i3][y+i3]:
        count3 += 1
        i3 += 1

    if count3 >= 5:
        flag = True

    # 斜方向 同向
    i4 = 1
    count4 = 1
    while color == map[x+i3][y+i3]:
        count4 += 1
        i4 += 1

    i4 = 1
    while color == map[x-i3][y-i3]:
        count4 += 1
        i4 += 1

    if count4 >= 5:
        flag = True

    return flag


def sendMessage(pos):
    global s
    global addr
    s.sendto(pos.encode(), addr)
    print('服务器发送信息', pos)


def callpos(event):
    global turn
    global Myturn

    if Myturn == -1:
        Myturn = turn
    else:
        if (Myturn != turn):
            msgbox.showinfo(title='提示', message='还没轮到自己走棋')
            return
    # print('clicked at ', event.x, event.y, turn)

    x = (event.x) // 40  # 610x610, 15x15
    y = (event.y) // 40
    print('clicked at ', x, y, turn)

    if map[x][y] != ' ':
        msgbox.showinfo(title='提示', message='已有棋子')
    else:
        img1 = imgs[turn]
        cv.create_image(
            (x*40+20, y*40+20),
            image=img1,
        )
        cv.pack()

        map[x][y] = str(turn)

        pos = str(x) + ',' + str(y)
        sendMessage('move|' + pos)
        print('服务器走棋的位置', pos)
        label1['text'] = '服务器走棋的位置' + pos

        # 输赢信息
        if common.win_lose(turn, map) == True:
            if turn == 0:
                msgbox.showinfo(title='提示', message='黑方赢了')
                sendMessage('over|黑方赢了')
            else:
                msgbox.showinfo(title='提示', message='白方赢了')
                sendMessage('over|白方赢了')

        # 换对手走棋
        if turn == 0:
            turn = 1
        else:
            turn = 0

    return None


def callexit(event):
    pos = 'exit|'
    sendMessage(pos)
    os._exit(0)


def startNewThread():
    thread = threading.Thread(target=receiveMessage, args=())  # 线程要的是函数名,而非函数返回值
    thread.setDaemon(True)
    thread.start()
    print('thread start')


def print_map():
    for j in range(0, 15):
        for i in range(0, 15):
            print(map[i][j], end=' ')
        print('w')


def drawOtherChess(x, y):
    global turn
    img1 = imgs[turn]
    cv.create_image((x*40+20, y*40+20), image=img1)
    cv.pack()
    map[x][y] = str(turn)
    if turn == 0:
        turn = 1
    else:
        turn = 0


def receiveMessage():
    global s
    while True:
        global addr
        data, addr = s.recvfrom(1024)
        data = data.decode('utf-8')
        a = data.split('|')  # 根据自己定义的协议,划分数据
        print('................服务器正在接收消息...')

        if not data:
            print('client has exited!')
            break
        elif a[0] == 'join':
            print('client 连接服务器!')
            label1['text'] = 'client 连接服务器成功,请你走棋!'
        elif a[0] == 'exit':
            print('client 对方退出!')
            label1['text'] = 'client 对方退出,游戏结束!'
        elif a[0] == 'over':
            print_map()
            label1['text'] = data.split('|')[0]
            msgbox.showinfo(title='提示', message=data.split('|')[1])
        elif a[0] == 'move':
            print('received:  ', data, 'from ', addr)
            p = a[1].split(',')
            x = int(p[0])
            y = int(p[1])
            print(p[0], p[1])
            label1['text'] = '客户端走的位置' + p[0] + p[1]
            drawOtherChess(x, y)
    s.close()



    
    
if __name__ == '__main__':
    sys.path.append(setting.BASE_PATH)
    # print(sys.path)
    root = tk.Tk()
    root.title('网络五子棋--服务器端(By王训志)')
    imgs = [
        tk.PhotoImage(file=os.path.join(setting.BASE_PATH, 'pictures', 'stone_black.PNG')),
        tk.PhotoImage(file=os.path.join(setting.BASE_PATH, 'pictures', 'stone_white.PNG')),
    ]
    turn = 0
    # 保存自己的角色,-1 表示还没确定下来
    Myturn = -1
    map = [[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] for y in range(15)]

    cv = tk.Canvas(root, bg='green', width=610, height=610)
    common.draw(cv)
    cv.bind('<Button-1>', callpos)
    cv.pack()

    label1 = tk.Label(root, text="服务器端....")
    label1.pack()

    button1 = tk.Button(root, text='退出游戏')
    button1.bind('<Button-1>', callexit)
    button1.pack()

    # 创建UDP socket
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    print('................服务器正在尝试通信...')

    s.bind(('127.0.0.1', 8080))
    addr = ('127.0.0.1', 8080)

    startNewThread()
    root.mainloop()


注意:仅展示部分代码,需要私

猜你喜欢

转载自blog.csdn.net/qq_49821869/article/details/114919311