ソリティアが、どのくらいの回数やれば成功するか、Pythonでシミュレーションしてみた


ソリティア(*)が、どのくらいの回数やれば成功するか、Pythonでシミュレーションしてみた

(*)一般的なソリティアではない点注意。ルールはコメント参照。

![my_solitaire.jpeg](https://qiita-image-store.s3.amazonaws.com/0/107418/632c3dec-03ab-35a1-a7e3-962363f3077c.jpeg)



```python:solitaire.py

# 当該カードをひっくり返す
def updown(card):
    if card[2] == "D":
        card[2] = "U"
    else:
        card[2] = "D"
    return card

# 同一スーツ、１つ下の数値のカードを見つけて、下に連結する
def find(lane,lanes):
    b = False
    for ls in lanes:
        if ls != lane:
            for l in ls:
                if not len(lane)==0:
                    if(lane[len(lane)-1][0]==l[0] and \
                       lane[len(lane)-1][1]-1==l[1] and \
                       l[2]=='U'):
                        b = True
                        ix = ls.index(l)
                        for c in range(ix,len(ls)):
                            lane.append(ls.pop(ix))
    return b

# 空のレーンに、キング（に連なるカードたち）を移す
def king_move(lanes):
    b = False
    for ls in lanes:
        if len(ls)==0:
            for ls_ot in lanes:
                if len(ls_ot)>0 and len(ls)==0:
                    for l in ls_ot:
                        if l[1] == 13 and len(ls)==0:
                            ix = ls_ot.index(l)
                            if ix >0 and len(ls)==0:
                                b = True
                                for c in range(ix,len(ls_ot)):
                                    ls.append(ls_ot.pop(ix))
                                break
    return b

# トップに来た裏カードを、表にする
def top_faceup(lanes):
    b = False
    for ls in lanes:
        if not len(ls)==0:
            if(ls[len(ls)-1][2] =='D'):
                b = True
                updown(ls[len(ls)-1])
    return b

#　エース（以降最小の）のカードがトップに来たら、外に出す 
def out_put(lanes):
    b = False
    for ls in lanes:
        if not len(ls)==0:
            for s in range(0,4):
                if(out_place[s][len(out_place[s])-1][0] == ls[len(ls)-1][0] and \
                   out_place[s][len(out_place[s])-1][1] == ls[len(ls)-1][1]-1 ):
                    out_place[s].append(ls.pop())
                    b = True
                    break
    return b


# メインループの始まり
import random

cn1=0 #試行回数
cn2=0 #成功数

# メイン
for k in range(0,10000):

    if k % 100 == 0:
        print(k)

    # 準備 ht:ハート、sp:スペード、di:ダイヤ、cb:クラブ
    out_place = [[['ht',0,'U']],[['sp',0,'U']],[['di',0,'U']],[['cb',0,'U']]]
    nums = [1,2,3,4,5,6,7,8,9,10,11,12,13]
    suits = ["ht","sp","di","cb"]
    cards = list()

    # ５２枚の準備
    for s in suits:
        for n in nums:
            cards.append([s,n,"D"])
    random.shuffle(cards)

    # ７つのレーン
    lane = [list(),list(),list(),list(),list(),list(),list()]

    # カードの初期配置　ここから↓
    # １枚表　６枚裏
    lane[0].append(updown(cards.pop()))
    for i in range(1,7):
        lane[i].append(cards.pop())
    # １枚表　●枚裏
    for j in range(1,7):
        lane[j].append(updown(cards.pop()))
        for i in range(j+1,7):
            lane[i].append(cards.pop())
    # ４枚ずつ表
    for j in range(1,5):
        for i in range(1,7):
            lane[i].append(updown(cards.pop()))
    # カードの初期配置　ここまで↑

    #なにか動く限り、ループ
    b1 = True
    while b1:
        b1 = False
        b1 = b1 or top_faceup(lane)
        b1 = b1 or out_put(lane)
        b1 = b1 or king_move(lane)
        for i in range(0,7):
            b1 = b1 or find(lane[i],lane)
            
    if sum(len(op) for op in out_place)-4 == 13*4:
        cn2=cn2+1
    cn1=cn1+1

print("全試行数:",cn1, " 成功数:",cn2, " 成功率:", "{0:.2f}％".format(cn2/cn1*100))



```

結果↓

~~１００００回実施して、１８２回。　約2％弱。~~

```
全試行数: 100000  成功数: 4143  成功率: 4.14％
```

成功率４％ほど。   ※注：処理を一部修正後(ソース内"#なにか動く限り、ループ")