Fix connected component giving a single edge, add backtracking for rulesets, add previous property, add a better default ruleset, fix trash only working on hover and not intersection, fix dragging, update play.py a bit to be more compatible

This commit is contained in:
csd4ni3l
2025-12-06 13:40:08 +01:00
parent 9df46b2ab6
commit 15bb259a4f
2 changed files with 96 additions and 83 deletions

View File

@@ -176,13 +176,13 @@ class Game(arcade.gui.UIView):
self.triggered_events.append(["game_launch", {}]) self.triggered_events.append(["game_launch", {}])
def get_rule_values(self, ruleset_num, rule_num, rule_dict, event_args): def get_rule_values(self, rule_num, rule_dict, rule_values, event_args):
args = [self.rule_values[f"{ruleset_num}_{rule_num}_{user_var}_{n}"] for n, user_var in enumerate(rule_dict["user_vars"])] args = [rule_values[f"{rule_num}_{user_var}_{n}"] for n, user_var in enumerate(rule_dict["user_vars"])]
return args + [event_args[var] for var in rule_dict.get("vars", []) if not var in rule_dict["user_vars"]] return args + [event_args[var] for var in rule_dict.get("vars", []) if not var in rule_dict["user_vars"]]
def check_rule(self, ruleset_num, rule_num, rule_dict, event_args): def check_rule(self, rule_num, rule_dict, rule_values, event_args):
return rule_dict["func"](*self.get_rule_values(ruleset_num, rule_num, rule_dict, event_args)) return rule_dict["func"](*self.get_rule_values(rule_num, rule_dict, rule_values, event_args))
def get_action_function(self, action_dict): def get_action_function(self, action_dict):
ACTION_FUNCTION_DICT = { ACTION_FUNCTION_DICT = {
@@ -207,8 +207,8 @@ class Game(arcade.gui.UIView):
return ACTION_FUNCTION_DICT[action_dict["type"]][action_dict["name"]] return ACTION_FUNCTION_DICT[action_dict["type"]][action_dict["name"]]
def run_do_rule(self, ruleset_num, rule_num, rule_dict, event_args): def run_do_rule(self, rule_num, rule_dict, rule_values, event_args):
self.get_action_function(rule_dict["action"])(*self.get_rule_values(ruleset_num, rule_num, rule_dict, event_args)) self.get_action_function(rule_dict["action"])(*self.get_rule_values(rule_num, rule_dict, rule_values, event_args))
def on_update(self, delta_time): def on_update(self, delta_time):
if self.mode == "import" and self.file_manager.submitted_content: if self.mode == "import" and self.file_manager.submitted_content:
@@ -238,57 +238,9 @@ class Game(arcade.gui.UIView):
while len(self.triggered_events) > 0: while len(self.triggered_events) > 0:
trigger, trigger_args = self.triggered_events.pop(0) trigger, trigger_args = self.triggered_events.pop(0)
for key, ruleset in self.rulesets.items():
if len(ruleset) == 2:
if_rule_dict = IF_RULES[ruleset[0]]
do_rule_dict = DO_RULES[ruleset[1]]
if not if_rule_dict["trigger"] == trigger: for rule in self.rulesets:
continue ...
if do_rule_dict["action"]["type"] == "shape_action" or "shape_type" in if_rule_dict["user_vars"]:
for shape in self.shapes:
event_args = trigger_args.copy()
if not "event_shape_type" in trigger_args:
event_args.update({"event_shape_type": shape.shape_type, "shape_size": shape.shape_size, "shape_x": shape.x, "shape_y": shape.y, "shape": shape, "shape_color": shape.shape_color})
if self.check_rule(key, 1, if_rule_dict, event_args):
self.run_do_rule(key, 2, do_rule_dict, event_args)
else:
event_args = trigger_args.copy()
if self.check_rule(key, 1, if_rule_dict, event_args):
self.run_do_rule(key, 2, do_rule_dict, event_args)
else:
if_rule_dicts = IF_RULES[ruleset[0]], IF_RULES[ruleset[2]]
do_rule_dict = DO_RULES[ruleset[3]]
if not (if_rule_dicts[0]["trigger"] == trigger and if_rule_dicts[0]["trigger"] == trigger):
continue
if do_rule_dict["action"]["type"] == "shape_action":
for shape in self.shapes:
event_args = trigger_args
if not "event_shape_type" in trigger_args:
event_args.update({"event_shape_type": shape.shape_type, "shape_size": shape.shape_size, "shape_x": shape.x, "shape_y": shape.y, "shape": shape, "shape_color": shape.shape_color})
if ruleset[1] == "and":
if self.check_rule(key, 1, if_rule_dicts[0], event_args) and self.check_rule(key, 2, if_rule_dicts[1], event_args):
self.run_do_rule(key, 3, do_rule_dict, event_args)
elif ruleset[1] == "or":
if self.check_rule(key, 1, if_rule_dicts[0], event_args) or self.check_rule(key, 2, if_rule_dicts[1], event_args):
self.run_do_rule(key, 3, do_rule_dict, event_args)
else:
event_args = trigger_args
if ruleset[1] == "and":
if self.check_rule(key, 1, if_rule_dicts[0], event_args) and self.check_rule(key, 2, if_rule_dicts[1], event_args):
self.run_do_rule(key, 3, do_rule_dict, event_args)
elif ruleset[1] == "or":
if self.check_rule(key, 1, if_rule_dicts[0], event_args) or self.check_rule(key, 2, if_rule_dicts[1], event_args):
self.run_do_rule(key, 3, do_rule_dict, event_args)
for shape in self.shapes: for shape in self.shapes:
for shape_b in self.shapes: for shape_b in self.shapes:

View File

@@ -50,12 +50,13 @@ def connection_between(p0, p3, start_dir_y, end_dir_y):
def connected_component(edges, start): def connected_component(edges, start):
graph = defaultdict(set) graph = defaultdict(set)
for u, v in edges: for u, v, _, __ in edges:
graph[u].add(v) graph[u].add(v)
graph[v].add(u) graph[v].add(u)
seen = set([start]) seen = set([start])
queue = deque([start]) queue = deque([start])
connected_edges = []
while queue: while queue:
node = queue.popleft() node = queue.popleft()
@@ -63,8 +64,11 @@ def connected_component(edges, start):
if neighbor not in seen: if neighbor not in seen:
seen.add(neighbor) seen.add(neighbor)
queue.append(neighbor) queue.append(neighbor)
connected_edges.append((node, neighbor))
elif (neighbor, node) not in connected_edges and (node, neighbor) not in connected_edges:
connected_edges.append((node, neighbor))
return list(seen) return connected_edges
def get_rule_defaults(rule_type): def get_rule_defaults(rule_type):
if rule_type == "if": if rule_type == "if":
@@ -101,6 +105,26 @@ def get_rule_defaults(rule_type):
for rule_key, rule_dict in DO_RULES.items() for rule_key, rule_dict in DO_RULES.items()
} }
def backtrace(node: dict):
dependencies = []
for prev_node_list in node.previous.values():
for prev_node in prev_node_list:
if prev_node.rule_type == "comparison":
comparison_children = backtrace(prev_node)
dependencies.append([
prev_node.rule_type,
prev_node.rule,
comparison_children,
prev_node.rule_num
])
elif prev_node.rule_type == "if":
dependencies.append([prev_node.rule_type, prev_node.rule, prev_node.rule_values, prev_node.rule_num])
elif prev_node.rule_type == "do":
dependencies.append([prev_node.rule_type, prev_node.rule, prev_node.rule_values, prev_node.rule_num])
return dependencies
class RuleBox(arcade.gui.UIBoxLayout): class RuleBox(arcade.gui.UIBoxLayout):
def __init__(self, x, y, width, height, rule_num, rule_type, rule): def __init__(self, x, y, width, height, rule_num, rule_type, rule):
super().__init__(space_between=5, x=x, y=y, width=width, height=height) super().__init__(space_between=5, x=x, y=y, width=width, height=height)
@@ -110,6 +134,8 @@ class RuleBox(arcade.gui.UIBoxLayout):
self.rule_type = rule_type self.rule_type = rule_type
self.initialize_rule() self.initialize_rule()
self.previous = {}
def initialize_rule(self): def initialize_rule(self):
if not self.rule_type == "comparison": if not self.rule_type == "comparison":
self.rule_dict = ( self.rule_dict = (
@@ -281,17 +307,20 @@ class RuleBox(arcade.gui.UIBoxLayout):
self.clear() self.clear()
self.initialize_rule() self.initialize_rule()
def __repr__(self):
return f"<{self.rule_type} ({self.rule}, {self.rule_num})>"
def get_connection_pos(rule_ui: RuleBox, idx): def get_connection_pos(rule_ui: RuleBox, idx):
if rule_ui.rule_type == "comparison": if rule_ui.rule_type == "comparison":
if idx == 1: if idx == 0:
button = rule_ui.previous_button_1 button = rule_ui.previous_button_1
y = button.top y = button.top
direction = 1 direction = 1
elif idx == 2: elif idx == 1:
button = rule_ui.previous_button_2 button = rule_ui.previous_button_2
y = button.top y = button.top
direction = 1 direction = 1
else: elif idx == 2:
button = rule_ui.next_button button = rule_ui.next_button
y = button.bottom y = button.bottom
direction = -1 direction = -1
@@ -374,16 +403,49 @@ class RuleUI(arcade.gui.UIAnchorLayout):
self.rule_space = self.add(arcade.gui.UIWidget(size_hint=(1, 1))) self.rule_space = self.add(arcade.gui.UIWidget(size_hint=(1, 1)))
# self.create_connected_ruleset([("if", "x_position_compare"), ("do", "move_x")]) self.create_connected_ruleset([
("if", "x_position_compare"),
("if", "y_position_compare"),
("comparison", "and"),
("comparison", "and"),
("do", "move_x")],
[
[0, 2, 0, 0], # x_position_compare 0 -> first comparison 0
[1, 2, 0, 1], # y_position_compare 0 -> first comparison 1
[2, 3, 2, 0], # first comparison 2 (output) -> second comparison 0
[1, 3, 0, 1], # y_position_compare 0 -> -> second comparison 1
[3, 4, 2, 0] # second comparison 2 (output) -> do
]
)
self.trash_spritelist = arcade.SpriteList() self.trash_spritelist = arcade.SpriteList()
self.trash_sprite = trash_bin self.trash_sprite = trash_bin
self.trash_sprite.position = (self.window.width * 0.9, self.window.height * 0.2) self.trash_sprite.position = (self.window.width * 0.9, self.window.height * 0.2)
self.trash_spritelist.append(self.trash_sprite) self.trash_spritelist.append(self.trash_sprite)
def set_previous(self, rule_ui_a, rule_ui_b, to_connect_idx, idx):
if rule_ui_a.rule_type == "if" and to_connect_idx == 0:
if idx not in rule_ui_b.previous:
rule_ui_b.previous[idx] = []
rule_ui_b.previous[idx].append(rule_ui_a)
elif rule_ui_a.rule_type == "comparison":
if to_connect_idx == 2:
if idx not in rule_ui_b.previous:
rule_ui_b.previous[idx] = []
rule_ui_b.previous[idx].append(rule_ui_a)
else:
if to_connect_idx not in rule_ui_a.previous:
rule_ui_a.previous[to_connect_idx] = []
rule_ui_a.previous[to_connect_idx].append(rule_ui_b)
elif rule_ui_a.rule_type == "do" and idx == 0:
if idx not in rule_ui_a.previous:
rule_ui_a.previous[0] = []
rule_ui_a.previous[0].append(rule_ui_b)
def connection(self, rule_ui, allowed_next_connection, idx): def connection(self, rule_ui, allowed_next_connection, idx):
if self.to_connect is not None: if self.to_connect is not None:
old_rule_type = self.rule_ui[self.to_connect].rule_type old_rule_ui = self.rule_ui[self.to_connect]
old_rule_type = old_rule_ui.rule_type
if ( if (
rule_ui.rule_type not in self.allowed_next_connection or rule_ui.rule_type not in self.allowed_next_connection or
old_rule_type not in allowed_next_connection or old_rule_type not in allowed_next_connection or
@@ -395,6 +457,9 @@ class RuleUI(arcade.gui.UIAnchorLayout):
return return
self.connections.append([self.to_connect, rule_ui.rule_num, self.to_connect_idx, idx]) self.connections.append([self.to_connect, rule_ui.rule_num, self.to_connect_idx, idx])
self.set_previous(old_rule_ui, rule_ui, self.to_connect_idx, idx)
self.allowed_next_connection = None self.allowed_next_connection = None
self.to_connect = None self.to_connect = None
self.to_connect_idx = None self.to_connect_idx = None
@@ -412,7 +477,7 @@ class RuleUI(arcade.gui.UIAnchorLayout):
self.to_connect = None self.to_connect = None
for connection in self.connections: for connection in self.connections:
if self.dragged_rule_ui.rule_num in connection: if self.dragged_rule_ui.rule_num in connection[:2]:
self.connections.remove(connection) self.connections.remove(connection)
self.rule_space.remove(self.dragged_rule_ui) self.rule_space.remove(self.dragged_rule_ui)
@@ -424,12 +489,10 @@ class RuleUI(arcade.gui.UIAnchorLayout):
else: else:
self.dragged_rule_ui = rule_ui self.dragged_rule_ui = rule_ui
def get_rulesets(self): def get_rulesets(self): # return actions that can happen and the dependencies needed
if self.connections: do_blocks = [rule_ui for rule_ui in self.rule_ui.values() if rule_ui.rule_type == "do"]
components = connected_component(self.connections, 0)
print(components)
return {} return [backtrace(do_block) for do_block in do_blocks]
def generate_pos(self): def generate_pos(self):
return random.randint( return random.randint(
@@ -446,13 +509,13 @@ class RuleUI(arcade.gui.UIAnchorLayout):
force or generate_rule(rule_type), force or generate_rule(rule_type),
) )
if rule_type == "if": if rule_type == "if":
rule_box.next_button.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["do", "comparison"], 1) rule_box.next_button.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["do", "comparison"], 0)
elif rule_type == "comparison": elif rule_type == "comparison":
rule_box.previous_button_1.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["if", "comparison"], 1) rule_box.previous_button_1.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["if", "comparison"], 0)
rule_box.previous_button_2.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["if", "comparison"], 2) rule_box.previous_button_2.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["if", "comparison"], 1)
rule_box.next_button.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["do", "comparison"], 3) rule_box.next_button.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["do", "comparison"], 2)
elif rule_type == "do": elif rule_type == "do":
rule_box.previous_button.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["if", "comparison"], 1) rule_box.previous_button.on_click = lambda event, rule_box=rule_box: self.connection(rule_box, ["if", "comparison"], 0)
rule_box.drag_button.on_click = lambda event, rule_box=rule_box: self.drag(rule_box) rule_box.drag_button.on_click = lambda event, rule_box=rule_box: self.drag(rule_box)
@@ -464,16 +527,14 @@ class RuleUI(arcade.gui.UIAnchorLayout):
return rule_box return rule_box
def create_connected_ruleset(self, rules): def create_connected_ruleset(self, rules, connections):
previous = None
for rule_type, rule in rules: for rule_type, rule in rules:
rule_box = self.add_rule(rule_type, rule) self.add_rule(rule_type, rule)
if previous: for connection in connections:
self.connections.append((previous.rule_num, rule_box.rule_num)) self.set_previous(self.rule_ui[connection[0]], self.rule_ui[connection[1]], connection[2], connection[3])
previous = rule_box self.connections.extend(connections)
def draw(self): def draw(self):
self.bezier_points = [] self.bezier_points = []
@@ -509,7 +570,7 @@ class RuleUI(arcade.gui.UIAnchorLayout):
self.dragged_rule_ui.center_y += event.dy self.dragged_rule_ui.center_y += event.dy
def on_update(self, dt): def on_update(self, dt):
if self.dragged_rule_ui and self.trash_sprite.rect.point_in_rect((self.window.mouse.data["x"], self.window.mouse.data["y"])): if self.dragged_rule_ui and self.trash_sprite.rect.intersection(self.dragged_rule_ui.rect):
if not self.trash_sprite._current_keyframe_index == self.trash_sprite.animation.num_frames - 1: if not self.trash_sprite._current_keyframe_index == self.trash_sprite.animation.num_frames - 1:
self.trash_sprite.update_animation() self.trash_sprite.update_animation()
else: else: