kopia lustrzana https://github.com/torrinworx/Blend_My_NFTs
379 wiersze
15 KiB
Python
379 wiersze
15 KiB
Python
# Purpose:
|
|
# The purpose of this file is to add logic and rules to the DNA that are sent to the NFTRecord.json file in
|
|
# dna_generator.py
|
|
|
|
import random
|
|
import logging
|
|
import traceback
|
|
import collections
|
|
|
|
from .helpers import TextColors
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def reconstruct_dna(deconstructed_dna):
|
|
reconstructed_dna = ""
|
|
for a in deconstructed_dna:
|
|
num = "-" + str(a)
|
|
reconstructed_dna += num
|
|
return ''.join(reconstructed_dna.split('-', 1))
|
|
|
|
|
|
def get_var_info(variant, hierarchy):
|
|
# Get info for variant dict
|
|
name = variant.split("_")[0]
|
|
order_number = variant.split("_")[1]
|
|
rarity_number = variant.split("_")[2]
|
|
attribute = ""
|
|
|
|
for a in hierarchy:
|
|
for var in list(hierarchy[a].keys()):
|
|
if var == variant:
|
|
attribute = a
|
|
break
|
|
attribute_index = list(hierarchy.keys()).index(attribute)
|
|
|
|
return [name, order_number, rarity_number, attribute, attribute_index] # list of Var info sent back
|
|
|
|
|
|
def logic_rarity(variant_list, enable_rarity, a):
|
|
number_list_of_i = []
|
|
rarity_list_of_i = []
|
|
if_zero_bool = None
|
|
variant_num = None
|
|
|
|
for b in variant_list:
|
|
number = b.split("_")[1]
|
|
rarity = b.split("_")[2]
|
|
|
|
number_list_of_i.append(int(number))
|
|
rarity_list_of_i.append(float(rarity))
|
|
|
|
for b in rarity_list_of_i:
|
|
if b == 0:
|
|
if_zero_bool = True
|
|
elif b != 0:
|
|
if_zero_bool = False
|
|
|
|
if enable_rarity:
|
|
try:
|
|
if if_zero_bool:
|
|
variant_num = random.choices(number_list_of_i, k=1)
|
|
elif not if_zero_bool:
|
|
variant_num = random.choices(number_list_of_i, weights=rarity_list_of_i, k=1)
|
|
except IndexError:
|
|
log.error(
|
|
f"\n{traceback.format_exc()}"
|
|
f"\n{TextColors.ERROR}Blend_My_NFTs Error:\n"
|
|
f"An issue was found within the Attribute collection '{a}'. For more information on "
|
|
f"Blend_My_NFTs compatible scenes, see:\n{TextColors.RESET}"
|
|
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
|
|
)
|
|
raise IndexError()
|
|
else:
|
|
try:
|
|
variant_num = random.choices(number_list_of_i, k=1)
|
|
except IndexError:
|
|
log.error(
|
|
f"\n{traceback.format_exc()}"
|
|
f"\n{TextColors.ERROR}Blend_My_NFTs Error:\n"
|
|
f"An issue was found within the Attribute collection '{a}'. For more information on "
|
|
f"Blend_My_NFTs compatible scenes, see:\n{TextColors.RESET}"
|
|
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
|
|
)
|
|
raise IndexError()
|
|
return str(variant_num[0])
|
|
|
|
|
|
def apply_rule_to_dna(hierarchy, deconstructed_dna, if_dict, result_dict, result_dict_type, enable_rarity):
|
|
"""
|
|
Applies a single given rule to the DNA. This function does not apply multiple rules at the same time.
|
|
|
|
Then returns the new deconstructed_dna with the single rule applied to it.
|
|
"""
|
|
|
|
# Check if Variants in if_dict are in deconstructed_dna, if so return if_list_selected = True:
|
|
if_list_selected = False # True if item in the if_dict (items in the IF dictionary of a rule) are selected
|
|
for attribute_index, a in enumerate(deconstructed_dna):
|
|
attribute = list(hierarchy.keys())[attribute_index]
|
|
|
|
for b in hierarchy[attribute]:
|
|
if hierarchy[attribute][b]["number"] == a and attribute in if_dict:
|
|
a_dna_var = b
|
|
|
|
if a_dna_var in list(if_dict[attribute].keys()):
|
|
if_list_selected = True
|
|
break
|
|
|
|
continue
|
|
|
|
# If Variants in if_dict are selected, select random or rarity variants or set the to Empty depending on the rule.
|
|
if result_dict_type == "NOT":
|
|
for a in result_dict: # For each Attribute in the NOT rule dictionary
|
|
attribute_index = list(hierarchy.keys()).index(a)
|
|
attribute = list(hierarchy.keys())[attribute_index]
|
|
full_att = list(result_dict[attribute].keys()) == list(
|
|
hierarchy[attribute].keys()) # True if full attribute in rules
|
|
|
|
if if_list_selected:
|
|
# If 'a' is a full Attribute and Variants in if_dict selected, set 'a' to empty (0):
|
|
if full_att:
|
|
deconstructed_dna[attribute_index] = "0"
|
|
|
|
# Not a full Attribute, invert the variants selected so that we get a list
|
|
# of just variants that can be selected. Because the NOT variants are what we don't want selected:
|
|
if not full_att:
|
|
var_selected_list = list(result_dict[a].keys()) # list of variants from 'NOT'
|
|
att_selected_list = list(hierarchy[a].keys()) # full list of variants from hierarchy attribute
|
|
|
|
# Invert Variants set in NOT rule:
|
|
var_selected_list = [i for i in att_selected_list if i not in var_selected_list]
|
|
|
|
var_selected_list_complete = {}
|
|
for i in var_selected_list:
|
|
var_selected_list_complete[i] = get_var_info(i, hierarchy)
|
|
result_dict[a] = var_selected_list_complete
|
|
|
|
# Select random or rarity from new result_dict with inverted variants if attribute is not full
|
|
variant_list = list(result_dict[a].keys())
|
|
deconstructed_dna[int(attribute_index)] = logic_rarity(variant_list, enable_rarity, a)
|
|
|
|
else: # if result_dict_type == "THEN" basically
|
|
for a in result_dict:
|
|
attribute_index = list(hierarchy.keys()).index(a)
|
|
attribute = list(hierarchy.keys())[attribute_index]
|
|
full_att = list(result_dict[attribute].keys()) == list(
|
|
hierarchy[attribute].keys()) # True if full attribute in rules
|
|
|
|
variant_list = list(result_dict[a].keys())
|
|
|
|
# If 'a' is a full Attribute and Variants in if_dict are not selected, set 'a' to empty (0):
|
|
# This makes it so Attributes specified in THEN rules will only appear when a given Variant in if_dict is selected.
|
|
# if full_att and not if_list_selected:
|
|
# deconstructed_dna[attribute_index] = "0"
|
|
|
|
# If Variants in if_dict selected, regardless if they make a full variant, select items from result_dict
|
|
if if_list_selected:
|
|
deconstructed_dna[int(attribute_index)] = logic_rarity(variant_list, enable_rarity, a)
|
|
|
|
return deconstructed_dna
|
|
|
|
|
|
def get_rule_break_type(hierarchy, deconstructed_dna, if_dict, result_dict, result_dict_type):
|
|
"""
|
|
Checks if a given rule has been broken by deconstructed_dna.
|
|
"""
|
|
# Check if Variants in 'if_dict' found in deconstructed_dna:
|
|
if_bool = False # True if Variant in 'deconstructed_dna' found in 'if_dict'
|
|
for a in if_dict: # Attribute in 'if_dict'
|
|
for b in if_dict[a]: # Variant in if_dict[Attribute]
|
|
var_order_num = str(if_dict[a][b][1]) # Order number of 'b' (Variant)
|
|
dna_order_num = str(
|
|
deconstructed_dna[if_dict[a][b][4]]) # Order Number of 'b's attribute in deconstructed_dna
|
|
|
|
if var_order_num == dna_order_num: # If DNA selected Variants found inside IF list variants:
|
|
if_bool = True
|
|
break
|
|
else:
|
|
continue
|
|
break
|
|
|
|
# Check if Variants in 'result_dict' found in deconstructed_dna:
|
|
full_att_bool = False
|
|
result_bool = False # True if Variant in 'deconstructed_dna' found in 'result_dict'
|
|
then_bool = False
|
|
for a in result_dict: # Attribute in 'result_dict'
|
|
for b in result_dict[a]: # Variant in if_dict[Attribute]
|
|
var_order_num = str(result_dict[a][b][1]) # Order number of 'b' (Variant)
|
|
dna_order_num = str(
|
|
deconstructed_dna[result_dict[a][b][4]]) # Order Number of 'b's attribute in deconstructed_dna
|
|
if var_order_num == dna_order_num: # If DNA selected Variants found inside THEN list variants:
|
|
if list(result_dict[a].keys()) == list(hierarchy[a].keys()):
|
|
full_att_bool = True
|
|
result_bool = True
|
|
# break
|
|
else:
|
|
then_bool = True
|
|
else:
|
|
continue
|
|
break
|
|
|
|
# Rule Bool return summary:
|
|
violates_rule = False
|
|
|
|
# If Variants in 'if_dict' found in deconstructed_dna and Variants in 'result_dict' not found in deconstructed_dna:
|
|
if if_bool and then_bool:
|
|
violates_rule = True
|
|
|
|
elif if_bool and result_bool and result_dict_type == "NOT":
|
|
violates_rule = True
|
|
|
|
# If Variants in 'if_dict' not found in deconstructed_dna, and 'result_dict' variants are found in
|
|
# deconstructed_dna, and they are a part of a full Attribute in 'then_dict'
|
|
elif not if_bool and result_bool and full_att_bool:
|
|
violates_rule = True
|
|
|
|
# If Variants in 'if_dict' not found in deconstructed_dna, but Variants in 'then_dict' are found in
|
|
# deconstructed_dna, and don't make up a full Attribute:
|
|
# elif not if_bool and result_bool and not full_att_bool:
|
|
# violates_rule = False
|
|
|
|
return violates_rule, if_bool, result_bool, full_att_bool
|
|
|
|
|
|
def create_dicts(hierarchy, rule_list_items, result_dict_type):
|
|
"""
|
|
Example of output structure:
|
|
|
|
structure = {
|
|
"attribute1": {
|
|
"variant1": [
|
|
"name",
|
|
"order_number",
|
|
"rarity_number"
|
|
"attribute"
|
|
"attribute_index"
|
|
],
|
|
"variant2": [
|
|
"name",
|
|
"order_number",
|
|
"rarity_number"
|
|
"attribute"
|
|
"attribute_index"
|
|
]
|
|
},
|
|
"attribute2": {
|
|
"variant1": [
|
|
"name",
|
|
"order_number",
|
|
"rarity_number"
|
|
"attribute"
|
|
"attribute_index"
|
|
],
|
|
"variant2": [
|
|
"name",
|
|
"order_number",
|
|
"rarity_number"
|
|
"attribute"
|
|
"attribute_index"
|
|
]
|
|
}
|
|
}
|
|
"""
|
|
|
|
items_returned = collections.defaultdict(dict)
|
|
for a in rule_list_items:
|
|
for b in hierarchy:
|
|
if a == b: # If 'a' is an Attribute, add all 'a' Variants to items_returned dict.
|
|
variant_list_of_a = list(hierarchy[a].keys())
|
|
variant_dict_of_a = {}
|
|
for c in variant_list_of_a:
|
|
variant_dict_of_a[c] = get_var_info(c, hierarchy)
|
|
|
|
items_returned[a] = variant_dict_of_a
|
|
|
|
if a in list(hierarchy[b].keys()): # If 'a' is a Variant, add all info about that variant to items_returned
|
|
items_returned[b][a] = get_var_info(a, hierarchy)
|
|
|
|
items_returned = dict(items_returned)
|
|
|
|
return dict(items_returned)
|
|
|
|
|
|
def logicafy_dna_single(hierarchy, single_dna, logic_file, enable_rarity):
|
|
deconstructed_dna = single_dna.split("-")
|
|
did_reconstruct = True
|
|
original_dna = str(single_dna)
|
|
|
|
while did_reconstruct:
|
|
did_reconstruct = False
|
|
for rule in logic_file:
|
|
# Items from 'IF' key for a given rule
|
|
if_dict = create_dicts(hierarchy, logic_file[rule]["IF"], "IF")
|
|
|
|
# TODO: This needs to be made more efficient with less repeating code, but it works I guess:
|
|
if "THEN" in logic_file[rule]:
|
|
result_dict_type = "THEN"
|
|
result_dict = create_dicts(hierarchy, logic_file[rule][result_dict_type], result_dict_type)
|
|
|
|
# Change 'then_bool' to 'result_bool'
|
|
violates_rule, if_bool, then_bool, full_att_bool = get_rule_break_type(
|
|
hierarchy,
|
|
deconstructed_dna,
|
|
if_dict,
|
|
result_dict,
|
|
result_dict_type,
|
|
)
|
|
|
|
# Check THEN rule again but swap IF and THEN dicts.
|
|
if not violates_rule:
|
|
violates_rule, if_bool, then_bool, full_att_bool = get_rule_break_type(
|
|
hierarchy,
|
|
deconstructed_dna,
|
|
result_dict,
|
|
if_dict,
|
|
result_dict_type,
|
|
)
|
|
|
|
if violates_rule:
|
|
log.debug(f"======={deconstructed_dna} VIOLATES RULE======")
|
|
|
|
deconstructed_dna = apply_rule_to_dna(
|
|
hierarchy,
|
|
deconstructed_dna,
|
|
if_dict,
|
|
result_dict,
|
|
result_dict_type,
|
|
enable_rarity
|
|
)
|
|
|
|
new_dna = reconstruct_dna(deconstructed_dna)
|
|
if new_dna != original_dna:
|
|
original_dna = str(new_dna)
|
|
did_reconstruct = True
|
|
break
|
|
# TODO: This needs to be made more efficient with less repeating code, but it works I guess:
|
|
if "NOT" in logic_file[rule]:
|
|
result_dict_type = "NOT"
|
|
result_dict = create_dicts(hierarchy, logic_file[rule][result_dict_type], result_dict_type)
|
|
|
|
# Change 'then_bool' to 'result_bool'
|
|
violates_rule, if_bool, then_bool, full_att_bool = get_rule_break_type(
|
|
hierarchy,
|
|
deconstructed_dna,
|
|
if_dict,
|
|
result_dict,
|
|
result_dict_type,
|
|
)
|
|
|
|
# Check NOT rule again but swap IF and NOT dicts.
|
|
if not violates_rule:
|
|
violates_rule, if_bool, then_bool, full_att_bool = get_rule_break_type(
|
|
hierarchy,
|
|
deconstructed_dna,
|
|
result_dict,
|
|
if_dict,
|
|
result_dict_type,
|
|
)
|
|
|
|
if violates_rule:
|
|
log.debug(f"======={deconstructed_dna} VIOLATES RULE======")
|
|
|
|
deconstructed_dna = apply_rule_to_dna(
|
|
hierarchy,
|
|
deconstructed_dna,
|
|
if_dict,
|
|
result_dict,
|
|
result_dict_type,
|
|
enable_rarity
|
|
)
|
|
|
|
new_dna = reconstruct_dna(deconstructed_dna)
|
|
if new_dna != original_dna:
|
|
original_dna = str(new_dna)
|
|
did_reconstruct = True
|
|
break
|
|
|
|
return str(reconstruct_dna(deconstructed_dna))
|