Gacha written in python-Practice 3 ・ Addition of step-up gacha functions-

Contents

Last time, I created a step-up gacha. This time, we will add control processing to the step-up gacha and modify it so that it can be used more practically.

** The following sources have been modified and implemented **

Gacha written in python-Practice 2 / Basics of step-up gacha-

Step-up gacha repair

As this renovation, the following control processing will be added to the step-up gacha.

--Repeat

repetition

Last time The step-up gacha I made was designed to end when it progressed to the final step. In the actual game, there is a gacha that allows you to draw the last step repeatedly after the last step. To achieve that, we need to add control for repetition. This time, let's think about how to make the following three types of step-up gacha.

--step_up_N: Basic step-up (finished at the final step) --step_up_S1: Repeat step-up (return to step 1 after the final step) --step_up_S2: Step-up final repeat (repeat the final step forever)

Addition of gacha type setting (repeated)

To define ** repeating steps ** for gacha drawing settings (gacha_lottery) Create a table called Gacha Type Setting (gacha_type).

gacha_type

id kind rotation_step_no
step_up_N step_up 0
step_up_S1 step_up 1
step_up_S2 step_up 4

The rotation_step_no item indicates the step to transition to after the final step. In the case of 0, it is defined as ending at the final step.

Repair of gacha pulling setting

Replace the item gacha_type in the gacha drawing setting (gacha_lottery) with thegacha type setting ID (gacha_type_id)set above.

gacha_lottery

id gacha_type_id step_no item_type times rarity omake_times omake_rarity cost
step_up_N_1 step_up_N 1 0 1 0 0 0 10
step_up_N_2 step_up_N 2 0 2 0 1 3 30
step_up_N_3 step_up_N 3 0 3 0 2 3 50
step_up_N_4 step_up_N 4 0 4 0 1 4 50
step_up_S1_1 step_up_S1 1 0 1 0 0 0 10
step_up_S1_2 step_up_S1 2 0 2 0 1 3 30
step_up_S1_3 step_up_S1 3 0 1 0 2 3 30
step_up_S1_4 step_up_S1 4 0 4 0 1 4 50
step_up_S2_1 step_up_S2 1 0 1 0 0 0 10
step_up_S2_2 step_up_S2 2 0 2 0 1 3 30
step_up_S2_3 step_up_S2 3 0 4 0 1 5 50
step_up_S2_4 step_up_S2 4 0 4 0 1 3 50

Repair of gacha period setting

Similarly, replace the item gacha_type in the gacha period setting (gacha) with thegacha type setting ID (gacha_type_id)so that the gacha drawing method setting (gacha_lottery) and the gacha type setting (gacha_type) are linked. I will.

gacha

id start_time end_time gacha_group gacha_type_id
10 2020-05-01 00:00:00 2020-05-31 23:59:59 A step_up_N
11 2020-06-01 00:00:00 2020-06-30 23:59:59 C step_up_N
12 2020-05-01 00:00:00 2020-07-31 23:59:59 A step_up_S1
13 2020-05-01 00:00:00 2020-05-31 23:59:59 A step_up_S2
14 2020-06-01 00:00:00 2020-06-30 23:59:59 C step_up_S2
gacha_step_p1.png

reset

Addition (reset) of gacha type setting

Depending on the operation, you may want to return to the initial state at a certain timing. Therefore, add the reset definition to the gacha type setting (gacha_type).

Depending on the reset definition added this time, it will be set to be automatically reset depending on the period.

reset_type There are two * reset_type * to define.

--daily: Return to the initial state (step 1) at the first access every day --monthly: Return to the initial state (step 1) at the first access every month

For monthly, it's not very practical, but it's set up to understand the difference

gacha_type

id kind rotation_step_no reset_type
step_up_N step_up 0 daily
step_up_S1 step_up 1 monthly
step_up_S2 step_up 4

This time, set gacha_type according to the gacha period setting (gacha). ** step_up_S2 ** has no reset setting because reset_type is empty.

gacha_step_p0.png

Implementation

The implementation specifications are summarized below.

Repeat (when executed to the final step)

-** When 0 is set in rotation_step_no ** --Increment (+1) the ** step_no ** of the user gacha step information and update (Gacha end state) -** When rotation_step_no ** is set to something other than 0 --Update ** step_no ** of user gacha step information with ** rotation_step_no **

reset

-** When daily is set in reset_type ** --If ** updated_time ** of user gacha step information is the date of the previous day, update with step_no = 1

-** When monthly is set in reset_type ** --If ** updated_time ** of user gacha step information is the date of the previous month, update with step_no = 1

Gacha DB information

If you want to change the structure / data or reset the history, please re-execute this process.

gacha_db.py


# -*- coding: utf-8 -*-
import sqlite3
import random

def get_items():
    items = {
        5101: {"rarity": 5, "item_name": "UR_Brave", "item_type": 1, "hp": 1200},
        4201: {"rarity": 4, "item_name": "SSR_Warrior", "item_type": 2, "hp": 1000},
        4301: {"rarity": 4, "item_name": "SSR_Wizard", "item_type": 3, "hp": 800},
        4401: {"rarity": 4, "item_name": "SSR_Priest", "item_type": 4, "hp": 800},
        3201: {"rarity": 3, "item_name": "SR_Warrior", "item_type": 2, "hp": 600},
        3301: {"rarity": 3, "item_name": "SR_Wizard", "item_type": 3, "hp": 500},
        3401: {"rarity": 3, "item_name": "SR_Priest", "item_type": 4, "hp": 500},
        2201: {"rarity": 2, "item_name": "R_Warrior", "item_type": 2, "hp": 400},
        2301: {"rarity": 2, "item_name": "R_Wizard", "item_type": 3, "hp": 300},
        2401: {"rarity": 2, "item_name": "R_Priest", "item_type": 4, "hp": 300},
        3199: {"rarity": 3, "item_name": "SR_Brave", "item_type": 1, "hp": 600},
        4101: {"rarity": 4, "item_name": "SSR_Brave", "item_type": 1, "hp": 1000},
        5201: {"rarity": 5, "item_name": "UR_Warrior", "item_type": 2, "hp": 1300},
        5301: {"rarity": 5, "item_name": "UR_Wizard", "item_type": 3, "hp": 1000},
    }

    return convert_values(items)

def get_gacha_items():
    items = {
        1:  {"gacha_group": "A", "weight": 3, "item_id": 5101},
        2:  {"gacha_group": "A", "weight": 9, "item_id": 4201},
        3:  {"gacha_group": "A", "weight": 9, "item_id": 4301},
        4:  {"gacha_group": "A", "weight": 9, "item_id": 4401},
        5:  {"gacha_group": "A", "weight": 20, "item_id": 3201},
        6:  {"gacha_group": "A", "weight": 20, "item_id": 3301},
        7:  {"gacha_group": "A", "weight": 20, "item_id": 3401},
        8:  {"gacha_group": "A", "weight": 40, "item_id": 2201},
        9:  {"gacha_group": "A", "weight": 40, "item_id": 2301},
        10: {"gacha_group": "A", "weight": 40, "item_id": 2401},
        11: {"gacha_group": "B", "weight": 15, "item_id": 4201},
        12: {"gacha_group": "B", "weight": 30, "item_id": 3201},
        13: {"gacha_group": "B", "weight": 55, "item_id": 2201},
        14: {"gacha_group": "C", "weight": 1, "item_id": 5101},
        15: {"gacha_group": "C", "weight": 1, "item_id": 5201},
        16: {"gacha_group": "C", "weight": 1, "item_id": 5301},
        17: {"gacha_group": "C", "weight": 9, "item_id": 4101},
        18: {"gacha_group": "C", "weight": 6, "item_id": 4201},
        19: {"gacha_group": "C", "weight": 6, "item_id": 4301},
        20: {"gacha_group": "C", "weight": 6, "item_id": 4401},
        21: {"gacha_group": "C", "weight": 20, "item_id": 3201},
        22: {"gacha_group": "C", "weight": 20, "item_id": 3301},
        23: {"gacha_group": "C", "weight": 20, "item_id": 3401},
        24: {"gacha_group": "C", "weight": 40, "item_id": 2201},
        25: {"gacha_group": "C", "weight": 40, "item_id": 2301},
        26: {"gacha_group": "C", "weight": 40, "item_id": 2401},
    }

    return convert_values(items)

def get_gacha():
    items = {
        10: {"start_time": "2020-05-01 00:00:00", "end_time": "2020-05-31 23:59:59", "gacha_group": "A",
            "gacha_type_id": "step_up_N"},
        11: {"start_time": "2020-06-01 00:00:00", "end_time": "2020-06-30 23:59:59", "gacha_group": "C",
             "gacha_type_id": "step_up_N"},
        12: {"start_time": "2020-05-01 00:00:00", "end_time": "2020-07-31 23:59:59", "gacha_group": "A",
             "gacha_type_id": "step_up_S1"},
        13: {"start_time": "2020-05-01 00:00:00", "end_time": "2020-05-31 23:59:59", "gacha_group": "A",
             "gacha_type_id": "step_up_S2"},
        14: {"start_time": "2020-06-01 00:00:00", "end_time": "2020-06-30 23:59:59", "gacha_group": "C",
             "gacha_type_id": "step_up_S2"},
    }

    return convert_values(items)

def get_gacha_type():
    items = {
        "step_up_N":  {"rotation_step_no": 0, "kind": "step_up", "reset_type": "daily"},
        "step_up_S1": {"rotation_step_no": 1, "kind": "step_up", "reset_type": "monthly"},
        "step_up_S2": {"rotation_step_no": 4, "kind": "step_up", "reset_type": None},
    }

    return convert_values(items)


def get_gacha_lottery():
    items = {
        "normal_1":  {"gacha_type_id": "normal", "step_no": 0, "item_type": 0, "times": 1, "rarity": 0, "omake_times": 0, "omake_rarity": 0, "cost":10},
        "normal_11":  {"gacha_type_id": "normal", "step_no": 0, "item_type": 0, "times": 10, "rarity": 0, "omake_times": 1, "omake_rarity": 3, "cost":100},
        "fighter":  {"gacha_type_id": "fighter", "step_no": 0, "item_type": 0, "times": 2, "rarity": 0, "omake_times": 0, "omake_rarity": 0, "cost":30},
        "omake_2":  {"gacha_type_id": "omake_2", "step_no": 0, "item_type": 0, "times": 9, "rarity": 2, "omake_times": 2, "omake_rarity": 3, "cost":150},
        "omake_fighter":  {"gacha_type_id": "omake_fighter", "step_no": 0, "item_type": 2, "times": 5, "rarity": 0, "omake_times": 1, "omake_rarity": 3, "cost":100},
        "step_up_N_1": {"gacha_type_id": "step_up_N", "step_no": 1, "item_type": 0, "times": 1, "rarity": 0, "omake_times": 0,
                     "omake_rarity": 0, "cost": 10},
        "step_up_N_2": {"gacha_type_id": "step_up_N", "step_no": 2, "item_type": 0, "times": 2, "rarity": 0, "omake_times": 1,
                      "omake_rarity": 3, "cost": 30},
        "step_up_N_3": {"gacha_type_id": "step_up_N", "step_no": 3, "item_type": 0, "times": 3, "rarity": 0, "omake_times": 2,
                      "omake_rarity": 3, "cost": 50},
        "step_up_N_4": {"gacha_type_id": "step_up_N", "step_no": 4, "item_type": 0, "times": 4, "rarity": 0, "omake_times": 1,
                      "omake_rarity": 4, "cost": 50},
        "step_up_S1_1": {"gacha_type_id": "step_up_S1", "step_no": 1, "item_type": 0, "times": 1, "rarity": 0,
                        "omake_times": 0, "omake_rarity": 0, "cost": 10},
        "step_up_S1_2": {"gacha_type_id": "step_up_S1", "step_no": 2, "item_type": 0, "times": 2, "rarity": 0,
                        "omake_times": 1, "omake_rarity": 3, "cost": 30},
        "step_up_S1_3": {"gacha_type_id": "step_up_S1", "step_no": 3, "item_type": 0, "times": 1, "rarity": 0,
                        "omake_times": 2, "omake_rarity": 3, "cost": 30},
        "step_up_S1_4": {"gacha_type_id": "step_up_S1", "step_no": 4, "item_type": 0, "times": 3, "rarity": 0,
                        "omake_times": 2, "omake_rarity": 4, "cost": 50},
        "step_up_S2_1": {"gacha_type_id": "step_up_S2", "step_no": 1, "item_type": 0, "times": 1, "rarity": 0,
                        "omake_times": 0, "omake_rarity": 0, "cost": 10},
        "step_up_S2_2": {"gacha_type_id": "step_up_S2", "step_no": 2, "item_type": 0, "times": 2, "rarity": 0,
                        "omake_times": 1, "omake_rarity": 3, "cost": 30},
        "step_up_S2_3": {"gacha_type_id": "step_up_S2", "step_no": 3, "item_type": 0, "times": 4, "rarity": 0,
                        "omake_times": 1, "omake_rarity": 5, "cost": 50},
        "step_up_S2_4": {"gacha_type_id": "step_up_S2", "step_no": 4, "item_type": 0, "times": 4, "rarity": 0,
                        "omake_times": 1, "omake_rarity": 3, "cost": 50},
    }

    return convert_values(items)

def get_users():
    items = {
        "u001": {"nick_name": "taro"},
        "u002": {"nick_name": "hana"},
    }

    return convert_values(items)


def convert_values(items: dict):
    values = []
    keys = []
    for id,info in items.items():
        if len(keys) == 0 :
            keys = list(info.keys())
            keys.insert(0,'id')

        value = list(info.values())
        value.insert(0,id)
        values.append(tuple(value))
    return keys,values

def print_rows(rows, keys: list):
    for row in rows:
        result = []
        for key in keys:
            result.append(str(row[key]))
        print(",".join(result))

def main():
    con = sqlite3.connect("data.db")
    con.row_factory = sqlite3.Row
    cursor = con.cursor()

    cursor.execute("DROP TABLE IF EXISTS gacha")

    #Refurbishment
    cursor.execute(
        """CREATE TABLE gacha 
          (id INTEGER PRIMARY KEY AUTOINCREMENT
          ,start_time DATETIME
          ,end_time DATETIME
          ,gacha_group VARCHAR(32)
          ,gacha_type_id VARCHAR(32)
          )
        """
    )

    cursor.execute("DROP TABLE IF EXISTS gacha_items")
    cursor.execute(
        """CREATE TABLE gacha_items 
          (id INTEGER PRIMARY KEY AUTOINCREMENT
          ,gacha_group VARCHAR(32)
          ,weight INTEGER
          ,item_id INTEGER
          )
        """
    )

    #add to
    cursor.execute("DROP TABLE IF EXISTS gacha_type")
    cursor.execute(
        """CREATE TABLE gacha_type 
          (id VARCHAR(32) PRIMARY KEY
          ,kind VARCHAR(32)
          ,rotation_step_no INTEGER
          ,reset_type VARCHAR(32)
          )
        """
    )

    #Refurbishment
    cursor.execute("DROP TABLE IF EXISTS gacha_lottery")
    cursor.execute(
        """CREATE TABLE gacha_lottery 
          (id VARCHAR(32) PRIMARY KEY
          ,gacha_type_id VARCHAR(32)
          ,step_no INTEGER
          ,item_type INTEGER
          ,times INTEGER
          ,rarity INTEGER
          ,omake_times INTEGER
          ,omake_rarity INTEGER
          ,cost INTEGER
          )
        """
    )

    cursor.execute("DROP TABLE IF EXISTS items")
    cursor.execute(
        """CREATE TABLE items 
          (id INTEGER PRIMARY KEY AUTOINCREMENT
          ,rarity INTEGER
          ,item_name VARCHAR(64)
          ,item_type INTEGER
          ,hp INTEGER
          )
        """
    )

    cursor.execute("DROP TABLE IF EXISTS users")
    cursor.execute(
        """CREATE TABLE users 
          (id VARCHAR(16) PRIMARY KEY
          ,nick_name VARCHAR(64)
          )
        """
    )

    #Refurbishment
    cursor.execute("DROP TABLE IF EXISTS gacha_user_step")
    cursor.execute(
        """CREATE TABLE gacha_user_step 
          (user_id VARCHAR(16) 
          ,gacha_id INTEGER
          ,gacha_type_id VARCHAR(32)
          ,step_no INTEGER DEFAULT 2
          ,updated_time DATETIME          
          ,PRIMARY KEY(user_id,gacha_id,gacha_type_id)
          )
        """
    )


    keys, values = get_items()
    sql = "insert into {0}({1}) values({2})".format('items', ','.join(keys), ','.join(['?'] * len(keys)))
    cursor.executemany(sql,values)
    select_sql = "SELECT * FROM items ORDER BY id"
    result = cursor.execute(select_sql)
    print("===items===")
    print_rows(result, keys)

    keys, values = get_gacha_items()
    sql = "insert into {0}({1}) values({2})".format('gacha_items', ','.join(keys), ','.join(['?'] * len(keys)))
    cursor.executemany(sql,values)
    select_sql = "SELECT * FROM gacha_items ORDER BY id"
    result = cursor.execute(select_sql)
    print("===gacha_items===")
    print_rows(result, keys)

    keys, values = get_gacha()
    sql = "insert into {0}({1}) values({2})".format('gacha', ','.join(keys), ','.join(['?'] * len(keys)))
    cursor.executemany(sql,values)
    select_sql = "SELECT * FROM gacha ORDER BY id"
    result = cursor.execute(select_sql)
    print("===gacha===")
    print_rows(result, keys)

    keys, values = get_gacha_type()
    sql = "insert into {0}({1}) values({2})".format('gacha_type', ','.join(keys), ','.join(['?'] * len(keys)))
    cursor.executemany(sql,values)
    select_sql = "SELECT * FROM gacha_type ORDER BY id"
    result = cursor.execute(select_sql)
    print("===gach_type===")
    print_rows(result, keys)

    keys, values = get_gacha_lottery()
    sql = "insert into {0}({1}) values({2})".format('gacha_lottery', ','.join(keys), ','.join(['?'] * len(keys)))
    cursor.executemany(sql,values)
    select_sql = "SELECT * FROM gacha_lottery ORDER BY id"
    result = cursor.execute(select_sql)
    print("===gacha_lottery===")
    print_rows(result, keys)

    keys, values = get_users()
    sql = "insert into {0}({1}) values({2})".format('users', ','.join(keys), ','.join(['?'] * len(keys)))
    cursor.executemany(sql,values)
    select_sql = "SELECT * FROM users ORDER BY id"
    result = cursor.execute(select_sql)
    print("===users===")
    print_rows(result, keys)

    con.commit()
    con.close()

if __name__ == '__main__':
    main()

Gacha processing

gacha.py


import random
import sqlite3
from datetime import datetime
from typing import Dict, List, Tuple, Optional

def convert_row2dict(row) -> Dict:
    if row is None:
        return {}
    return {key: row[key] for key in row.keys()}

def gacha(lots, times: int=1) -> List:
    return random.choices(tuple(lots), weights=lots.values(), k=times)

def get_rarity_name(rarity: int) -> str:
    rarity_names = {5: "UR", 4: "SSR", 3: "SR", 2: "R", 1: "N"}
    return rarity_names[rarity]

#Executable gacha information list
def get_gacha_list(cursor, now_time: int) -> Dict:
    select_sql = "SELECT * FROM gacha ORDER BY id"
    rows = cursor.execute(select_sql)
    results = {}
    for row in rows:
        start_time = int(datetime.strptime(row["start_time"], '%Y-%m-%d %H:%M:%S').timestamp())
        end_time = int(datetime.strptime(row["end_time"], '%Y-%m-%d %H:%M:%S').timestamp())
        #Narrow down the target gacha information within the range of the date and time
        if start_time <= now_time <= end_time:
            results[row["id"]] = convert_row2dict(row)

    return results

#Executable gacha information list (gacha_(Including lottery information)
def get_available_gacha_info_list(cursor, now_time: int, user_id:str) -> Dict:
    gacha_list = get_gacha_list(cursor, now_time)
    for gacha_id, info in gacha_list.items():
        lottery_info_list = get_gacha_lottery_by_type(cursor, info["gacha_type_id"])
        gacha_user = get_gacha_user_step(cursor, user_id, gacha_id, info["gacha_type_id"])

        set_list = []
        for lottery_info in lottery_info_list:
            if lottery_info["step_no"] > 0:
                now_step_no = 1
                if len(gacha_user) > 0:
                    now_step_no = gacha_user["step_no"] + 1
                if now_step_no == lottery_info["step_no"]:
                    set_list.append(lottery_info)
            else:
                set_list.append(lottery_info)
        gacha_list[gacha_id]["gacha_lottery_list"] = set_list
    return gacha_list

def get_gacha(cursor, gacha_id: int, now_time: int) -> Dict:
    select_sql = "SELECT * FROM gacha WHERE id = ? ORDER BY id"
    cursor.execute(select_sql, (gacha_id,))
    row = cursor.fetchone()
    start_time = int(datetime.strptime(row["start_time"], '%Y-%m-%d %H:%M:%S').timestamp())
    end_time = int(datetime.strptime(row["end_time"], '%Y-%m-%d %H:%M:%S').timestamp())
    #Narrow down the target gacha information within the range of the date and time
    if start_time <= now_time <= end_time:
        return convert_row2dict(row)

    return {}

def get_gacha_type(cursor, gacha_type_id: str) -> Dict:
    select_sql = "SELECT * FROM gacha_type WHERE id = ? ORDER BY id"
    cursor.execute(select_sql, (gacha_type_id,))
    return convert_row2dict(cursor.fetchone())

def get_gacha_lottery(cursor, gacha_lottery_id: str) -> Dict:
    select_sql = "SELECT * FROM gacha_lottery WHERE id = ? ORDER BY id"
    cursor.execute(select_sql, (gacha_lottery_id,))
    return convert_row2dict(cursor.fetchone())


def get_gacha_lottery_by_type(cursor, gacha_type_id: str) -> List:
    select_sql = "SELECT * FROM gacha_lottery WHERE gacha_type_id = ? ORDER BY id"
    rows = cursor.execute(select_sql, (gacha_type_id,))
    return [
        convert_row2dict(row)
        for row in rows
    ]

def get_items_all(cursor) -> Dict:
    select_sql = "SELECT * FROM items ORDER BY id"
    rows = cursor.execute(select_sql)
    return {
        row["id"]: convert_row2dict(row)
        for row in rows
    }

def get_gacha_items(cursor, gacha_group: str) -> Dict:
    select_sql = "SELECT * FROM gacha_items WHERE gacha_group = ? ORDER BY id"
    rows = cursor.execute(select_sql, (gacha_group,))
    return {
        row["id"]: convert_row2dict(row)
        for row in rows
    }

def get_gacha_items_all(cursor) -> Dict:
    select_sql = "SELECT * FROM gacha_items ORDER BY id"
    rows = cursor.execute(select_sql)
    return {
        row["id"]: convert_row2dict(row)
        for row in rows
    }

def get_gacha_info(cursor, gacha_id: int, gacha_lottery_id: str, now_time: int, user_id: str) -> Tuple[Optional[Dict], Optional[Dict]]:
    gacha = get_gacha(cursor, gacha_id, now_time)
    gacha_lottery = get_gacha_lottery(cursor, gacha_lottery_id)

    if len(gacha) == 0:
        print("===Gacha that cannot be executed_id:%s===" % (gacha_id))
        return None, None

    if len(gacha_lottery) == 0:
        print("===Gacha that does not exist_lottery_id:%s===" % (gacha_lottery_id))
        return None, None

    if gacha["gacha_type_id"] != gacha_lottery["gacha_type_id"]:
        print("===gacha_type_id is different:%s,%s===" % (gacha["gacha_type_id"],gacha_lottery["gacha_type_id"]))
        return None, None

    # step_up Gacha check
    if gacha_lottery["step_no"] > 0:
        # user_Reset step
        reset_gacha_user_step(cursor, user_id, gacha_id, gacha["gacha_type_id"], now_time)

        max_step_no = get_max_step_no(cursor, gacha["gacha_type_id"])
        gacha_user = get_gacha_user_step(cursor, user_id, gacha_id, gacha["gacha_type_id"])
        step_no = 1
        if len(gacha_user) > 0:
            step_no = gacha_user["step_no"]
        if max_step_no < step_no:
            print("===Up to the maximum step has been executed (maximum step):%s, the step you are going to perform:%s)===" %(max_step_no,gacha_lottery["step_no"]))
            return None, None

        if gacha_lottery["step_no"] != step_no:
            print("===Different from the steps that can be performed (current step:%s, the step you are going to perform:%s)===" %(step_no, gacha_lottery["step_no"]))
            return None, None

    return gacha, gacha_lottery


def get_gacha_user_step(cursor, user_id: str, gacha_id: int, gacha_type_id: str) -> Dict:
    select_sql = "SELECT * FROM gacha_user_step WHERE user_id = ? AND gacha_id = ? AND gacha_type_id = ?"
    cursor.execute(select_sql, (user_id,gacha_id,gacha_type_id))
    row = cursor.fetchone()

    return convert_row2dict(row)

def get_max_step_no(cursor, gacha_type_id: str) -> Dict:
    select_sql = "SELECT MAX(step_no) AS max_step_no FROM gacha_lottery WHERE gacha_type_id = ?"
    cursor.execute(select_sql, (gacha_type_id,))
    row = cursor.fetchone()

    row_dict = convert_row2dict(row)
    return row_dict["max_step_no"]

def insert_gacha_user_step(cursor, step_no: int, user_id: str, gacha_id: int, gacha_type_id: str, update_time: int) -> None:
    dt = datetime.fromtimestamp(update_time)
    keys = ("user_id", "gacha_id", "gacha_type_id", "updated_time", "step_no")
    sql = "insert into {0}({1}) values({2})".format('gacha_user_step', ','.join(keys), ','.join(['?'] * len(keys)))
    cursor.execute(sql, (user_id, gacha_id, gacha_type_id, dt, step_no))

def update_gacha_user_step(cursor, step_no: int, user_id: str, gacha_id: int, gacha_type_id: str, update_time: int) -> None:
    dt = datetime.fromtimestamp(update_time)
    sql = "UPDATE gacha_user_step SET step_no = ?, updated_time = ? WHERE user_id = ? AND gacha_id = ? AND gacha_type_id = ?"
    cursor.execute(sql, (step_no, dt, user_id, gacha_id, gacha_type_id))

def next_gacha_user_step(cursor, user_id: str, gacha_id: int, gacha_type_id: str, update_time: int) -> None:
    row = get_gacha_user_step(cursor, user_id, gacha_id, gacha_type_id)
    if len(row) == 0:
        insert_gacha_user_step(
            cursor,
            rotation_gacha_user_step(cursor, gacha_type_id, 1),
            user_id, gacha_id, gacha_type_id, update_time
        )
    else:
        update_gacha_user_step(
            cursor,
            rotation_gacha_user_step(cursor, gacha_type_id, row["step_no"]),
            user_id, gacha_id, gacha_type_id, update_time
        )

#Next step number with repeat judgment added
def rotation_gacha_user_step(cursor, gacha_type_id: str, step_no: int) -> int:
    if step_no == get_max_step_no(cursor, gacha_type_id):
        row = get_gacha_type(cursor, gacha_type_id)
        if row["rotation_step_no"] > 0:
            print("check:{0},{1},{2}".format(gacha_type_id, step_no,row["rotation_step_no"]))
            return row["rotation_step_no"]
        else:
            print("check:{0},{1},{2}".format(gacha_type_id, step_no,step_no + 1))
            return step_no + 1
    else:
        print("check:{0},{1},{2}".format(gacha_type_id, step_no, step_no + 1))
        return step_no + 1

#Reset process
def reset_gacha_user_step(cursor, user_id: str, gacha_id: int, gacha_type_id: str, now_time: int) -> None:
    user_step = get_gacha_user_step(cursor, user_id, gacha_id, gacha_type_id)
    if len(user_step) == 0:
        return

    dt = datetime.fromtimestamp(now_time)
    gacha_type = get_gacha_type(cursor, gacha_type_id)
    if gacha_type["reset_type"] == "daily":
        if dt.day != datetime.strptime(user_step["updated_time"], '%Y-%m-%d %H:%M:%S').day:
            update_gacha_user_step(cursor,1,user_id, gacha_id, gacha_type_id, now_time)
    elif gacha_type["reset_type"] == "monthly":
        if dt.month != datetime.strptime(user_step["updated_time"], '%Y-%m-%d %H:%M:%S').month:
            update_gacha_user_step(cursor,1,user_id, gacha_id, gacha_type_id, now_time)

def set_gacha(cursor, now_time: int, user_id:str):
    cursor = cursor
    now_time = now_time
    user_id = user_id
    items = get_items_all(cursor)

    #Extract the lottery target list
    def get_lots(gacha_group: str, lottery_info: dict) -> Tuple[Dict,Dict]:
        gacha_items = get_gacha_items(cursor, gacha_group)
        dic_gacha_items = {}
        for gacha_item_id, gacha_item in gacha_items.items():
            gacha_item["item_info"] = items[gacha_item["item_id"]]
            dic_gacha_items[gacha_item_id] = gacha_item

        lots = {}
        omake_lots = {}
        for id, info in dic_gacha_items.items():
            if lottery_info["item_type"] and lottery_info["item_type"] != info["item_info"]["item_type"]:
                continue

            if not(lottery_info["rarity"]) or lottery_info["rarity"] <= info["item_info"]["rarity"]:
                lots[id] = info["weight"]

            if lottery_info["omake_times"]:
                if not(lottery_info["omake_rarity"]) or lottery_info["omake_rarity"] <= info["item_info"]["rarity"]:
                    omake_lots[id] = info["weight"]

        return lots, omake_lots

    #Gacha execution
    def exec(exec_gacha_id: int, exec_gacha_lottery_id: str) -> List:
        gacha_info, gacha_lottery_info = get_gacha_info(cursor, exec_gacha_id, exec_gacha_lottery_id, now_time, user_id)
        if gacha_info is None or gacha_lottery_info is None:
            return []

        print("==%s==:gacha_group:%s" % (gacha_lottery_info["id"], gacha_info["gacha_group"]))
        lots, omake_lots = get_lots(gacha_info["gacha_group"], gacha_lottery_info)
        ids = gacha(lots, gacha_lottery_info["times"])
        if len(omake_lots) > 0:
            ids.extend(gacha(omake_lots, gacha_lottery_info["omake_times"]))

        #In the case of step-up gacha, update the number of executions
        if len(ids) > 0 and gacha_lottery_info["step_no"] > 0:
            next_gacha_user_step(cursor, user_id, exec_gacha_id, gacha_info["gacha_type_id"], now_time)

        return ids

    return exec

def test_step(cursor, user_id, now_time, gacha_id, gacha_type_id, step_no_list):
    print("[TEST:{0}]".format(gacha_type_id))

    #Feasible gacha_lottery
    available_list = get_available_gacha_info_list(cursor,now_time,user_id)
    for available_gacha_id,available in available_list.items():
        print(available_gacha_id,available)

    func_gacha = set_gacha(cursor, now_time, user_id)
    items = get_items_all(cursor)
    gacha_items = get_gacha_items_all(cursor)

    #Step-up gacha
    for step_no in step_no_list:
        gacha_lottery_id = "{0}_{1}".format(gacha_type_id,str(step_no))
        ids = func_gacha(gacha_id,gacha_lottery_id)
        if len(ids) > 0:
            for id in ids:
                item_info = items[gacha_items[id]["item_id"]]
                print("ID:%d, %s, %s" % (id, get_rarity_name(item_info["rarity"]), item_info["item_name"]))

def main():
    con = sqlite3.connect("data.db")
    con.row_factory = sqlite3.Row
    cursor = con.cursor()

    #Actually, the user ID will be obtained from the authentication information.
    user_id = "u001"

    #Specify the gacha execution date and time to check the operation
    now_time = int(datetime.strptime("2020-05-01 00:00:00", '%Y-%m-%d %H:%M:%S').timestamp())

    # step_up_N test
    test_step(cursor, user_id, now_time, 10, "step_up_N", [1,2,3,4,1,])

    print("\n [Confirm that it can be reset and executed by advancing the date]")
    now_time = int(datetime.strptime("2020-05-02 00:00:00", '%Y-%m-%d %H:%M:%S').timestamp())
    test_step(cursor, user_id, now_time, 10, "step_up_N", [1,2,3,4,])

    # step_up_S1 test
    print("\n [Confirm to return to step 1]")
    test_step(cursor, user_id, now_time, 12, "step_up_S1", [1,2,3,4,1,2,])
    print("\n [Confirmation that the date will be advanced and reset: The previous month was completed up to step2]")
    now_time = int(datetime.strptime("2020-06-01 00:00:00", '%Y-%m-%d %H:%M:%S').timestamp())
    test_step(cursor, user_id, now_time, 12, "step_up_S1", [1,2,3,4,])

    # step_up_S2 test
    print("\n [Confirm that it can be executed repeatedly in step 4]")
    test_step(cursor, user_id, now_time, 14, "step_up_S2", [1,2,3,4,4,])

    con.commit()
    con.close()

if __name__ == '__main__':
    main()

Execution result

[TEST:step_up_N]
10 {'id': 10, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-05-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_N', 'gacha_lottery_list': []}
12 {'id': 12, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-07-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_S1', 'gacha_lottery_list': [{'id': 'step_up_S1_2', 'gacha_type_id': 'step_up_S1', 'step_no': 2, 'item_type': 0, 'times': 1, 'rarity': 0, 'omake_times': 1, 'omake_rarity': 3, 'cost': 20}]}
13 {'id': 13, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-05-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_S2', 'gacha_lottery_list': [{'id': 'step_up_S2_1', 'gacha_type_id': 'step_up_S2', 'step_no': 1, 'item_type': 0, 'times': 1, 'rarity': 0, 'omake_times': 0, 'omake_rarity': 0, 'cost': 10}]}
==step_up_N_1==:gacha_group:A
check:step_up_N,1,2
ID:9, R, R_Wizard
==step_up_N_2==:gacha_group:A
check:step_up_N,2,3
ID:10, R, R_Priest
ID:9, R, R_Wizard
ID:4, SSR, SSR_Priest
==step_up_N_3==:gacha_group:A
check:step_up_N,3,4
ID:9, R, R_Wizard
ID:10, R, R_Priest
ID:9, R, R_Wizard
ID:7, SR, SR_Priest
ID:5, SR, SR_Warrior
==step_up_N_4==:gacha_group:A
check:step_up_N,4,5
ID:9, R, R_Wizard
ID:9, R, R_Wizard
ID:10, R, R_Priest
ID:5, SR, SR_Warrior
ID:1, UR, UR_Brave
===Up to the maximum step has been executed (maximum step):4, the step you are going to perform:1)===

[Confirmation that it will be reset and executable by advancing the date]
[TEST:step_up_N]
10 {'id': 10, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-05-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_N', 'gacha_lottery_list': []}
12 {'id': 12, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-07-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_S1', 'gacha_lottery_list': [{'id': 'step_up_S1_2', 'gacha_type_id': 'step_up_S1', 'step_no': 2, 'item_type': 0, 'times': 1, 'rarity': 0, 'omake_times': 1, 'omake_rarity': 3, 'cost': 20}]}
13 {'id': 13, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-05-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_S2', 'gacha_lottery_list': [{'id': 'step_up_S2_1', 'gacha_type_id': 'step_up_S2', 'step_no': 1, 'item_type': 0, 'times': 1, 'rarity': 0, 'omake_times': 0, 'omake_rarity': 0, 'cost': 10}]}
==step_up_N_1==:gacha_group:A
check:step_up_N,1,2
ID:7, SR, SR_Priest
==step_up_N_2==:gacha_group:A
check:step_up_N,2,3
ID:10, R, R_Priest
ID:5, SR, SR_Warrior
ID:6, SR, SR_Wizard
==step_up_N_3==:gacha_group:A
check:step_up_N,3,4
ID:10, R, R_Priest
ID:8, R, R_Warrior
ID:5, SR, SR_Warrior
ID:4, SSR, SSR_Priest
ID:7, SR, SR_Priest
==step_up_N_4==:gacha_group:A
check:step_up_N,4,5
ID:9, R, R_Wizard
ID:8, R, R_Warrior
ID:7, SR, SR_Priest
ID:5, SR, SR_Warrior
ID:3, SSR, SSR_Wizard

[Confirm to return to step 1]
[TEST:step_up_S1]
10 {'id': 10, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-05-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_N', 'gacha_lottery_list': []}
12 {'id': 12, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-07-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_S1', 'gacha_lottery_list': [{'id': 'step_up_S1_2', 'gacha_type_id': 'step_up_S1', 'step_no': 2, 'item_type': 0, 'times': 1, 'rarity': 0, 'omake_times': 1, 'omake_rarity': 3, 'cost': 20}]}
13 {'id': 13, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-05-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_S2', 'gacha_lottery_list': [{'id': 'step_up_S2_1', 'gacha_type_id': 'step_up_S2', 'step_no': 1, 'item_type': 0, 'times': 1, 'rarity': 0, 'omake_times': 0, 'omake_rarity': 0, 'cost': 10}]}
==step_up_S1_1==:gacha_group:A
check:step_up_S1,1,2
ID:10, R, R_Priest
==step_up_S1_2==:gacha_group:A
check:step_up_S1,2,3
ID:5, SR, SR_Warrior
ID:7, SR, SR_Priest
==step_up_S1_3==:gacha_group:A
check:step_up_S1,3,4
ID:7, SR, SR_Priest
ID:7, SR, SR_Priest
ID:7, SR, SR_Priest
==step_up_S1_4==:gacha_group:A
check:step_up_S1,4,1
ID:2, SSR, SSR_Warrior
ID:10, R, R_Priest
ID:7, SR, SR_Priest
ID:3, SSR, SSR_Wizard
ID:1, UR, UR_Brave
==step_up_S1_1==:gacha_group:A
check:step_up_S1,1,2
ID:9, R, R_Wizard
==step_up_S1_2==:gacha_group:A
check:step_up_S1,2,3
ID:10, R, R_Priest
ID:6, SR, SR_Wizard

[Confirmation that the date will be advanced and reset: The previous month was completed up to step 2]
[TEST:step_up_S1]
11 {'id': 11, 'start_time': '2020-06-01 00:00:00', 'end_time': '2020-06-30 23:59:59', 'gacha_group': 'C', 'gacha_type_id': 'step_up_N', 'gacha_lottery_list': [{'id': 'step_up_N_1', 'gacha_type_id': 'step_up_N', 'step_no': 1, 'item_type': 0, 'times': 1, 'rarity': 0, 'omake_times': 0, 'omake_rarity': 0, 'cost': 10}]}
12 {'id': 12, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-07-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_S1', 'gacha_lottery_list': [{'id': 'step_up_S1_4', 'gacha_type_id': 'step_up_S1', 'step_no': 4, 'item_type': 0, 'times': 3, 'rarity': 0, 'omake_times': 2, 'omake_rarity': 4, 'cost': 50}]}
14 {'id': 14, 'start_time': '2020-06-01 00:00:00', 'end_time': '2020-06-30 23:59:59', 'gacha_group': 'C', 'gacha_type_id': 'step_up_S2', 'gacha_lottery_list': []}
==step_up_S1_1==:gacha_group:A
check:step_up_S1,1,2
ID:10, R, R_Priest
==step_up_S1_2==:gacha_group:A
check:step_up_S1,2,3
ID:5, SR, SR_Warrior
ID:4, SSR, SSR_Priest
==step_up_S1_3==:gacha_group:A
check:step_up_S1,3,4
ID:8, R, R_Warrior
ID:5, SR, SR_Warrior
ID:6, SR, SR_Wizard
==step_up_S1_4==:gacha_group:A
check:step_up_S1,4,1
ID:6, SR, SR_Wizard
ID:5, SR, SR_Warrior
ID:7, SR, SR_Priest
ID:2, SSR, SSR_Warrior
ID:3, SSR, SSR_Wizard

[Confirm that it can be executed repeatedly in step 4]
[TEST:step_up_S2]
11 {'id': 11, 'start_time': '2020-06-01 00:00:00', 'end_time': '2020-06-30 23:59:59', 'gacha_group': 'C', 'gacha_type_id': 'step_up_N', 'gacha_lottery_list': [{'id': 'step_up_N_1', 'gacha_type_id': 'step_up_N', 'step_no': 1, 'item_type': 0, 'times': 1, 'rarity': 0, 'omake_times': 0, 'omake_rarity': 0, 'cost': 10}]}
12 {'id': 12, 'start_time': '2020-05-01 00:00:00', 'end_time': '2020-07-31 23:59:59', 'gacha_group': 'A', 'gacha_type_id': 'step_up_S1', 'gacha_lottery_list': [{'id': 'step_up_S1_2', 'gacha_type_id': 'step_up_S1', 'step_no': 2, 'item_type': 0, 'times': 1, 'rarity': 0, 'omake_times': 1, 'omake_rarity': 3, 'cost': 20}]}
14 {'id': 14, 'start_time': '2020-06-01 00:00:00', 'end_time': '2020-06-30 23:59:59', 'gacha_group': 'C', 'gacha_type_id': 'step_up_S2', 'gacha_lottery_list': []}
===Different from the steps that can be performed (current step:4, the step you are going to perform:1)===
===Different from the steps that can be performed (current step:4, the step you are going to perform:2)===
===Different from the steps that can be performed (current step:4, the step you are going to perform:3)===
==step_up_S2_4==:gacha_group:C
check:step_up_S2,4,4
ID:24, R, R_Warrior
ID:25, R, R_Wizard
ID:23, SR, SR_Priest
ID:25, R, R_Wizard
ID:23, SR, SR_Priest
==step_up_S2_4==:gacha_group:C
check:step_up_S2,4,4
ID:26, R, R_Priest
ID:25, R, R_Wizard
ID:15, UR, UR_Warrior
ID:22, SR, SR_Wizard
ID:20, SSR, SSR_Priest

Consideration

step_up_N (no repetition, reset daily)

Since there is no repeat setting, you can confirm that it has finished after executing the 4th time. Then, when the date changes, you can confirm that it is possible to execute from step 1 again.

step_up_S1 (repeated, reset monthly)

Since there is a repeat setting, you can return to Step 1 after executing the 4th time and confirm that it can be executed. Then, when the month changes, you will be returned to step 1 where you can see that it is feasible.

step_up_S2 (repeated, no reset)

Since there is a repeat setting, you can confirm that Step 4 can be executed repeatedly after executing the 4th time.

Advantages of specifications

Added Repeat and Reset control processing to the logic of Last time. It's a little harder, but it hasn't been refurbished so much. (* It took more time to put together the test code ... *)

With the addition of this function, it is possible to control the progress of gacha by master setting. It is possible to create various patterns of gacha by combining the settings of the master. In operation, it is necessary to repeat hypothesis and verification of an effective approach to users. Efficient operation can be realized by setting the master so that the planner can do it.

Recommended Posts

Gacha written in python-Practice 3 ・ Addition of step-up gacha functions-
Gacha written in python-Practice 2 ・ Basics of step-up gacha-
Gacha written in python-Practice 1-
Gacha written in python-Addition of period setting function-
Gacha written in Python-Data design-
Gacha written in Python -BOX gacha-
Simple gacha logic written in Python
Gacha written in python-Rarity confirmed with bonus-
Gacha written in python-Implementation in basic data structure-
Utilization of recursive functions used in competition pros
Summary of evaluation functions used in machine learning