#coding: utf-8
import sys
import re
import copy
def main():
hands = '1112345678999'
_validate(hands)
tiles = [0] * 9
for tile in hands:
tiles[int(tile) - 1] += 1
# 5 枚以上あったら終了
if tiles[int(tile) - 1] > 4:
sys.exit('Error')
pong, chow, pair = _pickup_melds(tiles)
_analyze(pong, chow, pair, tiles)
def _validate(hands):
"""
とりあえず 13 文字で [1-9] であることだけチェック
"""
if not re.compile(r'^[1-9]{13}$').search(hands):
sys.exit('Error')
def _pickup_melds(tiles):
"""
13 牌が取り得る全ての面子を抽出する。
"""
# 刻子
pongs = []
for i, count in enumerate(tiles):
if count >= 3:
pongs.append(('pong', i))
# 順子 同じ面子が複数取れる場合も重複して抽出する。
chows = []
for i in xrange(7):
for j in (1, 2, 3, 4):
if tiles[i] < j or tiles[i + 1] < j or tiles[i + 2] < j:
break
chows.append(('chow', i))
# 対子
pairs = []
for i, count in enumerate(tiles):
if count >= 2:
pairs.append(('pair', i))
return pongs, chows, pairs
def _analyze(pongs, chows, pairs, tiles):
"""
聴牌しているかどうかを調べて、聴牌時は出力する。
"""
fix_melds = []
# 単騎待ち (4面子確定)
for cmb in _get_cmb(pongs + chows, 4):
if cmb in fix_melds:
continue
fix_melds.append(cmb)
is_ready, ready = _is_ready_hands(_inspect(cmb, tiles))
if is_ready:
_print_melds(cmb, ready)
# 単騎以外 (3面子 + 雀頭)
for pair in pairs:
for cmb in _get_cmb(pongs + chows, 3):
if cmb + [pair] in fix_melds:
continue
fix_melds.append(cmb + [pair])
is_ready, ready = _is_ready_hands(_inspect(cmb + [pair], tiles))
if is_ready:
_print_melds(cmb + [pair], ready)
def _inspect(melds, tiles):
"""
tiles から全ての melds を取り出して、 tiles に矛盾(枚数が負になる)
がなければ残りの _tiles を返す。
"""
_tiles = copy.copy(tiles)
for meld in melds:
if meld[0] == 'pong':
_tiles[meld[1]] -= 3
if _is_invalid(_tiles):
break
elif meld[0] == 'pair':
_tiles[meld[1]] -= 2
if _is_invalid(_tiles):
break
elif meld[0] == 'chow':
_tiles[meld[1]] -= 1
_tiles[meld[1] + 1] -= 1
_tiles[meld[1] + 2] -= 1
if _is_invalid(_tiles):
break
else:
return _tiles
def _is_ready_hands(tiles):
"""
聴牌チェック
ここでの聴牌チェックは、_inspect の結果、残った牌が単騎待ち
であるか、または塔子を構成しているかのチェックである
"""
if not tiles:
return False, None
# tiles の枚数チェック 1 or 2 でなければならない
tiles_count = sum(tiles)
if tiles_count not in (1, 2):
return False, None
# 1 枚なら単騎待ち
if tiles_count == 1:
return True, '%d' % \
([i for i, c in enumerate(tiles) if c == 1][0] + 1)
# 1 枚以外 (== 2枚) なら待ちの形を調査して適切に返却する。
for idx, count in enumerate(tiles):
tile = idx + 1
if count == 2:
return True, '%d%d' % (tile, tile)
elif count == 1:
if tiles[idx + 1] == 1:
return True, '%d%d' % (tile, tile + 1)
elif tiles[idx + 2] == 1:
return True, '%d%d' % (tile, tile + 2)
else:
break
return False, None
def _get_cmb(melds, n):
"""
melds から n 個をとる組み合わせを返す。n = 3 or 4 しか使えない。
"""
if n not in (3, 4):
sys.exit('Error')
length = len(melds)
for i in range(length):
for j in range(i + 1, length):
for k in range(j + 1, length):
if n == 3:
yield [melds[i], melds[j], melds[k]]
else:
for l in range(k + 1, length):
yield [melds[i], melds[j], melds[k], melds[l]]
def _is_invalid(tiles):
"""
tiles の要素がひとつでも負の値になっていた場合は False を返す。
"""
return bool(len([count for count in tiles if count < 0]))
def _print_melds(melds, ready):
"""
melds と ready を形式を整えて画面に出力する。
"""
format_melds = []
for meld in melds:
if meld[0] == 'pong':
tile = meld[1] + 1
format_melds.append('(%d%d%d)' % tuple([tile] * 3))
if meld[0] == 'chow':
tile = meld[1] + 1
format_melds.append('(%d%d%d)' % (tile, tile + 1, tile + 2))
if meld[0] == 'pair':
tile = meld[1] + 1
format_melds.append('(%d%d)' % tuple([tile] * 2))
print '%s [%s]' % (' '.join(format_melds), ready)
if __name__ == '__main__':
main()