class doudizhu:
#单张
def dan(self):
return 1
#对子
def dui(self, l, paixing):
if l[0] == l[1]:
paixing = 2
return paixing, l[0]
#三不带
def san(self, l,paixing):
if l[0] == l[2]:
paixing = 3
return paixing, l[0]
#炸弹
def zha(self, l,paixing):
if l[0] == l[3]:
paixing = 4
return paixing, l[0]
#三带一
def sanyi(self, l, paixing):
if l[0] == l[2] or l[1] == l[3]:
paixing = 5
return paixing, l[2]
#三带二(未用,与飞机2归并)
def saner(self, l, paixing):
if (l[0] == l[2] and l[3] == l[4]) or (l[2] == l[4] and l[0] == l[1]):
paixing = 6
return paixing, l[2]
#顺子(思绪:后一个数要比当前数大1,有一个发生错误直接返回0)
def shunzi(self, l, paixing):
now = 0
print(l)
if l[-1] < 12:
for i in range(len(l)-1):
if l[i+1]-l[i] == 1:
now = 7
else:
return paixing, l[-1]
paixing = now
return paixing, l[-1]
#连对(思绪:先将长度对半折,同样要判断后2个数比当前数大1,且后一个数与当前数相称)
def liandui(self, l, paixing):
now = 0
print(l[-1])
if l[-1] < 12:
for i in range(int(len(l)/2)-1):
if l[(i+1)*2]-l[i*2] == 1 and l[i*2] == l[i*2+1]:
now = 6
else:
return paixing, l[-1]
paixing = now
return paixing, l[-1]
#飞机0(思绪:每隔3个数后一个数都要比当前数大1,3个数内,后一个数要相即是当前数,未符合条件直接返回0)
def feiji0(self, l, paixing):
now = 0
for i in range(len(l)-1):
if (i+1)%3 == 0 and l[i+1] - l[i] == 1:
now = 8
elif l[i] == l[i+1]:
now = 8
else:
return paixing, l[-1]
paixing = now
return paixing, l[-1]
#飞机1(思绪:盘算3个数类似的组合有几组,再与其他未到达3个类似的总牌数比力,相称则准确)
def feiji1(self, l, paixing):
a = 1
m = []
for i in range(len(l)-1):
if l[i] == l[i+1]:
a += 1
else:
a = 1
if a == 3:
m.append(l[i])
a = 1
if len(m)*3 == len(l):#特殊环境(4个3张)
if m[-1] - m[1] == len(m) - 2:
paixing = 9
a = m[-1]
elif m[-2] - m[0] == len(m) - 2:
paixing = 9
a = m[-2]
if len(m) != 0:#扫除无3个杂牌,制止后免的m[0],m[-1]堕落
if len(l) - len(m) == len(m)*3 and m[-1] - m[0] == len(m) - 1:
paixing = 9
a = m[-1]
return paixing, a
#飞机2(思绪:与飞机1类似,差别的是,此处的对子也须要统计,还须要思量特殊环境(用炸做对子))
def feiji2(self, l, paixing):
a = 1
b = 0
m = []
for i in range(len(l)-1):
if l[i] == l[i+1]:
a += 1
if a == 3:
if i+2 > len(l) or l[i] != l[i+2]:
m.append(l[i])
a = 1
elif a == 2:
if l[i] != l[i+2]:
b += 1
a = 1
elif a == 4:
b += 2
else:
a = 1
if len(m) != 0:#环境同上
if b == len(m) and m[-1] - m[0] == len(m) - 1:
paixing = 10
b = m[-1]
else:
b = l[-1]
return paixing, b
#四带2(思绪,四带2就3种环境,我就不多说了)
def sidaier1(self, l, paixing):
if l[0] == l[3] or l[1] == l[4] or l[2] == l[5]:
paixing = 11
return paixing, l[2]
#四代两对(思绪:与四带二一样也就3种环境,就是条件还要多加对子判断)
def sidaier2(self, l, paixing):
m = 0
if l[0] == l[3] and l[4] == l[5] and l[6] == l[7]:
paixing = 11
m = l[0]
if l[4] == l[7]:
m = l[4]
elif l[2] == l[5] and l[0] == l[1] and l[6] == l[7]:
paixing = 11
m = l[2]
elif l[7] == l[4] and l[0] == l[1] and l[2] == l[3]:
paixing = 11
m = l[4]
return paixing, m
def first(self, l):
m = len(l)
print("len m =",m)
paixing = 0#牌型的中心变量
n = []#用列表存牌型,用来应对多种牌型环境
bf_max = []#用列表存比对值,用来应对多种牌型环境
y = 0#判断同牌型的中心变量
if m == 2:
#判断王炸
if l[0] == 13:
print("wangzha")
paixing = 4
n.append(paixing)
bf_max.append(13)
else:
paixing, y = self.dui(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
elif m == 3:
paixing, y = self.san(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
elif m == 4:
#优先判断炸
print("zha")
paixing, y = self.zha(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
#再三带一
paixing, y = self.sanyi(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
paixing = 0
elif m >= 5:
#大于5张都判断一次顺子
paixing, y = self.shunzi(l, paixing)
paixing, y = self.saner(l, paixing)
#然后就是连对判断
if m%2 == 0:
paixing, y = self.liandui(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
#飞机不带
if m%3 == 0:
paixing, y = self.feiji0(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
#飞机带一
if m%4 == 0:
paixing, y = self.feiji1(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
#飞机带对子
if m % 5 == 0:
paixing, y = self.feiji2(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
#四带二
if m == 6:
paixing, y = self.sidaier1(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
#四带两对
if m == 8:
paixing, y = self.sidaier2(l, paixing)
if paixing:
n.append(paixing)
paixing = 0
bf_max.append(y)
y = 0
return n, bf_max
def comp(self, bf_type, bf_max, bf_lenth, l, socket):
m = len(l)#当前出牌的长度
x = 0#临时吸取客户端信息
n = []#存放颠末转化分列的出牌
#转化出牌,使得与之前的操持符合
for i in l:
n.append(int(i/4))
n.sort()#分列,这里有个易错点,sort()函数没有返回值,不须要也不能用其他列表去复制它,直接调用便能将n分列好
now_type = []#当前出牌牌型
now_max = []#当前出牌值
if m == 1:
if l[0] < 52:#小鬼大鬼单独算
now_type.append(1)
now_max.append(n[0])
else:
now_type.append(1)
now_max.append(l[0])
else:
now_type, now_max = self.first(n)
if len(now_type) != 0:#假如长度为0,则代表没有牌型,也就是出牌错误
if bf_type == 0:#未出牌,不须要与前次牌型比力
if len(now_type) > 1:#多种牌型判断
data = {
'type': "select",
'paixing': now_type
}
socket.send(json.dumps(data).encode())#像客户端发起牌型判断哀求
x = socket.recv(1024)
if now_type[x] != 0:#0为无牌型,扫除错误环境
return True, now_type[x], now_max[x], m
else:
j = 0#盘算公道有用牌型数目
r = 0#存放第一个公道牌型
for i in range(len(now_type)):
if now_type[i] == 4 and bf_type != 4:#如今出牌牌型为炸弹,前一刻不为炸弹
j += 1
elif now_type[i] == 4 and bf_type == 4 and now_max[i] > bf_max:
j += 1
elif now_type[i] == bf_type and now_max[i] > bf_max and m == bf_lenth:
j += 1
if j == 1:
r = i
if j > 1:#多种牌型,哀求用户决议
data = {
'type': "select",
'paixing': now_type
}
socket.send(json.dumps(data).encode())
x = socket.recv(1024).decode()
if now_type[x] != 0:
return True, now_type[x], now_max[x], m
elif j == 1:#一种牌型,直接返回
return True, now_type[r], now_max[r], m
return False, bf_type, bf_max, bf_lenth
这里就是斗田主游戏过程中常常要做的事,将前次出牌与下次出牌举行比力。
3、模拟洗牌、发牌
def beginer(self):#洗牌函数
a = [x for x in range(54)]#初始化列表0-53
random.shuffle(a)#随机打乱列表
a1 = a[1::3]#切片取数
a2 = a[2::3]
a3 = a[3::3]
a4 = a[-3:]#底牌
del a1[-1]#删除多余数
del a2[-1]#删除多余数
return a1, a2, a3, a4
这部分就比力简单,用0-53代表扑克牌,用随机打乱列表的函数来模拟洗牌。
4、叫田主
def jdz(self, client, i):#叫田主函数
a = 0#叫田主次数统计
b = 0#不叫次数统计
now = ""#记载当前拥有田主权的玩家
restart = False#重开标记
z = random.randint(0, 2)#随机取数,优先叫田主
n = z#复制此变量
client_ls = client[i]#临时存放当前房间用户的名字
while True:
jdz = "叫田主"
if a != 0:#模拟抢田主流程,有人叫田主之后便是抢田主
jdz = "抢田主"
if a == len(client_ls) and a > 1:#模拟先叫田主的人,有一次我抢的机会
jdz = "我抢"
#json格式传送信息
data = json.dumps({
"type": "jdz",
"message": jdz
})
client[client_ls[n]].send(data.encode())
recv = client[client_ls].recv(1024).decode()#吸取抢田主信息
if recv == 0:
print("wait for user %s" % client_ls[n])
else:
recv = json.loads(recv)
if recv["message"] == "yes":#用户选择抢田主
a += 1
now = client[i][n]
else:
b += 1
data = json.dumps({
"type": "warning",
"message": client[i][n]+":"+jdz
})
#给其他用户发送抢田主信息
client[client[i][(n + 1) % 3]].send(data.encode())
client[client[i][(n + 2) % 3]].send(data.encode())
#抢田主全部环境
if a == 0 and b == 3:
restart = True
now = client[i][z]
break
elif a == 1 and b == 2:
break
elif a + b == 4:
break
if a == 0:#假如不抢
del client_ls[n]#去除此人的抢田主机会
if n > len(client_ls):#顺位改变
n = n % len(client_ls)
else:
n = (n+1) % len(client_ls)
return now, restart
if __name__ == "__main__":
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#TCP/IP流
HostPort = ('127.0.0.1', 6666)#ip与端口
s.bind(HostPort) # 绑定地点端口
s.listen(99) # 监听毗连数,凌驾次数会拒绝之后的用户
client = {}#主字典,存放房间信息,登任命的名称与地点
station = [0 for x in range(33)]#存放房间状态,0代表等候,1代表游戏中
for i in range(33):
client[i] = []
t = threading.Thread(target=connect, args=(s,))#线程开启处理处罚毗连
t.start()
while True:
for i in range(33):
if station[i] == 1 and client[i][0] == 2 and client[i][1] == 2 and client[i][2] == 2:#暂无用处,预设定
station[i] = 0
if len(client[i]) == 3 and station[i] == 0:#房间状态判断
t2 = threading.Thread(target=player, args=(i, client))#开线程处理处罚游戏
t2.start()
print("房间%s启动" % i)
station[i] = 1
这里包罗了房间的预创建,房间状态的管理等,是服务器的启动代码。
三、客户端
#吸取信息处理处罚函数
def recv(s, d):
l = []
name = input("请输入用户名:")#输名字,用于存储地点和辨认
data2 = json.dumps({
"type": "login",
"name": name
})
s.send(data2.encode())
inp = input("开始匹配:")#预设定,可改为可选房间(如今无用)
data2 = json.dumps({
"type": "ready",
"name": name
})
s.send(data2.encode())
while True:
rec = s.recv(1024).decode()
if rec == 0:
break
else:
rec = json.loads(rec)
if rec["type"] == "fapai":#表现自己的牌与其他玩家的牌数
card = ""
rec[name].sort()#对牌排序,使得表现的时间更加雅观
for i in rec[name]:#用空格拼接牌
card = card + d[i] + " "
sys.stdout.write("{}\n{}\n我:{}\n".format(rec["player1"], rec["player2"], card))#表现排序,可用print()取代
sys.stdout.flush()
elif rec["type"] == "warning":#服务器关照用户表现关照
sys.stdout.write("{}\n".format(rec["message"]))#表现关照
sys.stdout.flush()
elif rec["type"] == "chupai":#服务器关照用户出牌
l.clear()#预清空
data = {
"type":"chupai",
"card": l
}
while True:
try:#用try防止使步伐竣事
inp = input("请出牌:")
if inp == "buchu":
data["type"] = "buchu"
else:
inp = inp.split()#规定每张牌以空格隔开
for i in inp:
l.append(d[i])
data["card"] = l
break
except KeyError:
print("输入错误,请查抄")#堕落关照
data1 = json.dumps(data)
s.send(data1.encode())
elif rec["type"] == "chupai1":#打印别的用户出的牌,这里可以改的更高大上,表现牌时同时表现其牌型
card = ""
rec["card"].sort()#排序
for i in rec["card"]:
card = d[i] + ""
sys.stdout.write("{}出牌:{}\n".format(rec["name"], card))
sys.stdout.flush()
elif rec["type"] == "chupai2":#表现自己出的牌
card = ""
for i in l:
card = d[i] + ""
sys.stdout.write("我出牌:{}\n".format(card))
sys.stdout.flush()
elif rec["type"] == "win":#表现胜利信息
sys.stdout.write("I'm win")
sys.stdout.flush()
elif rec["type"] == "false":#表现失败信息
sys.stdout.write("You are false")
sys.stdout.flush()
elif rec["type"] == "error":#表现出牌错误信息
l.clear()
sys.stdout.write("牌型错误,请重新出牌")
sys.stdout.flush()
elif rec["type"] == "jdz":#叫田主流程
s1 = "不叫"
if not rec["message"] == "叫田主":
s1 = "不抢"
sys.stdout.write("1.{} 2.{}\n".format(rec["message"], s1))
sys.stdout.flush()
inp = input("请输入:")
data = {
"type": "jdz",
"message": "no"
}
try:#与上面的try用处同等
inp = int(inp)
if inp == 1:
data["message"] = "yes"
except:
pass
data = json.dumps(data)
s.send(data.encode())
elif rec["type"] == "select":#表现多牌型选择
data1 = 0
while True:
try:
inp = int(input("请输入0-%s:%s:" % (len(rec["paixing"]), rec["paixing"])))
data1 = rec["paixing"][inp]
data1 = inp
break
except:
print("输入错误")
s.send(data1)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
HostPort = ('127.0.0.1', 6666)
s.connect(HostPort)#毗连
d = show()
recv(s, d)