Matthewの備忘録

忘れたときはここを見ろ。何か書いてある。

バックトラック版

 先日、バックトラックによらない麻雀の待ちを探索するPythonコードを張っておいたが、しばらくして、なんだかバックトラックのプログラムが書けないのではないかと思い、バックトラック版も書いた。ここに張っておく。
 計算量とか薀蓄とかその他どうでもよさそうなことはまた後ほど書く。今はSchemeで書いてみたいと思っている。
 因みに、このプログラムは可能な全組合せについて判定するようにしてある。全データカバレッジテストとでもいうのかな。しかしながら、全組合せが幾つあるのか手計算できずにいる(笑)。また、プロセス実行時間を簡単に計れないシステムを考慮して、実行の最初と最後に時間を出力している。10万行以上出力されるので、先頭行と最後の行を比較して処理時間を出してくれ。
 λ式を使えば、もっと短いプログラムになりそうな気がするが、後ほど考えてみよう。

#-------------------------------------------------------------------------------
# Name:        Ready Hand Judgement
# Purpose:     To judge wheather a hand in mahjong is ready or not
#
# Author:      Matthew
#
# Version:     0.2
# Created:     14/04/2010
# Copyright:   (c) Matthew 2010
# Licence:     GPL Ver. 3
#-------------------------------------------------------------------------------
#!/usr/bin/env python

import sys
from datetime import datetime, date, time

def check_ready(hand):
    hand = sorted(list(hand))

    # waiting for the only tile for a pair
    bfr = []
    for i in hand:
        waiting = '[%1s]'%(i)
        if waiting in bfr: continue
        rest = hand[:]
        rest.remove(i)
        search(rest, waiting, [])
        bfr.append(waiting)

    # waiting for the middle in a sequence
    bfr = []
    for i in hand:
        j = int(i)
        if str(j + 2) in hand:
            waiting = '[%1d%1d]'%(j, j + 2)
            if waiting in bfr: continue
            rest = hand[:]
            rest.remove(i)
            rest.remove(str(j + 2))
            search(rest, waiting, [])
            bfr.append(waiting)

    # waiting for either of two gates and
    # waiting for three or seven the tile
    bfr = []
    for i in hand:
        j = int(i)
        if str(j + 1) in hand:
            waiting = '[%1d%1d]'%(j, j + 1)
            if waiting in bfr: continue
            rest = hand[:]
            rest.remove(i)
            rest.remove(str(j + 1))
            search(rest, waiting, [])
            bfr.append(waiting)

    # waiting for a triplet
    bfr = []
    for i in hand:
        if hand.count(i) > 1:
            waiting = '[%1s%1s]'%(i, i)
            if waiting in bfr: continue
            rest = hand[:]
            rest.remove(i)
            rest.remove(i)
            search(rest, waiting, [])
            bfr.append(waiting)

def show_ready(elements, waiting):
    for i in elements:
        sys.stdout.write(i)
    sys.stdout.write(waiting)
    sys.stdout.write('\n')

def search(rest, waiting, elements):
    # show ready hands
    if not rest:
        if len(elements) == 4 or len(elements) == 6:
            show_ready(elements, waiting)
        return

    # head or pair(included in elements)
    r, t = rest[:], rest[0]
    if r.count(t) == 2:
        elements.append('(%1s%1s)'%(t, t))
        r.remove(t)
        r.remove(t)
        search(r, waiting, elements)
        elements.pop()

    # triplet
    r, t = rest[:], rest[0]
    if r.count(t) > 2:
        elements.append('(%1s%1s%1s)'%(t, t, t))
        r.remove(t)
        r.remove(t)
        r.remove(t)
        search(r, waiting, elements)
        elements.pop()

    # sequence
    r, t = rest[:], rest[0]
    if str(int(t) + 1) in r and str(int(t) + 2) in r:
        elements.append('(%1s%1s%1s)'%(t, str(int(t) + 1), str(int(t) + 2)))
        r.remove(t)
        r.remove(str(int(t) + 1))
        r.remove(str(int(t) + 2))
        search(r, waiting, elements)
        elements.pop()

# a kind of homogeneous product generator
def hp(list, counter, n):
    if n == 0: return [[]]
    tuples = []
    for i, x in enumerate(list):
        if counter[i] > 0:
            counter[i] -= 1
            tuples.extend([[x] + l for l in hp(list[i:], counter[i:], n - 1)])
    return tuples

def main():
    pass

if __name__ == '__main__':
    main()

# data coverage test
print datetime.now()
c = hp([1, 2, 3, 4, 5, 6, 7, 8, 9], [4, 4, 4, 4, 4, 4, 4, 4, 4], 13)
for i in c:
    check_ready("".join(map(str, i)))
print datetime.now()