FC2ブログ

1日3タックルを目指して頑張ります。 ロリ巨乳が大好きです。 > 今日の3タックルTopへ

Top /  若干エンジニアらしい話 /  Python /  恥を忍んで公開(史上最大のコーディングスキル判定)
« 平和な休日だなぁ  |  世界のハイボール ( ゚Д゚)マズー »

恥を忍んで公開(史上最大のコーディングスキル判定)

 2010/04/19 (Mon) -  カテゴリ:  若干エンジニアらしい話 »  Python

やるやる詐欺もアレなんで、とりあえず作るだけ作った。考える時間なんかを含めて5時間ってところか。途中で飽きてきて適当になった部分もあるけど、たぶん正しく動いてるはず。ちなみに七対子とか、純カラとかぜんぜん考慮してないよ。あと再帰は嫌いなんで使わなかった。

以下、コード (言語は Python2.5.4)

#coding: utf-8

import sys
import re
import copy

def main():
    hands = raw_input(u'> ')
    _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()

実行結果はこんな感じ

$python ma.py
> 1112345678999
(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]
$python ma.py
> 1111222233334
(111) (222) (333) (123) [4]
(111) (222) (333) (234) [1]
(123) (123) (123) (123) [4]
(123) (123) (123) (234) [1]
(222) (333) (234) (11) [11]
(123) (123) (234) (11) [23]
(111) (333) (123) (22) [24]
(111) (333) (234) (22) [12]
(111) (123) (234) (22) [33]
(111) (222) (123) (33) [34]
(111) (222) (234) (33) [13]
(111) (123) (234) (33) [22]
$python ma.py
> 1112223334445
(111) (222) (333) (444) [5]
(111) (234) (234) (234) [5]
(111) (234) (234) (345) [2]
(444) (123) (123) (123) [5]
(123) (234) (234) (11) [45]
(123) (234) (345) (11) [24]
(234) (234) (345) (11) [12]
(111) (234) (345) (22) [34]
(111) (222) (444) (33) [35]
(111) (222) (345) (33) [44]
(111) (222) (333) (44) [45]
(111) (222) (345) (44) [33]
(123) (123) (123) (44) [45]
(123) (123) (345) (44) [12]

まぁこのくらいがオイラの限界かな。

« 平和な休日だなぁ  | トップへ |  世界のハイボール ( ゚Д゚)マズー »

コメント

Comment Form
このエントリへコメントを書く
(任意)
(任意)
(任意)
(必須) HTML のタグは使用できません
(AAを貼るときは、[太字] ココにAAを書く [/太字] のように 太字 タグで括るときれいになります。左下の[太]ボタンをクリックして「ずれないAA 小」 というところにAAを書いてください。斜体、下線、打消 で括ってもずれないAAにすることができます。)
http:// は禁止ワードです。
(任意) ID生成と編集に使用します

コメントのプレビューのようなもの

ここにプレビューが出ます。

トップへ

トラックバック

http://3tackle.blog55.fc2.com/tb.php/415-eb2b4f9b

最新記事
カテゴリ
最新コメント
最新トラックバック
月別アーカイブ
カウンタ
_
カレンダー
11 | 2019/12 | 01
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 - - - -
これはやべぇ

インターネッツの設定をしてあげたついでに、接続確認と称してこのブログを開いたら、気になるあの娘が検索したアイテムがwww

万が一アダルトグッズが表示されたりしたらと思うと股間が熱くなるな。

とか妄想してたけど、インターネッツの設定をしてあげた直後に検索の履歴が残ってるわけないか
(´・ω・`)
にしても使い方によってはこの機能は凶悪だな。

プロフィール

タックル

Author:タックル
三度のメシより大ライスが好きなエンジニアです。
焼肉屋に行ったら最初の一杯は大ライスで乾杯! 全然関係ないけど食堂のカレーなら4杯は軽いよ。

ちなみにロリ巨乳は大ライスより好き!

プライベートでタックルされてくれるロリ巨乳のお友達を24時間365日募集中です。

エントリーの内容はタックルの日々の生活について多少脚色しながら面白おかしく書いている限りなくノンフィクションに近いフィクションです。

嘘を嘘と見抜けない人は(ry

Webサービス
フィードメーター - 今日の3タックル あわせて読みたいブログパーツ
タックル会
個人的なリンク