yhpg05 - 大貧民 をPythonで解いてみる

http://nabetani.sakura.ne.jp/hena/ord5dahimi/
で出題された大貧民をPythonで解くことに挑戦してみる。

もう、どうしようという感じでテストコードが通らないまま投稿してしまう。
多分、目視では正解していると思うのですが。

出せる手札の組み合わせを計算するのにitertoolsを使ってみた。
計算した組み合わせの中から出せる手札を探して回る作戦。


複数枚手札を出す場合の結果と期待値が同じ並びにならないので、テストコードが通らない。
結果の比較の仕方をもっと工夫しないとうまくいかない。ぐぬぬ。

2017/7/23追記
コメントいただいた内容を参考に、テストコードを通るように修正。
同じランクが出せていたバグを発見したので、こっそり修正。 test("C7H7,S7CTH8D5HACQS8JoD6SJS5H4", "HAJo,JoSJ,H8S8,H8Jo,CQJo,CTJo,JoS8" );
でC7Joも出せる手札に加わっていた。



```py:yhpg05.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

# https://yhpg.doorkeeper.jp/
# http://nabetani.sakura.ne.jp/hena/ord5dahimi/

import itertools

#数値の大小で求められるように都合よく変換する
converter = {
    "3":0,
    "4":1,
    "5":2,
    "6":3,
    "7":4,
    "8":5,
    "9":6,
    "T":7,
    "J":8,
    "Q":9,
    "K":10,
    "A":11,
    "2":12,
    "o":13
}

def solve(data):
    data = data.split(",")
    field = list(sorted([data[0][i: i+2] for i in range(0, len(data[0]), 2)]))
    hands = [data[1][i: i+2] for i in range(0, len(data[1]), 2)]
    # 手札にあるカードで場に出ている枚数を出す場合の組み合わせを作っておく
    hands = list(sorted(itertools.combinations(hands,len(field))))

    # 出せる手札が１枚もない場合は終わり
    if len(hands) == 0:
        return list("-")

    result = []
    if len(field) == 1:
        #場が1枚だけカードが出ている
        for hand in hands:
            if isAvailableCard(field[0][1:2], hand[0][1:2]):
                result.append(hand[0])
    else:
        #場に２枚以上カードが出ている
        #場の値がいくつなのか取得する
        num = getFieldNum(field)
        #手札から出せるカードを調べる
        temp = []
        temp.append(getAvailableCards(num,hands))
        temp = temp[0]
        for card in temp:
            result.append("".join(card))

    if len(result) == 0:
        return list("-")
    else:
        return result

#場に２枚以上カードが出ている場合のランクから計算用の値に変換して返す
def getFieldNum(field):
    for card in field:
        if card == "Jo":
            #ジョーカーは無視
            continue
        else:
            #同じランクの同時出ししか対応しないので、最初に見つけたのを返す
            return converter[card[1]]

#場にカードが一枚しか出ていない前提で手持ちのカードが出せる場合はtrueが返る
def isAvailableCard(field, hand):
    return converter[field] < converter[hand]

#場にカードが２枚以上出されている場合に、出せる手札のlistを作成して返す
def getAvailableCards(num, hands):
    result = []
    for cards in hands:
        for card in cards:
            if card == "Jo":
                #ジョーカーは無視
                continue
            else:
                #if  converter[card[1]] < num: 
                if  converter[card[1]] <= num:
                    continue
                else:
                    #ほかの組がすべて同じランクだったら、出せる手札
                    if isSameRank(cards):
                        result.append(cards)
                    break

    return result

#ランクがすべて同じ値ならtrueを返す
def isSameRank(cards):
    rank = -1
    for card in cards:
        if card == "Jo":
            #ジョーカーは無視
            continue
        else:
            if rank == -1:
                rank = card[1]
            else:
                if rank == card[1]:
                    continue
                else:
                    #違うランクが含まれている場合はNG
                    return False
    # ここまで来た場合はすべてのカードが同じランク
    return True

def test(data, expect):
    def sort(hands):
        result = []
        for hand in hands:
            result.append(sorted([hand[i: i+2] for i in range(0, len(hand), 2)]))
        return sorted(result)
    result = solve(data)
    expect = sort(expect.split(","))
    result = sort(result)
    if result == expect:
        print("成功: " , result , "/" , expect )
    else:
        print("失敗: " , result , "/" , expect )

test( "DJ,", "-" );
test( "H7,HK", "HK" );
test( "S3,D4D2", "D4,D2" );
test( "S9,C8H4", "-" );
test( "S6,S7STCK", "CK,ST,S7" );
test( "H4,SAS8CKH6S4", "S8,CK,H6,SA" );
test( "ST,D6S8JoC7HQHAC2CK", "Jo,C2,CK,HA,HQ" );
test( "SA,HAD6S8S6D3C4H2C5D4CKHQS7D5", "H2" );
test( "S2,D8C9D6HQS7H4C6DTS5S6C7HAD4SQ", "-" );
test( "Jo,HAC8DJSJDTH2", "-" );
test( "S4Jo,CQS6C9DQH9S2D6S3", "DQCQ,D6S6,H9C9" );
test( "CTDT,S9C2D9D3JoC6DASJS4", "JoC2,SJJo,DAJo" );
test( "H3D3,DQS2D6H9HAHTD7S6S7Jo", "JoHA,JoD6,JoH9,D6S6,D7S7,JoS6,HTJo,JoDQ,S2Jo,JoD7,JoS7" );
test( "D5Jo,CQDAH8C6C9DQH7S2SJCKH5", "CQDQ" );
test( "C7H7,S7CTH8D5HACQS8JoD6SJS5H4", "HAJo,JoSJ,H8S8,H8Jo,CQJo,CTJo,JoS8" );
test( "SAHA,S7SKCTS3H9DJHJH7S5H2DKDQS4", "-" );
test( "JoC8,H6D7C5S9CQH9STDTCAD9S5DAS2CT", "CTDT,H9D9,S9D9,DACA,CTST,H9S9,DTST" );
test( "HTST,SJHJDJCJJoS3D2", "DJCJ,SJDJ,JoHJ,CJHJ,SJJo,HJSJ,DJJo,JoCJ,JoD2,SJCJ,DJHJ" );
test( "C7D7,S8D8JoCTDTD4CJ", "D8S8,JoS8,CTJo,DTJo,JoCJ,CTDT,D8Jo" );
test( "DJSJ,DTDKDQHQJoC2", "JoDK,HQDQ,DQJo,C2Jo,JoHQ" );
test( "C3H3D3,CKH2DTD5H6S4CJS5C6H5S9CA", "S5H5D5" );
test( "D8H8S8,CQHJCJJoHQ", "JoCQHQ,JoHJCJ" );
test( "H6D6S6,H8S8D8C8JoD2H2", "D2H2Jo,D8JoS8,D8S8C8,C8D8H8,JoC8S8,H8JoC8,S8H8C8,JoS8H8,C8JoD8,D8H8S8,D8JoH8" );
test( "JoD4H4,D3H3S3C3CADASAD2", "DACASA" );
test( "DJHJSJ,SQDQJoHQCQC2CA", "SQJoCQ,DQCQJo,JoSQHQ,SQCQHQ,DQHQSQ,HQDQCQ,HQDQJo,SQDQCQ,CQJoHQ,SQJoDQ" );
test( "H3D3Jo,D4SKH6CTS8SAS2CQH4HAC5DADKD9", "HASADA" );
test( "C3JoH3D3,S2S3H7HQCACTC2CKC6S7H5C7", "-" );
test( "H5C5S5D5,C7S6D6C3H7HAH6H4C6HQC9", "C6D6S6H6" );
test( "H7S7C7D7,S5SAH5HAD5DAC5CA", "SADACAHA" );
test( "D4H4S4C4,S6SAH6HAD6DAC6CAJo", "C6H6S6D6,SAJoDACA,S6H6C6Jo,SACAJoHA,HADASAJo,HADAJoCA,CADAHASA,D6C6JoH6,S6D6C6Jo,H6JoS6D6" );
test( "DTCTSTHT,S3SQH3HQD3DQC3CQJo", "HQSQJoDQ,SQCQDQJo,DQCQHQJo,SQHQJoCQ,CQDQHQSQ" );
test( "JoS8D8H8,S9DTH9CTD9STC9CAC2", "H9C9D9S9" );
```