一.本案例基于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("" ,callback)
cv.pack()
label1=Label(root,text="客户端...")
label1.pack()
button1=Button(root,text="退出游戏")
button1.bind("" ,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("" ,callpos)
cv.pack()
label1=Label(root,text="服务器端...")
label1.pack()
button1=Button(root,text="退出游戏")
button1.bind("" ,callexit)
button1.pack()
#创建UDP SOCKET
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.bind(('localhost',8000))
addr=('localhost',8000)
startNewThread()
root.mainloop()