14 網絡通信案例——基於UDP的網絡五子棋遊戲(2)

14.4 程序源碼

1、服務器端程序設計的步驟

from tkinter import *
from tkinter.messagebox import *
import socket
import threading
import os

主程序

root = Tk()
root.title('網絡五子棋v2.0——服務器端')
imgs = [PhotoImage(file = 'BlackStone.gif'), PhotoImage(file='WhiteStone.gif')]
turn = 0
Myturn = -1
map = [[' '] * 15 for y in range(15)]
# print(map)

cv = Canvas(root, bg = 'green', width = 610, heigth = 610)
drawQiPan()
cv.bind('<Button>-1', callpos)
cv.pack()
label1 = Label(root, text = '服務器端...')
label1.pack()
button1 = Button(root, text = '退出遊戲')
button1 = bind('<Button-1>', callexit)
button1.pack()

創建UDP SOCKET

s =socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('localhost', 8000))
addr = ('localhost', 8000)
startNewThread() # 啓動線程接收客戶端消息
receiveMessage()
root.mainloop()

退出函數

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

走棋函數

def callpos(event):
    global turn
    global Myturn
    
    if Myturn == -1:
        Myturn = turn
    else:
        if Myturn != turn:
            showinfo(title = '提示', message='還沒輪到自己走棋')
            return
        
    x = (event.x) // 40
    y = (event.y) // 40
    print('clicked at', x, y, turn)
    
    if map[x][y] != ' ':
        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 win_lose() == True:
            if turn == 0:
                showinfo(title = '提示', message = '黑方你贏了')
                sendMessage('over|黑方你贏了')
            else:
                showinfo(title = '提示', message = '白方你贏了')
                sendMessage('over|白方你贏了')
        
        # 換下一次走棋
        if turn == 0:
            turn = 1
        else:
            turn = 0

畫對方棋子

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 drawQiPan():
    for i in range(15):
        cv.create_line(20, 20+40*i, 580, 20+40*i, width = 2)
    for i in range(15):
        cv.create_line(20+40*i, 20, 20+40*i, 580, width = 2)
        
    cv.pack()

輸贏判斷

掃描整個棋盤判斷輸贏的算法

def win_lose():
    a = str(turn)
    print('a=', a)
    
    # 判斷X=Y軸上是否形成五子連珠
    for i in range(11):
        for j in range(11):
            if map[i][j]==a and map[i+1][j+1]==a and map[i+2][j+2]==a and map[i+3][j+3]==a and map[i+4][j+4]==a:
                print('X=Y軸上形成五子連珠!')
                return True
    
    # 判斷X=-Y軸上是否形成五子連珠
    for i in range(4, 15):
        for j in range(11):
            if map[i][j]==a and map[i-1][j+1]==a and map[i-2][j+2]==a and map[i-3][j+3]==a and map[i-4][j+4]==a:
                print('X=-Y軸上形成五子連珠!')
                return True
    
    # 判斷Y軸上是否形成五子連珠
    for i in range(15):
        for j in range(4, 15):
            if map[i][j]==a and map[i][j-1]==a and map[i][j-2]==a and map[i][j-3]==a and map[i][j-4]==a:
                print('Y軸上形成五子連珠!')
                return True
    
    # 判斷X軸上是否形成五子連珠
    for i in range(11):
        for j in range(15):
            if map[i][j]==a and map[i+1][j]==a and map[i+2][j]==a and map[i+3][j]==a and map[i+4][j]==a:
                print('X軸上形成五子連珠!')
                return True
    
    return False

輸出map地圖

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

處理接收消息data

通信協議具體實現

def receiveMessage(): # 接收消息函數!
    global s
    while True:
        # 接收客戶端發送的消息
        global addr
        data, addr = s.recvfrom(1024)
        data = data.decode('utf-8')
        a = data.split('|')             # 分割數據!
        
        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('對方贏信息!')
            label1['text'] = data.split('|')[0]
            showinfo(title='提示', message=data.split('|')[1])
        elif a[0] == 'move':
            print('receive:', 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 sendMessage(pos):
    global s 
    global addr 
    s.sendto(pos.encode(), addr)

啓動線程接收客戶端的消息

def startNewThread():
    thread = threading.Thread(target=receiveMessage, args = ())
    thread.setDaemon(True)
    thread.start()

2、客戶端程序設計的步驟

from tkinter import *
from tkinter.messagebox import *
import socket
import threading
import os

主程序

root = Tk()
root.title('網絡五子棋v2.0——UDP客戶端')
imgs = [PhotoImage(file = 'BlackStone.gif'), PhotoImage(file='WhiteStone.gif')]
turn = 0
Myturn = -1
map = [[' '] * 15 for y in range(15)]
# print(map)

cv = Canvas(root, bg = 'green', width = 610, heigth = 610)
drawQiPan()
cv.bind('<Button>-1', callpos)
cv.pack()
label1 = Label(root, text = '客戶端...')
label1.pack()
button1 = Button(root, text = '退出遊戲')
button1 = bind('<Button-1>', callexit)
button1.pack()

創建UDP SOCKET

s =socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
port = 8000
host = 'localhost'
post = 'join|'
sendMessage(pos)
startNewThread() # 啓動線程接收客戶端消息
receiveMessage()
root.mainloop()

退出函數

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

走棋函數

def callback(event):
    global turn
    global Myturn
    
    if Myturn == -1:
        Myturn = turn
    else:
        if Myturn != turn:
            showinfo(title = '提示', message='還沒輪到自己走棋')
            return
        
    x = (event.x) // 40
    y = (event.y) // 40
    print('clicked at', x, y, turn)
    
    if map[x][y] != ' ':
        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 win_lose() == True:
            if turn == 0:
                showinfo(title = '提示', message = '黑方你贏了')
                sendMessage('over|黑方你贏了')
            else:
                showinfo(title = '提示', message = '白方你贏了')
                sendMessage('over|白方你贏了')
        
        # 換下一次走棋
        if turn == 0:
            turn = 1
        else:
            turn = 0
        

畫棋盤

def drawQiPan():
    for i in range(15):
        cv.create_line(20, 20+40*i, 580, 20+40*i, width = 2)
    for i in range(15):
        cv.create_line(20+40*i, 20, 20+40*i, 580, width = 2)
        
    cv.pack()

輸贏判斷(和服務器端一樣!)

掃描整個棋盤判斷輸贏的算法

def win_lose():
    a = str(turn)
    print('a=', a)
    
    # 判斷X=Y軸上是否形成五子連珠
    for i in range(11):
        for j in range(11):
            if map[i][j]==a and map[i+1][j+1]==a and map[i+2][j+2]==a and map[i+3][j+3]==a and map[i+4][j+4]==a:
                print('X=Y軸上形成五子連珠!')
                return True
    
    # 判斷X=-Y軸上是否形成五子連珠
    for i in range(4, 15):
        for j in range(11):
            if map[i][j]==a and map[i-1][j+1]==a and map[i-2][j+2]==a and map[i-3][j+3]==a and map[i-4][j+4]==a:
                print('X=-Y軸上形成五子連珠!')
                return True
    
    # 判斷Y軸上是否形成五子連珠
    for i in range(15):
        for j in range(4, 15):
            if map[i][j]==a and map[i][j-1]==a and map[i][j-2]==a and map[i][j-3]==a and map[i][j-4]==a:
                print('Y軸上形成五子連珠!')
                return True
    
    # 判斷X軸上是否形成五子連珠
    for i in range(11):
        for j in range(15):
            if map[i][j]==a and map[i+1][j]==a and map[i+2][j]==a and map[i+3][j]==a and map[i+4][j]==a:
                print('X軸上形成五子連珠!')
                return True
    
    return False

接收消息

處理接收消息data

通信協議具體實現

def receiveMessage(): # 接收消息函數!
    global s
    while True:
        data = s.recv(1024).decode('utf-8')
        a = data.split('|')             # 分割數據!
        
        if not data:
            print('server has exited!')
            break
        elif a[0] == 'exit':
            print('對方退出!')
            label1['text'] = '對方退出,遊戲結束!'
        elif a[0] == 'over':
            print('對方贏信息!')
            label1['text'] = data.split('|')[0]
            showinfo(title='提示', message=data.split('|')[1])
        elif a[0] == 'move':
            print('receive:', data)
            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 sendMessage(pos):
    global s 
    s.sendto(pos.encode(), (host, port))

啓動線程接收服務器端的消息

def startNewThread():
    thread = threading.Thread(target=receiveMessage, args = ())
    thread.setDaemon(True)
    thread.start()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章