diff --git a/game/play.py b/game/play.py index 68b99f8..207d17f 100644 --- a/game/play.py +++ b/game/play.py @@ -7,16 +7,12 @@ from utils.constants import button_style, dropdown_style, LOGICAL_GATES, LEVELS, from utils.preload import button_texture, button_hovered_texture, logic_gate_textures class LogicalGate(arcade.Sprite): - def __init__(self, id, x, y, gate_type, value): - super().__init__(center_x=x, center_y=y, img=logic_gate_textures[gate_type][value if value is not None else 0]) + def __init__(self, id, x, y, gate_type): + super().__init__(center_x=x, center_y=y, img=logic_gate_textures[gate_type][0]) self.id = id self.gate_type = gate_type - - if gate_type == "INPUT": - self.value = value - else: - self.value = 0 + self.value = None self.input: list[LogicalGate] = [] self.output: LogicalGate | None = None @@ -34,7 +30,8 @@ class LogicalGate(arcade.Sprite): self.value = None elif len(self.input) > 1: if len(self.input) == 2: - self.value = int(LOGICAL_GATES[self.gate_type](self.input[0].calculate_value(), self.input[1].calculate_value())) # have to convert to int cause it might return boolean + value = LOGICAL_GATES[self.gate_type](self.input[0].calculate_value(), self.input[1].calculate_value()) + self.value = int(value) if value is not None else value # have to convert to int cause it might return boolean else: self.value = multi_gate([input.calculate_value() for input in self.input], LOGICAL_GATES[self.gate_type]) else: @@ -71,28 +68,28 @@ class Game(arcade.gui.UIView): self.selected_output = None self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1))) - self.tools_box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=5), anchor_x="right", anchor_y="bottom", align_x=-5, align_y=20) + self.tools_box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=5), anchor_x="right", anchor_y="center", align_x=-5) if not level_num == -1: self.task_label = self.anchor.add(arcade.gui.UILabel(text=generate_task_text(LEVELS[level_num]), font_size=20, multiline=True), anchor_x="center", anchor_y="top", align_y=-15) for requirement in LEVELS[level_num]: if requirement[1] == "INPUT": for _ in range(requirement[0]): - self.add_gate(random.randint(0, 200), random.randint(200, self.window.height - 100), "INPUT", requirement[2]) + self.add_gate(random.randint(50, 200), random.randint(200, self.window.height - 100), "INPUT") elif requirement[1] == "OUTPUT": for _ in range(requirement[0]): - self.add_gate(random.randint(self.window.width - 500, self.window.width - 350), random.randint(200, self.window.height - 100), "OUTPUT", requirement[2]) + self.add_gate(random.randint(self.window.width - 500, self.window.width - 350), random.randint(200, self.window.height - 100), "OUTPUT") else: for _ in range(requirement[0]): self.add_gate(random.randint(300, self.window.width - 600), random.randint(200, self.window.height - 100), requirement[1]) else: self.task_label = self.anchor.add(arcade.gui.UILabel(text="Task: Have fun! Do whatever you want!", font_size=20), anchor_x="center", anchor_y="top", align_y=-15) - for gate in list(LOGICAL_GATES.keys()) + ["INPUT 0", "INPUT 1", "OUTPUT"]: - button = self.tools_box.add(arcade.gui.UIFlatButton(width=self.window.width * 0.1, height=self.window.height * 0.075, text=f"Create {gate} gate", style=dropdown_style)) + for gate in list(LOGICAL_GATES.keys()) + ["INPUT", "OUTPUT"]: + button = self.tools_box.add(arcade.gui.UIFlatButton(width=self.window.width * 0.125, height=self.window.height * 0.075, text=f"Create {gate} gate", style=dropdown_style)) if "INPUT" in gate: - func = lambda: (random.randint(0, 200), random.randint(200, self.window.height - 100)) + func = lambda: (random.randint(50, 200), random.randint(200, self.window.height - 100)) elif gate == "OUTPUT": func = lambda: (random.randint(self.window.width - 500, self.window.width - 350), random.randint(200, self.window.height - 100)) else: @@ -100,10 +97,10 @@ class Game(arcade.gui.UIView): button.on_click = lambda event, func=func, gate=gate: self.add_gate(*func(), gate) - screenshot_button = self.tools_box.add(arcade.gui.UIFlatButton(width=self.window.width * 0.1, height=self.window.height * 0.075, text="Screenshot", style=dropdown_style)) + screenshot_button = self.tools_box.add(arcade.gui.UIFlatButton(width=self.window.width * 0.125, height=self.window.height * 0.075, text="Screenshot", style=dropdown_style)) screenshot_button.on_click = lambda event: self.screenshot() - hide_button = self.tools_box.add(arcade.gui.UIFlatButton(width=self.window.width * 0.1, height=self.window.height * 0.075, text="Hide", style=dropdown_style)) + hide_button = self.tools_box.add(arcade.gui.UIFlatButton(width=self.window.width * 0.125, height=self.window.height * 0.075, text="Hide", style=dropdown_style)) hide_button.on_click = lambda event: self.hide_show_panel() self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50) @@ -238,20 +235,21 @@ class Game(arcade.gui.UIView): if self.selected_output is not None: self.add_connection() - def add_gate(self, x, y, gate_type, value=None): - if gate_type == "INPUT 0": - gate_type = "INPUT" - value = 0 - elif gate_type == "INPUT 1": - gate_type = "INPUT" - value = 1 - - sprite = LogicalGate(len(self.gates), x, y, gate_type, value) + def add_gate(self, x, y, gate_type): + sprite = LogicalGate(len(self.gates), x, y, gate_type) self.gates.append(sprite) self.spritelist.append(sprite) self.evaluate() + def connection_between(self, p0, p3): + dx = p3[0] - p0[0] + offset = max(60, abs(dx) * 0.45) + c1 = (p0[0] + offset, p0[1]) + c2 = (p3[0] - offset, p3[1]) + + return cubic_bezier_points(p0, c1, c2, p3, segments=100) + def on_mouse_press(self, x, y, button, modifiers): unprojected_vec = self.camera.unproject((x, y)) world_vec = arcade.math.Vec2(unprojected_vec.x, unprojected_vec.y) @@ -274,18 +272,25 @@ class Game(arcade.gui.UIView): width_x = gate.center_x - world_vec.x if abs(width_x) < (58 if gate.gate_type not in ["INPUT", "OUTPUT"] else 43): # INPUT and OUTPUT buttons are smaller, so they have to be adjusted to 43 self.dragged_gate = gate + if gate.gate_type == "INPUT": + gate.value = not gate.value + self.evaluate() break else: if width_x > 0: - self.select_input(gate.id) - else: + if not gate.gate_type == "INPUT": + self.select_input(gate.id) + elif not gate.gate_type == "OUTPUT": self.select_output(gate.id) - def on_mouse_drag(self, x, y, dx, dy, _buttons, _modifiers): - if self.dragged_gate is not None: + def on_mouse_drag(self, x, y, dx, dy, button, _modifiers): + if button == arcade.MOUSE_BUTTON_MIDDLE: + self.camera.position = self.camera.position - arcade.math.Vec2(dx / self.camera.zoom, dy / self.camera.zoom) + + elif self.dragged_gate is not None: self.dragged_gate.center_x += dx / self.camera.zoom self.dragged_gate.center_y += dy / self.camera.zoom - + def on_mouse_release(self, x, y, button, modifiers): self.dragged_gate = None @@ -297,14 +302,6 @@ class Game(arcade.gui.UIView): if symbol == arcade.key.ESCAPE: self.main_exit() - def connection_between(self, p0, p3): - dx = p3[0] - p0[0] - offset = max(60, abs(dx) * 0.45) - c1 = (p0[0] + offset, p0[1]) - c2 = (p3[0] - offset, p3[1]) - - return cubic_bezier_points(p0, c1, c2, p3, segments=100) - def on_draw(self): super().on_draw() @@ -331,4 +328,4 @@ class Game(arcade.gui.UIView): if self.selected_output is not None and self.selected_input is None: points = self.connection_between(get_gate_port_position(self.gates[self.selected_output], "output"), (mouse_x, mouse_y)) - arcade.draw_line_strip(points, arcade.color.WHITE, 6) \ No newline at end of file + arcade.draw_line_strip(points, arcade.color.WHITE, 6) diff --git a/menus/level_selector.py b/menus/level_selector.py index f7c5a3a..9edf727 100644 --- a/menus/level_selector.py +++ b/menus/level_selector.py @@ -47,7 +47,7 @@ class LevelSelector(arcade.gui.UIView): completed_notice = '\n(Completed)' if n in self.data['completed_levels'] else '' - level_button = self.grid.add(arcade.gui.UITextureButton(width=self.window.width / 8, height=self.window.height / 12, text=f"{difficulty} Level {n + 1}{completed_notice}", texture=button_texture, texture_hovered=button_hovered_texture, style=button_style, multiline=True), row=row, column=col) + level_button = self.grid.add(arcade.gui.UITextureButton(width=self.window.width / 8, height=self.window.height / 13, text=f"{difficulty} Level {n + 1}{completed_notice}", texture=button_texture, texture_hovered=button_hovered_texture, style=button_style, multiline=True), row=row, column=col) level_button.on_click = lambda event, n=n: self.play(n) row, col = (n + 1) // 5, (n + 1) % 5 diff --git a/menus/tutorial.py b/menus/tutorial.py index fdbaba9..d2f8a66 100644 --- a/menus/tutorial.py +++ b/menus/tutorial.py @@ -18,6 +18,7 @@ class Tutorial(arcade.gui.UIView): - To create connections, click on the + buttons, left is the input, right is the output - On levels, a node has to have 2 inputs(Except the OUTPUT and NOT node), but only 1 output - On DIY mode, a node can have more than 2 inputs, except for OUTPUT and NOT +- You can change an INPUT's gate value by clicking on it - You have to connect the nodes in a way to meet the required result Logical Gates explanation: diff --git a/utils/constants.py b/utils/constants.py index 2e43c2a..ad7c4f4 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -22,145 +22,129 @@ LOGICAL_GATES = { LEVELS = [ # EASY [ - [2, "INPUT", 1], + [2, "INPUT"], [1, "AND"], [1, "OUTPUT", 1] ], [ - [1, "INPUT", 1], - [1, "INPUT", 0], + [2, "INPUT"], [1, "OR"], [1, "OUTPUT", 1] ], [ - [1, "INPUT", 1], + [1, "INPUT"], [1, "NOT"], [1, "OUTPUT", 0] ], [ - [2, "INPUT", 1], + [2, "INPUT"], [1, "NAND"], [1, "OUTPUT", 0] ], [ - [1, "INPUT", 1], - [1, "INPUT", 0], + [2, "INPUT"], [1, "XOR"], [1, "OUTPUT", 1] ], [ - [1, "INPUT", 0], - [1, "INPUT", 1], + [2, "INPUT"], [1, "NOT"], [1, "AND"], [1, "OUTPUT", 1] ], [ - [2, "INPUT", 1], - [1, "INPUT", 0], + [3, "INPUT"], [1, "AND"], [1, "OR"], [1, "OUTPUT", 1] ], [ - [2, "INPUT", 1], + [2, "INPUT"], [1, "NOT"], [1, "NAND"], [1, "OUTPUT", 1] ], # INTERMEDIATE [ - [2, "INPUT", 0], - [1, "INPUT", 1], + [3, "INPUT"], [1, "NOR"], [1, "AND"], [1, "OUTPUT", 1] ], [ - [2, "INPUT", 1], - [1, "INPUT", 0], + [3, "INPUT"], [1, "XNOR"], [1, "OR"], [1, "OUTPUT", 1] ], [ - [1, "INPUT", 1], - [1, "INPUT", 0], + [2, "INPUT"], [1, "NOT"], [1, "XOR"], [1, "OUTPUT", 0] ], [ - [2, "INPUT", 1], - [2, "INPUT", 0], + [4, "INPUT"], [1, "OR"], [1, "AND"], [1, "XOR"], [1, "OUTPUT", 0] ], [ - [2, "INPUT", 0], - [1, "INPUT", 1], + [3, "INPUT"], [1, "NOT"], [1, "NOR"], [1, "OR"], [1, "OUTPUT", 1] ], [ - [3, "INPUT", 1], - [1, "INPUT", 0], + [4, "INPUT"], [2, "NAND"], [1, "OR"], [1, "OUTPUT", 1] ], [ - [4, "INPUT", 0], + [4, "INPUT"], [2, "NOR"], [1, "AND"], [1, "OUTPUT", 1] ], [ - [2, "INPUT", 1], - [1, "INPUT", 0], + [3, "INPUT"], [1, "NOT"], [1, "AND"], [1, "NAND"], [1, "OUTPUT", 0] ], [ - [2, "INPUT", 1], - [2, "INPUT", 0], + [4, "INPUT"], [2, "XOR"], [1, "XNOR"], [1, "OUTPUT", 1] ], [ - [3, "INPUT", 1], - [1, "INPUT", 0], + [4, "INPUT"], [1, "AND"], [1, "NAND"], [1, "OR"], [1, "OUTPUT", 1] ], [ - [2, "INPUT", 0], - [2, "INPUT", 1], + [4, "INPUT"], [1, "NOR"], [1, "AND"], [1, "XOR"], [1, "OUTPUT", 0] ], [ - [1, "INPUT", 1], - [3, "INPUT", 0], + [4, "INPUT"], [1, "NOT"], [2, "OR"], [1, "NAND"], [1, "OUTPUT", 0] ], [ - [2, "INPUT", 1], - [2, "INPUT", 0], + [4, "INPUT"], [1, "XNOR"], [1, "NOR"], [1, "AND"], @@ -168,8 +152,7 @@ LEVELS = [ ], # HARD [ - [3, "INPUT", 1], - [1, "INPUT", 0], + [4, "INPUT"], [1, "NOT"], [1, "AND"], [1, "OR"], @@ -177,8 +160,7 @@ LEVELS = [ [1, "OUTPUT", 0] ], [ - [3, "INPUT", 1], - [2, "INPUT", 0], + [5, "INPUT"], [1, "AND"], [1, "OR"], [1, "NAND"], @@ -186,23 +168,20 @@ LEVELS = [ [1, "OUTPUT", 1] ], [ - [2, "INPUT", 1], - [2, "INPUT", 0], + [4, "INPUT"], [2, "NOT"], [1, "NAND"], [1, "OR"], [1, "OUTPUT", 1] ], [ - [2, "INPUT", 1], - [2, "INPUT", 0], + [4, "INPUT"], [3, "NAND"], [1, "OR"], [1, "OUTPUT", 0] ], [ - [3, "INPUT", 0], - [1, "INPUT", 1], + [4, "INPUT"], [1, "NOT"], [2, "NOR"], [1, "XOR"], @@ -210,16 +189,14 @@ LEVELS = [ [1, "OUTPUT", 1] ], [ - [4, "INPUT", 0], - [2, "INPUT", 1], + [6, "INPUT"], [2, "NOR"], [1, "XOR"], [1, "XNOR"], [1, "OUTPUT", 1] ], [ - [3, "INPUT", 1], - [3, "INPUT", 0], + [6, "INPUT"], [2, "AND"], [1, "OR"], [1, "NAND"], @@ -227,8 +204,7 @@ LEVELS = [ [1, "OUTPUT", 0] ], [ - [2, "INPUT", 1], - [3, "INPUT", 0], + [5, "INPUT"], [1, "NOT"], [2, "XOR"], [1, "NAND"], @@ -236,8 +212,7 @@ LEVELS = [ [1, "OUTPUT", 0] ], [ - [4, "INPUT", 1], - [2, "INPUT", 0], + [6, "INPUT"], [2, "XOR"], [1, "NAND"], [1, "NOR"], @@ -246,8 +221,7 @@ LEVELS = [ ], # EXTRA HARD [ - [2, "INPUT", 1], - [2, "INPUT", 0], + [4, "INPUT"], [1, "AND"], [1, "OR"], [1, "NAND"], @@ -256,8 +230,7 @@ LEVELS = [ [1, "OUTPUT", 1] ], [ - [2, "INPUT", 1], - [1, "INPUT", 0], + [3, "INPUT"], [1, "NOT"], [1, "AND"], [1, "OR"], @@ -266,8 +239,7 @@ LEVELS = [ [1, "OUTPUT", 1] ], [ - [3, "INPUT", 1], - [3, "INPUT", 0], + [6, "INPUT"], [2, "XOR"], [1, "AND"], [1, "NAND"], @@ -276,8 +248,7 @@ LEVELS = [ [1, "OUTPUT", 0] ], [ - [2, "INPUT", 1], - [2, "INPUT", 0], + [4, "INPUT"], [2, "NOT"], [1, "NAND"], [1, "NOR"], @@ -286,8 +257,7 @@ LEVELS = [ [1, "OUTPUT", 0] ], [ - [4, "INPUT", 1], - [2, "INPUT", 0], + [6, "INPUT"], [2, "NOR"], [2, "XNOR"], [1, "AND"], @@ -295,8 +265,7 @@ LEVELS = [ [2, "OUTPUT", 1] ], [ - [3, "INPUT", 1], - [3, "INPUT", 0], + [6, "INPUT"], [2, "NAND"], [2, "XOR"], [1, "NOR"], @@ -305,8 +274,7 @@ LEVELS = [ [1, "OUTPUT", 1] ], [ - [4, "INPUT", 1], - [4, "INPUT", 0], + [8, "INPUT"], [2, "AND"], [2, "OR"], [1, "XOR"],