GOTO M.

趣味のコーディングとか、勉強とか、読書とか

麻雀の待ちリストアップ 「あなたのスキルで飯は食えるか?」より

同期に触発されて、麻雀の待ちを列挙するプログラムを書いてみた。
(ただし、字牌無し、マンズのみ、七対子非対応版)

また、元ネタ(※)を読んでいなかったので、出力形式がかなり要求仕様と異なる。
(※ http://www.itmedia.co.jp/enterprise/articles/1004/03/news002.html

実行例

C:\etc\src\python>python majung_simple.py 1112345678999
set([1, 2, 3, 4, 5, 6, 7, 8, 9])

C:\etc\src\python>python majung_simple.py 1232344562226
set([3, 6])

ソース(所要時間1時間半くらい)

#! -*- encoding=utf-8 -*-
#! -*- encoding=utf-8 -*-

# 標準入力を扱うため、sysをインポート
import sys

################################################################
# 標準入力を整形し、メインの再帰関数(list_mati)を呼び出す    #
################################################################

def main():
  # 初期化
  li = map(lambda n: int(n), list(sys.argv[1]))
  li.sort()
  print list_mati(li)


################################################################
# メインの再帰関数                                             #
# 渡された牌リストを調べ                                       #
# ・メンツが含まれない場合、待ちを調べる                       #
# ・メンツが含まれない場合、メンツを取り除いた牌リストを用いて #
#  自身を再帰的に呼び出す                                     #
################################################################

def list_mati(li):

  rtn = set([])

    ###################################################
    # メンツが含まれない場合(牌が1,2枚だった場合)#
    ###################################################

  # 残り1牌だった場合、その牌を返す
  if len(li) == 1:
    return set([li[0]])

  # 残り2牌だった場合
  elif len(li) == 2:
    # トイツができていた場合
    if li[0] == li[1]:
      rtn.add(li[0])
    # ターツができていた場合
    elif li[0]+1 == li[1]:
      if li[0] != 1:
        rtn.add(li[0]-1)
      if li[1] != 9:
        rtn.add(li[1]+1)
    # カンチャン待ちの場合
    elif li[0]+2 == li[1]:
      rtn.add(li[0]+1)
    # 役を作成出来ない場合
    else:
      pass
  else:

    ###################################################
    # メンツが含まれる可能性がある場合                #
    #(牌が3枚以上だった場合)                       #
    # ※ただし、牌が0枚だった場合もここで処理される  #
    ###################################################

    # 次局面(牌リスト)を列挙する

    if len(li) % 3 == 1:
      # まだターツを取り除いていない場合
      # ターツを取り除いた各局面につき再帰する
      for p in enumerate_toitsu_del_situations(li):
        rtn |= list_mati(p)
    # シュンツを取り除いた各局面につき再帰する
    for p in enumerate_shuntsu_del_situations(li):
      rtn |= list_mati(p)
    # コーツを取り除いた各局面について再帰する
    for p in enumerate_kohtsu_del_situations(li):
      rtn |= list_mati(p)

  # 待ち牌のセットを返す
  return rtn

##################################################################
# 与えられた牌リストについて、メンツ(またはコウツ)を取り除いた #
# 牌リストのリストを返すメソッド群                               #
##################################################################

def enumerate_toitsu_del_situations(li):
  rtn = []
  for toex in enumerate_toitsu(li):
    li_aft = li[:]
    li_aft.remove(toex)
    li_aft.remove(toex) 
    rtn.append(li_aft)
  return rtn

def enumerate_shuntsu_del_situations(li):
  rtn = []
  for toex in enumerate_shuntsu(li):
    li_aft = li[:]
    li_aft.remove(toex)
    li_aft.remove(toex+1)
    li_aft.remove(toex+2) 
    rtn.append(li_aft)
  return rtn

def enumerate_kohtsu_del_situations(li):
  rtn = []
  for toex in enumerate_kohtsu(li):
    li_aft = li[:]
    li_aft.remove(toex)
    li_aft.remove(toex)
    li_aft.remove(toex) 
    rtn.append(li_aft)
  return rtn

##################################################################
# 与えられた牌リストについて、メンツ(またはコウツ)を           #
# 列挙するメソッド群                                             #
# ※ただし、返り値は各メンツ(またはコウツ)の先頭牌である       #
# 例)                                                           #
#   enumerate_shuntsu([1,2,3,4]) → [1,2]                      #
##################################################################

def enumerate_toitsu(li):
  rtn = set([])
  for i in range(0, len(li)-1):
    if li[i] == li[i+1]:
       rtn.add(li[i])
  return rtn

def enumerate_shuntsu(li):
  set0 = set(li)
  set1 = set(map(lambda x: x-1, li))
  set2 = set(map(lambda x: x-2, li))
  return set0 & set1 & set2

def enumerate_kohtsu(li):
  rtn = set([])
  for i in range(0, len(li)-2):
    if li[i] == li[i+2]:
       rtn.add(li[i])
  return rtn


# main関数を呼び出す
if __name__ == '__main__': main()