杭电网络编程实验
介绍:
1.交互式游戏设计——吹牛(RemoteBet) (基于TCP)(要求使用封帧技术)
通过在远端主机上搭建一个远程吹牛服务器,其它n台主机可以通过客户端程序与远程的吹牛服务器联系,进行交互式游戏设计。命令行格式如下:
RemoteBet <ServerIP>
如: RemoteBet 127.0.0.1
游戏背景:小黑和他的朋友们经常去学校东门外面的helens里喝酒。他们很喜欢玩“吹牛”的游戏。但是由于疫情被迫封在了学校里,请你帮助他们设计一款程序,能够让他们在宿舍里仍然能够通过电脑进行游戏。
规则如下:
(1)游戏开始时,服务器向每个玩家发送一个序列。该序列是一个长度为5的数组,数组中每个元素的值为1-6不等。每个玩家仅能看到自己手中的骰子点数。
(2)第一个玩家A轮流进行猜测。猜测所有人拥有的点数之和。例如可以猜测,5个3,6个2等。
(3)当玩家A猜测完成后,下一个玩家B进行猜测。玩家B需要在玩家A的基础之上进行猜测,例如:若玩家A猜测为X个Y,玩家B则需要猜测X+N个Y+M,且满足N*10+M>0,N,M属于整数。
(4)直到有玩家不相信所有人有这么多的数量,可以喊“开!”,此时进行游戏判定:若实际拥有量 与上家吹嘘拥有量
之间满足
<
,则事实为假,上家输;若
>=
,则事实为真,本家输。二者的计算关系为
,
(5)每当一轮游戏结束时,将这一轮输的玩家将被视为下一轮游戏的开始。
游戏样例:
Alice、Bob、Cristina、Dave四个人进行游戏。
————————————准备阶段————————————
Alice:1 5 6 5 2
Bob: 2 2 4 6 3
Cristina:3 3 3 4 6
Dave:1 1 3 5 1
————————————游戏开始————————————
Alice:3个3
Bob:3个4
Cristina:4个1
Dave:6个1
Alice:6个5
Bob:开!
————————————游戏结算————————————
此时,Alice喊出了6个5,而5在Alice手里有2个,Bob没有,Cristina没有,Dave手里有1个,四个人一共有3个5, 为35,
为65,
,Alice输。
———————————下一轮游戏准备阶段———————————
Alice:1 1 1 1 4
Bob: 2 2 2 1 5
Cristina:3 3 4 4 6
Dave:1 4 3 5 2
————————————游戏开始————————————
Alice:2个4
Bob:3个3
Cristina:4个4
Dave:开!
————————————游戏结算————————————
此时,Cristina喊出了4个4,而4在Alice手里有1个,Bob没有,Cristina手里有2个,Dave手里有1个,四个人一共有4个4, 为44,
为44,
,Dave输。
命令行说明:
猜测时:2 3
开: open!
服务器端代码:
# 导入套接字模块
import random
import re
import socket
# 导入线程模块
import threading
import sys
count = 0
user_count = 0
start = False
numlist = []
userlist = []
over = False
turn = -1
sumnumber = 0
count_turn = 0
last = [0, 0]
first = False
sbexit=False
def playing_client(userlist: list):
global count
for i in range(count):
str1 = "您的牌为:" + str(numlist[5 * i]) + " " + str(numlist[5 * i + 1]) + " " + str(
numlist[5 * i + 2]) + " " + str(numlist[5 * i + 3]) + " " + str(numlist[5 * i + 4])
senddata = str1.encode(encoding='utf-8')
userlist[i].sendall(senddata)
def start_game():
global count, userlist
global numlist
print("count" + str(count))
for i in range(count):
str1 = "————————————游戏开始————————————".encode(encoding='utf-8')
userlist[i].sendall(str1)
for j in range(5):
a = random.randint(1, 6)
numlist.append(a)
playing_client(userlist)
return
def nextgame():
global user_count, userlist, first,last
global numlist
first = False
last[0]=0
last[1]=0
for i in range(user_count):
for j in range(5):
a = random.randint(1, 6)
numlist[5 * (user_count - 1) + j] = a
playing_client(userlist)
return
# 定义个函数,使其专门重复处理客户的请求数据(也就是重复接受一个用户的消息并且重复回答,直到用户选择下线)
def start_client_request(tcp_client_1, tcp_client_address):
global count, start, userlist, last, turn, first,sbexit
owner = False
if start:
tcp_client_1.close()
return
count = count + 1
this_count = count
print("this_count"+str(this_count))
str1="您是第{}号玩家".format(this_count).encode(encoding='utf-8')
tcp_client_1.sendall(str1)
# 5 循环接收和发送数据
while True:
if sbexit:
print("exit")
sys.exit()
try:
recv_data = tcp_client_1.recv(1024)
except ConnectionResetError:
sbexit = True
str1 = "sb exit".encode(encoding='utf-8')
for user1 in userlist:
if user1!=tcp_client_1:
user1.send(str1)
user1.close()
sys.exit()
recv_meg = recv_data.decode(encoding='utf-8')
print(recv_meg)
if start == False:
if recv_meg == "start":
start = True
start_game()
turn = this_count
str1 = "your turn".encode(encoding='utf-8')
tcp_client_1.sendall(str1)
continue
else:
str2 = "输入不符合规则".encode(encoding='utf-8')
tcp_client_1.sendall(str2)
continue
if turn == this_count:
if recv_meg == 'open!' and first:
m1 = numlist.count(last[1]) * 10 + last[1]
m2 = last[0] * 10 + last[1]
if m1 < m2:
message = "{0}号玩家输,{1}号玩家赢".format((this_count + count -2) % count + 1,
this_count ).encode(encoding="utf-8")
message2 = "————————————游戏开始————————————".encode(encoding="utf-8")
for user in userlist:
user.sendall(message)
user.sendall(message2)
nextgame()
continue
else:
message = "{0}号玩家赢,{1}号玩家输".format((this_count + count -2) % count + 1,
this_count ).encode(encoding="utf-8")
message2 = "————————————游戏开始————————————".encode(encoding="utf-8")
for user in userlist:
user.sendall(message)
user.sendall(message2)
nextgame()
continue
m = re.match('^[1-9] [1-6]+', recv_meg)
if m is not None and m.group(0) == recv_meg:
number = recv_meg.split(" ")
if ((int(number[0]) - last[0]) * 10 + (int(number[1]) - last[1]) <= 0):
str2 = "输入不符合规则".encode(encoding='utf-8')
tcp_client_1.send(str2)
continue
# 发送除当前用户外的每个用户
message = "{}号用户:".format(this_count)
message = message + recv_meg
message = message.encode(encoding='utf-8')
for user in userlist:
if user != tcp_client_1:
user.send(message)
last[0] = int(number[0])
last[1] = int(number[1])
first = True
str1 = "your turn".encode(encoding='utf-8')
userlist[turn % count].send(str1)
turn = turn % count + 1
else:
str2 = "输入不符合规则".encode(encoding='utf-8')
tcp_client_1.sendall(str2)
continue
else:
str1 = "未到您的回合".encode(encoding='utf-8')
tcp_client_1.sendall(str1)
return
if __name__ == '__main__':
# 1 创建服务端套接字对象
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用,使程序退出后端口马上释放
tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 2 绑定端口
tcp_server.bind(("", 61234))
tcp_server.settimeout(20)
# 3 设置监听
tcp_server.listen(128)
# 4 循环等待客户端连接请求(也就是最多可以同时有128个用户连接到服务器进行通信)
while True:
if sbexit:
print("main exit")
sys.exit()
tcp_client_1, tcp_client_address = tcp_server.accept()
if start:
tcp_client_1.close()
break
print('connect')
userlist.append(tcp_client_1)
thd = threading.Thread(target=start_client_request, args=(tcp_client_1, tcp_client_address))
# 设置守护主线程 即如果主线程结束了 那子线程中也都销毁了 防止主线程无法退出
#thd.setDaemon(True)
# 启动子线程对象
thd.start()
客户端代码:
import socket
import threading
import re
exit=True
def sendmessage():
global tcp_client_1,exit
while exit:
send = input()
send_data = send.encode(encoding='utf-8')
if not exit:
break
tcp_client_1.send(send_data)
def getmessage():
global tcp_client_1
while True:
recv_data = tcp_client_1.recv(1024)
recv_meg = recv_data.decode(encoding='utf-8')
print(recv_meg)
if 'sb exit'==recv_meg:
exit=False
break
if __name__ == '__main__':
# 1 创建客户端套接字对象tcp_client_1
# 参数介绍:AF_INET 代表IPV4类型, SOCK_STREAM代表tcp传输协议类型 ,注:AF_INET6代表IPV6
tcp_client_1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
cmd=input("请输入:RemoteBet <ServerIP>")
m=re.match(r'^RemoteBet ((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$', cmd)
if m is not None and m.group(0) == cmd:
break
cmd=cmd.split(' ')
print(cmd[1])
# 2 通过客户端套接字的connect方法与服务器套接字建立连接
# 参数介绍:前面的ip地址代表服务器的ip地址,后面的61234代表服务端的端口号 。
print("connecting")
tcp_client_1.connect((cmd[1], 61234))
print("连接成功")
# 将编号好的数据存到变量send_data中,注:encode(encoding='utf-8)是将数据转换成utf-8的格式发送给服务器
thd = threading.Thread(target=sendmessage, args=())
# thd.setDaemon(True)
thd.start()
thd = threading.Thread(target=getmessage, args=())
# thd.setDaemon(True)
thd.start()