麻雀(1)
何年かかるかわかりませんがプログラムの練習で麻雀を作ってみようと思います。
m | 萬子 | 0-8 |
p | 筒子 | 9-17 |
s | 索子 | 18-26 |
h | 字牌 | 東:27~中:33 |
pair | 対子 |
triplet | 暗刻 |
Triplet | 明刻 |
seq | 順子 |
kan | 暗槓 |
Kan | 明槓 |
prevailing_wind | 場風 |
seat_wind | 自風 |
dealer | 親 |
wall_seq | 136枚の牌をシャッフルしたシーケンス。 |
hand | 手牌。1次元ndarrayでsize34 |
文字列入力をhand配列へ。
import numpy as np ji_dic2= dict((s, i + 27) for i, s in enumerate("東南西北白發中")) def str_to_hand(string): hand = np.zeros(34, dtype=int) suit = np.zeros(9, dtype=int) while string: if string[0] in "123456789": suit[int(string[0]) - 1] +=1 elif string[0] in "mps": mps = (string[0]=="p")*9 + (string[0]=="s")*18 hand[mps:mps+9] += suit suit.fill(0) else: hand[ji_dic2[string[0]]] += 1 string = string[1:] return hand data = ["123m123p123789s中中"] data.append("1133557799m1133p") data.append("19m19p19s東南西北白發中") data.append("123m123p123789s白中") data.append("111222333999m11s") data.append("112233777999m11p") data.append("白1m1s白白1m1m222m11s中中") for i, hand in enumerate(data): print(i, str_to_hand(data[i])) # 0 [1 1 1 ..., 0 0 2] # 1 [2 0 2 ..., 0 0 0] # 2 [1 0 0 ..., 1 1 1] # 3 [1 1 1 ..., 1 0 1] # 4 [3 3 3 ..., 0 0 0] # 5 [2 2 2 ..., 0 0 0] # 6 [3 3 0 ..., 3 0 2]
クラスで作ってみると
import numpy as np ji_dic2= dict((s, i + 27) for i, s in enumerate("東南西北白發中")) class Hand(): def __init__(self, hand): if isinstance(hand, str): hand = self.str_to_hand(hand) self.hand = hand def str_to_hand(self, string): hand = np.zeros(34, dtype=int) suit = np.zeros(9, dtype=int) string = string.replace(" ", "").replace("[", "").replace("]", "") while string: if string[0] in "123456789": suit[int(string[0]) - 1] +=1 elif string[0] in "mps": mps = (string[0]=="p")*9 + (string[0]=="s")*18 hand[mps:mps+9] += suit suit.fill(0) elif string[0] in "東南西北白發中": hand[ji_dic2[string[0]]] += 1 string = string[1:] return hand def hand_to_str(self): pass if __name__ == "__main__": data = ["123m123p123789s中中"] data.append("1133557799m1133p") data.append("19m19p19s東南西北白發中中") data.append("123m123p123789s白中") data.append("111222333999m11s") data.append("112233777999m11p") data.append("白1m1s白白1m1m222m11s 中中") for i, hand in enumerate(data): h = Hand(hand).hand print(i, h) # 0 [1 1 1 ..., 0 0 2] # 1 [2 0 2 ..., 0 0 0] # 2 [1 0 0 ..., 1 1 2] # 3 [1 1 1 ..., 1 0 1] # 4 [3 3 3 ..., 0 0 0] # 5 [2 2 2 ..., 0 0 0] # 6 [3 3 0 ..., 3 0 2]
役判定から点数計算までの流れ(仮)
・手牌の形だけで判断できる役
・国士無双かどうか
・七対子かどうか
・その他:1雀頭、4面子になっているか
・まず雀頭で場合分け
・刻子で場合分け
・残りが順子か
・手牌の形と状態(面前かどうかなど)で判断できる役
・風牌
・状態で判断できる役(天和、地和、海底、嶺上、立直、一発、ツモなど)
・ドラのせ
・点数計算
# coding: utf-8 import numpy as np from itertools import combinations ji_dic = dict((i + 27, s) for i, s in enumerate("東南西北白發中")) ji_dic2= dict((s, i + 27) for i, s in enumerate("東南西北白發中")) yaku_dic = {0:"7 pairs", 1:"13 orphans", 2:"Concealed four pongs"} def hand_to_str(hand): string = str(np.repeat(np.arange(1, 10), hand[:9])) string += "m " * any(hand[:9]) string += str(np.repeat(np.arange(1, 10), hand[9:18])) string += "p " * any(hand[9:18]) string += str(np.repeat(np.arange(1, 10), hand[18:27])) string += "s " * any(hand[18:27]) for i in range(27, 34): string += ji_dic[i] * hand[i] return string def is_winhand(hand): # seven pairs seven_pairs = sum(hand == 2) == 7 yaku = "seven_pairs" if seven_pairs else "" # 13 orphans if np.all(hand[[0, 8, 9, 17, 18, 26, 27, 28, 29, 30, 31, 32, 33]] > 0): return "13 orphans" else: result = melding(hand) return yaku if yaku else result if result else "not a winning hand" # parsing def melding(tiles): # 順子さがし def find_seq(tiles, melds): while sum(tiles): where = np.where(tiles > 0)[0] # 牌が存在する位置の0次元目→横方向インデックス if len(where)>2 and where[0] + 2 == where[1] + 1 == where[2]: melds.append((where[0], "seq")) tiles[where[0]] -= 1 tiles[where[1]] -= 1 tiles[where[2]] -= 1 else: melds = [] break return melds # 刻子さがし def find_triplet(tiles, pair): items = np.where(tiles > 2)[0] triplets = [] # find all subsets of triplets(刻子の全組み合わせを列挙) for i in range(len(items) + 1): for c in combinations(items, i): triplets.append(c) # find seq for each triplet-subset(それぞれについて順子さがし) result=[] for triplet in triplets: melds = [pair] tile = tiles.copy() for t in triplet: melds.append((t, "triplet")) tile[t] -= 3 seq = find_seq(tile, melds) if seq: result.append(seq) return result # find pair(対子さがし) m = [] for i in np.where(tiles > 1)[0]: hand = tiles.copy() meld = (i, "pair") hand[i] -= 2 meld = find_triplet(hand, meld) if meld: m.append(meld) return m if m else "" class Hand(): def __init__(self, hand): #手牌が配列ではなく文字列なら、配列に変換 if isinstance(hand, str): hand = self.str_to_hand(hand) self.hand = hand # 文字入力をNumPy配列に変換 def str_to_hand(self, string): hand = np.zeros(34, dtype=int) suit = np.zeros(9, dtype=int) string = string.replace(" ", "").replace("[", "").replace("]", "") while string: if string[0] in "123456789": suit[int(string[0]) - 1] +=1 elif string[0] in "mps": mps = (string[0]=="p")*9 + (string[0]=="s")*18 hand[mps:mps+9] += suit suit.fill(0) elif string[0] in "東南西北白發中": hand[ji_dic2[string[0]]] += 1 string = string[1:] return hand if __name__ == "__main__": data = ["123m123p123789s中中"] data.append("1133557799m1133p") # 7 pairs data.append("19m19p19s東南西北白發中中") # 13 orphans data.append("123m123p123789s白中") # not a winning hand data.append("111222333999m11s") data.append("112233777999m11p") data.append("白1m1s白白1m1m222m11s 中中") for i, hand in enumerate(data): h = Hand(hand).hand print(i, is_winhand(h)) # 0 [[[(33, 'pair'), (0, 'seq'), (9, 'seq'), (18, 'seq'), (24, 'seq')]]] # 1 seven_pairs # 2 13 orphans # 3 not a winning hand # 4 [[[(18, 'pair'), (8, 'triplet'), (0, 'seq'), (0, 'seq'), (0, 'seq')], [(18, 'pair'), (0, 'triplet'), (1, 'triplet'), (2, 'triplet'), (8, 'triplet')]]] # 5 [[[(9, 'pair'), (6, 'triplet'), (8, 'triplet'), (0, 'seq'), (0, 'seq')]]] # 6 [[[(33, 'pair'), (0, 'triplet'), (1, 'triplet'), (18, 'triplet'), (31, 'triplet')]]]