├── .gitattributes ├── .gitignore ├── AI.py ├── AI_VS.py ├── Game.py ├── README.md ├── __pycache__ ├── AI.cpython-36.pyc └── Game.cpython-36.pyc ├── debug_info.txt ├── main.py └── output.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /AI.py: -------------------------------------------------------------------------------- 1 | import Game 2 | 3 | AI_name_list=['no_AI','zr_AI','zx_AI(zrzr)','zx_AI(zrno)','zx_AI(nozr)','zx_AI(nono)','human'] 4 | 5 | def init(player,gametable): 6 | if player.AI_name == None: 7 | player.AI_name = player.name 8 | while player.AI_name not in AI_name_list: 9 | print("可用的AI方法有:", ", ".join(AI_name_list)) 10 | player.AI_name = str(input("没有名为%s的AI,请指定正确的AI方法:")) 11 | if player.AI_name == 'no_AI': 12 | return no_AI(player,gametable) 13 | elif player.AI_name == 'zr_AI': 14 | return zr_AI(player,gametable) 15 | elif player.AI_name == 'zx_AI(zrzr)': 16 | return zx_AI(player,gametable,"zr_AI","zr_AI")#first 4 peng , second 4 drop 17 | elif player.AI_name == 'zx_AI(zrno)': 18 | return zx_AI(player,gametable,"zr_AI","no_AI") 19 | elif player.AI_name == 'zx_AI(nozr)': 20 | return zx_AI(player,gametable,"no_AI","zr_AI") 21 | elif player.AI_name == 'zx_AI(nono)': 22 | return zx_AI(player,gametable,"no_AI","no_AI") 23 | elif player.AI_name == 'human': 24 | return human(player,gametable) 25 | else: 26 | print("没有%s这个AI方法"%player.AI_name) 27 | return None 28 | 29 | class human(): 30 | def __init__(self,player,gametable): 31 | self.name='no_AI' 32 | self.player = player 33 | self.gametable = gametable 34 | 35 | def Print_tiles(self): 36 | print("你当前有牌:") 37 | T_list = [None] * 14 38 | k = 0 39 | for t in range(0, 34): 40 | e = self.player.cnt[t] 41 | if e: 42 | t_name = Game.get_tile_name(t) 43 | while (e > 0): 44 | T_list[k] = t 45 | print(t_name, end="\t") 46 | k += 1 47 | e -= 1 48 | print() 49 | return k, T_list 50 | 51 | def Print_peng_tiles(self): 52 | out="" 53 | for i in range(0, 34): 54 | e = self.player.cnt_p[i] 55 | if e: 56 | t_name = Game.get_tile_name(i) 57 | while (e > 0): 58 | out += t_name+"x3\t" 59 | e -= 1 60 | if out == "": 61 | print("你当前已经碰的牌:\t无\n") 62 | else: 63 | out += "\n" 64 | print("你当前已经碰的牌:\t",out) 65 | 66 | def think_peng(self): 67 | self.Print_tiles() 68 | print("你可以碰牌: ",Game.get_tile_name(self.gametable.receive_tiles[-1][1]),"\n你要碰吗?(1要,2不要)") 69 | ans=input() 70 | if ans =="1": 71 | return True 72 | elif ans == "2": 73 | return False 74 | 75 | def think(self): 76 | print("\n\n你摸到的牌:\t",Game.get_tile_name(self.player.last_draw),end="\n\n") 77 | print("桌面上已出去的牌:",end="\t") 78 | for t in range(0,34): 79 | if self.gametable.receive_cnt[t]: 80 | print(Game.get_tile_name(t),"x",self.gametable.receive_cnt[t],end="\t") 81 | print("\n") 82 | 83 | self.Print_peng_tiles() 84 | k,T_list = self.Print_tiles() 85 | 86 | for i in range(1,k+1): 87 | print(i,end="\t") 88 | print("\n") 89 | while 1: 90 | print("输入要出的牌编号:") 91 | o = input() 92 | if o == 'debug': 93 | self.debug() 94 | try: 95 | return T_list[int(o)-1] 96 | except: 97 | print("编号有误请重新输入") 98 | 99 | 100 | def debug(self): 101 | from time import strftime,localtime,time 102 | debug_info_str = "\nDEBUG\t[%s]\nGameTable_Tiles\t=\t%s\nGameTable_T_name\t=\t%s\nGameTable_receive_tiles\t=\t%s\nGameTable_r_t_name\t=\t%s\n "%( 103 | strftime('%Y-%m-%d %H:%M:%S', localtime(time())),str(self.gametable.Tiles),Game.get_Tiles_names(self.gametable.Tiles),str(self.gametable.receive_cnt),Game.get_Cnt_names(self.gametable.receive_cnt)) 104 | 105 | print(debug_info_str) 106 | with open('debug_info.txt','a') as f: 107 | f.write(debug_info_str) 108 | 109 | 110 | 111 | 112 | class no_AI(): 113 | def __init__(self,player,gametable): 114 | self.name='no_AI' 115 | self.player = player 116 | self.gametable = gametable 117 | 118 | def think_peng(self): 119 | return True 120 | 121 | def think(self,player = None): 122 | if player == None: 123 | player = self.player 124 | t = player.last_draw 125 | for i in range(0, 3): 126 | for j in range(1, 8): 127 | k = i * 9 + j 128 | if player.cnt[k] == 1: 129 | if player.cnt[k - 1] + player.cnt[k + 1] == 0: 130 | t = k 131 | return t 132 | elif player.cnt[k - 1] + player.cnt[k + 1] == 1: 133 | t = k 134 | # 0 135 | k = i * 9 + 0 136 | if player.cnt[k] == 1: 137 | if player.cnt[k + 1] == 0: 138 | t = k 139 | elif player.cnt[k + 1] == 1: 140 | t = k 141 | # 1 142 | k = i * 9 + 8 143 | if player.cnt[k] == 1: 144 | if player.cnt[k - 1] == 0: 145 | t = k 146 | elif player.cnt[k - 1] == 1: 147 | t = k 148 | for k in range(0, 7): 149 | if player.cnt[k + 27] == 1: 150 | t = k + 27 151 | return t 152 | return t 153 | 154 | class zr_AI(): 155 | def __init__(self,player,gametable,s0=10,s1=6,s2=6,k0=2,k1=1,k2=1): 156 | self.name='zr_AI' 157 | self.player = player 158 | self.gametable = gametable 159 | self.s0 = s0 160 | self.s1 = s1 161 | self.s2 = s2 162 | self.k0 = k0 163 | self.k1 = k1 164 | self.k2 = k2 165 | 166 | def get_num_t(self,t): 167 | return 4-self.player.cnt[t]-self.gametable.receive_cnt[t]#改进空间为计算其他玩家持牌的概率??? 168 | 169 | def get_first_level_ts(self,t): 170 | ts=set() 171 | p=t%9 172 | if t>=0: 173 | if t<=26: 174 | p = t % 9 175 | if p*(p-8): 176 | return [t+1,t-1] 177 | else: 178 | return [t+1-1*p//4] 179 | elif t<=33: 180 | return [] 181 | return None 182 | 183 | def get_second_level_ts(self,t): 184 | ts=set() 185 | p=t%9 186 | if t>=0: 187 | if t<=26: 188 | p = t % 9 189 | if p*(p-8)*(p-1)*(p-7): 190 | return [t+2,t-2] 191 | else: 192 | return [t+(p*p*p-12*p*p+11*p)//42+2] 193 | elif t<=33: 194 | return [] 195 | return None 196 | 197 | def think_peng(self): 198 | l_r_t=self.player.cnt[self.gametable.receive_tiles[-1][1]] 199 | sum_rp,sum_rpp = 0,0 200 | for t in range(0,34): 201 | sum_rp += self.rp_t(t) 202 | self.player.cnt[l_r_t] -= 2 203 | for t in range(0,34): 204 | sum_rpp += self.rp_t(t) 205 | sum_rpp += 3 * self.s0 206 | self.player.cnt[l_r_t] += 2 207 | 208 | return (sum_rpp>=sum_rp) 209 | 210 | def think(self,player = None,gametable = None): 211 | if player == None: 212 | player = self.player 213 | if gametable == None: 214 | gametable = self.gametable 215 | 216 | min_p=16*(self.s0+self.s1*self.s2+self.k0+self.k1*self.k2) 217 | drop_t=None 218 | for t in range(0,34): 219 | p_t=self.rp_t(t) 220 | if p_t!=0 and p_t<=min_p: 221 | min_p=p_t 222 | drop_t=t 223 | if drop_t == None: 224 | print("?") 225 | return drop_t 226 | 227 | def rp_t(self,t): 228 | p_t = 0 229 | sn_0 = self.player.cnt[t] 230 | if sn_0: 231 | sn_1, sn_2, kn_0, kn_1, kn_2 = 0, 0, 0, 0, 0 232 | kn_0 = self.get_num_t(t) 233 | for t1 in self.get_first_level_ts(t): 234 | sn_1 += self.player.cnt[t1] 235 | kn_1 += self.get_num_t(t1) 236 | for t2 in self.get_second_level_ts(t): 237 | sn_2 += self.player.cnt[t2] 238 | kn_2 += self.get_num_t(t2) 239 | p_t = sn_0 * self.s0 + (sn_1 * self.s1) + (sn_2 * self.s2) + kn_0 * self.k0 + (kn_1 * self.k1) + (kn_2 * self.k2) 240 | return p_t 241 | 242 | class zx_AI(zr_AI,no_AI): 243 | def __init__(self,player,gametable,peng,drop): 244 | zr_AI.__init__(self,player,gametable) 245 | no_AI.__init__(self,player,gametable) 246 | self.name='zx_AI' 247 | self.AI4peng=peng 248 | self.AI4drop=drop 249 | # self.player = player 250 | # self.gametable = gametable 251 | self.n=1 252 | def get_p_Ts(self,Ts): 253 | p=0 254 | for t in Ts: 255 | n = (4-self.player.cnt[t]-self.gametable.receive_cnt[t]) 256 | p += n/122 257 | return p 258 | 259 | 260 | def think_peng(self): 261 | l_r_t = self.player.cnt[self.gametable.receive_tiles[-1][1]] 262 | max_n=self.n 263 | n = 0 264 | while n <= max_n: 265 | p = self.get_p_Ts(self.get_n_ava_t(n)) 266 | if p > 0: 267 | self.player.cnt[l_r_t] -= 2 268 | self.player.peng += 1 269 | pp = self.get_p_Ts(self.get_n_ava_t(n)) 270 | self.player.cnt[l_r_t] += 2 271 | self.player.peng -= 1 272 | if self.player.is_show in ["normal", "full"]: 273 | print("神级判碰") 274 | return pp>=p 275 | n += 1 276 | 277 | if self.AI4peng == "zr_AI": 278 | return zr_AI.think_peng(self) 279 | elif self.AI4peng == "no_AI": 280 | return no_AI.think_peng(self) 281 | else: 282 | print ("AI4peng not found!") 283 | 284 | def think(self): 285 | t_best=self.get_best_drop_t(self.n) 286 | if t_best !=None: 287 | return t_best 288 | 289 | if self.AI4drop == "zr_AI": 290 | return zr_AI.think(self,self.player,self.gametable) 291 | elif self.AI4drop == "no_AI": 292 | return no_AI.think(self,self.player) 293 | else: 294 | print ("AI4drop not found!") 295 | 296 | 297 | def get_best_drop_t(self,max_n): 298 | n=0 299 | while n<=max_n: 300 | p_max,t_best=self.get_n_drop_t(n) 301 | if p_max != 0: 302 | if self.player.is_show in ["normal","full"]: 303 | print("雀神在此!%d级听牌!"%n,end=" ") 304 | return t_best 305 | n += 1 306 | 307 | return None 308 | 309 | 310 | def get_n_drop_t(self,n): 311 | n=int(n) 312 | p_max = 0 313 | for t in range(0, 34): 314 | if self.player.cnt[t]>0: 315 | self.player.cnt[t] -= 1 316 | Tx = self.get_n_ava_t(n) 317 | p_x=self.get_p_Ts(Tx) 318 | if p_x>=p_max: 319 | p_max=p_x 320 | t_best=t 321 | self.player.cnt[t] += 1 322 | return p_max,t_best 323 | 324 | def get_n_ava_t(self,n): 325 | if n == 0: 326 | T0 = [] 327 | T0p = [] 328 | for t in range(0, 34): 329 | self.player.cnt[t] += 1 330 | if self.player.hu_judge(): 331 | T0.append(t) 332 | self.player.cnt[t] -= 1 333 | return T0 334 | elif n>=1: 335 | T0 = self.get_n_ava_t(n-1) 336 | p0 = self.get_p_Ts(T0) 337 | T1 = [] 338 | T1p = [] 339 | for t in range(0, 34): 340 | self.player.cnt[t] += 1 341 | T0x = self.get_n_ava_t(n-1) 342 | p0x = self.get_p_Ts(T0x) 343 | if p0x > p0: 344 | T1.append(t) 345 | self.player.cnt[t] -= 1 346 | return T1 347 | else: 348 | print("n应该为正整数") 349 | # def get_zero_avalible_t(self,d_t): 350 | # T0=[] 351 | # T0p=[] 352 | # for t in range(0,34) and t!=d_t: 353 | # self.player.cnt[t] +=1 354 | # if self.player.hu_jugde(): 355 | # T0.append(t) 356 | # self.player.cnt[t] -= 1 357 | # return T0 358 | # 359 | # def get_first_avalible_t(self,d_t): 360 | # T0=self.get_zero_avalible_t() 361 | # p0=get_p_Ts(T0) 362 | # T1=[] 363 | # T1p=[] 364 | # for t in range(0,34) and t != d_t: 365 | # self.player.cnt[t] +=1 366 | # T0x = self.get_zero_avalible_t() 367 | # p0x=get_p_Ts(T0x) 368 | # if p0x > p0: 369 | # T1.append(t) 370 | # T1p.append(p0x) 371 | # self.player.cnt[t] -= 1 372 | # return T1,T1p 373 | # 374 | # def get_second_avalible_t(self): 375 | # T1,T1p=self.get_first_avalible_t() 376 | # p1=get_p_Ts(T1) 377 | # T2=[] 378 | # T2p=[] 379 | # for t in range(0,33): 380 | # self.player.cnt[t] +=1 381 | # T1x = self.get_first_avalible_t() 382 | # p1x=get_p_Ts(T1x) 383 | # if p1x > p1: 384 | # T2.append(t) 385 | # T2p.append(p1x) 386 | # self.player.cnt[t] -= 1 387 | # return T2,T2p 388 | 389 | 390 | def no_ai(self,player = None): 391 | if player == None: 392 | player = self.player 393 | t = player.last_draw 394 | for i in range(0, 3): 395 | for j in range(1, 8): 396 | k = i * 9 + j 397 | if player.cnt[k] == 1: 398 | if player.cnt[k - 1] + player.cnt[k + 1] == 0: 399 | t = k 400 | return t 401 | elif player.cnt[k - 1] + player.cnt[k + 1] == 1: 402 | t = k 403 | # 0 404 | k = i * 9 + 0 405 | if player.cnt[k] == 1: 406 | if player.cnt[k + 1] == 0: 407 | t = k 408 | elif player.cnt[k + 1] == 1: 409 | t = k 410 | # 1 411 | k = i * 9 + 8 412 | if player.cnt[k] == 1: 413 | if player.cnt[k - 1] == 0: 414 | t = k 415 | elif player.cnt[k - 1] == 1: 416 | t = k 417 | for k in range(0, 7): 418 | if player.cnt[k + 27] == 1: 419 | t = k + 27 420 | return t 421 | return t 422 | -------------------------------------------------------------------------------- /AI_VS.py: -------------------------------------------------------------------------------- 1 | import Game 2 | import time 3 | from multiprocessing import Lock,Process 4 | 5 | def write_output(output): 6 | with Lock(): 7 | with open("output.txt","a+") as f: 8 | f.write(output) 9 | 10 | def AI_VS(AI_names,n): 11 | game = Game.Game("play","mute",Player_names=["张忆杭","齐洋","张瑞","张子新"],AI_names=AI_names, debug = False,Rules={"放炮":True, "碰": True, "吃":False}) 12 | start_t=time.time() 13 | game.play(n) 14 | end_t=time.time() 15 | print("耗费时间:",end_t-start_t,"秒") 16 | output="[%s vs %s vs %s vs %s] %d次测试:\n"%(AI_names[0],AI_names[1],AI_names[2],AI_names[3],n) 17 | for p in game.players_list: 18 | output += p.AI_name+"胜率:"+str(p.win_n / game.game_round)+"\n" 19 | write_output(output) 20 | # with open("output.txt", "a+") as f: 21 | # f.write(output) 22 | print("胜率:",output) 23 | 24 | if __name__ == "__main__": 25 | n=int(input("测试次数:")) 26 | #vs_list=[["no_AI","zx_AI(zrzr)"],["no_AI","zx_AI(zrno)"],["no_AI","zx_AI(nozr)"],["no_AI","zx_AI(nono)"]] 27 | vs_list=[["zx_AI(zrzr)","zx_AI(zrno)","zx_AI(nozr)","zx_AI(nono)"]] 28 | for AI_names in vs_list: 29 | Process(target = AI_VS, args=(AI_names,n)).start() 30 | print ("process all started!") 31 | -------------------------------------------------------------------------------- /Game.py: -------------------------------------------------------------------------------- 1 | from random import shuffle 2 | from copy import deepcopy 3 | import AI 4 | from time import time 5 | 6 | 7 | class Game(): 8 | globle_name_set=set() 9 | def __init__(self,name,is_show = 'normal',Player_names=["player1","player2","player3","player4"], AI_names = ['no_AI','no_AI','no_AI','no_AI'],debug = False ,Rules={"放炮":False, "碰": False, "吃":False}): 10 | if name in Game.globle_name_set: 11 | name = input("发现%s重名!请重新输入游戏名称:"%name) 12 | self.name=name 13 | self.debug = debug 14 | self.gametable= Gametable(debug) 15 | self.is_show=is_show#'mute','little','normal','full' 16 | if len(Player_names) <4: 17 | for i in range(len(Player_names),4): 18 | Player_names.append("player%d"%i) 19 | self.players_list=[Player(self.gametable,Rules,Player_names[0],AI_names[0],self.is_show), 20 | Player(self.gametable,Rules,Player_names[1],AI_names[1],self.is_show), 21 | Player(self.gametable,Rules,Player_names[2],AI_names[2],self.is_show), 22 | Player(self.gametable,Rules,Player_names[3],AI_names[3],self.is_show)] 23 | self.Rules=Rules 24 | self.gamestate=0 25 | self.game_round = 0 26 | self.turn = 0 27 | 28 | def play(self,r):#开始游戏 29 | self.game_round = r 30 | self.time_count={'whole_time':0,'round_time':[0]*r} 31 | #r=int(input("玩多少局?")) 32 | for p in self.players_list: 33 | p.win_n=0 34 | whole_start_t = time() 35 | while(r>0): 36 | self.gametable.shuffle()#洗牌 37 | self.start()#发牌 38 | self.turn=1 39 | r_start_t = time() 40 | while(self.gamestate==1):#游戏中。。。 41 | if self.is_show in ['normal','full']: 42 | print("\n\n第%d轮"%self.turn) 43 | self.turn+=1 44 | i=0 45 | while i<4 and (self.gamestate==1): 46 | if self.players_list[i].draw(self.gametable): 47 | if self.is_win(self.players_list[i]): 48 | break 49 | # if self.game_player_list[0].cnt[18]>2: 50 | # print("!") 51 | else: 52 | if self.is_show != 'mute': 53 | print("平局") 54 | self.gamestate = 3 55 | 56 | self.players_list[i].drop(self.gametable) 57 | for k in range(0, 4): 58 | if self.players_list[k].pong(self.gametable): 59 | if self.is_win(self.players_list[k]): 60 | break 61 | i = k 62 | self.players_list[i].drop(self.gametable) 63 | i += 1 64 | for p in self.players_list:#玩家归还牌 65 | p.reset() 66 | r_end_t = time() 67 | self.time_count['round_time'][self.game_round-r]=r_end_t-r_start_t 68 | r -= 1 69 | #print("end",self.turn) 70 | whole_end_t = time() 71 | self.time_count['whole_time'] = whole_end_t - whole_start_t 72 | 73 | def start(self):#发牌 74 | for i in range(0, 13): 75 | for p in self.players_list: 76 | p.draw(self.gametable) 77 | self.gamestate=1 78 | 79 | def is_win(self,p): 80 | if p.hu_judge(): 81 | self.winner = p.name 82 | self.gamestate = 2 83 | p.win_n += 1 84 | if self.is_show != 'mute': 85 | p.show("和牌") 86 | print("!!!胜利者是:", self.winner, "\n") 87 | return True 88 | return False 89 | 90 | def print_win_rate(self): 91 | for p in self.players_list: 92 | print(p.name+" ("+p.AI_name+") 胜率: %f"%(p.win_n / self.game_round)) 93 | 94 | class Player(): 95 | 96 | globle_name_list=[''] 97 | 98 | def __init__(self,gametable,Rules,name = str(len(globle_name_list)),AI_name = None,is_show='normal'): 99 | self.gametable = gametable 100 | self.set_name(name) 101 | self.set_AI(AI_name) 102 | if is_show == 'little': 103 | is_show = 'mute' 104 | self.is_show = is_show 105 | self.reset() 106 | self.Rules=Rules 107 | self.win_n = 0 108 | #初始化相关方法: 109 | def set_name(self,name): 110 | if name in Player.globle_name_list: 111 | name = input("发现%s名字已经存在!请重新输入玩家名称:"%name) 112 | self.name=name 113 | Player.globle_name_list.append(name) 114 | 115 | def set_AI(self,AI_name): 116 | self.AI_name = AI_name 117 | self.AI = AI.init(self,self.gametable) 118 | 119 | def reset(self): 120 | self.cnt = [0] * 34 # 0-8:"万";9-17:"条";18-26:"桶";27-33:"东","南","西","北","白","发","中" 121 | self.cnt_p = [0] * 34 122 | self.last_draw = None 123 | self.peng = 0 124 | self.state = True 125 | ## 126 | 127 | 128 | #显示相关方法: 129 | def show(self,type,tile = -1): 130 | if self.is_show == 'mute': 131 | pass 132 | elif self.is_show == 'normal': 133 | if type == '摸牌': 134 | print(self.name, ":", type, end=" ") 135 | elif type == '出牌': 136 | print(self.name,":", type, get_tile_name(tile), end="\n") 137 | elif type == '和牌': 138 | print(self.name,":我和牌了!") 139 | self.show_tiles() 140 | else: 141 | print("操作类型有误") 142 | elif self.is_show == 'full': 143 | print(self.name, type, get_tile_name(tile)) 144 | self.show_tiles() 145 | else: 146 | print("显示模式有误,应该为mute,normal 或 full") 147 | 148 | def get_tiles_names(self): 149 | s = "玩家: "+self.name+" -> " 150 | return s + get_Cnt_names(self.cnt) 151 | 152 | def show_tiles(self): 153 | print(self.get_tiles_names()) 154 | 155 | #游戏行为相关方法: 156 | def draw(self,gametable = None): 157 | if gametable == None: 158 | gametable = self.gametable 159 | 160 | t=gametable.draw() 161 | 162 | if t!="End": 163 | # if t == 18: 164 | # print("!") 165 | # if self.name=="a" and t == 8: 166 | # print("!") 167 | self.cnt[t]+=1 168 | self.last_draw=t 169 | 170 | # if self.cnt[18]>4: 171 | # print("!") 172 | self.show("摸牌",t) 173 | 174 | return True 175 | elif t=="End": 176 | return False 177 | 178 | def drop(self,gametable = None): 179 | if gametable == None: 180 | gametable = self.gametable 181 | t=self.AI.think() 182 | # if self.cnt[t]<=0: 183 | # print("!") 184 | if t==None: 185 | print(t) 186 | self.cnt[t] -= 1 187 | gametable._receive(self.name,t) 188 | self.show("出牌", t) 189 | 190 | def pong(self,gametable = None): 191 | if gametable == None: 192 | gametable = self.gametable 193 | 194 | if self.Rules["放炮"]: 195 | self.cnt[gametable.receive_tiles[-1][1]] += 1 196 | if self.hu_judge(): 197 | if self.is_show in ["normal", "full"]: 198 | print(gametable.receive_tiles[-1][0],"放炮给",self.name,gametable.receive_tiles[-1][1],sep=" ") 199 | return True 200 | self.cnt[gametable.receive_tiles[-1][1]] -= 1 201 | 202 | if self.Rules["碰"]: 203 | if self.cnt[gametable.receive_tiles[-1][1]]==2: 204 | 205 | self.cnt[gametable.receive_tiles[-1][1]] += 1 206 | if self.hu_judge(): 207 | return False 208 | self.cnt[gametable.receive_tiles[-1][1]] -= 1 209 | 210 | if self.AI.think_peng(): 211 | self.cnt_p[gametable.receive_tiles[-1][1]]+=1 212 | self.cnt[gametable.receive_tiles[-1][1]] -= 2 213 | self.peng +=1 214 | if self.is_show in ["normal","full"]: 215 | print("\n",self.name,"碰了",gametable.receive_tiles[-1][0],"的",get_tile_name(gametable.receive_tiles[-1][1])) 216 | return True 217 | if self.Rules["吃"]: 218 | #还没写 219 | pass 220 | 221 | return False 222 | 223 | pass 224 | 225 | 226 | def hu_judge(self): 227 | tmp = deepcopy(self.cnt) 228 | for t in range(0,34): 229 | if (tmp[t] >= 2): 230 | tmp[t] -= 2 231 | #34 232 | ret = 0 233 | for i in range(0, 19, 9): 234 | for j in range(0, 9): 235 | if (tmp[i + j] >= 3): 236 | tmp[i + j] -= 3 237 | ret += 1 238 | while (j + 2 < 9 and tmp[i + j] and tmp[i + j + 1] and tmp[i + j + 2]): 239 | tmp[i + j] -= 1 240 | tmp[i + j + 1] -= 1 241 | tmp[i + j + 2] -= 1 242 | ret += 1 243 | for j in range(0, 7): 244 | if (tmp[27 + j] >= 3): 245 | tmp[27 + j] -= 3 246 | ret += 1 247 | 248 | tmp[t] += 2 249 | if (ret+self.peng == 4): 250 | return True 251 | return False 252 | ## 253 | 254 | 255 | #其他方法: 256 | def get_tile_name(t): 257 | if t >= 0: 258 | if t <= 26: 259 | return str(t % 9 + 1) + Gametable.type[t // 9] 260 | elif t <= 33: 261 | return Gametable.s_type[t - 27] 262 | return '' 263 | 264 | def get_Tiles_names(tiles): 265 | names = '' 266 | for t in tiles: 267 | names += get_tile_name(t)+' ' 268 | return names 269 | 270 | def get_Cnt_names(cnt): 271 | names = '' 272 | for i in range(len(cnt)): 273 | e = cnt[i] 274 | while(e>0): 275 | names += get_tile_name(i)+' ' 276 | e -= 1 277 | 278 | return names 279 | 280 | 281 | 282 | 283 | class Gametable(): 284 | type = ["万", "条", "桶"] 285 | s_type = ["东", "南", "西", "北", "白", "发", "中"] 286 | 287 | def __init__(self,debug = False): 288 | self.Tiles=list(range(0,34))*4 289 | #数字对应牌型0-8:"万";9-17:"条";18-26:"桶";27-33:"东","南","西","北","白","发","中" 290 | #self.shuffle()#洗牌,没必要在这里洗,每次开局会洗 291 | self.debug=debug 292 | 293 | 294 | def shuffle(self): 295 | self.draw_loc = 0 296 | self.receive_cnt = [0] * 34 297 | self.receive_tiles = [] 298 | if self.debug: 299 | self.Tiles=[25, 26, 22, 28, 24, 14, 12, 16, 12, 13, 20, 2, 26, 30, 17, 33, 9, 22, 31, 33, 27, 19, 25, 20, 11, 2, 22, 20, 25, 4, 32, 4, 16, 28, 19, 5, 24, 29, 26, 7, 20, 15, 0, 24, 9, 11, 25, 15, 2, 27, 30, 22, 31, 18, 6, 4, 29, 16, 14, 3, 9, 21, 3, 21, 8, 17, 1, 13, 29, 11, 18, 2, 23, 32, 10, 31, 1, 7, 12, 15, 19, 6, 14, 17, 4, 28, 32, 23, 1, 17, 14, 33, 27, 5, 8, 10, 18, 13, 21, 8, 30, 1, 26, 28, 0, 7, 3, 5, 21, 30, 12, 8, 5, 7, 10, 15, 9, 10, 13, 31, 19, 6, 23, 29, 18, 23, 32, 27, 0, 6, 3, 24, 33, 11, 0, 16] 300 | else: 301 | shuffle(self.Tiles) 302 | def draw(self): 303 | if self.draw_loc>=136: 304 | return "End" 305 | t=self.Tiles[self.draw_loc] 306 | self.draw_loc+=1 307 | # if t==18: 308 | # print("!") 309 | return t 310 | 311 | def _receive(self,p_name,t): 312 | self.receive_tiles.append([p_name,t]) 313 | self.receive_cnt[t] += 1 314 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mahjong 2 | -自制麻将AI框架 3 | -运行main.py,输入1开始玩 4 | -**不要轻易运行AI_VS,这是做大量测试用的** 5 | -------------------------------------------------------------------------------- /__pycache__/AI.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Haskely/Mahjong/a6996d87d1e648bc8646885d355cb4c50d6be352/__pycache__/AI.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/Game.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Haskely/Mahjong/a6996d87d1e648bc8646885d355cb4c50d6be352/__pycache__/Game.cpython-36.pyc -------------------------------------------------------------------------------- /debug_info.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Haskely/Mahjong/a6996d87d1e648bc8646885d355cb4c50d6be352/debug_info.txt -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # coding = utf-8 3 | 4 | import Game#自己写的Game类 5 | 6 | m=input("modol:") 7 | 8 | if m=="1": 9 | #构造Game类有如下可用参数: 10 | name = "play" # ! 游戏名,必填,无默认值 11 | 12 | is_show = 'full'#显示模式,可填 mute(完全静默),little(只显示每轮结果), normal(显示游戏过程必要的信息),full(显示游戏过程所有细节),默认 normal 13 | Player_names=["master1","master2","master3","你"] # 4元 列表,对应四个玩家名字,不填默认["player1","player2","player3","player4"],(如果不全剩下的默认,如果超出选择前四个) 14 | AI_names = ['zx_AI(zrzr)','zx_AI(zrzr)','zx_AI(zrzr)','human']#4元 列表,对应四个玩家采用哪个AI,可选:'no_AI','zr_AI','zx_AI(zrzr)','zx_AI(zrno)','zx_AI(nozr)','zx_AI(nono)','human'(human是手动玩);默认全部"no_AI" 15 | debug = False#开启的话,会载入固定的初始牌以便于重现问题 16 | Rules={"放炮":True, "碰": True, "吃":False}#字典,规则设定,默认全部False(这样比较难和牌),吃规则还没编 17 | 18 | game = Game.Game( name , 19 | is_show = is_show , 20 | Player_names = Player_names , 21 | AI_names = AI_names , 22 | debug = debug , 23 | Rules = Rules )#初始化游戏啦 24 | game.play(3)#玩3局 25 | game.print_win_rate()#打印胜率 26 | 27 | elif m == "2": 28 | game = Game.Game("测试", "normal",AI_names=["zx_AI(zrzr)"]*3+["zx_AI(nozr)"],Rules={"放炮":True, "碰": True, "吃":False}) 29 | game.play(10) 30 | print(game.time_count["whole_time"]) 31 | game.print_win_rate() 32 | 33 | elif m=="3": 34 | game = Game.Game("测试2", "little",AI_names=["zx_AI(zrzr)"]*4) 35 | for i in range(0, 10): 36 | game.play(100) 37 | # game.players_dic["a"].AI.s1 -=0.5 38 | # game.players_dic["a"].AI.s2 -= 0.5 39 | # game.players_dic["a"].AI.s3 -= 0.5 40 | # game.players_dic["a"].AI.k1 += 0.5 41 | # game.players_dic["a"].AI.k2 += 0.5 42 | # game.players_dic["a"].AI.k3 += 0.5 43 | print(game.time_count[0]) 44 | game.print_win_rate() 45 | -------------------------------------------------------------------------------- /output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Haskely/Mahjong/a6996d87d1e648bc8646885d355cb4c50d6be352/output.txt --------------------------------------------------------------------------------