Pythonで書くガチャ-データ設計-

#内容
ガチャのデータ設計を考えてみます。

##ガチャに必要なデータ要素
ここまでガチャのデータとして、**ID**と**重みの値**のみで実践してきましたが、

- [Pythonで書くシンプルなガチャロジック](https://qiita.com/saib-ryo/items/dffb3ab05762cdc26da0)
- [pythonで書くガチャ-おまけ付きレアリティ確定-](https://qiita.com/saib-ryo/items/77f676172f998227a79f)

```python
# 抽選対象のアイテムIDと重みのdictionary
item_dic = {"id_1":1,"id_2":5,"id_3":14,"id_4":30,"id_5":50}
```
実際にガチャを作る場合に必要な要素を列挙してみます。

- ID
- 重み（weight）
- レア度（rarity）
- アイテム名(item_name)

表にすると以下のようになります
**gacha_items**

|  id | weight | rarity |       item_name    |
|:---:|:----:|:------:|:------------------:|
|  1  |  1   |   5    |       UR_HOGE      |
|  2  |  1   |   5    |       UR_FUGA      |
|  3  |  9   |   4    |       SSR_HOGE     |
|  4  |  9   |   4    |       SSR_FUGA     |
|  5  |  20  |   3    |       SR_HOGE      |
|  6  |  20  |   3    |       SR_FUGA      |
|  7  |  30  |   2    |       R_HOGE       |
|  8  |  30  |   2    |       R_FUGA       |
|  9  |  40  |   1    |       N_HOGE       |
|  10 |  40  |   1    |       N_FUGA       |

- weightは小さい方が、当選確率が低い
- rarityは高いほうが、希少度が高い意味付け

レア度と、その名称の対応表は以下のようになります。
**ratiry_names**

|  id |  rarity_name      |
|:---:|:---------------------:|
|  5  |  UR  |
|  4  |  SSR |
|  3  |  SR  |
|  2  |  R   |
|  1  |  N   |

```python
gacha_items = {
    1: {"weight":1, "rarity":5, "item_name":"UR_HOGE"},
    2: {"weight":1, "rarity":5, "item_name":"UR_FUGA"},
    3: {"weight":9, "rarity":4, "item_name":"SSR_HOGE"},
    4: {"weight":9, "rarity":4, "item_name":"SSR_FUGA"},
    5: {"weight":20,"rarity":3, "item_name":"SR_HOGE"},
    6: {"weight":20,"rarity":3, "item_name":"SR_FUGA"},
    7: {"weight":30,"rarity":2, "item_name":"R_HOGE"},
    8: {"weight":30,"rarity":2, "item_name":"R_FUGA"},
    9: {"weight":40,"rarity":1, "item_name":"N_HOGE"},
    10:{"weight":40,"rarity":1, "item_name":"N_FUGA"}
}

rarity_names = {5: "UR", 4: "SSR", 3: "SR", 2: "R", 1: "N"}
```
各々のディクショナリの説明

- gacha_itemsは、idをキーとして、重みなどの情報を保持
- rarity_namesは、レア度（id）に対応したレア名を保持

## おまけのレアリティを指定したガチャ
おまけについて、レアリティ以上の指定ができるガチャを作成してみます。
（@shiracamusさんのソースを流用します）

```python:gacha.py
import random

def gacha(gacha_items: dict, times: int=1) -> list:
    # 抽選に必要な、IDと重みの辞書を作成
    lots = {key: info["weight"] for key, info in gacha_items.items()}
    return random.choices(tuple(lots), weights=lots.values(), k=times)

def gacha_omake(gacha_items: dict, times: int, omake_rarity: int) -> list:
    # おまけの対象をレアリティ制限で辞書を作成
    omake = {key: info for key, info in gacha_items.items() if info["rarity"] >= omake_rarity}
    ids = gacha(gacha_items, times)
    omake and ids.extend(gacha(omake))
    return ids

def main():
    # ガチャアイテム情報
    gacha_items = {
        1: {"weight": 1, "rarity": 5, "item_name": "UR_HOGE"},
        2: {"weight": 1, "rarity": 5, "item_name": "UR_FUGA"},
        3: {"weight": 9, "rarity": 4, "item_name": "SSR_HOGE"},
        4: {"weight": 9, "rarity": 4, "item_name": "SSR_FUGA"},
        5: {"weight": 20, "rarity": 3, "item_name": "SR_HOGE"},
        6: {"weight": 20, "rarity": 3, "item_name": "SR_FUGA"},
        7: {"weight": 30, "rarity": 2, "item_name": "R_HOGE"},
        8: {"weight": 30, "rarity": 2, "item_name": "R_FUGA"},
        9: {"weight": 40, "rarity": 1, "item_name": "N_HOGE"},
        10: {"weight": 40, "rarity": 1, "item_name": "N_FUGA"}
    }

    # レアリティ名
    rarity_names = {5: "UR", 4: "SSR", 3: "SR", 2: "R", 1: "N"}

    # 抽選回数
    times = 10

    # おまけレアリティ（以上）
    omake_rarity = 3

    # 抽選してidのリストを取得
    ids = gacha_omake(gacha_items, times, omake_rarity)

    # 結果出力
    for id in ids:
        print("ID:%d, %s, %s" % (id, rarity_names[gacha_items[id]["rarity"]], gacha_items[id]["item_name"]))

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

## 実行結果
```
ID:8, R, R_FUGA
ID:7, R, R_HOGE
ID:5, SR, SR_HOGE
ID:5, SR, SR_HOGE
ID:5, SR, SR_HOGE
ID:3, SSR, SSR_HOGE
ID:5, SR, SR_HOGE
ID:10, N, N_FUGA
ID:5, SR, SR_HOGE
ID:9, N, N_HOGE
ID:5, SR, SR_HOGE
```
## 考察
前回との違いを考察してみます。
（[pythonで書くガチャ-おまけ付きレアリティ確定-](https://qiita.com/saib-ryo/items/77f676172f998227a79f)）

- 結果表示用に情報が付加されている
- おまけ対象を**レアリティ値**にて制御している

ガチャに必要最低限な情報は、IDと確率（重み）ですが、
その他に、これらの情報を付加したことにより、実際のガチャに近い**挙動**と**結果**を得ることが出来ました。

重要な事は処理を実現するために**データの定義（データ設計）**を行い、それに応じたロジックを記述しているということです。

ガチャそのものはシンプルなロジックです。
しかし、実サービスにおいてはガチャ運用に対応できるデータ設計が必要となり、データ設計が全てと言っても過言ではありません。
（実際にはこれらのデータをDBに格納して運用することになります）