diff --git a/social_norms_trees/atomic_mutations.py b/social_norms_trees/atomic_mutations.py index 578b794..68a5b8b 100644 --- a/social_norms_trees/atomic_mutations.py +++ b/social_norms_trees/atomic_mutations.py @@ -17,9 +17,7 @@ ExistingNode = TypeVar("ExistingNode", bound=Behavior) NewNode = TypeVar("NewNode", bound=Behavior) -CompositeIndex = TypeVar( - "CompositeIndex", bound=Tuple[Composite, int] -) +CompositeIndex = TypeVar("CompositeIndex", bound=Tuple[Composite, int]) BehaviorIdentifier = TypeVar( "BehaviorIdentifier", bound=Union[ExistingNode, NewNode, CompositeIndex] ) @@ -38,7 +36,8 @@ # The argument annotations are vital, because they tell the UI which prompt # to use. -#TODO: pass in the parent node, and do the action on the parent node directly. + +# TODO: pass in the parent node, and do the action on the parent node directly. def remove(node: ExistingNode, parent: Composite) -> ExistingNode: """Remove a node. Examples: @@ -54,7 +53,7 @@ def remove(node: ExistingNode, parent: Composite) -> ExistingNode: Sequence(name='', children=[Behavior(name='Success', id=None), Behavior(name='Failure', id=None)]) - + >>> removed = remove(failure_node, tree) >>> pprint(tree) ... # doctest: +NORMALIZE_WHITESPACE @@ -119,7 +118,7 @@ def move( Sequence(name='', children=[Behavior(name='Success', id=None), Behavior(name='Failure', id=None)]) - + >>> move(failure_node, (tree, 0)) >>> pprint(tree) ... # doctest: +NORMALIZE_WHITESPACE @@ -132,13 +131,13 @@ def move( return - # # # ============================================================================= # # # Node and Position Selectors # # # ============================================================================= from typing import Union, Generator + def iterate_nodes(tree: Union[Behavior, Sequence]): """ Examples: @@ -169,9 +168,9 @@ def iterate_nodes(tree: Union[Behavior, Sequence]): Behavior(name='Dummy', id=None), Sequence(name='', children=[Behavior(name='Dummy', id=None)]), Behavior(name='Dummy', id=None)] - """ + """ yield tree - + # Check if the node is a Sequence and has children to iterate over if hasattr(tree, "children"): for child in tree.children: @@ -216,4 +215,3 @@ class QuitException(Exception): def end_experiment(): """I'm done, end the experiment.""" raise QuitException("User ended the experiment.") - diff --git a/social_norms_trees/behavior_tree_library.py b/social_norms_trees/behavior_tree_library.py index 85a7da4..1836cec 100644 --- a/social_norms_trees/behavior_tree_library.py +++ b/social_norms_trees/behavior_tree_library.py @@ -1,11 +1,13 @@ from dataclasses import dataclass, field from typing import Optional, List + @dataclass class Behavior: name: str id: Optional[str] = None + @dataclass class Composite: name: str @@ -22,10 +24,12 @@ def remove_child(self, child: Behavior): if child in self.children: self.children.remove(child) + @dataclass class Sequence(Composite): pass + @dataclass class Selector(Composite): - pass \ No newline at end of file + pass diff --git a/social_norms_trees/interactive_ui.py b/social_norms_trees/interactive_ui.py index 93bfb9b..be9a873 100644 --- a/social_norms_trees/interactive_ui.py +++ b/social_norms_trees/interactive_ui.py @@ -10,79 +10,79 @@ from social_norms_trees.behavior_tree_library import Behavior -def run_interactive_list(nodes: List, mode: str, new_behavior: Optional[Behavior] = None): +def run_interactive_list( + nodes: List, mode: str, new_behavior: Optional[Behavior] = None +): """ Runs an interactive list UI to insert a new action. """ selected_index = 0 - + if mode == "move": selected_index = nodes.index(new_behavior) nodes.remove(new_behavior) - - + def get_display_text_insert(): result = [] for i in range(len(nodes) + 1): if i == selected_index: - result.append(('fg:green', f"-> {{{new_behavior.name}}}\n")) + result.append(("fg:green", f"-> {{{new_behavior.name}}}\n")) elif i < selected_index: - result.append(('fg:white', f"-> {nodes[i].name}\n")) + result.append(("fg:white", f"-> {nodes[i].name}\n")) else: - #now that selected index has moved behind this behavior, index is index-1 - result.append(('fg:white', f"-> {nodes[i - 1].name}\n")) + # now that selected index has moved behind this behavior, index is index-1 + result.append(("fg:white", f"-> {nodes[i - 1].name}\n")) return FormattedText(result) - def get_display_text_select(): result = [] for i in range(len(nodes)): if i == selected_index: - result.append(('fg:green', f"-> {nodes[i].name}\n")) + result.append(("fg:green", f"-> {nodes[i].name}\n")) else: - result.append(('fg:white', f"-> {nodes[i].name}\n")) + result.append(("fg:white", f"-> {nodes[i].name}\n")) return FormattedText(result) instructions_set = { "insert": "Use the Up/Down arrow keys to select where to insert the action. ", - "select": "Use the Up/Down arrow keys to select the desired action to operate on. ", - "move": "Use the Up/Down arrow keys to select the new position for the action. " + "select": "Use the Up/Down arrow keys to select the desired action to operate on. ", + "move": "Use the Up/Down arrow keys to select the new position for the action. ", } - # FormattedText used to define text and also text styling - instructions = FormattedText([ - ('fg:#00ff00 bold', instructions_set[mode] +"Press Enter to confirm. Press esc to exit at anytime.") - ]) + instructions = FormattedText( + [ + ( + "fg:#00ff00 bold", + instructions_set[mode] + + "Press Enter to confirm. Press esc to exit at anytime.", + ) + ] + ) instructions_window = Window( - content=FormattedTextControl(instructions), - height=1, - align="center" + content=FormattedTextControl(instructions), height=1, align="center" ) - - #initial window display + # initial window display display_mode = { "insert": get_display_text_insert, "move": get_display_text_insert, - "select" :get_display_text_select + "select": get_display_text_select, } display = Window( - content=FormattedTextControl( - display_mode[mode] - ), + content=FormattedTextControl(display_mode[mode]), style="class:output", height=10, - align="center" + align="center", ) # Key bindings kb = KeyBindings() - @kb.add('up') + @kb.add("up") def move_up(event): nonlocal selected_index if selected_index > 0: @@ -92,7 +92,7 @@ def move_up(event): elif mode == "select": display.content.text = get_display_text_select() - @kb.add('down') + @kb.add("down") def move_down(event): nonlocal selected_index @@ -101,20 +101,20 @@ def move_down(event): selected_index += 1 display.content.text = get_display_text_insert() elif mode == "select": - if selected_index < len(nodes)-1: + if selected_index < len(nodes) - 1: selected_index += 1 display.content.text = get_display_text_select() - @kb.add('enter') + @kb.add("enter") def select_action(event): if mode == "insert" or mode == "move": - #nodes.insert(selected_index, new_behavior) + # nodes.insert(selected_index, new_behavior) app.exit(result=selected_index) - #app.exit(result=nodes) + # app.exit(result=nodes) elif mode == "select": app.exit(result=nodes[selected_index]) - @kb.add('escape') + @kb.add("escape") def exit_without_changes(event): app.exit(result=None) @@ -125,8 +125,10 @@ def exit_without_changes(event): VSplit( [ Window(), # Left padding - HSplit([instructions_window, Window(height=1), display], align="center"), - Window() # Right padding + HSplit( + [instructions_window, Window(height=1), display], align="center" + ), + Window(), # Right padding ], align="center", ), @@ -135,10 +137,12 @@ def exit_without_changes(event): ) layout = Layout(root_container) - style = Style.from_dict({ - "output": "bg:#282c34 #ffffff bold", # Dark grey background, text bold - "instructions": "fg:#00ff00 bold", - }) + style = Style.from_dict( + { + "output": "bg:#282c34 #ffffff bold", # Dark grey background, text bold + "instructions": "fg:#00ff00 bold", + } + ) app = Application(layout=layout, key_bindings=kb, full_screen=True, style=style) - return app.run() \ No newline at end of file + return app.run()