diff --git a/AI.py b/AI.py index ac9301b..07cd1ed 100644 --- a/AI.py +++ b/AI.py @@ -8,19 +8,17 @@ class AI(object): def __init__(self): super().__init__() - self.color = np.array(13, dtype=np.int) - self.number = np.zeros(13, dtype=np.int) - self.isPlayed = np.array(13, dtype=np.int) - self.cardPoint = np.zeros(5) - self.colorNumber = np.zeros(4) + self.isPlayed = [False] * 13 + self.cardPoint = [0] * 5 + self.colorNumber = [0] * 4 # 重置AI def reset(self, color, number): self.color = color self.number = number - self.isPlayed = np.zeros(13, dtype=np.int) - self.colorNumber = np.zeros(4) - self.cardPoint = np.zeros(5) + self.isPlayed = [False] * 13 + self.cardPoint = [0] * 5 + self.colorNumber = [0] * 4 self.calculate_card_point() # 计算不同花色牌点数 @@ -35,24 +33,25 @@ def color_card_point(self, color): # 顺序出牌法,给出AI推荐出的牌 def ai_play(self, lastPlayedNumber, lastplayedColor, order, firstPlayedColor): - if order == 0: + # if order == 0: + if firstPlayedColor is None: for i in range(13): - if self.isPlayed[i] == 0: - self.isPlayed[i] = 1 + if not self.isPlayed[i]: + # self.isPlayed[i] = True return create_card(self.color[i], self.number[i]) for i in range(13): - if (self.color[i] == firstPlayedColor) and (self.isPlayed[i] == 0): - self.isPlayed[i] = 1 + if (self.color[i] == firstPlayedColor) and (not self.isPlayed[i]): + # self.isPlayed[i] = True return create_card(firstPlayedColor, self.number[i]) for i in range(13): - if self.isPlayed[i] == 0: - self.isPlayed[i] = 1 + if not self.isPlayed[i]: + # self.isPlayed[i] = True return create_card(self.color[i], self.number[i]) # 出牌 def remove(self, myColor, myNumber): for i in range(13): if (self.color[i] == myColor) and (self.number[i] == myNumber): - self.isPlayed[i] = 1 + self.isPlayed[i] = True diff --git a/AIplay.py b/AIplay.py new file mode 100644 index 0000000..550d87b --- /dev/null +++ b/AIplay.py @@ -0,0 +1,354 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Dec 15 10:02:59 2018 +AI based on Bridge Experience +@author: hanyl +""" + +from card import Card +#from model import Model +#from player import AIplayer +#常量定义 +NullColor,Club,Diamond,Heart,Spade=-1,0,1,2,3 +OutHand,NullPlayer,Declarer,OL,Dummy,OLMate=-1,-1,0,1,2,3 +North,West,South,East = 0,1,2,3 +#Declarer_p,OL_p,OLMate_p=0,1,2 +Colors=[Club,Diamond,Heart,Spade] +Players=[Declarer,OL,Dummy,OLMate] +Positions=[North,West,South,East] +Cards=range(52) +#Players_p=[Declarer_p,OL_p,OLMate_p] +Nums=range(13) +NullNum=-1 +NullCard=Card(-1) + + +class CTC(object): + """ + 牌表map的value,可扩充内容 + """ + def __init__(self,Player): + self.Player = Player #可取值-1,0,1,2,3,分别表示OutHand,Declarer……情况 + +class AIplay(object): + def __init__(self): + self.CardTable={} #字典的形式实现,key为Card类型,value为CTC + self.CardNumTable=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]] #[player][color],记录实际的张数,>=0时对其他人不可见 + #可以取值<0,表示该门已经垫了多少张/该门已经无牌,对外可见 + self.CardsNow={} #map,key=Card,value=int,初始值为0,存储当前玩家手上有的牌,value>0,表示该牌可出 + self.PlayerNow=NullPlayer #当前玩家的身份,可取枚举值0,1,2,3 + self.TrickLeadingColors={} #记录每一轮的LeadingColor,map,(轮数,花色) + self.TrickLeadingColors[-1]=NullColor + self.TrickLeadingColor=NullColor + self.LastTrickLeadingColor=NullColor + self.TrickNow=0 #当前的轮数,取值0-12 + self.FirstCard=NullCard + self.SecondCard=NullCard + self.ThirdCard=NullCard #本轮出的前三张牌 + #self.TrickCards=[self.FirstCard,self.SecondCard,self.ThirdCard] + self.DeclarerM=NullPlayer #表示庄家在Model模块中的位置 + self.TrickPosition=0 #取值1,2,3,4表示当前几号位出牌 + self.CardReturn=NullCard + self.Trump=NullColor #本局主色 + #self.ReturnNum=NullNum + #self.ReturnColor=NullColor + + + def setCardTable(self,Model): + """ + 叫完牌后,打牌之前调用一下 + 初始化CT,CNT,DeclarerM等信息 + """ + self.DeclarerM=(Model.win_bid_position)%4 #win_bid_position is int ,win_bid_position先假定为庄家 + #需要model里存一个庄家的位置!!! + self.OpenLeadingM=(self.DeclarerM-1)%4 + self.DummyM=(self.DeclarerM-2)%4 + self.OPMateM=(self.DeclarerM-3)%4 + #确定各角色在Modelplayer里的调用位置,接口,与enums中定义的玩家方位有关 + for player in Players: + for card in Model.players[(self.DeclarerM-player) % 4].cards: #-/+与enums中定义的玩家方位的转向(逆、顺)有关 + self.CardTable[card] = CTC(player) + for color in Colors: + self.CardNumTable[player][color] = Model.players[(self.DeclarerM-player) % 4].color_num[color] + + #下面的函数是从Model中获取信息的接口 + def getColorInfo(self,Model): + if Model.king_color is not None: + self.Trump= Model.king_color + else: + self.Trump = NullColor + if self.TrickPosition is not 1: #当前玩家几号位出牌 + self.TrickLeadingColors[self.TrickNow]=Model.first_played_color + else: + self.TrickLeadingColors[self.TrickNow]=NullColor#一号位玩家所引花色 + self.TrickLeadingColor=self.TrickLeadingColors[self.TrickNow] + self.LastTrickLeadingColor=self.TrickLeadingColors.get(self.TrickNow-1,NullColor) + + def getCardInfo(self,Model): + if self.TrickPosition is 4: + self.ThirdCard = Model.trick_history[(Model.current_player_position+1)%4] + self.SecondCard = Model.trick_history[(Model.current_player_position+2)%4] + self.FirstCard = Model.trick_history[(Model.current_player_position+3)%4] + elif self.TrickPosition is 3: + self.SecondCard = Model.trick_history[(Model.current_player_position+1)%4] + self.FirstCard = Model.trick_history[(Model.current_player_position+2)%4] + elif self.TrickPosition is 2: + self.FirstCard = Model.trick_history[(Model.current_player_position+1)%4] + ''' + if Model.current_player is not None: + for card in Model.current_player.cards: + self.CardsNow[card]=0 + ''' + + + def Position(self,P_P,P_D): + """ + 由庄家位置、当前位置计算当前玩家的角色 + """ + if P_P is P_D: + return Declarer + elif P_P is (P_D+2)%4: + return Dummy + elif P_P is (P_D+1)%4: + return OLMate + return OL + + def getTrickHistory(self,Model): + if Model.play_order is None: + self.TrickPosition=1 + else: + self.TrickPosition=Model.play_order+1 #出牌位置 + if Model.trick is not None: + self.TrickNow=Model.trick #当前是第几轮 + self.PlayerNow=self.Position(Model.current_player_position,self.DeclarerM) #当前玩家的角色 + self.getColorInfo(Model) + self.getCardInfo(Model) + self.ReturnNum = NullNum + self.ReturnColor = NullColor #置零 + + def refT(self,card,LC): #根据card的情况,更新CT和CNT; + player = self.CardTable[card].Player + if player is not OutHand: + self.CardNumTable[player][Card(card).color]-=1 + if Card(card).color is not LC: + self.CardNumTable[player][LC]-=1 + self.CardTable[card].Player = OutHand + + def refreshCardTable(self,Model): + """ + 根据上轮和本轮的出牌更新CardTable以及CardNumTable; + 在前者信息变动的时候,同时更新后者, + CNT存储了每个角色的牌的数量,当实际发生出牌时,数量减一; + 当本门没有换出其他花色时,可以达到负值,表示本门已经垫出几张牌;当小于0时,表明为所有人可知的信息 + """ + if self.TrickNow is not 0: + for player in Positions: + self.refT(Model.play_table.history[self.TrickNow-1][player],self.LastTrickLeadingColor) + if self.TrickPosition > 3: + self.refT(self.ThirdCard,self.TrickLeadingColor) + if self.TrickPosition > 2: + self.refT(self.SecondCard,self.TrickLeadingColor) + if self.TrickPosition > 1: + self.refT(self.FirstCard,self.TrickLeadingColor) + + def playPrepare(self,Model): + """ + 调用上述四个获取历史信息,得到所有本轮出牌需要的信息 + 更新CT,CNT + """ + self.getTrickHistory(Model) + self.refreshCardTable(Model) + + #牌的信息,接口函数 + def gCard(self,color,number): #由Color,Number生成Card对象 + return Card(color*13+number) + + #获取牌信息的函数 + #主要使用的是 cardsNum,haveCard,haveCardOut + def KnowElsePlayers(self,player,color): + #若有一人牌打完了,且大家都知道,且不是明手,那么就知道剩余一人的牌 + if self.PlayerNow is not Dummy: + for p in Players: + if p is not self.PlayerNow and p is not Dummy and p is not player: + if self.CardNumTable[p][color] < 0: + return True + else: + if self.CardNumTable[OL][color] < 0 or self.CardNumTable[OLMate][color]<0: + return True + return False + + def OutColorNum(self,player,color): + ocn = 0 + for c in Colors: #若其他花色都打完了 + if c is not color and self.CardNumTable[player][c]< 0: + ocn+=1 + return ocn + + def cardsNumG(self,player,color): #上帝视角God + n = self.CardNumTable[player][color] + if n<0: + return 0 + return n + + def cardsNumO(self,player,color): #O表示一次计算,One time + """ + return 某玩家player某种花色color的牌的数量 + >=0的返回值表示结果,-1表示不知道 + """ + if player is self.PlayerNow: #自己的牌自己知道 + return self.cardsNumG(player,color) + if player is Dummy: #明手的牌都知道 + return self.cardsNumG(player,color) + if player is Declarer and self.PlayerNow is Dummy: #Dummy知道Declarer的牌 + return self.cardsNumG(player,color) + if self.CardNumTable[player][color]<0: #如果是出完了 大家也知道 + return 0 + if self.KnowElsePlayers(player,color): #知道其余三人的情况,那也能推知剩余一人的情况 + return self.cardsNumG(player,color) + if self.OutColorNum(player,color)>=3: #如果其他三门都出完了,大家也知道 + return self.cardsNumG(player,color) + return -1 #表示不知道 + def cardsNum(self,player,color): #多次计算 + t =[] + for i in range(4): + tj=[] + for j in range(4): + tj.append(0) + t.append(tj) + uk = 0 + for p in Players: + for c in Colors: + t[p][c]=self.cardsNumO(p,c) + if t[p][c] is -1: + uk += 1 + ukc = 0 #列未知的个数 + uka = 0 #行未知的个数 + for i in range(uk): #最多重复uk次,因为至少获取一个新知识,或者没有,没有则无法再有 + for p in Players: #逐行检查 + for c in Colors: + if t[p][c] is -1: + uka += 1 + cuk = c + if uka is 1: #只有一个不知道,那根据总的衡算,也应该能推知 + t[p][cuk]=self.cardsNumG(p,cuk) #由于不知道,肯定是大于等于0 + uka = 0 + for c in Colors: + for p in Players: + if t[p][c] is -1: + ukc += 1 + puk = p + if ukc is 1: + t[puk][c]=self.cardsNumG(puk,c) + ukc = 0 + return t[player][color] + + + def haveCardG(self,player,card):#上帝视角God + if self.CardTable[card].Player is player: + return True + return False + + def haveCard(self,player,card): + """ + return 判断某玩家player有无某张牌card + 1,0,-1 分别表示有,无,不知道 + """ + if player is self.PlayerNow: #自己的牌自己知道 + return self.haveCardG(player,card) + elif player is Dummy: #Dummy的牌都知道 + return self.haveCardG(player,card) + elif self.CardTable[card].Player is Dummy: + print("***",end='') + return 0 + elif self.CardTable[card].Player is self.PlayerNow: + #如果这张牌在已知的人的手里,则返回无 + print("---",end='') + return 0 + if player is NullPlayer: + return 0 + if self.CardTable[card].Player is OutHand: + return 0 + if self.PlayerNow is Dummy and player is Declarer: #Dummy知道Declarer的牌 + return self.haveCardG(player,card) + if self.CardNumTable[player][card.color]<0: #如果是出完了 大家也知道 + return 0 + if self.KnowElsePlayers(player,card.color): #知道其余三人的情况,那也能推知剩余一人的情况 + return self.haveCardG(player,card) + if self.PlayerNow is not Dummy: + for p in Players: + if p is not self.PlayerNow and p is not Dummy and self.cardsNum(p,card.color) is 0: + return self.haveCardG(player,card) + if self.PlayerNow is Dummy: + if self.cardsNum(OL,card.color) is 0 or self.cardsNum(OLMate,card.color) is 0: + return self.haveCardG(player,card) + + ''' + if self.OutColorNum(player,card.color)>=3: #知道本人其余门的情况,能推知该门的情况 + return self.haveCardG(player,card) #知道本人其余门的情况,不能推断出本门的具体情况 + ''' + return -1 + + def haveCardOut(self,card): + if self.CardTable[card].Player is OutHand: + return True + else: + return False + + def AvailableCards_1(self): #首先判断可出的牌,在CardsNow中赋值为1 + ccn = 0 #本轮可以出的牌数 + self.CardsNow ={} + for c in Cards: + if self.CardTable[c].Player is self.PlayerNow: + self.CardsNow[c]=0 + if self.TrickPosition > 1: #被动出牌玩家 + for card in list(self.CardsNow.keys()):#遍历所有的牌 + if Card(card).color is self.TrickLeadingColor: + self.CardsNow[card] += 1 + ccn += 1 + if ccn is 0: #包含两种情况,1.是首引玩家 2.是被动玩家,但没有同花色 + for card in list(self.CardsNow.keys()): + self.CardsNow[card]+=1 #所有的牌都加1 + + #添加其他规则!让你的AI更智能! + + def CardSelect(self): + """ + 给CardsNow中的Card赋值,值大于1,代表该牌合法,值越大代表这张牌更适合现在的情况,最后对CardsNow中的card进行排序,输入最大的一个 + 主要的逻辑:将桥牌经验总结成规则,放入CardSelect的if分支中,可以通过对累加值进行调整实现优先级 + """ + #首先判断可出的牌,在CardsNow中赋值为1,_1代表第一条规则 + self.AvailableCards_1() + #添加其他规则!!DIY!! + + + def CardGet(self): + self.CardReturn=sorted(self.CardsNow.items(),key=lambda v:v[-1],reverse=1)[0][0] #按value从大到小排序,取其key + self.CardsNow = {} #字典置零 + + + def Play(self,Model): + self.PlayPrepare(Model) + self.CardSelect() + self.CardGet() + return self.CardReturn + + def reset(self): #只在需要回溯或者最终的时候调用,平时的记录是连续的 + self.CardTable={} #字典的形式实现,key为Card类型,value为CTC + self.CardNumTable=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]] #[player][color],记录实际的张数,>=0时对其他人不可见 + #可以取值<0,表示该门已经垫了多少张/该门已经无牌,对外可见 + self.CardsNow={} #map,key=Card,value=int,初始值为0,存储当前玩家手上有的牌,value>0,表示该牌可出 + self.PlayerNow=NullPlayer #当前玩家的身份,可取枚举值0,1,2,3 + self.TrickLeadingColors={} #记录每一轮的LeadingColor,map,(轮数,花色) + self.TrickLeadingColors[-1]=NullColor + self.TrickLeadingColor=NullColor + self.LastTrickLeadingColor=NullColor + self.TrickNow=0 #当前的轮数,取值0-12 + self.FirstCard=NullCard + self.SecondCard=NullCard + self.ThirdCard=NullCard #本轮出的前三张牌 + #self.TrickCards=[self.FirstCard,self.SecondCard,self.ThirdCard] + self.DeclarerM=NullPlayer #表示庄家在Model模块中的位置 + self.TrickPosition=0 #取值1,2,3,4表示当前几号位出牌 + self.CardReturn=NullCard + self.Trump=NullColor + diff --git a/AIplayTest.py b/AIplayTest.py new file mode 100644 index 0000000..2af79f6 --- /dev/null +++ b/AIplayTest.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Dec 15 15:57:11 2018 +AI based on Bridge Experience +AIplay Test + +@author: hanyl +""" + +from model import Model +from AIplay import AIplay,CTC +from random import shuffle + +NullCTC=CTC(-1) + +def Deal(M,N,w,x): + """ + 发牌程序,Model,N:发牌方式,w:庄家位置,x:发几张牌 + """ + n = N #只要取N与52互质,则能正好每人13张 + M.win_bid_position = (w)%4 #定义庄家的位置,win_bid_position是Dummy + for i in range(4): + for j in range(x): + n=(n+N)%52 + M.players[i].get_card(Card(n)) + +def TestSetF(AI,M): + """ + 测试setCardTable(self,Model):在初始化发牌后,52张牌很好 + """ + CNT=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]; + for w in Positions: + N = 1 + #for N in [1,3,5,7,9,11,15,17,19,21,23,25,27,29]: + Deal(M,N,w,13) #发牌 + AI.setCardTable(M) #set函数检验 + for c in Colors: + for n in Nums: + ctc = AI.CardTable.get(c*13+n,NullCTC) + if ctc is not NullCTC: + CNT[ctc.Player][c]+=1 #牌表 + tn=0 + for p in Players: + for c in Colors: + if AI.CardNumTable[p][c] is CNT[p][c]: + tn+=1 + #牌张表 + print(tn,end=' ') + print(CNT,end=' ') + CNT=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]; + AI.reset() + M.reset() + print("for N=",N) + +def TestGetHF(AI,M): + M.king_color = Club #方块 + M.play_order=2 #第几个出牌 + M.first_played_color=Diamond #梅花 + M.current_player_position=South # + M.pie=5 #第6轮,已完成5轮 + for i in Positions: + M.pie_history[i]=i+18 + M.current_player_position=2 + for i in range(M.pie): + AI.TrickLeadingColors[i]=0 #之前的记录 + pie={0:0,1:1,2:2,3:3,'win':3} + M.play_table.history.append(pie) + for i in range(4): + AI.CardTable[i]=CTC(AI.Position(i,DeclarerP))#伪造一个CardTable的前四项 + print(AI.CardTable[i].Player,end=' ') + print() + AI.CardTable[18]=CTC(-1) + AI.CardTable[21]=CTC(-1) + AI.getTrickHistory(M) + TGHFPrint(AI,1)#打印结果函数 + print(AI.CardNumTable) + AI.refreshCardTable(M) + print(AI.CardNumTable) + + + + +def TGHFPrint(AI,bl=1): + if bl: + print("AI.TrickPosition ",AI.TrickPosition) #=player_order+1 + print("AI.TrickLeadingColor ",AI.TrickLeadingColor) #first_played_color + print("AI.LastTrickLeadingColor ",AI.LastTrickLeadingColor) + print("AI.LeadingColors",AI.TrickLeadingColors) + print("AI.TrickNow ",AI.TrickNow)#pie + print("AI.PlayerNow ",AI.PlayerNow)# + print("AI.Card ",AI.FirstCard,AI.SecondCard,AI.ThirdCard) + + +def AIPlaySet(AI): + #发牌 + N = 52-12 + x=[i for i in range(N)] + shuffle(x) + for i in range(N): + card = x[i] + player = i%4 + color = int(card/13) + AI.CardTable[card]=CTC(player) + AI.CardNumTable[player][color]+=1 + for c in range(N,52): + AI.CardTable[c]=CTC(NullPlayer) + for c in Colors: + for n in Nums: + print(AI.CardTable[AI.gCard(c,n)].Player,end=' ') + print() + for p in Players: + print(AI.CardNumTable[p]) + ''' #cardsNumG(p,c) + for p in Players: + for c in Colors: + print(AI.cardsNumG(p,c),end=' ') + print() + ''' + ''' #haveCardG(c,p) + for c in range(N): + print(AI.haveCardG(AI.CardTable[c].Player,c),end=' ') + ''' + + AI.PlayerNow = Declarer + player = Declarer + for c in range(N): + print(AI.haveCard(AI.CardTable[c].Player,Card(c)),end=' ') + + + + +if __name__ == '__main__': + AI =AIplay() + #M = Model() + #TestSetF(AI,M) #检验setCardTable函数 + + ''' #检验GetHistory,refreshCardTable函数 + DeclarerP = South #庄家位置, + N = 5 #发牌因子 + Deal(M,N,DeclarerP,8) + AI.setCardTable(M) + TestGetHF(AI,M) + ''' + AIPlaySet(AI) + + + \ No newline at end of file diff --git a/AIplay_inter.py b/AIplay_inter.py new file mode 100644 index 0000000..1e7a99c --- /dev/null +++ b/AIplay_inter.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Dec 27 16:18:11 2018 + +@author: hanyl +""" + +from model import Model +from AIplay import AIplay,CTC +from random import shuffle + + +def Deal(M,N,w,x): + """ + 发牌程序,Model,N:发牌方式,w:庄家位置,x:发几张牌 + """ + n = N #只要取N与52互质,则能正好每人13张 + M.win_bid_position = (w)%4 #定义庄家的位置,win_bid_position是Dummy + for i in range(4): + for j in range(x): + n=(n+N)%52 + M.players[i].get_card(Card(n)) + +def TestSetF(AI,M): + """ + 测试setCardTable(self,Model):在初始化发牌后,52张牌很好 + """ + CNT=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]; + for w in Positions: + N = 1 + #for N in [1,3,5,7,9,11,15,17,19,21,23,25,27,29]: + Deal(M,N,w,13) #发牌 + AI.setCardTable(M) #set函数检验 + for c in Colors: + for n in Nums: + ctc = AI.CardTable.get(c*13+n,NullCTC) + if ctc is not NullCTC: + CNT[ctc.Player][c]+=1 #牌表 + tn=0 + for p in Players: + for c in Colors: + if AI.CardNumTable[p][c] is CNT[p][c]: + tn+=1 + #牌张表 + print(tn,end=' ') + print(CNT,end=' ') + CNT=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]; + AI.reset() + M.reset() + print("for N=",N) + +def TestGetHF(AI,M): + M.king_color = Club #方块 + M.play_order=2 #第几个出牌 + M.first_played_color=Diamond #梅花 + M.current_player_position=South # + M.trick=5 #第6轮,已完成5轮 + for i in Positions: + M.trick_history[i]=i+18 + M.current_player_position=2 + for i in range(M.trick): + AI.TrickLeadingColors[i]=0 #之前的记录 + trick={0:0,1:1,2:2,3:3,'win':3} + M.play_table.add(trick) + for i in range(4): + AI.CardTable[i]=CTC(AI.Position(i,DeclarerP))#伪造一个CardTable的前四项 + print(AI.CardTable[i].Player,end=' ') + print() + AI.CardTable[18]=CTC(-1) + AI.CardTable[21]=CTC(-1) + AI.getTrickHistory(M) + TGHFPrint(AI,1)#打印结果函数 + print(AI.CardNumTable) + AI.refreshCardTable(M) + print(AI.CardNumTable) + + + + +def TGHFPrint(AI,bl=1): + if bl: + print("AI.TrickPosition ",AI.TrickPosition) #=player_order+1 + print("AI.TrickLeadingColor ",AI.TrickLeadingColor) #first_played_color + print("AI.LastTrickLeadingColor ",AI.LastTrickLeadingColor) + print("AI.LeadingColors",AI.TrickLeadingColors) + print("AI.TrickNow ",AI.TrickNow)#trick + print("AI.PlayerNow ",AI.PlayerNow)# + print("AI.Card ",AI.FirstCard,AI.SecondCard,AI.ThirdCard) + + +def AIPlaySet(AI): + #发牌 + N = 52-12 + x=[i for i in range(N)] + shuffle(x) + for i in range(N): + card = x[i] + player = i%4 + color = int(card/13) + AI.CardTable[card]=CTC(player) + AI.CardNumTable[player][color]+=1 + for c in range(N,52): + AI.CardTable[c]=CTC(NullPlayer) + for c in Colors: + for n in Nums: + print(AI.CardTable[AI.gCard(c,n)].Player,end=' ') + print() + for p in Players: + print(AI.CardNumTable[p]) + ''' #cardsNumG(p,c) + for p in Players: + for c in Colors: + print(AI.cardsNumG(p,c),end=' ') + print() + ''' + ''' #haveCardG(c,p) + for c in range(N): + print(AI.haveCardG(AI.CardTable[c].Player,c),end=' ') + ''' + + AI.PlayerNow = Dummy + player = Dummy + for c in range(52): + print(AI.haveCardOut(c),end=' ') + for c in range(N): + print(AI.haveCard(AI.CardTable[c].Player,Card(c)),end=' ') + print() + for c in range(N): + print(AI.haveCard(player,Card(c)),end=' ') + + for c in range(52): + if AI.CardTable[c].Player is AI.PlayerNow: + AI.CardsNow[c]=0 + print() + print(AI.CardsNow) + + AI.CardSelect() + + + +if __name__ == '__main__': + AI =AIplay() + M = Model() + TestSetF(AI,M) #检验setCardTable函数 + + #检验GetHistory,refreshCardTable函数 + DeclarerP = South #庄家位置, + N = 5 #发牌因子 + Deal(M,N,DeclarerP,8) + AI.setCardTable(M) + TestGetHF(AI,M) + \ No newline at end of file diff --git a/AIplay_unittest_self.py b/AIplay_unittest_self.py new file mode 100644 index 0000000..179ab5c --- /dev/null +++ b/AIplay_unittest_self.py @@ -0,0 +1,292 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Dec 25 13:22:05 2018 + +@author: hanyl +""" +import unittest +import random +from AIplay import AIplay,CTC +from card import Card +from model import Model + +NullColor,Club,Diamond,Heart,Spade=-1,0,1,2,3 +OutHand,NullPlayer,Declarer,OL,Dummy,OLMate=-1,-1,0,1,2,3 +North,West,South,East = 0,1,2,3 +#Declarer_p,OL_p,OLMate_p=0,1,2 +Colors=[Club,Diamond,Heart,Spade] +Players=[Declarer,OL,Dummy,OLMate] +Positions=[North,West,South,East] +Cards=range(52) +#Players_p=[Declarer_p,OL_p,OLMate_p] +Nums=range(13) +NullNum=-1 +NullCard=Card(-1) +NullCTC=CTC(-1) + + +class AIplaytest_self(unittest.TestCase): + def setUp(self): + self.AI = AIplay() + x = [i for i in range(52)] + random.shuffle(x) #发牌 + for i in range(52): + card = x[i] + player = int(i/13) + color = int(card/13) + self.AI.CardTable[card]=CTC(player) + self.AI.CardNumTable[player][color]+=1 #把CT、CNT的内容写好 + + + Trick = 2 #墩数,可调整 + for t in range(Trick): + c = random.choice(Colors) #假设每一敦的主花色是这么随机选的 + for p in Players: + for i in range(4): + self.AI.CardNumTable[p][(c+i)%4]-=1 #先把这个花色的牌出一张 + if self.AI.CardNumTable[p][(c+i)%4] >= 0: #如果有就退出,否则换下一个花色 + for card in Cards: + if int(card/13) is (c+i)%4 and self.AI.CardTable[card].Player is p: + self.AI.CardTable[card].Player = OutHand + break + break + + ''' + for c in Colors: + for n in Nums: + print(AI.CardTable[AI.gCard(c,n)].Player,end=' ') + print() + for p in Players: + print(AI.CardNumTable[p]) + ''' + + + def tearDown(self): + self.AI.reset() + + def testgCard(self): + Cs=[] + Cts=[] + for c in Cards: + Cs.append(Card(c)) + for c in Colors: + for n in Nums: + Cts.append(self.AI.gCard(c,n)) + self.assertEqual(Cs, Cts, 'test gCard fail') + + def testKnowElsePlayer(self): + r = [] + for i in range(4): + rj=[] + for j in range(4): + rk=[] + for k in range(4): + rk.append(True) + rj.append(rk) + r.append(rj) + for self.AI.PlayerNow in Players: + for p in Players: + for c in Colors: + r[self.AI.PlayerNow][p][c]=self.AI.KnowElsePlayers(p,c) + print("Know else player.") + for p1 in Players: + for p2 in Players: + print(r[p1][p2]) + print() + print("Players NumTable") + for p in Players: + print(self.AI.CardNumTable[p]) + + def testOutColorNum(self): + t =[] + for i in range(4): + tj=[] + for j in range(4): + tj.append(0) + t.append(tj) + for p in Players: + for c in Colors: + t[p][c]=self.AI.OutColorNum(p,c) + print("OutColorNum") + for p in Players: + print(t[p]) + print("Players NumTable") + for p in Players: + print(self.AI.CardNumTable[p]) + + def testcardsNumG(self): + t =[] + for i in range(4): + tj=[] + for j in range(4): + tj.append(0) + t.append(tj) + for p in Players: + for c in Colors: + t[p][c]=self.AI.cardsNumG(p,c) + print("CardsNumG") + for p in Players: + print(t[p]) + print("Players NumTable") + for p in Players: + print(self.AI.CardNumTable[p]) + + + def testcardsNum(self): + r = [] + for i in range(4): + rj=[] + for j in range(4): + rk=[] + for k in range(4): + rk.append(True) + rj.append(rk) + r.append(rj) + for self.AI.PlayerNow in Players: + for p in Players: + for c in Colors: + r[self.AI.PlayerNow][p][c]=self.AI.cardsNum(p,c) + print("cardsNum:") + for p1 in Players: + for p2 in Players: + print(r[p1][p2]) + print() + print("Players NumTable:") + for p in Players: + print(self.AI.CardNumTable[p]) + + def testhaveCardG(self): + print("haveCardG_GOD_TRUE:") + for c in Colors: + for n in Nums: + print(self.AI.CardTable[self.AI.gCard(c,n)].Player,end=' ') + print() + print("haveCardG:") + t =[] + for i in range(4): + tj=[] + for j in range(52): + tj.append(0) + t.append(tj) + for p in Players: + for c in Colors: + for n in Nums: + if(self.AI.haveCardG(p,self.AI.gCard(c,n))): + print(p,end=' ') + else: + print("*",end=' ') + print() + print() + print("Players NumTable:") + for p in Players: + print(self.AI.CardNumTable[p]) + t =[] + for i in range(4): + tj=[] + for j in range(4): + tj.append(0) + t.append(tj) + for c in Cards: + if self.AI.CardTable[c].Player is not OutHand: + t[self.AI.CardTable[c].Player][int(c/13)]+=1 + for p in Players: + print(t[p]) + + def testhaveCard(self): + print("haveCardG_GOD_TRUE:") + for c in Colors: + for n in Nums: + print(self.AI.CardTable[self.AI.gCard(c,n)].Player,end=' ') + print() + print("Players NumTable:") + for p in Players: + print(self.AI.CardNumTable[p]) + print("haveCard:") + t =[] + for i in range(4): + tj=[] + for j in range(52): + tj.append(0) + t.append(tj) + for self.AI.PlayerNow in Players: + for p in Players: + for c in Colors: + for n in Nums: + if self.AI.haveCard(p,self.AI.gCard(c,n)) is -1: + print("*",end=' ') + elif self.AI.haveCard(p,self.AI.gCard(c,n)) is True: + print("1",end=' ') + else: + print("0",end=' ') + print() + print() + + + def testhaveCardOut(self): + print("haveCardG_GOD_TRUE:") + for c in Colors: + for n in Nums: + print(self.AI.CardTable[self.AI.gCard(c,n)].Player,end=' ') + print() + Cs=[] + for ca in Cards: + Cs.append(self.AI.haveCardOut(ca)) + kt = 0 + print("haveCardOut") + for c in Colors: + for n in Nums: + if (Cs[self.AI.gCard(c,n)]): + kt+=1 + print("-1",end=' ') + else: + print("*",end=' ') + print() + print("kt=",kt) + + def testAvailableCards_1(self): + pass + """ + for self.AI.TrickLeadingColor in Colors: + for self.AI.TrickPosition in range(1,5): + for self.AI.PlayerNow in Players: + self.AI.AvailableCards_1() + print("AvailableCardsList:",self.AI.TrickLeadingColor," ",self.AI.TrickPosition) + print(self.AI.CardsNow) + print() + """ + + + def testCardSelect(self): + pass + """ + for self.AI.TrickLeadingColor in Colors: + for self.AI.TrickPosition in range(1,5): + for self.AI.PlayerNow in Players: + self.AI.CardSelect() + print("AfterCardSelect:",self.AI.TrickLeadingColor," ",self.AI.TrickPosition) + print(self.AI.CardsNow) + print() + """ + + def testCardGet(self): + for self.AI.TrickLeadingColor in Colors: + for self.AI.TrickPosition in range(1,5): + for self.AI.PlayerNow in Players: + self.AI.CardSelect() + print("CardGet:",self.AI.TrickLeadingColor," ",self.AI.TrickPosition) + print(self.AI.CardsNow,end=" ") + self.AI.CardGet() + print(self.AI.CardReturn) + print() + + + +if __name__ =='__main__': + + unittest.main() + + + + + + diff --git a/bid_table.py b/bid_table.py index d77bf3b..3f7fb90 100644 --- a/bid_table.py +++ b/bid_table.py @@ -1,12 +1,14 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- +from utils import ReadOnlyIterable, PASS, bid_greater class BidTable(object): def __init__(self): super().__init__() self.history = [] - self.top = 0 + self.top = PASS + self.history_iter = ReadOnlyIterable(self.history) def add_bid(self, bid_player, bid_result): # 待修改 @@ -15,23 +17,31 @@ def add_bid(self, bid_player, bid_result): :param bid_result: 叫牌结果 :return: None """ - if bid_result > self.top: - self.top = bid_result + # if bid_result > self.top: + # self.top = bid_result # self.history[bid_result // 10][bid_result % 10] = bid_player + if self.check(bid_result): + self.top = bid_result self.history.append((bid_player, bid_result)) - return bid_result // 10, bid_result % 10, bid_player + return bid_player, bid_result + # def check(self, bid_result): + # return bid_result.number > self.top.number or ( + # bid_result.number == self.top.number and + # bid_result.number != 0 and bid_result.color > self.top.color) def check(self, bid_result): - return bid_result > self.top + """检测""" + return bid_greater(bid_result, self.top) def reset(self): - # self.history = [[None, None, None, None, None] for _ in range(8)] self.history.clear() - self.top = 0 + self.top = PASS def get_bid_result(self, i): bid_result = dict(self.history[-4:]).get(i, None) if bid_result is None: return '' + if bid_result.number == 0: + return 'PASS' else: - return ['♣', '♦', '♥', '♠', 'NT'][bid_result % 10] + str(bid_result // 10) + return ['♣', '♦', '♥', '♠', 'NT'][bid_result.color] + str(bid_result.number) diff --git a/card.py b/card.py index 14425f8..a83d4df 100644 --- a/card.py +++ b/card.py @@ -2,6 +2,11 @@ # -*- coding: utf-8 -*- from enums import Suit +from itertools import chain + + +def color2str(color): + return ('♣', '♦', '♥', '♠', 'NT')[color] class Card(int): @@ -9,16 +14,17 @@ class Card(int): color = property(lambda self: self // 13) number = property(lambda self: self % 13) suit = property(lambda self: Suit(self // 13)) - str_color = property(lambda self: ['♣', '♦', '♥', '♠', 'NT'][self // 13]) + str_color = property(lambda self: color2str(self // 13)) def card2str(card): number, str_color = card.number, card.str_color - number2str = {i: str(i+2) for i in range(9)} - number2str[9] = 'J' - number2str[10] = 'Q' - number2str[11] = 'K' - number2str[12] = 'A' + # number2str = {i: str(i+2) for i in range(9)} + # number2str[9] = 'J' + # number2str[10] = 'Q' + # number2str[11] = 'K' + # number2str[12] = 'A' + number2str = dict(zip(range(13), chain(map(str, range(2, 11)), 'JQKA'))) return str_color + number2str[number] diff --git a/controller.py b/controller.py deleted file mode 100644 index 6897b3e..0000000 --- a/controller.py +++ /dev/null @@ -1,513 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- - -from model import Model -from card import fifty_two_cards, Card, card2str -from enums import State, DateInfo -from player import HumanPlayer -from PyQt5.QtCore import QObject, pyqtSignal -from queue import Queue -import threading -import random -import time -from collections import namedtuple - - -def wrapper(fun): - """装饰器,仅用于帮助分析函数调用时所在线程及运行时间,在最终版中应移去""" - def inner(*args, **kwargs): - t1 = time.time() - res = fun(*args, **kwargs) - t2 = time.time() - print(threading.current_thread().name, fun.__name__, 'take %s seconds' % (t2 - t1)) - return res - return inner - - -class GameAction(object): - """游戏事件,并非指图形界面的鼠标点击之类的事件,而是后台线程中的事件。 - 例如,游戏中,游戏进入新的阶段产生的事件;重新开始游戏的事件等。 - 在事件中封装事件相关信息,包括针对事件进行处理的函数""" - def __init__(self, name='', target=None, *args, **kwargs): - """ - :param name: - :param target: 处理事件的函数 - :param args: target的位置参数 - :param kwargs: target的关键字参数 - """ - self.name = name - self.func = target - self.args = args - self.kwargs = kwargs - - def execute(self): - """ - 执行事件回调函数 - :return: 返回self.func的返回值 - """ - if (self.func is not None) and callable(self.func): - return self.func(*self.args, **self.kwargs) - - -PlayerInfo = namedtuple('PlayerInfo', - ['cards', 'played_card', 'is_visible']) - - -class Controller(QObject): - """ - Controller主要包括后台线程和供UI调用的函数,内部实现了桥牌游戏的全部逻辑。 - 后台线程实现借鉴了状态机思想(不过似乎并不是标准的状态机),其中状态是model.state, - 后台线程可能处于三种稳定状态Stop,Biding,Play(稳定是指这三种状态可长时间保持,在这三个状态下,后台线程可能阻塞)。 - 在不同状态下,后台线程产生不同行为;在相同状态下,后台线程产生同样的行为。 - 后台线程同时使用了事件机制,不同状态间的转换采用事件实现 - """ - # 引入事件,主要是考虑到游戏进行到新的阶段(比如叫牌结束), - # 以及UI鼠标点击事件(如点击“新游戏”按钮)都能导致游戏状态及model中数据的改变, - # 且两种改变来自两个线程。 - # 我希望能将两种“改变”同等对待,采样同样的方法处理。不论事件来自何处,都是在后台线程处理 - # 只有后台线程能够改变model中的数据 - - # __init__函数之前的,都是为UI提供的接口 - # 信号 - output_signal = pyqtSignal(str) - view_update_signal = pyqtSignal() - - state = property(lambda self: self.__model.state) - max_bid = property(lambda self: self.__model.bid_table.top) - win_bid_position = property(lambda self: self.__model.win_bid_position) - king_color = property(lambda self: self.__model.king_color) - current_player_position = property(lambda self: self.__model.current_player_position) - - def send(self, data=None, info=None): - """供GUI调用,用于给controller发送叫牌、出牌结果。""" - if info == DateInfo.Bid: - # 来自GUI叫牌区的用户点击(叫牌结果) - if self.__model.state == State.Biding and ( - self.__model.current_player is not None) and \ - self.__model.current_player.controlled_by_human and ( - data is not None): - # and (data == 0 or self.check_bid_valid(data)): - if self.check_bid_valid(data): - self._received_data = data - else: - self._received_data = 0 - self._thread_event.set() - elif info == DateInfo.HumanPlay: - # 来自GUI人类玩家出牌 - if self.__model.state == State.Play and\ - self.__model.current_player_position == 0 and \ - (data is not None): - # 此时接收的data是牌的序号 - card = self.__model.current_player.cards[data] - # 出牌合法性检查 - if self.check_play_valid(card): - self._received_data = data - self._thread_event.set() - elif info == DateInfo.AIPlay: - # GUI队友AI出牌 - if self.__model.state == State.Play and \ - self.__model.current_player_position == 2 and \ - self.__model.current_player.controlled_by_human and \ - (data is not None): - # 此时接收的data是牌的序号 - card = self.__model.current_player.cards[data] - # 出牌合法性检查 - if self.check_play_valid(card): - self._received_data = data - self._thread_event.set() - elif info is None: - # 仅用于唤醒后台线程 - self._received_data = None - self._thread_event.set() - - def get_bid_result(self, i): - """ - :param i:玩家位置 - :return: 第i名玩家的叫牌结果(字符串形式) - """ - return self.__model.bid_table.get_bid_result(i) if self.__model.state == State.Biding else '' - - def get_player_info(self, i): - """ - 读取第i个玩家的信息的接口 - :param i: 玩家序号(位置) - :return: 命名元组PlayerInfo,其包含手牌、刚出的牌、是否应当显示3个元素 - """ - played_card = self.__model.trick_history.get(i, None) - player = self.__model.players[i] - if player.controlled_by_human or player.drink_tea: - return PlayerInfo(player.cards_iter, played_card, True) - else: - return PlayerInfo([None]*len(player.cards), played_card, False) - - def new_game(self): - """ - UI“新游戏”按钮的槽函数 - :return: - """ - # print('new game', threading.current_thread().name) - self.__action_queue.put_nowait(self.begin_game_action) - self.notify() - - def __init__(self, model=None): - super().__init__() - if model is None: - self.__model = Model() - else: - self.__model = model - - # 状态转移采用事件机制,Queue存储了未处理的事件 - self.on_state_functions = {} - self.__action_queue = Queue() - - # 线程相关 - self._thread = threading.Thread(target=self.__run) - self._thread.setDaemon(True) - self._received_data = None - self._thread_event = threading.Event() # 用于控制线程同步,同时也是接收到数据的标志 - self._thread_event.clear() - self.notify = lambda: self.send() - self._thread.start() - - # 预定义的GameAction - self.begin_game_action = GameAction('begin game action', target=self.__begin_game) - self.bid_end_action = GameAction('bid end action', target=self.__bid_end) - self.trick_begin_action = GameAction('trick begin action', target=self.__trick_begin) - self.trick_end_action = GameAction('trick end action', target=self.__trick_end) - self.calculate_score_action = GameAction('calculate score action', - target=self.__calculate_score) - - def __get_data(self): - """ - 后台线程取得来自UI的数据,与send函数配合,实现同步 - :return:来自UI的数据 - """ - if not self._thread_event.is_set(): - self._thread_event.wait() - data = self._received_data - self._received_data = None - self._thread_event.clear() - return data - - def output(self, *messages, sep=' ', end='\n'): - """ - 输出消息。 - 消息通过output_signal发出,若UI将此信号绑定至某个可显示组件上 - (将此信号连接至组件的setText函数), - 则可实现Controller向UI发送字符串信息。 - 各参数含义与print函数相同。 - :param messages:待输出的信息,数组类型 - :param sep: 各消息之间的分隔符 - :param end: 结尾符号 - :return: None - """ - # 可根据需要修改此函数实现,将消息发送至不同地方 - pass - msg = sep.join((str(item) for item in messages)) + end - self.output_signal.emit(msg) - print(*messages, sep=sep, end=end) - - def check_bid_valid(self, bid_result): - """ - 检测叫牌合法性 - :param bid_result:叫牌结果 - :return: 布尔值 - """ - return self.__model.bid_table.check(bid_result) - - def check_play_valid(self, card): - """检测出牌合法性""" - if self.__model.first_played_color is None: - # 第一个出牌 - return True - if card.color == self.__model.first_played_color: - # 花色与第一个出牌花色相同 - return True - if self.__model.current_player.color_num[self.__model.first_played_color] == 0: - # 第一个出牌的花色已经没了 - return True - return False - - def __deal(self, start_player_position=None): - """ - 发牌 - :param start_player_position: 初始发牌玩家位置 - :return: None - """ - cards = list(fifty_two_cards) - random.shuffle(cards) - if isinstance(start_player_position, - int) and 0 <= start_player_position < 4: - self.__model.current_player_position = start_player_position - else: - self.__model.current_player_position = random.randint(0, 3) - for card in cards: - self.__model.current_player.get_card(card) - self.__model.current_player_position = (self.__model.current_player_position + 1) % 4 - for player in self.__model.players: - player.init_AI() - - self.output('发牌结束') - - def __bid_begin(self, start_player_position=None): - """ - 做一些叫牌开始前的准备工作,初始化叫牌相关的变量 - :param start_player_position: - :return: None - """ - if isinstance(start_player_position, - int) and 0 <= start_player_position < 4: - self.__model.current_player_position = start_player_position - else: - self.__model.current_player_position = random.randint(0, 3) - # self.__model.king_color = None - # self.__model.pass_num = 0 - # self.__model.last_bid_number, self.__model.last_bid_color, self.__model.last_bid_player_position = None, None, None - - self.output('叫牌开始') - self.__model.state = State.Biding - - def __reset(self): - """ - 重置所有数据,清空事件队列 - :return: - """ - self.__model.reset() - self._thread_event.clear() - self._received_data = None - while self.__action_queue.qsize() > 0: - self.__action_queue.get_nowait() - - @wrapper - def __begin_game(self, deal_start_position=None, bid_start_position=None): - """ - 开始游戏事件的回调函数 - :param deal_start_position: 开始发牌的玩家位置 - :param bid_start_position: 开始叫牌的玩家位置 - :return: - """ - # print('begin game', threading.current_thread().name) - self.output('begin game') - self.__reset() - - self.__deal(deal_start_position) - - if isinstance(bid_start_position, - int) and 0 <= bid_start_position < 4: - self.__model.current_player_position = bid_start_position - else: - self.__model.current_player_position = random.randint(0, 3) - - self.output('叫牌开始') - self.__model.state = State.Biding - self.view_update_signal.emit() - - def __biding(self): - """ - 叫牌,此函数的一次执行对应一名玩家的一次叫牌. - 叫牌结束,将产生bid_end事件 - :return: - """ - - # 没有人叫牌,或有一人叫、连续三人pass,或叫牌叫到了7无将 - # 跳转至下一状态 - if (self.__model.win_bid_position is None) and self.__model.pass_num == 4: - self.__action_queue.put_nowait(self.bid_end_action) - return - if (self.__model.win_bid_position is not None) and self.__model.pass_num == 3: - self.__action_queue.put_nowait(self.bid_end_action) - return - if self.__model.bid_table.top == 74: # 7无将 - self.__action_queue.put_nowait(self.bid_end_action) - return - - self.output('轮到{}叫牌'.format(self.__model.current_player_position)) - - if self.__model.current_player.controlled_by_human: - bid_result = self.__get_data() - else: - bid_result = self.__model.current_player.bid(self.__model.last_bid_number, self.__model.last_bid_color, self.__model.last_bid_player_position) - - if bid_result is None: - return - - if not self.__model.bid_table.check(bid_result): - # 不合法的叫牌被转换成pass - # 实际上合法性检验是由其他地方完成的 - # 比如,对应人类叫牌结果,是由GUI通过send函数传递的,在这个函数内部会进行检查 - # 而AI的叫牌结果,AI自己应当保证叫牌合法;若AI给出了不合法的结果,认为是AI设计有疏漏,controller不会帮忙解决,只会将不合法的叫牌转化为pass - bid_result = 0 - if bid_result > 74: - # 限定bid_result最大为7无将 - bid_result = 74 - - if bid_result == 0: - self.__model.pass_num += 1 - else: - self.__model.pass_num = 0 - self.__model.last_bid_number, self.__model.last_bid_color, self.__model.last_bid_player_position = bid_result // 10, bid_result % 10, self.__model.current_player_position - self.__model.win_bid_position = self.__model.current_player_position - self.__model.bid_table.add_bid(self.__model.current_player_position, bid_result) - - self.output(self.__model.current_player_position, 'bidResult:', bid_result) - - self.__model.current_player_position = (self.__model.current_player_position + 1) % 4 - self.view_update_signal.emit() - - def __bid_end(self): - """ - 叫牌结束后的处理,确定将牌 - 若没有出现4人全pass,则产生trick_begin事件;否则,跳转至Stop状态 - :return: - """ - self.__model.king_color = self.__model.last_bid_color - self.output('叫牌结束') - if self.__model.king_color is not None: - # 进入出牌状态,同时设置current_player_position为第一个出牌的人 - self.__model.state = State.Play - self.__model.trick = 0 - self.__action_queue.put_nowait(self.trick_begin_action) - self.output('win bid position: {0}, king color: {1}'.format( - self.__model.win_bid_position, - ['♣', '♦', '♥', '♠', 'NT'][self.__model.king_color])) - else: - self.output('无人叫牌') - self.__model.state = State.Stop - self.view_update_signal.emit() - - def __trick_begin(self): - """ - 开始出牌前进行一些准备工作,c初始化一些变量,在新一轮出牌开始时调用 - :return: - """ - if self.__model.trick == 0: - self.__model.current_player_position = ( - self.__model.win_bid_position + 1) % 4 - self.__model.drink_tea_player_position = ( - self.__model.win_bid_position + 2) % 4 - else: - self.__model.current_player_position = self.__model.win_player_position - - self.__model.play_order = 0 - self.__model.last_played_number, self.__model.last_played_color, self.__model.first_played_color = None, None, None - self.__model.state = State.Play - self.output('第{0}轮出牌开始'.format(self.__model.trick)) - self.view_update_signal.emit() - - def __play(self): - """ - 出牌 - 此函数的一次执行对应一轮出牌 - :return: - """ - if self.__model.play_order == 4: - self.__action_queue.put_nowait(self.trick_end_action) - return - - if self.__model.trick == 0 and self.__model.current_player_position == self.__model.drink_tea_player_position: - self.__model.current_player.drink_tea = True - teammate_position = self.__model.current_player.teammate_position - teammate = self.__model.players[teammate_position] - if isinstance(self.__model.current_player, HumanPlayer) or isinstance( - teammate, HumanPlayer): - # 人类玩家或人类玩家的队友明牌,则二者都由人类控制 - teammate.controlled_by_human = True - self.__model.current_player.controlled_by_human = True - - self.output('第{}轮,轮到玩家{}出牌'.format(self.__model.trick, - self.__model.current_player_position)) - - if self.__model.current_player.controlled_by_human: - card_index = self.__get_data() - if card_index is None: - return - card = self.__model.current_player.lose_card_by_index(card_index) - else: - card = self.__model.current_player.play(self.__model.last_played_number, - self.__model.last_played_color, - self.__model.trick, - self.__model.first_played_color) - self.__model.last_played_color, self.__model.last_played_number = card.number, card.color - - if self.__model.play_order == 0: - self.__model.first_played_color = card.color - - self.output('第{}轮,玩家{}出牌{}'.format(self.__model.trick, - self.__model.current_player_position, - card2str(card))) - - self.__model.trick_history[self.__model.current_player_position] = card - self.__model.play_order += 1 - self.view_update_signal.emit() - self.__model.current_player_position = \ - (self.__model.current_player_position + 1) % 4 - - def __trick_end(self): - """ - 一轮出牌结束后调用,在此函数中计算此轮胜者 - :return: - """ - for (player, card) in sorted(list(self.__model.trick_history.items()), - key=lambda x: x[0]): - self.output(player, ':', card2str(card), end=', ') - self.output('将牌', self.king_color, 'first_played_color', self.__model.first_played_color) - values = [] - for position, card in self.__model.trick_history.items(): - if card.color == self.__model.king_color: - v = 200 + card - elif card.color == self.__model.first_played_color: - v = 100 + card - else: - v = card - values.append((position, v)) - values.sort(key=lambda x: x[-1]) - self.output(values) - self.__model.win_player_position = values[-1][0] - self.__model.trick_history['win'] = self.__model.win_player_position - self.__model.play_table.add(self.__model.trick_history) - self.__model.trick_history = {} - self.output('第{}轮,玩家{}获胜'.format(self.__model.trick, - self.__model.win_player_position)) - - self.__model.trick += 1 - if self.__model.trick == 13: - # 出牌结束,产生计分事件 - self.__action_queue.put_nowait(self.calculate_score_action) - else: - # 否则产生trick_begin事件 - self.__action_queue.put_nowait(self.trick_begin_action) - self.view_update_signal.emit() - - def __on_state(self): - """当处于某个状态时,调用相应函数。 - 若始终处于其中一个状态,则相应函数会被反复调用。 - 因而应保证下列每个函数多次调用不会使数据出错。 - """ - functions = {State.Stop: self._thread_event.wait, - State.Biding: self.__biding, State.Play: self.__play, - State.End: self._thread_event.wait} - return functions.get(self.__model.state, lambda: None)() # 实际上所有函数都返回None - - def __process_game_action(self, game_action): - """处理事件,事件可能使状态发生变化,事实上状态的转移也采用了事件机制""" - return game_action.execute() # 实际返回值不会被利用 - - def __run(self): - """ - 后台线程run函数 - :return: None - """ - while True: - while self.__action_queue.qsize() > 0: - game_event = self.__action_queue.get() - self.__process_game_action(game_event) - self.__on_state() - - def __calculate_score(self): - # TODO: 完成分值的计算 - # 假设有些数据需要存放在Model中,可以往Model里添加成员, - # 但请确保新增成员有合适初值(一般为None),且在reset中重置 - pass - - self.__model.state = State.End - self.view_update_signal.emit() diff --git a/model.py b/model.py index 75f4774..df57137 100644 --- a/model.py +++ b/model.py @@ -19,7 +19,7 @@ def __init__(self): self.bid_table = BidTable() # 叫牌表 self.win_bid_position = None # 叫牌获胜的玩家 self.pass_num = 0 # pass数量 - self.last_bid_number = None + self.last_bid_number = 0 self.last_bid_color = None self.last_bid_player_position = None @@ -53,7 +53,7 @@ def reset(self): self.bid_table.reset() self.win_bid_position = None self.pass_num = 0 - self.last_bid_number = None + self.last_bid_number = 0 self.last_bid_color = None self.last_bid_player_position = None diff --git a/play_table.py b/play_table.py index 3b23519..195c14f 100644 --- a/play_table.py +++ b/play_table.py @@ -1,16 +1,19 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- +from utils import ReadOnlyIterable + class PlayTable(object): def __init__(self): - self.history = [] + self.__history = [] + self.history = ReadOnlyIterable(self.__history) - def add(self, pie): + def add(self, trick): """ - :param pie: 字典,包含一轮出牌信息;包含5个键0,1,2,3,'win',值分别4个玩家的出牌以及本轮胜者 + :param trick: 字典,包含一轮出牌信息;包含5个键0,1,2,3,'win',值分别4个玩家的出牌以及本轮胜者 :return: """ - self.history.append(pie) + self.__history.append(trick) def reset(self): - self.history.clear() + self.__history.clear() diff --git a/player.py b/player.py index 121878d..5d3e550 100644 --- a/player.py +++ b/player.py @@ -2,8 +2,9 @@ # -*- coding: utf-8 -*- from enums import Color +from utils import ReadOnlyIterable, PASS, BidResult +# from collections import namedtuple from AI import AI -from collections import Iterable # 找队友,player初始化用 @@ -12,22 +13,33 @@ def find_teammate(my_position): return (my_position + 2) % 4 -class Player(object): - class CardIterator(Iterable): - """ - 用于访问玩家手牌的只读接口 - """ - def __init__(self, player): - self._list = player.cards +# BidResult = namedtuple('BidResult', ['number', 'color']) +# PASS = BidResult(0, None) +# MAX_BID_RESULT = BidResult(7, 4) +# +# +# def bid_greater(b1, b2): +# return b1.number > b2.number or ( +# b1.number == b2.number and +# b1.number != 0 and b1.color > b2.color) - def __iter__(self): - return self._list.__iter__() - def __len__(self): - return self._list.__len__() - - def __getitem__(self, item): - return self._list.__getitem__(item) +class Player(object): + # class CardIterator(Iterable): + # """ + # 用于访问玩家手牌的只读接口 + # """ + # def __init__(self, player): + # self.__cards = player.cards + # + # def __iter__(self): + # return self.__cards.__iter__() + # + # def __len__(self): + # return self.__cards.__len__() + # + # def __getitem__(self, item): + # return self.__cards.__getitem__(item) def __init__(self, position): super().__init__() @@ -38,8 +50,8 @@ def __init__(self, position): self.position = position # 自己的位置 self.teammate_position = find_teammate(position) # 队友位置 self.ai = AI() # 每个玩家都有一个AI。AI只给出出牌建议,并不能决定出哪张牌 - # self.player_info = self.PlayerInfo(self) # 供GUI访问的接口 - self.cards_iter = Player.CardIterator(self) + # self.cards_iter = Player.CardIterator(self) # 供UI访问的手牌的只读接口 + self.cards_iter = ReadOnlyIterable(self.cards) # 供UI访问的手牌的只读接口 def get_card(self, card): self.cards.append(card) @@ -79,7 +91,10 @@ def reset(self): self.color_num = [0, 0, 0, 0] # 各花色手牌数量 self.card_num = 0 # 手牌总数 self.drink_tea = False # 是否喝茶 - # self.ai.reset() + + def get_candidates(self, first_played_color): + return dict(filter(lambda x: (x[-1].color == first_played_color) or ( + first_played_color is None), enumerate(self.cards))) class AIPlayer(Player): @@ -105,15 +120,19 @@ def bid(self, last_bid_number, last_bid_color, last_bid_player_position): if self.strategy == 0: # 如果是对家叫的牌,则不跟他抢, 0表示pass if last_bid_player_position is None: - return 0 + # return BidResult(0, color=None) + PASS if last_bid_player_position == self.teammate_position: - return 0 + # return BidResult(0, color=None) + PASS else: for name, color in Color.__members__.items(): number = self.bid_number(color.value) if number > last_bid_number: - return int(number * 10 + color.value) - return 0 + # return int(number * 10 + color.value) + return BidResult(number, color.value) + # return BidResult(0, color=None) + return PASS # 出牌 def play(self, last_played_number, last_played_color, order, first_played_color): diff --git a/timeGUI.py b/timeGUI.py deleted file mode 100644 index e101357..0000000 --- a/timeGUI.py +++ /dev/null @@ -1,459 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- - -import sys -from PyQt5.QtWidgets import * -from PyQt5.QtCore import * -from PyQt5.QtGui import * -from enums import State, DateInfo -from controller import Controller -import os - - -class Poker(QLabel): - """ - 扑克,继承自QLabel,是牌的图形显示 - """ - def __init__(self, parent, number, visible=False, *args, **kwargs): - """ - :param parent: 父窗口 - :param number: 牌,0~52,52表示牌的背面 - :param visible: 是否可见 - :param args: 传递给QLabel的位置参数 - :param kwargs: 传递给QLabel的关键字参数 - """ - super().__init__(parent, *args, **kwargs) - self.setPixmap(QPixmap('cards' + os.path.sep + str(number) + '.png')) - self.resize(57, 87) - self.setVisible(visible) - self.x, self.y = 0, 0 - - -class QPlayer(object): - """ - 玩家类,记录玩家手牌应当显示的位置 - """ - def __init__(self, position, beginpointx, beginpointy, playpointx, - playpointy): - self.hand_pokers = [] # 手牌 - self.played_poker = None # 刚出的牌 - self.bpx = beginpointx # 手牌位置 - self.bpy = beginpointy - self.px = playpointx # 刚出的牌的位置 - self.py = playpointy - self.interval = 20 # 间隔 - self.position = position - - def update(self, hand_pokers, played_poker): - """ - 更新手牌及打出的牌的显示 - :param hand_pokers: 手牌,Poker组成的list - :param played_poker: 刚出的牌,Poker - :return: None - """ - self.clear() - self.hand_pokers = hand_pokers - self.played_poker = played_poker - self.move() - - def clear(self): - """将所有牌移除(设为不可见)""" - for poker in self.hand_pokers: - poker.setVisible(False) - self.hand_pokers.clear() - if self.played_poker: - self.played_poker.setVisible(False) - self.played_poker = None - - def move_horizontal(self): - """使手牌沿水平方向摆放""" - for i, poker in enumerate(self.hand_pokers): - poker.move(self.bpx + self.interval * i, self.bpy) - poker.setVisible(True) - - def move_vertical(self): - """使手牌沿竖直方向摆放""" - for i, poker in enumerate(self.hand_pokers): - poker.move(self.bpx, self.bpy + (self.interval + 15) * i) - poker.setVisible(True) - - def move(self): - """将牌摆放到合适的位置""" - if self.position == 1 or self.position == 3: - self.move_vertical() - else: - self.move_horizontal() - if self.played_poker: - self.played_poker.move(self.px, self.py) - self.played_poker.setVisible(True) - - -####################################### -class WelcomePage(QMainWindow): - close_signal = pyqtSignal() - - def __init__(self): - super().__init__() - self.initUI() - - def initUI(self): - self.setWindowTitle('时光桥牌') - # 设置窗口的图标,引用当前目录下的time.png图片 - self.setWindowIcon(QIcon('time.png')) - self.setGeometry(300, 300, 600, 600) - - self.btn = QToolButton(self) - self.btn.setText("开始游戏") - self.btn.resize(100, 60) - self.btn.move(250, 400) - self.show() - - def closeEvent(self, event): - #是否确认退出 - reply = QMessageBox.question(self, 'Message', "Are you sure to quit?", - QMessageBox.Yes | QMessageBox.No, - QMessageBox.No) - - if reply == QMessageBox.Yes: - event.accept() - else: - event.ignore() - - -class TimeBridgeGUI(QWidget): - def __init__(self, parent=None, controller=None): - super(TimeBridgeGUI, self).__init__(parent) - #坐标指示器 - grid = QGridLayout() - x = 1 - y = 0 - grid.setHorizontalSpacing(1) - grid.setVerticalSpacing(1) - grid.setContentsMargins(10, 10, 600, 600) - self.cText = "x: {0}, y: {1}".format(x, y) - self.pmText = "提示信息" - #self.setMouseTracking(True) - self.cLabel = QLabel(self.cText, self) - self.pmLabel = QLabel(self.pmText, self) - grid.addWidget(self.cLabel, 0, 0, Qt.AlignTop) - grid.addWidget(self.pmLabel, 1, 0, Qt.AlignTop) - - #grid_2 = QGridLayout() - #grid_2.setContentsMargins(60, 90, 60, 90) - self.text0 = "N" - self.text1 = "W" - self.text3 = "E" - self.text2 = "S" - self.label0 = QLabel(self.text0, self) - self.label1 = QLabel(self.text1, self) - self.label3 = QLabel(self.text3, self) - self.label2 = QLabel(self.text2, self) - self.label0.move(350, 120) - self.label1.move(80, 340) - self.label3.move(640, 340) - self.label2.move(350, 580) - - self.setLayout(grid) - - ############################################## - #player - # 桥牌游戏可能使用的全部牌的图像形式,包括52张正面向上的牌与52张背面向上的牌 - self._pokers = [Poker(self, i) for i in range(52)] + \ - [Poker(self, 52) for _ in range(52)] - - points = [(240, 612, 371.5, 520), (4, 100, 100, 306.5), - (240, 4, 371.5, 93), (739, 100, 643, 306.5)] # 玩家手牌放置位置 - self.players = [QPlayer(i, *(points[i])) for i in range(4)] # 界面中玩家采用逆时针的顺序显示 - ################################################### - - self.resize(800, 700) - self.setFixedSize(800, 700) - #self.setStyleSheet("background: black") - - self.controller = Controller() if controller is None else controller - self.connect_with_controller() - - def connect_with_controller(self): - """将controller内的view_update_signal连接至两个更新函数""" - # 信号的连接 - self.controller.view_update_signal.connect(self.update_players) - self.controller.view_update_signal.connect(self.update) - self.controller.output_signal.connect(self.pmLabel.setText) - - def get_poker(self, card): - """ - 根据数字形式的牌,找到对应的图像形式的牌 - :param card: 牌, Card类型 - :return: - """ - # 注意参数和返回值都可能是None - if card is None: - return None - return self._pokers[card] - - def update_players(self): - """ - 更新玩家手牌的显示 - :return: - """ - for i, player in enumerate(self.players): - hand_cards, played_card, is_visible = self.controller.get_player_info(i) - played_poker = self.get_poker(played_card) - if is_visible: - hand_pokers = [self.get_poker(card) for card in hand_cards] - else: - hand_pokers = [self.get_poker(52 + 13 * i + j) for j in - range(len(hand_cards))] - player.update(hand_pokers, played_poker) - - ################################################################################## - - def mousePressEvent(self, e): - # TODO: 在之后的版本中,下面的print函数可删除 - print(e.x(), e.y()) - - if 200 <= e.x() <= 600 and 180 <= e.y() <= 520: - # 叫牌区域 - x = int((e.x() - 200) / 80) - y = int((e.y() - 180) / 48) - text = "x: {0}, y: {1}".format(x, y) - self.cLabel.setText(text) - if 0 <= x < 5 and 0 <= y < 7: - bid_result = 10 * (y + 1) + x - self.controller.send(bid_result, DateInfo.Bid) - - # 手牌区 - if self.players[0].bpy <= e.y() <= self.players[0].bpy + 87: - # 人类手牌区 - player = self.players[0] - info = DateInfo.HumanPlay - elif self.players[2].bpy <= e.y() <= self.players[2].bpy + 87: - # AI2 手牌区 - player = self.players[2] - info = DateInfo.AIPlay - else: - return - - if len(player.hand_pokers) > 0 and (player.bpx <= e.x() <= player.bpx + (len(player.hand_pokers) - 1) * player.interval + 57): - # 计算选中牌的下标 - clicklength = e.x() - player.bpx - if clicklength <= player.interval * len(player.hand_pokers): - card_index = clicklength // player.interval - else: - card_index = len(player.hand_pokers) - 1 - - # print(card_index) - self.controller.send(card_index, info) - - def paintEvent(self, e): - # print('paintEvent') - qp = QPainter() - qp.begin(self) - self.draw_player_area(qp) - - # print(self.controller.state) - if self.controller.state in (State.Play, State.End): - self.draw_play_area(qp) - self.draw_play_text(qp) - self.draw_play_table(qp) - # print('play draw') - # elif self.controller.state == State.BidEnd: - # #目前存在Bug - # #有时候这里的代码没被全部调用 - # self.label0.deleteLater() - # self.label1.deleteLater() - # self.label3.deleteLater() - # self.label2.deleteLater() - elif self.controller.state == State.Biding: - self.draw_bid_update(qp) - self.draw_bid_area(qp) - self.draw_bid_text(qp) - # print('draw end') - qp.end() - return - - def draw_player_area(self, qp): - col = QColor(0, 0, 0) - col.setNamedColor('#d4d4d4') - qp.setPen(col) - #基础区域 - qp.setBrush(QColor(180, 180, 180)) - qp.drawRect(240, 4, 297, 87) - qp.drawRect(240, 609, 297, 87) - qp.drawRect(4, 100, 57, 507) - qp.drawRect(739, 100, 57, 507) - - # qp.drawRect(200, 180, 400, 336) - - - def draw_bid_area(self, qp): - #叫牌区域 - pen = QPen(Qt.black, 1, Qt.SolidLine) - qp.setPen(pen) - - # 横线之间间隔48, 竖线之间间隔80 - delta_x, delta_y = 80, 48 - # 画横线 - for i in range(0, 8): - qp.drawLine(200, 180 + i * delta_y, 600, 180 + i * delta_y) - # 画竖线 - for i in range(0, 6): - qp.drawLine(200 + i * delta_x, 180, 200 + i * delta_x, 516) - - def draw_bid_text(self, qp): - colorList = ['♣', '♦', '♥', '♠', 'NT'] - qp.setPen(QColor(71, 53, 135)) - qp.setFont(QFont('', 20)) - for x in range(0, 5): - for y in range(1, 8): - text = '{0} {1}'.format(y, colorList[x]) - qp.drawText(223 + 80 * x, 162 + 48 * y, text) - #左上角提示区更新 - cp = self.controller.current_player_position - if cp is None: - return - - # 下面的代码我注释掉了, - # 在界面显示轮到谁出牌、叫牌是由Controller决定 - # controller有一个信号output_signal,被连接到pmLabel的setText函数 - # pmLabel打算给Controller输出信息用,当然GUI如果有某些信息也可以用它输出, - # 但轮到谁叫牌、轮到谁出牌,还是由Controller决定 - # self.pmText = '轮到{0}叫牌'.format(cp) - # self.pmLabel.setText(self.pmText) - #玩家叫牌提示信息更新 - - text = self.controller.get_bid_result(cp) - if cp == 0: - self.label0.setText(text) - elif cp == 1: - self.label1.setText(text) - elif cp == 2: - self.label2.setText(text) - elif cp == 3: - self.label3.setText(text) - - def bid_map(self, x, y): - # 将叫牌区格位映射到坐标 - return 80 * x + 200, 48 * y + 180, 80, 48 - - def draw_bid_update(self, qp): - if not self.controller.max_bid: - return - xb = self.controller.max_bid % 10 - yb = self.controller.max_bid // 10 - 1 - if self.controller.win_bid_position is None: - return - #叫牌表更新 - qp.setBrush(QColor(self.controller.win_bid_position * 20, 100 + self.controller.win_bid_position * 10, 230 - self.controller.win_bid_position * 15))#皮这一下就很开心 - qp.drawRect(*self.bid_map(xb, yb)) - qp.setBrush(QColor(200, 200, 200))#把失效区域涂灰 - for y in range(0, 7): - for x in range(0, 5): - if y < yb or (y == yb and x < xb): - qp.drawRect(*self.bid_map(x, y)) - else: - return - - def draw_play_table(self,qp): - ''' 更新出牌表 ''' - play_table = self.controller.play_table - rank = [2,3,0,1,'win'] - player_name = ['S','E','N','W'] - color_list = ['♣', '♦', '♥', '♠'] - poker_list = ['2','3','4','5','6','7','8','9','10','J','Q','K','A'] - for y in range(1,len(play_table)): - for x in range(1,5): - card_number = play_table[y-1][rank[x-1]] - index = card_number//13 - number = card_number%13 - qp.drawText(50 * x + 230, 20 * y + 215, color_list[index]+poker_list[number]) - qp.drawText(480, 20 * y + 215, player_name[play_table[y-1][rank[4]]] ) - - def draw_play_area(self, qp): - qp.setBrush(QColor(180, 180, 180)) - qp.drawRect(371.5, 93, 57, 87) - qp.drawRect(371.5, 520, 57, 87) - qp.drawRect(100, 306.5, 57, 87) - qp.drawRect(643, 306.5, 57, 87) - qp.setBrush(QColor(130, 130, 130)) - qp.drawRect(200, 180, 400, 340) - qp.setBrush(QColor(201, 200, 205)) - qp.drawRect(225, 200, 350, 320) - pen = QPen(Qt.black, 1, Qt.SolidLine) - qp.setPen(pen) - qp.drawLine(200, 180, 600, 180) - qp.drawLine(200, 180, 200, 520) - qp.drawLine(600, 180, 600, 520) - qp.drawLine(200, 520, 600, 520) - for x in range(1, 16): - qp.drawLine(225, 20 * x + 200, 575, 20 * x + 200) - for x in range(1, 7): - qp.drawLine(50 * x + 225, 200, 50 * x + 225, 520) - - def draw_play_text(self, qp): - player_list = ['N', 'W', 'S', 'E'] - color_list = ['♣', '♦', '♥', '♠', 'NT'] - if self.controller.win_bid_position is not None: - contract = '契约:{0}由{1}叫出'.format(str(self.controller.max_bid // 10) + color_list[self.controller.max_bid % 10], player_list[self.controller.win_bid_position]) - qp.drawText(355, 193, contract) - text_list = ['轮次', 'N出牌', 'W出牌', 'S出牌', 'E出牌', '获胜方'] - for x in range(0, 6): - qp.drawText(50 * x + 230, 215, text_list[x]) - for y in range(1, 13): - qp.drawText(246.5, 20 * y + 215, str(y)) - qp.drawText(535, 20 * y + 215, '回溯') - qp.drawText(232, 495, '总胜场') - qp.drawText(237, 515, '总分') - - -class MainWindow(QMainWindow): - close_event = pyqtSignal() - - def __init__(self): - super().__init__() - self.setWindowTitle('时光桥牌') - self.setWindowIcon(QIcon('time.png')) - self.controller = Controller() - widget = TimeBridgeGUI(self, controller=self.controller) - self.setCentralWidget(widget) - # self.show() - - # 菜单项 - # TODO: 添加其他的菜单项 - self.bar = self.menuBar() - - self.item = self.bar.addMenu('选项') - self.new_game = QAction(self, text='新游戏') - self.item.addAction(self.new_game) - - self.connect_with_controller() - - def connect_with_controller(self): - """ - 将菜单项与controller中的槽函数连接起来 - :return: - """ - self.new_game.triggered.connect(self.controller.new_game) - - def closeEvent(self, event): - #是否确认退出 - reply = QMessageBox.question(self, 'Message', "是否确认退出?", - QMessageBox.Yes | QMessageBox.No, - QMessageBox.No) - if reply == QMessageBox.Yes: - event.accept() - else: - event.ignore() - - -if __name__ == "__main__": - App = QApplication(sys.argv) - main = MainWindow() - # main.show() - ex = WelcomePage() - ex.btn.clicked.connect(main.show) - ex.btn.clicked.connect(main.controller.new_game) - ex.btn.clicked.connect(ex.hide) - ex.close_signal.connect(ex.close) - ex.show() - sys.exit(App.exec_()) diff --git a/utils.py b/utils.py deleted file mode 100644 index 57931ee..0000000 --- a/utils.py +++ /dev/null @@ -1,69 +0,0 @@ -import numpy as np - -#手牌类 -class Card: - def __init__(self): - self.color = np.array(13, dtype=np.int) - self.number = np.zeros(13, dtype=np.int) - self.isPlayed = np.array(13, dtype=np.int) - self.cardNumber = np.zeros(5) - - #初始化手牌对象 - def init(self, color, number): - self.color = color - self.number = number - self.isPlayed = np.zeros(13, dtype=np.int) - self.calculateCardNumber() - - #计算不同花色牌点数 - def calculateCardNumber(self): - for i in range(13): - if self.number[i] == 1: - self.cardNumber[self.color[i]] +=14 - else: - self.cardNumber[self.color[i]] += self.number[i] - - #某花色手牌数 - def colorCardNumber(self, color): - return self.cardNumber[color] - - #顺序出牌法 - def AIPlay(self, lastPlayedNumber, lastplayedColor, order): - if order == 1: - for i in range(13): - if self.isPlayed[i] == 0: - self.isPlayed[i] == 1 - return self.number[i], self.color[i] - - for i in range(13): - if (self.color[i] == lastplayedColor) and ( self.isPlayed[i] == 0 ): - self.isPlayed[i] == 1 - return self.number[i], lastplayedColor - - for i in range(13): - if self.isPlayed[i] == 0 : - self.isPlayed[i] == 1 - return self.number[i], self.color[i] - - #检验出牌合法性,待补充 - def checkDisplayPrinciple(self,thisRoundColor, myColor, myNumber): - if self.cardNumber[thisRoundColor] == 0: return True - if myColor != thisRoundColor: return False - return True - - #人类玩家出牌 - def HunmanPlay(self, thisRoundColor, myColor, myNumber): - if self.checkDisplayPrinciple(thisRoundColor, myColor, myNumber): - self.remove( myColor, myNumber) - return True - else: - return False - - def remove(self, myColor, myNumber): - for i in range(13): - if self.color[i] == myColor and self.number == myNumber: - self.isPlayed[i] = 0 - -if __name__ == '__main__': - - a = 1