四則ゲーム

#四則ゲーム
四則ゲームとは以下のような楽しいゲームです。

###用意するもの
1組52枚のトランプと、それを置く机があれば、どこでも遊べます。
プレイヤー数は2人以上6人程度が妥当だと思います。

###ゲームの進め方
トランプをシャッフルして、デッキを山札として机に置きます。
最初の親をじゃんけんなどで決めてゲームを開始します。

- ゲームの開始時に親が目標数を宣言します。(慣れるまでは10～100程度の自然数が良いです)
- 親は山札から1枚めくって机に表にして置きます。
- 以降は時計回りに1人ずつターンが回っていきます。
- 自分のターンになったら好きなタイミングで山札から1枚めくって机に表にして置き、次の人のターンになります。

参加しているプレイヤーは、自分のターンかどうかに関わらず、いつでも以下の宣言ができます。

- 場に出たカードの数を<B>すべて</B>使って四則演算により目標数を作る事ができるなら、机を叩きます。

一番最初に机を叩いた人は、四則演算で目標数を作る方法を全員に説明します。
正しく説明できれば、そのプレイヤーの勝ちになります。
勝ったプレイヤーは机に並んでいるカードをすべてもらって、その枚数が自分のポイントになります。(得点)
そして次のゲームの親になります。

正しく説明できなかった場合は、そのプレイヤーの反則になります。
机に並んでいるカードと同じ枚数だけ、自分のポイントのカードから山札に戻します。(失点)
山札をシャッフルしてゲームを再開します。

上記を1回のゲームとして、これを山札が無くなるまで繰り返します。
山札がなくなった時点で終了し、一番たくさんの枚数のカードを持っている人が勝ちになります。

★注意事項★
もらったカードには、枚数で得点をあらわす以外の役割はありません。
複数人が区別できないくらい同時に叩いたときは、誰が答えるかじゃんけん等で決めてください

###トランプの数字
トランプのスートは関係ありません。以下のように1～13の数字をそれぞれ表します。

|トランプ|数字|
|:------|:---|
|A      |1   |
|2      |2   |
|3      |3   |
|4      |4   |
|5      |5   |
|6      |6   |
|7      |7   |
|8      |8   |
|9      |9   |
|T      |10  |
|J      |11  |
|Q      |12  |
|K      |13  |

###演算規則
目標数の作り方は以下のような四則演算の組み合わせです。
机に並んでいる順番は関係なく、好きな2枚を選んで四則演算で新しい数字を作る操作を繰り返します。
四則演算は以下の4種類です。

- a + b
- a - b
- a × b
- a ÷ b

0で割ることはできません。

###演算の例

例1.
目標数 : <B>10</B>
机の上のカード : K 6 Q 9

13 - 6 = 7
7 + 12 = 19
19 - 9 = 10

例2.
目標数 : <B>88</B>
机の上のカード : 2 9 6 7 6

2 + 9 = 11
6 ÷ 6 = 1
1 + 7 = 8
8 × 11 = 88

例3.
目標数 : <B>88</B>
机の上のカード : 6 6 5 9 3

6 × 5 = 30
30 × 9 = 270
270 - 6 = 264
264 ÷ 3 = 88

例4.
目標数 : <B>314</B>
机の上のカード : 3 2 9 K A

3 + 9 = 12
12 × 13 = 156
156 + 1 = 157
157 × 2 = 314

例5.
目標数 : <B>314</B>
机の上のカード : 9 7 4 6 Q 3

9 × 7 = 63
63 - 12 = 51
4 ÷ 3 = 4/3
4/3 + 51 = 157/3
157/3 × 6 = 314

例6.
目標数 : <B>0.01</B>
机の上のカード : J 6 T 9 K K

13 × 6 = 78
78 + 13 = 91
91 + 9 = 100
11 - 10 = 1
1 ÷ 100 = 0.01

例7.
目標数 : <B>-314</B>
机の上のカード : K 7 7 3 2 5

7 × 5 = 35
13 - 35 = -22
-22 × 7 = -154
-154 - 3 = -157
-157 × 2 = -314

###Pythonで解く！
ここからがこの記事の本題です。
机の上のカードで目標数が作れるかどうかを判定するプログラムを作りたいです。

任意の2枚を選んで新しい数を作り、その繰り返しで目標数が作れるかどうかを、そのまま実装します。

```python:shisoku.py
import random
from itertools import combinations


def int_number(value):
    return int(value) if value.is_integer() else value


class Shisoku:
    EPS = 0.00001

    def __init__(self):
        self.cards = []
        self.deck = list(range(1, 14)) * 4
        random.shuffle(self.deck)

    def deal_card(self):
        card = self.deck.pop()
        self.cards.append(card)

    def make(self, target):
        answer = []
        self.solve(target, self.cards, answer)
        return answer[::-1]

    def solve(self, target, cards, answer):
        if len(cards) <= 1:
            return cards and abs(cards[0] - target) < self.EPS

        def is_target(a, op, b, value, rest):
            if self.solve(target, [value] + rest, answer):
                answer.append([a, op, b, "=", value])
                return True
            return False

        for i, j in combinations(range(len(cards)), 2):
            a, b = cards[i], cards[j]
            rest = cards[:i] + cards[i + 1:j] + cards[j + 1:]

            if (is_target(a, "+", b, a + b, rest)
                    or is_target(a, "-", b, a - b, rest)
                    or is_target(b, "-", a, b - a, rest)
                    or is_target(a, "*", b, a * b, rest)
                    or b and is_target(a, "/", b, int_number(a / b), rest)
                    or a and is_target(b, "/", a, int_number(b / a), rest)):
                return True

        return False


def main():
    target = int_number(float(input("Input target number: ")))

    game = Shisoku()
    answer = game.make(target)

    while not answer:
        game.deal_card()
        print("**********")
        print("Target :", target)
        print("Cards :", *game.cards)
        answer = game.make(target)

    for exp in answer:
        print(*exp)


if __name__ == "__main__":
    main()
```

###コードの計算量

全探索で実装しました。机の上のカードが6枚程度なら、かなり高速に解を表示します。
カードが7枚になると、それなりに計算に時間がかかります。

読んでいただき、ありがとうございました。
四則ゲームは面白いので、ぜひ友達と遊んでみてくださいね！

###追記

いろんな方からアドバイスをいただいて、高速かつ読みやすいコードに修正させていただくことができました！
ありがとうございます！