python網絡編程案例——五子棋遊戲

一.本案例基於UDP的socket編程方法來製作五子棋程序,網絡五子棋採用C/S架構,分爲服務器端和客戶端,遊戲時服務端首先啓動,當客戶端啓動連接後,服務器端可以走棋,輪到自己棋纔可以在棋盤上落子,同時下方標籤會顯示對方走起信息,服務器端用戶可以通過“退出遊戲”按鈕可以結束遊戲;

1.數據通信協議

網絡五子棋遊戲設計的難點在於對方進行通信,這裏使用面向非連接的Socket編程,Socket編程用於C/S開發,在這類應用中,客戶端和服務器端通常需要先建立連接,然後發送和接收數據,交互完成後需要斷開連接,本章採用基於UDP的Socket編程實現,這裏雖然兩臺計算機不分主次,但涉及時候假設一臺作爲服務器端,等待其他方加入,其他想加入必須輸入服務器端主機的IP;
下面展示一些 數據通信協議代碼

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('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()

2.判斷輸贏的算法

   本遊戲關鍵技術就是判斷輸贏的算法,對於算法實現大致可以分爲以下幾個部分:

(1)判斷X=Y軸上是否形成五子連珠;

(2)判斷X=-Y軸上是否形成五子連珠;

(3)判斷X軸上是否形成五子連珠;

(4)判斷Y軸上是否形成五子連珠;

#輸贏判斷
def win_lose():
    a=str(turn)
    print("a=",a)
    for i in range(0,11):
        for j in range(0,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
    for i in range(4,15):
        for j in range(0,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
    for i in range(0,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-2]==a and map[i][j-4]==a:
                print("Y軸上形成了五子連珠")
                return True
    for i in range(0,11):
        for j in range(0,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

二. 源代碼:
1.客戶端編程代碼如下:

from tkinter import *
from tkinter.messagebox import *
import socket
import threading
import os
#主程序
root=Tk()
root.title("網絡五子棋v2.0--UDP客戶端")
imgs=[PhotoImage(file='E:\\game\\BlackStone.gif'),PhotoImage(file='E:\\game\\WhiteStone.gif')]
turn=0
Myturn=-1
#畫對方棋子
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 sendMessage(pos):
    global s
    s.sendto(pos.encode(),(host,port))
#退出函數
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
    #print("clicked at",event.x,event.y)
    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(0,15):
        cv.create_line(20,20+40*i,580,20+40*i,width=2)
    for i in range(0,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)
    for i in range(0,11):
        for j in range(0,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
    for i in range(4,15):
        for j in range(0,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
    for i in range(0,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-2]==a and map[i][j-4]==a:
                print("Y軸上形成了五子連珠")
                return True
    for i in range(0,11):
        for j in range(0,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

#接受消息
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('對方退出!')
            lanel1["text"] = '對方退出!遊戲結束!'
        elif a[0] == 'over':
            print('對方贏信息!')
            label1["text"] = data.split("|")[0]
            showinfo(title="提示", message=data.split("|")[1])
        elif a[0] == 'move':
            print('received:', 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 startNewThread():
    thread=threading.Thread(target=receiveMessage,args=())
    thread.setDaemon(True)
    thread.start()
#主程序

map=[[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "] for y in range(15)]
cv=Canvas(root,bg='green',width=610,height=610)
drawQiPan()
cv.bind("<Button-1>",callback)
cv.pack()
label1=Label(root,text="客戶端...")
label1.pack()
button1=Button(root,text="退出遊戲")
button1.bind("<Button-1>",callexit)
button1.pack()
#創建UDP
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
port=8000
host='localhost'
pos='join|'
sendMessage(pos)
startNewThread()
root.mainloop()

2.數據通信協議源代碼:
下面展示一些 數據通信協議源代碼

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('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 win_lose():
    a=str(turn)
    print("a=",a)
    for i in range(0,11):
        for j in range(0,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
    for i in range(4,15):
        for j in range(0,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
    for i in range(0,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-2]==a and map[i][j-4]==a:
                print("Y軸上形成了五子連珠")
                return True
    for i in range(0,11):
        for j in range(0,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
def checkwin(x,y):
    flag=False
    count=1
    color=map[x][y]
    i=1
    #橫向判斷
    while color==map[x+i][y]:
        count=count+1
        i=i+1
    i=1
    while color==map[x-i][y]:
        count=count+1
        i=i+1
    if count>=5:
        flag=True
    #豎向判斷
    i=1
    while color==map[x][y+i]:
        count=count+1
        i=i+1
    i=1
    while color==map[x][y-i]:
        count=count+1
        i=i+1
    if count>=5:
        flag=True
    #x=y判斷
    i=1
    j=1
    while color==map[x+i][y+i]:
        count=count+1
        i=i+1
        j=j+1
    if count>=5:
        flag=True
    j=1
    i=1
    while color==map[x-i][y-i]:
        count=count+1
        i=i+1
        j=j+1
    if count>=5:
        flag=True

3.服務器端源代碼

客戶端源代碼如下:
from tkinter import *
from tkinter.messagebox import *
import socket,threading,os
def drawQiPan():
    for i in range(0,15):
        cv.create_line(20,20+40*i,580,20+40*i,width=2)
    for i in range(0,15):
        cv.create_line(20+40*i,20,20+40*i,580,width=2)
    cv.pack()
#走棋函數
def callpos(event):
    global turn
    global Myturn
    if Myturn==-1:  #第一次確認自己的角色
        Myturn=turn
    else:
        if(Myturn!=turn):
            showinfo(title="提示",message="還沒輪到自己下棋")
            return
    #print("clicked at",event.x,event.y,true)
    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 sendMessage(pos):
    global s
    global addr
    s.sendto(pos.encode(),addr)
#退出函數
def callexit(event):
    pos="exit|"
    sendMessage(pos)
    os._exit(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 win_lose():
    a=str(turn)
    print("a=",a)
    for i in range(0,11):
        for j in range(0,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
    for i in range(4,15):
        for j in range(0,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
    for i in range(0,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-2]==a and map[i][j-4]==a:
                print("Y軸上形成了五子連珠")
                return True
    for i in range(0,11):
        for j in range(0,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(0,15):
        for i in range(0,15):
            print(map[i][j],end=' ')
        print('w')
#接受消息
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('對方贏信息!')
            labl1["text"]==data.split("|")[0]
            showinfo(title="提示",message=data.split("1")[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()
root=Tk()
root.title("網絡五子棋v2.0-服務器端")
imgs=[PhotoImage(file='E:\\game\\BlackStone.gif'),PhotoImage(file='E:\\game\\WhiteStone.gif')]
turn=0
Myturn=-1
map=[[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "] for y in range(15)]
cv=Canvas(root,bg='green',width=610,height=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()
root.mainloop()

運行結果展示:
在這裏插入圖片描述
所有的都在這裏了,歡迎各位大佬批評指正,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章