今回は神経衰弱を一人で行う
シミュレーションプログラムを考えてみました


解説動画はこちら



神経衰弱

今回は神経衰弱です

トランプで行うゲームのひとつで
カードを伏せた状態で混ぜ
プレイヤーは好きな2枚を
その場で表に向けます

2枚が同じ数字であれば
それらを得ることができ
もう一度プレイできます

2枚が異なる数の場合
カードを元通りに伏せて
次のプレイヤーの順番に

すべてのカードが取られるまで行い
取ったカードの枚数が多い
プレイヤーの勝ちになります


今回は友達がいないので
一人で神経衰弱をしてみる事を考えてみます

何ターンあれば全部空けられるのか?
仮想のカードを作って
シミュレーションしてみましょう


考え方

最初はデッキを用意して
52枚をシャッフルします
# 52枚のカードのデッキ
deck = [i for i in range(1,14)] * 4

import numpy as np

# シャッフルする
np.random.shuffle(deck)
print(deck)
[1, 11, 7, 6, 11, ・・・]

こんな感じで52枚分のデータを用意します


ターン時の行動を考えてみましょう

こんな感じでしょうか?

カードを1枚開ける
  知ってる数字があればそれを開ける
カードをもう1枚開ける
  同じ数字なら取る 次の行動へ
  違う数字ならターンプラス1
2枚ペアが判明していたらそれを開ける

ここら辺の行動を関数にまとめて
ゲームできる様にしてみました


ゲーム関数

import numpy as np
import collections

# under : カードを覚えておくための変数

# 2個以上分かったカードがあるかどうか
def check_2(under):
    c = collections.Counter(under.values())    
    for i in range(1,14):
        if c[i]>=2:
            return i
    return 0

# 2個のペアのインデックスを返す
def get_2pair(under , num):
    pair = [k for  k , v in under.items() if v == num]
    return pair

# ペアカードを取る
def get_card(under , pair):
    for i in pair:
        under[i] = 99
    return under

# 開けていないカードを1枚選択する
def open_1card(under):
    # 候補 0:未開、1-13:開封済、99:取得済
    cand = [k for k,v in under.items() if v==0]
    return np.random.choice(cand)

# 数字が判明しているかをチェック
def under_check(under,num,select):
    # 候補 0:未開、1-13:開封済、99:取得済
    cand = [k for k,v in under.items() if v==num and k!=select]
    if len(cand)>0:
        return True
    else:
        return False
    
# 数字が判明しているものを選択する
def open_under_card(under,num,select):
    # 候補 0:未開、1-13:開封済、99:取得済
    cand = [k for k,v in under.items() if v==num and k!=select]
    return np.random.choice(cand)

# ログのプリント
def logs(turn , gets , sheets , num):
    text = 'ターン : {0:02} , 取得済み : {1:02} , {2}枚目 , 数字:{3:02}'.format(turn , gets , sheets , num)
    print(text)
# 神経衰弱ゲームをシミュレーションする
def game():
    
    # デッキを作ってシャッフルする
    deck = [i for i in range(1,14)] * 4
    np.random.shuffle(deck)
    
    # 回数と開けたカードの記憶
    turn , gets , under = 1, 0 , {i:0 for i in range(52)}
    
    # ゲーム開始
    for j in range(100):
        # 上がりの判定
        if all([i==99 for i in under.values()]):
            print('上がり , turn : {0:02}'.format(turn))
            break
        
        #2枚開いたのがあればそれを消す
        pair_num = check_2(under)
        if pair_num > 0:
            print('判明済み有り : {0:2}'.format(pair_num))
            # ペアを開きに変える
            pair = get_2pair(under , pair_num)
            under = get_card(under , pair)
            gets +=1
            print('get card : {0:02},{1:02} , 数字 : {2:02}'.format(pair[0] , pair[1] , pair_num))

        else:
            #1枚めくる
            select_one = open_1card(under)
            # 開けたカードの数字
            num_one = deck[select_one]
            # 数字を覚えておく
            under[select_one] = num_one
            logs(turn , gets , 1 , num_one)
            
            #数字が分かっているものがあればそれを取る
            if under_check(under,num_one,select_one):
                target = open_under_card(under , num_one , select_one)
                under[select_one] , under[target] = 99 , 99
                print('判明済み有り : {0:2}'.format(num_one))
                logs(turn , gets , 2 , num_one)
                gets +=1
                print('get card : {0:02},{1:02} , 数字 : {2:02}'.format(select_one , target , num_one))

            else:
                #分かってない場合はもう一枚めくる
                select_2nd = open_1card(under)
                num_2nd = deck[select_2nd]
                # 数字を覚えておく
                under[select_2nd] = num_2nd               
                logs(turn , gets , 2 , num_2nd)
   
                #同じ数字ならカードを取る
                if num_one==num_2nd:
                    under[select_one] , under[select_2nd] = 99 , 99
                    gets +=1
                    print('get card : {0:02},{1:02} , 数字 : {2:02}'.format(select_one,select_2nd,num_one))
                else:
                    #違う数字ならターンプラス1
                    turn +=1
                    print('ターン終了')
                    
    return turn
    
game()

ターン : 01 , 取得済み : 00 , 1枚目 , 数字:04
ターン : 01 , 取得済み : 00 , 2枚目 , 数字:02
ターン終了
ターン : 02 , 取得済み : 00 , 1枚目 , 数字:07
ターン : 02 , 取得済み : 00 , 2枚目 , 数字:05
ターン終了
ターン : 03 , 取得済み : 00 , 1枚目 , 数字:09
ターン : 03 , 取得済み : 00 , 2枚目 , 数字:02
ターン終了
判明済み有り :  2
get card : 08,51 , 数字 : 02
ターン : 04 , 取得済み : 01 , 1枚目 , 数字:10
ターン : 04 , 取得済み : 01 , 2枚目 , 数字:09
ターン終了
判明済み有り :  9
get card : 29,34 , 数字 : 09
ターン : 05 , 取得済み : 02 , 1枚目 , 数字:07
判明済み有り :  7
ターン : 05 , 取得済み : 02 , 2枚目 , 数字:07
get card : 06,22 , 数字 : 07
ターン : 05 , 取得済み : 03 , 1枚目 , 数字:13
ターン : 05 , 取得済み : 03 , 2枚目 , 数字:03
ターン終了
ターン : 06 , 取得済み : 03 , 1枚目 , 数字:08
ターン : 06 , 取得済み : 03 , 2枚目 , 数字:08
get card : 37,31 , 数字 : 08
ターン : 06 , 取得済み : 04 , 1枚目 , 数字:01
ターン : 06 , 取得済み : 04 , 2枚目 , 数字:08
ターン終了
ターン : 07 , 取得済み : 04 , 1枚目 , 数字:06
ターン : 07 , 取得済み : 04 , 2枚目 , 数字:07
ターン終了
ターン : 08 , 取得済み : 04 , 1枚目 , 数字:12
ターン : 08 , 取得済み : 04 , 2枚目 , 数字:05
ターン終了
判明済み有り :  5
get card : 09,48 , 数字 : 05
ターン : 09 , 取得済み : 05 , 1枚目 , 数字:12
判明済み有り : 12
ターン : 09 , 取得済み : 05 , 2枚目 , 数字:12
get card : 28,38 , 数字 : 12
ターン : 09 , 取得済み : 06 , 1枚目 , 数字:08
判明済み有り :  8
ターン : 09 , 取得済み : 06 , 2枚目 , 数字:08
get card : 19,46 , 数字 : 08
ターン : 09 , 取得済み : 07 , 1枚目 , 数字:03
判明済み有り :  3
ターン : 09 , 取得済み : 07 , 2枚目 , 数字:03
get card : 03,26 , 数字 : 03
ターン : 09 , 取得済み : 08 , 1枚目 , 数字:04
判明済み有り :  4
ターン : 09 , 取得済み : 08 , 2枚目 , 数字:04
get card : 30,21 , 数字 : 04
ターン : 09 , 取得済み : 09 , 1枚目 , 数字:13
判明済み有り : 13
ターン : 09 , 取得済み : 09 , 2枚目 , 数字:13
get card : 13,25 , 数字 : 13
ターン : 09 , 取得済み : 10 , 1枚目 , 数字:05
ターン : 09 , 取得済み : 10 , 2枚目 , 数字:05
get card : 17,18 , 数字 : 05
ターン : 09 , 取得済み : 11 , 1枚目 , 数字:01
判明済み有り :  1
ターン : 09 , 取得済み : 11 , 2枚目 , 数字:01
get card : 01,23 , 数字 : 01
ターン : 09 , 取得済み : 12 , 1枚目 , 数字:11
ターン : 09 , 取得済み : 12 , 2枚目 , 数字:04
ターン終了
ターン : 10 , 取得済み : 12 , 1枚目 , 数字:01
ターン : 10 , 取得済み : 12 , 2枚目 , 数字:13
ターン終了
ターン : 11 , 取得済み : 12 , 1枚目 , 数字:09
ターン : 11 , 取得済み : 12 , 2枚目 , 数字:11
ターン終了
判明済み有り : 11
get card : 11,15 , 数字 : 11
ターン : 12 , 取得済み : 13 , 1枚目 , 数字:03
ターン : 12 , 取得済み : 13 , 2枚目 , 数字:01
ターン終了
判明済み有り :  1
get card : 35,45 , 数字 : 01
ターン : 13 , 取得済み : 14 , 1枚目 , 数字:02
ターン : 13 , 取得済み : 14 , 2枚目 , 数字:10
ターン終了
判明済み有り : 10
get card : 20,24 , 数字 : 10
ターン : 14 , 取得済み : 15 , 1枚目 , 数字:06
判明済み有り :  6
ターン : 14 , 取得済み : 15 , 2枚目 , 数字:06
get card : 10,49 , 数字 : 06
ターン : 14 , 取得済み : 16 , 1枚目 , 数字:12
ターン : 14 , 取得済み : 16 , 2枚目 , 数字:11
ターン終了
ターン : 15 , 取得済み : 16 , 1枚目 , 数字:10
ターン : 15 , 取得済み : 16 , 2枚目 , 数字:03
ターン終了
判明済み有り :  3
get card : 12,32 , 数字 : 03
ターン : 16 , 取得済み : 17 , 1枚目 , 数字:04
判明済み有り :  4
ターン : 16 , 取得済み : 17 , 2枚目 , 数字:04
get card : 39,14 , 数字 : 04
ターン : 16 , 取得済み : 18 , 1枚目 , 数字:12
判明済み有り : 12
ターン : 16 , 取得済み : 18 , 2枚目 , 数字:12
get card : 40,50 , 数字 : 12
ターン : 16 , 取得済み : 19 , 1枚目 , 数字:11
判明済み有り : 11
ターン : 16 , 取得済み : 19 , 2枚目 , 数字:11
get card : 41,47 , 数字 : 11
ターン : 16 , 取得済み : 20 , 1枚目 , 数字:07
判明済み有り :  7
ターン : 16 , 取得済み : 20 , 2枚目 , 数字:07
get card : 43,05 , 数字 : 07
ターン : 16 , 取得済み : 21 , 1枚目 , 数字:06
ターン : 16 , 取得済み : 21 , 2枚目 , 数字:10
ターン終了
判明済み有り : 10
get card : 42,44 , 数字 : 10
ターン : 17 , 取得済み : 22 , 1枚目 , 数字:09
判明済み有り :  9
ターン : 17 , 取得済み : 22 , 2枚目 , 数字:09
get card : 27,16 , 数字 : 09
ターン : 17 , 取得済み : 23 , 1枚目 , 数字:13
判明済み有り : 13
ターン : 17 , 取得済み : 23 , 2枚目 , 数字:13
get card : 02,07 , 数字 : 13
ターン : 17 , 取得済み : 24 , 1枚目 , 数字:02
判明済み有り :  2
ターン : 17 , 取得済み : 24 , 2枚目 , 数字:02
get card : 00,33 , 数字 : 02
ターン : 17 , 取得済み : 25 , 1枚目 , 数字:06
判明済み有り :  6
ターン : 17 , 取得済み : 25 , 2枚目 , 数字:06
get card : 36,04 , 数字 : 06
上がり , turn : 17

こんな感じで26ペアを取れた時点で
ゲームは終了です

多分合ってるとは
思うんですけどねー

大したデバッグはしていないので
間違いがあったらごめんなさいね



1000回でシミュレーション

最後にこれを1000回繰り返して
何手で上がれたかのシミュレーションです

ログを抜いたgame2関数を用意して
1000回実行した際の
結果をまとめてみました

calc = {}
for i in range(1000):
    key = game2()
    if key in calc:
        calc[key]+=1
    else:
        calc[key] =1
for k,v in sorted(calc.items()):
    print('ターン数 : {0:02} , {1:03}回'.format(k,v))
ターン数 : 11 , 001回
ターン数 : 13 , 004回
ターン数 : 14 , 041回
ターン数 : 15 , 149回
ターン数 : 16 , 266回
ターン数 : 17 , 295回
ターン数 : 18 , 168回
ターン数 : 19 , 059回
ターン数 : 20 , 016回
ターン数 : 21 , 001回

これでみると
大体17回もあれば
上がれるみたいですねー

ほぼほぼ20回以内に
上がれているので
記憶力があれば
20ターンもかからないようです

今回は神経衰弱を
シミュレーションしてみました

それでは