[ create a new paste ] login | about

Link: http://codepad.org/YGGYXpYZ    [ raw code | output | fork ]

tackle - Python, pasted on May 25:
#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()


Output:
1
2
3
4
5
6
7
8
9
10
11
(111) (999) (234) (567) [8]
(111) (999) (234) (678) [5]
(111) (999) (345) (678) [2]
(999) (123) (456) (11) [78]
(999) (123) (678) (11) [45]
(999) (345) (678) (11) [12]
(123) (456) (789) (11) [99]
(111) (234) (567) (99) [89]
(111) (234) (789) (99) [56]
(111) (456) (789) (99) [23]
(123) (456) (789) (99) [11]


Create a new paste based on this one


Comments: