So far, we have only considered gacha processing, but in actual games
--Display the screen to execute the gacha --User select and execute gacha
Consider the data design and processing that can be handled in consideration of the above. Also, from this time, DB will be used to store data related to gacha.
** The following sources have been modified and implemented **
Gacha written in python-Addition of period setting function-
id | start_time | end_time | gacha_group | gacha_lottery_id |
---|---|---|---|---|
1 | 2020-05-01 00:00:00 | 2020-05-31 23:59:59 | A | normal_1 |
2 | 2020-05-01 00:00:00 | 2020-05-31 23:59:59 | A | normal_11 |
6 | 2020-06-01 00:00:00 | 2020-06-30 23:59:59 | C | normal_1 |
7 | 2020-06-01 00:00:00 | 2020-06-30 23:59:59 | C | normal_11 |
The role of ** Gacha Information ** is to set a group of target gacha items
in the implementation period
and link it to the how to draw the gacha
. The only difference between the IDs ** 1 ** and ** 2 ** (or ** 6 ** and ** 7 **) is that the how to draw a gacha
is one-shot (once) or 11 consecutive. is.
Setting redundant information increases time and effort and leads to setting mistakes during operation. If possible, I would like to avoid setting the same information more than once, so I will modify the data structure.
From the ** gacha ** information, add an item called gacha_type
instead of gacha_lottery_id.
Add gacha_type
to ** gacha_lottery ** to link gacha information and gacha method definition information.
gacha
id | start_time | end_time | gacha_group | gacha_type |
---|---|---|---|---|
1 | 2020-05-01 00:00:00 | 2020-05-31 23:59:59 | A | normal |
6 | 2020-06-01 00:00:00 | 2020-06-30 23:59:59 | C | normal |
gacha_lottery
id | gacha_type | item_type | times | rarity | omake_times | omake_rarity | cost |
---|---|---|---|---|---|---|---|
normal_1 | normal | 0 | 1 | 0 | 0 | 0 | 10 |
normal_11 | normal | 0 | 10 | 0 | 1 | 3 | 100 |
Imagine that the following is displayed on the actual in-game gacha screen.
Since various information will be acquired from the DB from this implementation, first create a table and set the data.
gacha_db
# -*- 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},
#Added below
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},
#Added below
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 = {
1: {"start_time": "2020-05-01 00:00:00", "end_time": "2020-05-31 23:59:59", "gacha_group": "A",
"gacha_type": "normal"},
3: {"start_time": "2020-05-25 00:00:00", "end_time": "2020-05-31 23:59:59", "gacha_group": "B",
"gacha_type": "fighter"},
4: {"start_time": "2020-06-01 00:00:00", "end_time": "2020-06-04 23:59:59", "gacha_group": "C",
"gacha_type": "omake_2"},
5: {"start_time": "2020-05-20 00:00:00", "end_time": "2020-05-31 23:59:59", "gacha_group": "A",
"gacha_type": "omake_fighter"},
6: {"start_time": "2020-06-01 00:00:00", "end_time": "2020-06-30 23:59:59", "gacha_group": "C",
"gacha_type": "normal"},
}
return convert_values(items)
def get_gacha_lottery():
items = {
"normal_1": {"gacha_type": "normal", "item_type": 0, "times": 1, "rarity": 0, "omake_times": 0, "omake_rarity": 0, "cost":10},
"normal_11": {"gacha_type": "normal", "item_type": 0, "times": 10, "rarity": 0, "omake_times": 1, "omake_rarity": 3, "cost":100},
"fighter": {"gacha_type": "fighter", "item_type": 0, "times": 2, "rarity": 0, "omake_times": 0, "omake_rarity": 0, "cost":30},
"omake_2": {"gacha_type": "omake_2", "item_type": 0, "times": 9, "rarity": 2, "omake_times": 2, "omake_rarity": 3, "cost":150},
"omake_fighter": {"gacha_type": "omake_fighter", "item_type": 2, "times": 5, "rarity": 0, "omake_times": 1, "omake_rarity": 3, "cost":100}
}
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()
# gacha
cursor.execute("DROP TABLE IF EXISTS gacha")
#id
#start_time
#end_time
#gacha_group
#gacha_lottery_id
cursor.execute(
"""CREATE TABLE gacha
(id INTEGER PRIMARY KEY AUTOINCREMENT
,start_time DATETIME
,end_time DATETIME
,gacha_group VARCHAR(32)
,gacha_type VARCHAR(32)
)
"""
)
# 1: {"gacha_group": "A", "weight": 3, "item_id": 5101},
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
)
"""
)
# "normal_1": {"item_type": 0, "times": 1, "rarity": 0, "omake_times": 0, "omake_rarity": 0, "cost":10},
cursor.execute("DROP TABLE IF EXISTS gacha_lottery")
cursor.execute(
"""CREATE TABLE gacha_lottery
(id VARCHAR(32) PRIMARY KEY
,gacha_type VARCHAR(32)
,item_type INTEGER
,times INTEGER
,rarity INTEGER
,omake_times INTEGER
,omake_rarity INTEGER
,cost INTEGER
)
"""
)
# 5101: {"rarity": 5, "item_name": "UR_Brave", "item_type": 1, "hp": 1200},
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
)
"""
)
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_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)
con.commit()
con.close()
if __name__ == '__main__':
main()
gacha.py
import random
import sqlite3
from datetime import datetime
def convert_row2dict(row) -> dict:
keys = row.keys()
return {key: row[key] for key in 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) -> 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"])
gacha_list[gacha_id]["gacha_lottery_list"] = lottery_info_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_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,))
row = cursor.fetchone()
return convert_row2dict(row)
def get_gacha_lottery_by_type(cursor, gacha_type: str) -> list:
select_sql = "SELECT * FROM gacha_lottery WHERE gacha_type = ? ORDER BY id"
rows = cursor.execute(select_sql, (gacha_type,))
results = []
for row in rows:
row_dict = convert_row2dict(row)
results.append(row_dict)
return results
def get_items_all(cursor) -> dict:
select_sql = "SELECT * FROM items ORDER BY id"
rows = cursor.execute(select_sql)
results = {}
for row in rows:
row_dict = convert_row2dict(row)
results[row["id"]] = row_dict
return results
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,))
results = {}
for row in rows:
row_dict = convert_row2dict(row)
results[row["id"]] = row_dict
return results
def get_gacha_items_all(cursor) -> dict:
select_sql = "SELECT * FROM gacha_items ORDER BY id"
rows = cursor.execute(select_sql)
results = {}
for row in rows:
row_dict = convert_row2dict(row)
results[row["id"]] = row_dict
return results
def get_gacha_info(cursor, gacha_id: int, gacha_lottery_id: str, now_time: int):
gacha = get_gacha(cursor, gacha_id, now_time)
gacha_lottery = get_gacha_lottery(cursor, gacha_lottery_id)
if gacha["gacha_type"] != gacha_lottery["gacha_type"]:
return None, None
return gacha, gacha_lottery
def set_gacha(cursor, now_time: int):
cursor = cursor
now_time = now_time
items = get_items_all(cursor)
#Extract the lottery target list
def get_lots(gacha_group: str, lottery_info: 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)
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"]))
return ids
return exec
def main():
con = sqlite3.connect("data.db")
con.row_factory = sqlite3.Row
cursor = con.cursor()
#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())
#Initialization (set execution date and time, items, etc.)
func_gacha = set_gacha(cursor, now_time)
items = get_items_all(cursor)
gacha_items = get_gacha_items_all(cursor)
#Execute one-shot gacha
# gacha_id and gacha_lottery_id passed at runtime
ids = func_gacha(1,"normal_1")
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"]))
#Execute 11 consecutive gachas
# gacha_id and gacha_lottery_id passed at runtime
ids = func_gacha(1,"normal_11")
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"]))
#con.commit()
con.close()
if __name__ == '__main__':
main()
==normal_1==:gacha_group:A
ID:8, R, R_Warrior
==normal_11==:gacha_group:A
ID:5, SR, SR_Warrior
ID:8, R, R_Warrior
ID:3, SSR, SSR_Wizard
ID:10, R, R_Priest
ID:9, R, R_Wizard
ID:9, R, R_Wizard
ID:8, R, R_Warrior
ID:1, UR, UR_Brave
ID:10, R, R_Priest
ID:10, R, R_Priest
ID:7, SR, SR_Priest
You may be wondering if you are passing ** gacha_id ** and ** gacha_lottery_id ** as arguments when you run the gacha. All you need to execute a gacha is ** gacha_lottery_id **, and if you get the ** gacha_id ** that corresponds to the date and time when the process is performed, there is no problem with the gacha process.
However, if the gacha is executed at the switching timing
of the gacha execution period, the user may execute the gacha of the target item different from the target item that he / she wants to draw.
In order to avoid such a thing, ** gacha_id ** is also passed as an argument when executing the gacha, and if that ** gacha_id ** is not covered by the implementation period, the gacha has ended and the user is informed. Notify and display the gacha screen again. Since gacha is a process that assumes that billing is involved, it is necessary to prevent processing that is strictly different from the user's intention.
In this source, error handling is omitted a little, so please consider and implement the error handling required for the application.
Recommended Posts