diff --git a/addons/task_goal/core/domain.gd b/addons/task_goal/core/domain.gd index b6f72d0..8de4042 100644 --- a/addons/task_goal/core/domain.gd +++ b/addons/task_goal/core/domain.gd @@ -18,20 +18,15 @@ extends Resource const verbose: int = 0 -func _m_verify_g( - state: Dictionary, - method: String, - state_var: String, - arg: String, - desired_val: Variant, - depth: int -) -> Array: + +func _m_verify_g(state: Dictionary, method: String, state_var: String, arg: String, desired_val: Variant, depth: int) -> Array: if state[state_var][arg] != desired_val: assert(false, "Depth %s: method %s didn't achieve\nGoal %s[%s] = %s" % [depth, method, state_var, arg, desired_val]) if verbose >= 3: print("Depth %s: method %s achieved\nGoal %s[%s] = %s" % [depth, method, state_var, arg, desired_val]) return [] + static func _goals_not_achieved(state: Dictionary, multigoal: Multigoal) -> Dictionary: var unachieved: Dictionary = {} for n in multigoal.state: @@ -43,6 +38,7 @@ static func _goals_not_achieved(state: Dictionary, multigoal: Multigoal) -> Dict unachieved[n][arg] = val return unachieved + func _m_verify_mg(state: Dictionary, method: String, multigoal: Multigoal, depth: int) -> Variant: var goal_dict = _goals_not_achieved(state, multigoal) if goal_dict: @@ -51,6 +47,7 @@ func _m_verify_mg(state: Dictionary, method: String, multigoal: Multigoal, depth print("Depth %s: method %s achieved %s" % [depth, method, multigoal]) return [] + var _action_dict: Dictionary = {} var _task_method_dict: Dictionary = { @@ -62,8 +59,10 @@ var _unigoal_method_dict: Dictionary = {} var _multigoal_method_list: Array = [] + func _init(domain_name: String) -> void: set_name(domain_name) + func display() -> void: print(self) diff --git a/addons/task_goal/core/multigoal.gd b/addons/task_goal/core/multigoal.gd index 4ba66ad..50a8310 100644 --- a/addons/task_goal/core/multigoal.gd +++ b/addons/task_goal/core/multigoal.gd @@ -34,12 +34,14 @@ var _state: Dictionary = {} set(value): _state = value + ## multigoal_name is the name to use for the multigoal. The keyword ## args are the names and desired values of state variables. func _init(multigoal_name, state_variables: Dictionary): resource_name = multigoal_name _state = state_variables + ## Print the multigoal's state-variables and their values. ## - heading (optional) is a heading to print beforehand. func display(heading: String = "") -> void: @@ -47,6 +49,7 @@ func display(heading: String = "") -> void: print(heading) print(_state) + ## Return a list of all state-variable names in the multigoal. func state_vars() -> Array: return _state.keys() diff --git a/addons/task_goal/core/plan.gd b/addons/task_goal/core/plan.gd index e7c6ddf..f6e1a35 100644 --- a/addons/task_goal/core/plan.gd +++ b/addons/task_goal/core/plan.gd @@ -1,6 +1,6 @@ # Copyright (c) 2023-present. This file is part of V-Sekai https://v-sekai.org/. # K. S. Ernest (Fire) Lee & Contributors (see .all-contributorsrc). -# plan.gd +# plan.gd # SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: 2021 University of Maryland @@ -296,9 +296,7 @@ func _refine_task_and_continue(state, task1, todo_list, plan, depth, stn) -> Var for method in relevant: if verbose >= 3: print("Depth %s trying %s: " % [depth, method.get_method()]) - var subtasks: Variant = method.get_object().callv( - method.get_method(), [state] + task1.slice(1) - ) + var subtasks: Variant = method.get_object().callv(method.get_method(), [state] + task1.slice(1)) # Can't just say "if subtasks:", because that's wrong if subtasks == [] if subtasks is Array and stn.is_consistent(): if verbose >= 3: @@ -346,9 +344,7 @@ func _refine_unigoal_and_continue(state, goal1, todo_list, plan, depth, stn) -> var verification = [] if verify_goals: - verification = [ - ["_verify_g", str(method.get_method()), state_var_name, arg, val, depth] - ] + verification = [["_verify_g", str(method.get_method()), state_var_name, arg, val, depth]] else: verification = [] @@ -364,9 +360,7 @@ func _refine_unigoal_and_continue(state, goal1, todo_list, plan, depth, stn) -> return false -func _refine_multigoal_and_continue( - state: Dictionary, goal1: Multigoal, todo_list: Array, plan: Array, depth: int, stn -) -> Variant: +func _refine_multigoal_and_continue(state: Dictionary, goal1: Multigoal, todo_list: Array, plan: Array, depth: int, stn) -> Variant: if verbose >= 3: print("Depth %s multigoal %s: " % [depth, goal1]) var relevant: Array = current_domain._multigoal_method_list @@ -400,6 +394,7 @@ func _refine_multigoal_and_continue( print("Depth %s could not achieve multigoal %s" % [depth, goal1]) return false + func find_plan(state: Dictionary, todo_list: Array, stn: SimpleTemporalNetwork = SimpleTemporalNetwork.new()) -> Variant: if verbose >= 1: var todo_array: Array = [] @@ -408,12 +403,12 @@ func find_plan(state: Dictionary, todo_list: Array, stn: SimpleTemporalNetwork = var todo_string = "[" + ", ".join(todo_array) + "]" print("FindPlan> find_plan, verbose=%s:" % verbose) print(" state = %s\n todo_list = %s" % [state, todo_string]) - + var result: Variant = seek_plan(state, todo_list, [], 0, stn) - + if verbose >= 1: print("FindPlan> result = ", result, "\n") - + return result @@ -424,12 +419,12 @@ func seek_plan(state: Dictionary, todo_list: Array, plan: Array, depth: int, stn todo_array.push_back(_item_to_string(x)) var todo_string = "[" + ", ".join(todo_array) + "]" print("Depth %s todo_list %s" % [depth, todo_string]) - + if todo_list.is_empty(): if verbose >= 3: print("depth %s no more tasks or goals, return plan" % [depth]) return plan - + var item1 = todo_list.front() todo_list.pop_front() @@ -444,7 +439,7 @@ func seek_plan(state: Dictionary, todo_list: Array, plan: Array, depth: int, stn return _refine_unigoal_and_continue(state, item1, todo_list, plan, depth, stn) assert(false, "Depth %s: %s isn't an action, task, unigoal, or multigoal\n" % [depth, item1]) - + return false diff --git a/addons/task_goal/core/simple_temporal_network.gd b/addons/task_goal/core/simple_temporal_network.gd index 623db48..f8cfc12 100644 --- a/addons/task_goal/core/simple_temporal_network.gd +++ b/addons/task_goal/core/simple_temporal_network.gd @@ -11,10 +11,11 @@ var constraints: Array[TemporalConstraint] = [] var stn_matrix: Array[Array] = [] var num_nodes: int = 0 var node_intervals: Array[Vector2i] = [] -var node_indices: Dictionary = {} +var node_indices: Dictionary = {} -func to_dictionary() -> Dictionary: - return { "resource_name": resource_name, "constraints": constraints, "matrix": stn_matrix, "number_of_nodes": num_nodes, "node_intervals": node_intervals } + +func to_dictionary() -> Dictionary: + return {"resource_name": resource_name, "constraints": constraints, "matrix": stn_matrix, "number_of_nodes": num_nodes, "node_intervals": node_intervals} func get_node_index(node_interval: Vector2i) -> int: @@ -33,6 +34,7 @@ func _init_matrix() -> void: else: stn_matrix[i][j] = INF + func add_temporal_constraint(from_constraint: TemporalConstraint, to_constraint: TemporalConstraint = null, min_gap: float = 0, max_gap: float = 0) -> bool: if not from_constraint is TemporalConstraint: return false @@ -45,7 +47,7 @@ func add_temporal_constraint(from_constraint: TemporalConstraint, to_constraint: if not max_gap is float or max_gap < 0: return false - + constraints.append(from_constraint) constraints.append(to_constraint) @@ -122,7 +124,7 @@ func propagate_constraints() -> bool: return false return true - + func is_consistent() -> bool: var skip = false @@ -130,7 +132,7 @@ func is_consistent() -> bool: if skip: skip = false continue - for j in range(i+1, constraints.size()): + for j in range(i + 1, constraints.size()): var c1 := constraints[i] if c1 == null: return false diff --git a/addons/task_goal/core/temporal_constraint.gd b/addons/task_goal/core/temporal_constraint.gd index 22f7340..7b32322 100644 --- a/addons/task_goal/core/temporal_constraint.gd +++ b/addons/task_goal/core/temporal_constraint.gd @@ -17,11 +17,13 @@ var time_interval: Vector2i var duration: int var temporal_qualifier: TemporalQualifier + func _init(start: int, end: int, duration: int, qualifier: TemporalQualifier, resource: String): time_interval = Vector2i(start, end) self.duration = duration temporal_qualifier = qualifier resource_name = resource + func to_dictionary() -> Dictionary: - return { "resource_name": resource_name, "time_interval" : time_interval, "duration": duration, "temporal_qualifier": temporal_qualifier } + return {"resource_name": resource_name, "time_interval": time_interval, "duration": duration, "temporal_qualifier": temporal_qualifier} diff --git a/goal_task_tests/test_dream.gd b/goal_task_tests/test_dream.gd index a8efd8b..b9585f6 100644 --- a/goal_task_tests/test_dream.gd +++ b/goal_task_tests/test_dream.gd @@ -5,8 +5,8 @@ func reserve_practice_room(state, time_interval: TemporalConstraint, stn: SimpleTemporalNetwork) -> Variant: # Update the state to show that the practice room is reserved - state['shared_resource']['practice_room'] = 'reserved' - state['task_status']['reserve_room'] = 'done' + state["shared_resource"]["practice_room"] = "reserved" + state["task_status"]["reserve_room"] = "done" # Add the time interval to the STN and propagate the constraints stn.update_state({"practice": time_interval}) if not stn.is_consistent(): @@ -16,7 +16,7 @@ func reserve_practice_room(state, time_interval: TemporalConstraint, stn: Simple func attend_audition(state, time_interval: TemporalConstraint, stn: SimpleTemporalNetwork) -> Variant: # Update the state to show that the audition has been attended - state['task_status']['audition'] = 'attended' + state["task_status"]["audition"] = "attended" # Add the time interval to the STN and propagate the constraints stn.update_state({"audition": time_interval}) if not stn.propagate_constraints(): @@ -26,7 +26,7 @@ func attend_audition(state, time_interval: TemporalConstraint, stn: SimpleTempor func practice_craft(state, time_interval: TemporalConstraint, stn: SimpleTemporalNetwork) -> Variant: # Update the state to show that the practice has been done - state['task_status']['practice'] = 'done' + state["task_status"]["practice"] = "done" # Add the time interval to the STN and propagate the constraints stn.update_state({"craft": time_interval}) if not stn.propagate_constraints(): @@ -36,7 +36,7 @@ func practice_craft(state, time_interval: TemporalConstraint, stn: SimpleTempora func network(state, time_interval: TemporalConstraint, stn: SimpleTemporalNetwork) -> Variant: # Update the state to show that networking has been done - state['task_status']['networking'] = 'done' + state["task_status"]["networking"] = "done" # Add the time interval to the STN and propagate the constraints stn.update_state({"network": time_interval}) if not stn.propagate_constraints(): @@ -56,7 +56,7 @@ func get_possible_dream_time_intervals(stn: SimpleTemporalNetwork) -> Array[Temp max_start = start # Generate all possible time intervals within the minimum and maximum start times - var time_intervals : Array[TemporalConstraint] = [] + var time_intervals: Array[TemporalConstraint] = [] for start_time in range(min_start, max_start + 1): var end_time = start_time + 24 var time_interval = TemporalConstraint.new(start_time, end_time, end_time - start_time, TemporalConstraint.TemporalQualifier.OVERALL, "") @@ -64,5 +64,3 @@ func get_possible_dream_time_intervals(stn: SimpleTemporalNetwork) -> Array[Temp time_intervals.append(time_interval) return time_intervals - - diff --git a/goal_task_tests/test_game_project.gd b/goal_task_tests/test_game_project.gd index 24ab6e3..34c1c91 100644 --- a/goal_task_tests/test_game_project.gd +++ b/goal_task_tests/test_game_project.gd @@ -5,31 +5,21 @@ extends GutTest + func test_propagate_constraints() -> void: var simple_temporal_network: SimpleTemporalNetwork = SimpleTemporalNetwork.new() # Add 10 specific 3D game creation tasks - var tasks = [ - "Model Character", - "Create Environment", - "Design Level", - "Implement Gameplay Mechanics", - "Animate Characters", - "Add Sound Effects", - "Optimize Performance", - "Test and Debug", - "Polish Game", - "Publish Game" - ] + var tasks = ["Model Character", "Create Environment", "Design Level", "Implement Gameplay Mechanics", "Animate Characters", "Add Sound Effects", "Optimize Performance", "Test and Debug", "Polish Game", "Publish Game"] for i in range(tasks.size()): var task_name = tasks[i] var task_duration = 10 - var task_start_time = i * task_duration # Set the start time for each task - + var task_start_time = i * task_duration # Set the start time for each task + # Create a new temporal constraint using the task name, start time, duration, and AT_END qualifier var task_constraint = TemporalConstraint.new(task_start_time, task_start_time + task_duration, task_duration, TemporalConstraint.TemporalQualifier.AT_END, task_name) - + # Add the constraint to the simple temporal network simple_temporal_network.add_temporal_constraint(task_constraint) @@ -41,20 +31,9 @@ func test_propagate_constraints() -> void: continue var dictionary = c.to_dictionary() constraints_array.append(dictionary) - + simple_temporal_network.propagate_constraints() - + print_rich(constraints_array) - assert_eq_deep(constraints_array, [ - { "resource_name": "Model Character", "time_interval": Vector2i(0, 10), "duration": 10, "temporal_qualifier": 1 }, - { "resource_name": "Create Environment", "time_interval": Vector2i(10, 20), "duration": 10, "temporal_qualifier": 1 }, - { "resource_name": "Design Level", "time_interval": Vector2i(20, 30), "duration": 10, "temporal_qualifier": 1 }, - { "resource_name": "Implement Gameplay Mechanics", "time_interval": Vector2i(30, 40), "duration": 10, "temporal_qualifier": 1 }, - { "resource_name": "Animate Characters", "time_interval": Vector2i(40, 50), "duration": 10, "temporal_qualifier": 1 }, - { "resource_name": "Add Sound Effects", "time_interval": Vector2i(50, 60), "duration": 10, "temporal_qualifier": 1 }, - { "resource_name": "Optimize Performance", "time_interval": Vector2i(60, 70), "duration": 10, "temporal_qualifier": 1 }, - { "resource_name": "Test and Debug", "time_interval": Vector2i(70, 80), "duration": 10, "temporal_qualifier": 1 }, - { "resource_name": "Polish Game", "time_interval": Vector2i(80, 90), "duration": 10, "temporal_qualifier": 1 }, - { "resource_name": "Publish Game", "time_interval": Vector2i(90, 100), "duration": 10, "temporal_qualifier": 1 } - ]) + assert_eq_deep(constraints_array, [{"resource_name": "Model Character", "time_interval": Vector2i(0, 10), "duration": 10, "temporal_qualifier": 1}, {"resource_name": "Create Environment", "time_interval": Vector2i(10, 20), "duration": 10, "temporal_qualifier": 1}, {"resource_name": "Design Level", "time_interval": Vector2i(20, 30), "duration": 10, "temporal_qualifier": 1}, {"resource_name": "Implement Gameplay Mechanics", "time_interval": Vector2i(30, 40), "duration": 10, "temporal_qualifier": 1}, {"resource_name": "Animate Characters", "time_interval": Vector2i(40, 50), "duration": 10, "temporal_qualifier": 1}, {"resource_name": "Add Sound Effects", "time_interval": Vector2i(50, 60), "duration": 10, "temporal_qualifier": 1}, {"resource_name": "Optimize Performance", "time_interval": Vector2i(60, 70), "duration": 10, "temporal_qualifier": 1}, {"resource_name": "Test and Debug", "time_interval": Vector2i(70, 80), "duration": 10, "temporal_qualifier": 1}, {"resource_name": "Polish Game", "time_interval": Vector2i(80, 90), "duration": 10, "temporal_qualifier": 1}, {"resource_name": "Publish Game", "time_interval": Vector2i(90, 100), "duration": 10, "temporal_qualifier": 1}]) diff --git a/goal_task_tests/test_integration.gd b/goal_task_tests/test_integration.gd index 8f8bed2..c251a0d 100644 --- a/goal_task_tests/test_integration.gd +++ b/goal_task_tests/test_integration.gd @@ -66,11 +66,5 @@ func before_each(): func test_together_goal(): - var plan = planner.find_plan( - state1.duplicate(true), [["entity_met_entity", "seb", "mia", "coffee_shop"]] - ) - assert_eq( - plan, - [["travel_location", "seb", "coffee_shop"], ["travel_location", "mia", "coffee_shop"]], - "" - ) + var plan = planner.find_plan(state1.duplicate(true), [["entity_met_entity", "seb", "mia", "coffee_shop"]]) + assert_eq(plan, [["travel_location", "seb", "coffee_shop"], ["travel_location", "mia", "coffee_shop"]], "") diff --git a/goal_task_tests/test_logistics.gd b/goal_task_tests/test_logistics.gd index 945083c..f353567 100644 --- a/goal_task_tests/test_logistics.gd +++ b/goal_task_tests/test_logistics.gd @@ -87,11 +87,7 @@ func find_airport(state, l): func m_drive_truck(state, t, l): - if ( - t in state.trucks - and l in state.locations - and state.in_city[state.truck_at[t]] == state.in_city[l] - ): + if t in state.trucks and l in state.locations and state.in_city[state.truck_at[t]] == state.in_city[l]: return [["drive_truck", t, l]] @@ -124,11 +120,7 @@ func m_unload_plane(state, o, a): func move_within_city(state, o, l): - if ( - o in state.packages - and state.at[o] in state.locations - and state.in_city[state.at[o]] == state.in_city[l] - ): + if o in state.packages and state.at[o] in state.locations and state.in_city[state.at[o]] == state.in_city[l]: var t = find_truck(state, o) if t: return [["truck_at", t, state.at[o]], ["at", o, t], ["truck_at", t, l], ["at", o, l]] @@ -136,29 +128,15 @@ func move_within_city(state, o, l): func move_between_airports(state, o, a): - if ( - o in state.packages - and state.at[o] in state.airports - and a in state.airports - and state.in_city[state.at[o]] != state.in_city[a] - ): + if o in state.packages and state.at[o] in state.airports and a in state.airports and state.in_city[state.at[o]] != state.in_city[a]: var plane = find_plane(state, o) if plane: - return [ - ["plane_at", plane, state.at[o]], - ["at", o, plane], - ["plane_at", plane, a], - ["at", o, a] - ] + return [["plane_at", plane, state.at[o]], ["at", o, plane], ["plane_at", plane, a], ["at", o, a]] return false func move_between_city(state, o, l): - if ( - o in state.packages - and state.at[o] in state.locations - and state.in_city[state.at[o]] != state.in_city[l] - ): + if o in state.packages and state.at[o] in state.locations and state.in_city[state.at[o]] != state.in_city[l]: var a1 = find_airport(state, state.at[o]) var a2 = find_airport(state, l) if a1 and a2: @@ -175,37 +153,13 @@ func before_each(): # If we've changed to some other domain, this will change us back. planner.current_domain = the_domain - planner.declare_actions( - [ - Callable(self, "drive_truck"), - Callable(self, "load_truck"), - Callable(self, "unload_truck"), - Callable(self, "fly_plane"), - Callable(self, "load_plane"), - Callable(self, "unload_plane") - ] - ) - - planner.declare_unigoal_methods( - "at", - [ - Callable(self, "m_load_truck"), - Callable(self, "m_unload_truck"), - Callable(self, "m_load_plane"), - Callable(self, "m_unload_plane") - ] - ) + planner.declare_actions([Callable(self, "drive_truck"), Callable(self, "load_truck"), Callable(self, "unload_truck"), Callable(self, "fly_plane"), Callable(self, "load_plane"), Callable(self, "unload_plane")]) + + planner.declare_unigoal_methods("at", [Callable(self, "m_load_truck"), Callable(self, "m_unload_truck"), Callable(self, "m_load_plane"), Callable(self, "m_unload_plane")]) planner.declare_unigoal_methods("truck_at", [Callable(self, "m_drive_truck")]) planner.declare_unigoal_methods("plane_at", [Callable(self, "m_fly_plane")]) - planner.declare_unigoal_methods( - "at", - [ - Callable(self, "move_within_city"), - Callable(self, "move_between_airports"), - Callable(self, "move_between_city") - ] - ) + planner.declare_unigoal_methods("at", [Callable(self, "move_within_city"), Callable(self, "move_between_airports"), Callable(self, "move_between_city")]) # planner.print_domain() @@ -219,54 +173,18 @@ func before_each(): state1.at = {"package1": "location1", "package2": "location2"} state1.truck_at = {"truck1": "location3", "truck6": "location10"} state1.plane_at = {"plane2": "airport2"} - state1.in_city = { - "location1": "city1", - "location2": "city1", - "location3": "city1", - "airport1": "city1", - "location10": "city2", - "airport2": "city2" - } + state1.in_city = {"location1": "city1", "location2": "city1", "location3": "city1", "airport1": "city1", "location10": "city2", "airport2": "city2"} func test_move_goal_1(): - var plan = planner.find_plan( - state1.duplicate(true), [["at", "package1", "location2"], ["at", "package2", "location3"]] - ) - assert_eq( - plan, - [ - ["drive_truck", "truck1", "location1"], - ["load_truck", "package1", "truck1"], - ["drive_truck", "truck1", "location2"], - ["unload_truck", "package1", "location2"], - ["load_truck", "package2", "truck1"], - ["drive_truck", "truck1", "location3"], - ["unload_truck", "package2", "location3"] - ] - ) + var plan = planner.find_plan(state1.duplicate(true), [["at", "package1", "location2"], ["at", "package2", "location3"]]) + assert_eq(plan, [["drive_truck", "truck1", "location1"], ["load_truck", "package1", "truck1"], ["drive_truck", "truck1", "location2"], ["unload_truck", "package1", "location2"], ["load_truck", "package2", "truck1"], ["drive_truck", "truck1", "location3"], ["unload_truck", "package2", "location3"]]) ## Goal 2: package1 is at location10 (transport to a different city) func test_move_goal_2(): var plan = planner.find_plan(state1.duplicate(true), [["at", "package1", "location10"]]) - assert_eq( - plan, - [ - ["drive_truck", "truck1", "location1"], - ["load_truck", "package1", "truck1"], - ["drive_truck", "truck1", "airport1"], - ["unload_truck", "package1", "airport1"], - ["fly_plane", "plane2", "airport1"], - ["load_plane", "package1", "plane2"], - ["fly_plane", "plane2", "airport2"], - ["unload_plane", "package1", "airport2"], - ["drive_truck", "truck6", "airport2"], - ["load_truck", "package1", "truck6"], - ["drive_truck", "truck6", "location10"], - ["unload_truck", "package1", "location10"] - ] - ) + assert_eq(plan, [["drive_truck", "truck1", "location1"], ["load_truck", "package1", "truck1"], ["drive_truck", "truck1", "airport1"], ["unload_truck", "package1", "airport1"], ["fly_plane", "plane2", "airport1"], ["load_plane", "package1", "plane2"], ["fly_plane", "plane2", "airport2"], ["unload_plane", "package1", "airport2"], ["drive_truck", "truck6", "airport2"], ["load_truck", "package1", "truck6"], ["drive_truck", "truck6", "location10"], ["unload_truck", "package1", "location10"]]) ## Goal 3: package1 is at location1 (no actions needed) @@ -278,12 +196,4 @@ func test_move_goal_3(): ## Goal 4: package1 is at location2 func test_move_goal_4(): var plan = planner.find_plan(state1.duplicate(true), [["at", "package1", "location2"]]) - assert_eq( - plan, - [ - ["drive_truck", "truck1", "location1"], - ["load_truck", "package1", "truck1"], - ["drive_truck", "truck1", "location2"], - ["unload_truck", "package1", "location2"] - ] - ) + assert_eq(plan, [["drive_truck", "truck1", "location1"], ["load_truck", "package1", "truck1"], ["drive_truck", "truck1", "location2"], ["unload_truck", "package1", "location2"]]) diff --git a/goal_task_tests/test_railroad.gd b/goal_task_tests/test_railroad.gd index 4915b1d..3be29a7 100644 --- a/goal_task_tests/test_railroad.gd +++ b/goal_task_tests/test_railroad.gd @@ -9,86 +9,61 @@ var domain_name := "railroad_htn" var the_domain := preload("res://addons/task_goal/core/domain.gd").new("plan") var planner := preload("res://addons/task_goal/core/plan.gd").new() + func move_train_1(state: Dictionary) -> Dictionary: print("Moving train 1...") # Update state to reflect the movement of train 1 return state + func move_train_2(state: Dictionary) -> Dictionary: print("Moving train 2...") # Update state to reflect the movement of train 2 return state + func move_train_3(state: Dictionary) -> Dictionary: print("Moving train 3...") # Update state to reflect the movement of train 3 return state + func move_train_4(state: Dictionary) -> Dictionary: print("Moving train 4...") # Update state to reflect the movement of train 4 return state + func move_train_5(state: Dictionary) -> Dictionary: print("Moving train 5...") # Update state to reflect the movement of train 5 return state + func move_train_6(state: Dictionary) -> Dictionary: print("Moving train 6...") # Update state to reflect the movement of train 6 return state + func make_train_movement(state: Dictionary) -> Array: - return [ - ["move_train_1"], - ["move_train_2"], - ["move_train_3"], - ["move_train_4"], - ["move_train_5"], - ["move_train_6"] - ] + return [["move_train_1"], ["move_train_2"], ["move_train_3"], ["move_train_4"], ["move_train_5"], ["move_train_6"]] + func test_railroad() -> void: - var state0 := { - "train_1": true, - "train_2": true, - "train_3": true, - "train_4": true, - "train_5": true, - "train_6": true - } + var state0 := {"train_1": true, "train_2": true, "train_3": true, "train_4": true, "train_5": true, "train_6": true} planner._domains.push_back(the_domain) planner.current_domain = the_domain - planner.declare_actions([ - Callable(self, "move_train_1"), - Callable(self, "move_train_2"), - Callable(self, "move_train_3"), - Callable(self, "move_train_4"), - Callable(self, "move_train_5"), - Callable(self, "move_train_6") - ]) - planner.declare_task_methods( - "make_train_movement", - [ - Callable(self, "make_train_movement") - ] - ) + planner.declare_actions([Callable(self, "move_train_1"), Callable(self, "move_train_2"), Callable(self, "move_train_3"), Callable(self, "move_train_4"), Callable(self, "move_train_5"), Callable(self, "move_train_6")]) + planner.declare_task_methods("make_train_movement", [Callable(self, "make_train_movement")]) # Initialize the state var state1 := state0.duplicate(true) # Set the expected result - var expected := [ - ["move_train_1"], - ["move_train_2"], - ["move_train_3"], - ["move_train_4"], - ["move_train_5"], - ["move_train_6"] - ] + var expected := [["move_train_1"], ["move_train_2"], ["move_train_3"], ["move_train_4"], ["move_train_5"], ["move_train_6"]] var plan := planner.find_plan(state1.duplicate(true), [["make_train_movement"]]) diff --git a/goal_task_tests/test_sandwich.gd b/goal_task_tests/test_sandwich.gd index 4ae8b12..ece3aa1 100644 --- a/goal_task_tests/test_sandwich.gd +++ b/goal_task_tests/test_sandwich.gd @@ -9,11 +9,13 @@ var domain_name := "sandwich_htn" var the_domain := preload("res://addons/task_goal/core/domain.gd").new("plan") var planner := preload("res://addons/task_goal/core/plan.gd").new() + func take_lettuce(state: Dictionary) -> Dictionary: print("Taking lettuce...") state["lettuce"] -= 1 return state + func take_tomato(state: Dictionary) -> Dictionary: print("Taking tomato...") state["tomato"] -= 1 @@ -42,39 +44,37 @@ func assemble_sandwich(state: Dictionary) -> Dictionary: state["bread"] -= 2 return state + func make_complete_sandwich(state: Dictionary) -> Array: - return [ - ["take_tomato"], - ["take_cheese"], - ["take_bread"], - ["take_lettuce"], - ["assemble_sandwich"] - ] + return [["take_tomato"], ["take_cheese"], ["take_bread"], ["take_lettuce"], ["assemble_sandwich"]] + func test_sandwich() -> void: - var state0 := { - "bread": 2, - "cheese": 1, - "lettuce": 1, - "tomato": 1, - "sandwich": 0 - } + var state0 := {"bread": 2, "cheese": 1, "lettuce": 1, "tomato": 1, "sandwich": 0} planner._domains.push_back(the_domain) planner.current_domain = the_domain - - planner.declare_actions([ - Callable(self, "take_lettuce"), - Callable(self, "take_tomato"), - Callable(self, "assemble_sandwich"), - Callable(self, "take_cheese"), - Callable(self, "take_bread"), - ]) - planner.declare_task_methods( - "make_sandwich", - [ - Callable(self, "make_complete_sandwich"), - ] + + ( + planner + . declare_actions( + [ + Callable(self, "take_lettuce"), + Callable(self, "take_tomato"), + Callable(self, "assemble_sandwich"), + Callable(self, "take_cheese"), + Callable(self, "take_bread"), + ] + ) + ) + ( + planner + . declare_task_methods( + "make_sandwich", + [ + Callable(self, "make_complete_sandwich"), + ] + ) ) # Initialize the state @@ -91,7 +91,6 @@ func test_sandwich() -> void: # Find a plan to make a sandwich var plan := planner.find_plan(state1.duplicate(true), [["make_sandwich"]]) - # Check if the plan matches the expected result assert_eq(plan, expected) diff --git a/goal_task_tests/test_simple_gtn.gd b/goal_task_tests/test_simple_gtn.gd index 6a0c19d..318ddc8 100644 --- a/goal_task_tests/test_simple_gtn.gd +++ b/goal_task_tests/test_simple_gtn.gd @@ -31,11 +31,7 @@ var planner = preload("res://addons/task_goal/core/plan.gd").new() } # prototypical initial state -var state0: Dictionary = { - "loc": {"alice": "home_a", "bob": "home_b", "taxi1": "park", "taxi2": "station"}, - "cash": {"alice": 20, "bob": 15}, - "owe": {"alice": 0, "bob": 0} -} +var state0: Dictionary = {"loc": {"alice": "home_a", "bob": "home_b", "taxi1": "park", "taxi2": "station"}, "cash": {"alice": 20, "bob": 15}, "owe": {"alice": 0, "bob": 0}} # initial goal var goal1: Multigoal = Multigoal.new("goal1", {"loc": {"alice": "park"}}) @@ -155,18 +151,9 @@ func _ready(): goal1.state["loc"] = {"alice": "park"} goal2.state["loc"] = {"bob": "park"} goal3.state["loc"] = {"alice": "park", "bob": "park"} - planner.declare_actions( - [ - Callable(self, "walk"), - Callable(self, "call_taxi"), - Callable(self, "ride_taxi"), - Callable(self, "pay_driver") - ] - ) + planner.declare_actions([Callable(self, "walk"), Callable(self, "call_taxi"), Callable(self, "ride_taxi"), Callable(self, "pay_driver")]) - planner.declare_unigoal_methods( - "loc", [Callable(self, "travel_by_foot"), Callable(self, "travel_by_taxi")] - ) + planner.declare_unigoal_methods("loc", [Callable(self, "travel_by_foot"), Callable(self, "travel_by_taxi")]) # GTPyhop provides a built-in multigoal method called m_split_multigoal to # separate a multigoal G into a�collection of unigoals. It returns a list of @@ -266,15 +253,7 @@ func test_simple_gtn(): state1 = state0.duplicate(true) plan = planner.find_plan(state1, [goal3]) # print("Plan %s" % [plan]) - assert_eq( - plan, - [ - ["call_taxi", "alice", "home_a"], - ["ride_taxi", "alice", "park"], - ["pay_driver", "alice", "park"], - ["walk", "bob", "home_b", "park"] - ] - ) + assert_eq(plan, [["call_taxi", "alice", "home_a"], ["ride_taxi", "alice", "park"], ["pay_driver", "alice", "park"], ["walk", "bob", "home_b", "park"]]) var new_state = planner.run_lazy_lookahead(state1, [["loc", "alice", "park"]], SimpleTemporalNetwork.new()) # print("Alice is now at the park, so the planner will return an empty plan:") plan = planner.find_plan(new_state, [["loc", "alice", "park"]]) diff --git a/goal_task_tests/test_simple_htn.gd b/goal_task_tests/test_simple_htn.gd index 6464f74..d745a44 100644 --- a/goal_task_tests/test_simple_htn.gd +++ b/goal_task_tests/test_simple_htn.gd @@ -34,11 +34,7 @@ var planner = preload("res://addons/task_goal/core/plan.gd").new() } # prototypical initial state -var state0: Dictionary = { - "loc": {"alice": "home_a", "bob": "home_b", "taxi1": "park", "taxi2": "station"}, - "cash": {"alice": 20, "bob": 15}, - "owe": {"alice": 0, "bob": 0} -} +var state0: Dictionary = {"loc": {"alice": "home_a", "bob": "home_b", "taxi1": "park", "taxi2": "station"}, "cash": {"alice": 20, "bob": 15}, "owe": {"alice": 0, "bob": 0}} ############################################################################### # Helper functions: @@ -147,22 +143,8 @@ func travel_by_taxi(state, p, y): func test_simple_gtn(): planner._domains.push_back(the_domain) planner.current_domain = the_domain - planner.declare_actions( - [ - Callable(self, "walk"), - Callable(self, "call_taxi"), - Callable(self, "ride_taxi"), - Callable(self, "pay_driver") - ] - ) - planner.declare_task_methods( - "travel", - [ - Callable(self, "do_nothing"), - Callable(self, "travel_by_foot"), - Callable(self, "travel_by_taxi") - ] - ) + planner.declare_actions([Callable(self, "walk"), Callable(self, "call_taxi"), Callable(self, "ride_taxi"), Callable(self, "pay_driver")]) + planner.declare_task_methods("travel", [Callable(self, "do_nothing"), Callable(self, "travel_by_foot"), Callable(self, "travel_by_taxi")]) ############################################################################### # Running the examples @@ -204,9 +186,7 @@ func test_simple_gtn(): # print("-- If verbose=3, the planner will print even more information.") # # print("Find a plan that will first get Alice to the park, then get Bob to the park.") - var plan = planner.find_plan( - state1.duplicate(true), [["travel", "alice", "park"], ["travel", "bob", "park"]] - ) + var plan = planner.find_plan(state1.duplicate(true), [["travel", "alice", "park"], ["travel", "bob", "park"]]) # print("Plan %s" % [plan]) assert_eq( @@ -226,9 +206,7 @@ func test_simple_gtn(): #happen repeatedly until either the taxi arrives or run_lazy_lookahead decides #it has tried too many times.""" # ) - var new_state = planner.run_lazy_lookahead( - state1.duplicate(true), [["travel", "alice", "park"]], SimpleTemporalNetwork.new() - ) + var new_state = planner.run_lazy_lookahead(state1.duplicate(true), [["travel", "alice", "park"]], SimpleTemporalNetwork.new()) # print("") # print("If run_lazy_lookahead succeeded, then Alice is now at the park,") diff --git a/goal_task_tests/test_simple_temporal_network.gd b/goal_task_tests/test_simple_temporal_network.gd new file mode 100644 index 0000000..26a0ed4 --- /dev/null +++ b/goal_task_tests/test_simple_temporal_network.gd @@ -0,0 +1,61 @@ +extends "res://addons/gut/test.gd" + +var stn = null + + +func setup(): + stn = SimpleTemporalNetwork.new() + + +func test_to_dictionary(): + var result = stn.to_dictionary() + assert_eq(typeof(result), TYPE_DICTIONARY, "to_dictionary should return a Dictionary") + + +func test_get_node_index(): + var node_interval = Vector2i(1, 2) + var index = stn.get_node_index(node_interval) + assert_eq(index, -1, "get_node_index should return -1 for non-existing node") + + +func test_init_matrix(): + stn.num_nodes = 3 + stn._init_matrix() + assert_eq(stn.stn_matrix.size(), 3, "_init_matrix should initialize matrix with size equal to num_nodes") + + +func test_add_temporal_constraint(): + var from_constraint = TemporalConstraint.new(1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource") + var result = stn.add_temporal_constraint(from_constraint) + assert_true(result, "add_temporal_constraint should return true when adding valid constraint") + + +func test_get_temporal_constraint_by_name(): + var constraint = TemporalConstraint.new(1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource") + stn.constraints.append(constraint) + var result = stn.get_temporal_constraint_by_name("resource") + assert_eq(result, constraint, "get_temporal_constraint_by_name should return the correct constraint") + + +func test_propagate_constraints(): + stn.num_nodes = 3 + stn._init_matrix() + var result = stn.propagate_constraints() + assert_true(result, "propagate_constraints should return true when there are no negative diagonal values") + + +func test_is_consistent(): + var result = stn.is_consistent() + assert_true(result, "is_consistent should return true when constraints are consistent") + + +func test_update_state(): + var state = {"constraint": TemporalConstraint.new(1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource")} + stn.update_state(state) + assert_eq(stn.constraints.size(), 1, "update_state should add the constraint to the constraints array") + + +func test_is_consistent_with(): + var constraint = TemporalConstraint.new(1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource") + var result = stn.is_consistent_with(constraint) + assert_true(result, "is_consistent_with should return true when the network is consistent with the given constraint") diff --git a/goal_task_tests/test_temporal_logistics.gd b/goal_task_tests/test_temporal_logistics.gd index 7a6c3f4..ce00859 100644 --- a/goal_task_tests/test_temporal_logistics.gd +++ b/goal_task_tests/test_temporal_logistics.gd @@ -87,11 +87,7 @@ func find_airport(state, l): func m_drive_truck(state, t, l): - if ( - t in state.trucks - and l in state.locations - and state.in_city[state.truck_at[t]] == state.in_city[l] - ): + if t in state.trucks and l in state.locations and state.in_city[state.truck_at[t]] == state.in_city[l]: return [["drive_truck", t, l]] @@ -124,11 +120,7 @@ func m_unload_plane(state, o, a): func move_within_city(state, o, l): - if ( - o in state.packages - and state.at[o] in state.locations - and state.in_city[state.at[o]] == state.in_city[l] - ): + if o in state.packages and state.at[o] in state.locations and state.in_city[state.at[o]] == state.in_city[l]: var t = find_truck(state, o) if t: return [["truck_at", t, state.at[o]], ["at", o, t], ["truck_at", t, l], ["at", o, l]] @@ -136,29 +128,15 @@ func move_within_city(state, o, l): func move_between_airports(state, o, a): - if ( - o in state.packages - and state.at[o] in state.airports - and a in state.airports - and state.in_city[state.at[o]] != state.in_city[a] - ): + if o in state.packages and state.at[o] in state.airports and a in state.airports and state.in_city[state.at[o]] != state.in_city[a]: var plane = find_plane(state, o) if plane: - return [ - ["plane_at", plane, state.at[o]], - ["at", o, plane], - ["plane_at", plane, a], - ["at", o, a] - ] + return [["plane_at", plane, state.at[o]], ["at", o, plane], ["plane_at", plane, a], ["at", o, a]] return false func move_between_city(state, o, l): - if ( - o in state.packages - and state.at[o] in state.locations - and state.in_city[state.at[o]] != state.in_city[l] - ): + if o in state.packages and state.at[o] in state.locations and state.in_city[state.at[o]] != state.in_city[l]: var a1 = find_airport(state, state.at[o]) var a2 = find_airport(state, l) if a1 and a2: @@ -168,43 +146,20 @@ func move_between_city(state, o, l): var state1: Dictionary + func test_move_goal_1(): state1.clear() planner._domains.push_back(the_domain) # If we've changed to some other domain, this will change us back. planner.current_domain = the_domain - planner.declare_actions( - [ - Callable(self, "drive_truck"), - Callable(self, "load_truck"), - Callable(self, "unload_truck"), - Callable(self, "fly_plane"), - Callable(self, "load_plane"), - Callable(self, "unload_plane") - ] - ) + planner.declare_actions([Callable(self, "drive_truck"), Callable(self, "load_truck"), Callable(self, "unload_truck"), Callable(self, "fly_plane"), Callable(self, "load_plane"), Callable(self, "unload_plane")]) - planner.declare_unigoal_methods( - "at", - [ - Callable(self, "m_load_truck"), - Callable(self, "m_unload_truck"), - Callable(self, "m_load_plane"), - Callable(self, "m_unload_plane") - ] - ) + planner.declare_unigoal_methods("at", [Callable(self, "m_load_truck"), Callable(self, "m_unload_truck"), Callable(self, "m_load_plane"), Callable(self, "m_unload_plane")]) planner.declare_unigoal_methods("truck_at", [Callable(self, "m_drive_truck")]) planner.declare_unigoal_methods("plane_at", [Callable(self, "m_fly_plane")]) - planner.declare_unigoal_methods( - "at", - [ - Callable(self, "move_within_city"), - Callable(self, "move_between_airports"), - Callable(self, "move_between_city") - ] - ) + planner.declare_unigoal_methods("at", [Callable(self, "move_within_city"), Callable(self, "move_between_airports"), Callable(self, "move_between_city")]) # planner.print_domain() @@ -218,14 +173,7 @@ func test_move_goal_1(): state1.at = {"package1": "location1", "package2": "location2"} state1.truck_at = {"truck1": "location3", "truck6": "location10"} state1.plane_at = {"plane2": "airport2"} - state1.in_city = { - "location1": "city1", - "location2": "city1", - "location3": "city1", - "airport1": "city1", - "location10": "city2", - "airport2": "city2" - } + state1.in_city = {"location1": "city1", "location2": "city1", "location3": "city1", "airport1": "city1", "location10": "city2", "airport2": "city2"} var stn = SimpleTemporalNetwork.new() @@ -237,31 +185,29 @@ func test_move_goal_1(): var load_plane2_duration = 2 var unload_plane2_duration = 3 - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, drive_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "drive_truck1"), - TemporalConstraint.new(0, 0, load_truck1_duration, TemporalConstraint.TemporalQualifier.AT_START, "load_truck1")) # No gap between driving and loading + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, drive_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "drive_truck1"), TemporalConstraint.new(0, 0, load_truck1_duration, TemporalConstraint.TemporalQualifier.AT_START, "load_truck1")) # No gap between driving and loading + + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, load_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "load_truck1"), TemporalConstraint.new(0, 0, drive_truck6_duration, TemporalConstraint.TemporalQualifier.AT_START, "drive_truck6")) # No gap between loading and driving - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, load_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "load_truck1"), - TemporalConstraint.new(0, 0, drive_truck6_duration, TemporalConstraint.TemporalQualifier.AT_START, "drive_truck6")) # No gap between loading and driving + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, drive_truck6_duration, TemporalConstraint.TemporalQualifier.AT_END, "drive_truck6"), TemporalConstraint.new(0, 0, unload_truck1_duration, TemporalConstraint.TemporalQualifier.AT_START, "unload_truck1")) # No gap between driving and unloading - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, drive_truck6_duration, TemporalConstraint.TemporalQualifier.AT_END, "drive_truck6"), - TemporalConstraint.new(0, 0, unload_truck1_duration, TemporalConstraint.TemporalQualifier.AT_START, "unload_truck1")) # No gap between driving and unloading + stn.add_temporal_constraint(TemporalConstraint.new(0, 2, unload_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "unload_truck1"), TemporalConstraint.new(0, 0, fly_plane2_duration, TemporalConstraint.TemporalQualifier.OVERALL, "fly_plane2")) # A 2 time unit gap between unloading and flying - stn.add_temporal_constraint(TemporalConstraint.new(0, 2, unload_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "unload_truck1"), - TemporalConstraint.new(0, 0, fly_plane2_duration, TemporalConstraint.TemporalQualifier.OVERALL, "fly_plane2")) # A 2 time unit gap between unloading and flying + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, fly_plane2_duration, TemporalConstraint.TemporalQualifier.AT_END, "fly_plane2"), TemporalConstraint.new(0, 0, load_plane2_duration, TemporalConstraint.TemporalQualifier.AT_START, "load_plane2")) # No gap between flying and loading - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, fly_plane2_duration, TemporalConstraint.TemporalQualifier.AT_END, "fly_plane2"), - TemporalConstraint.new(0, 0, load_plane2_duration, TemporalConstraint.TemporalQualifier.AT_START, "load_plane2")) # No gap between flying and loading + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, load_plane2_duration, TemporalConstraint.TemporalQualifier.AT_END, "load_plane2"), TemporalConstraint.new(0, 0, unload_plane2_duration, TemporalConstraint.TemporalQualifier.AT_START, "unload_plane2")) # No gap between loading and unloading - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, load_plane2_duration, TemporalConstraint.TemporalQualifier.AT_END, "load_plane2"), - TemporalConstraint.new(0, 0, unload_plane2_duration, TemporalConstraint.TemporalQualifier.AT_START, "unload_plane2")) # No gap between loading and unloading - # Define the initial state and goal state1.at.package1 = "location1" - var plan = planner.find_plan(state1.duplicate(true), [ - ["at", "package1", "airport2"], - ], stn) - assert_eq( - plan, - [["drive_truck", "truck1", "location1"], ["load_truck", "package1", "truck1"], ["drive_truck", "truck1", "airport1"], ["unload_truck", "package1", "airport1"], ["fly_plane", "plane2", "airport1"], ["load_plane", "package1", "plane2"], ["fly_plane", "plane2", "airport2"], ["unload_plane", "package1", "airport2"]] + var plan = ( + planner + . find_plan( + state1.duplicate(true), + [ + ["at", "package1", "airport2"], + ], + stn + ) ) + assert_eq(plan, [["drive_truck", "truck1", "location1"], ["load_truck", "package1", "truck1"], ["drive_truck", "truck1", "airport1"], ["unload_truck", "package1", "airport1"], ["fly_plane", "plane2", "airport1"], ["load_plane", "package1", "plane2"], ["fly_plane", "plane2", "airport2"], ["unload_plane", "package1", "airport2"]]) diff --git a/goal_task_tests/test_temporal_logistics_small.gd b/goal_task_tests/test_temporal_logistics_small.gd index 3ebbba6..ed98900 100644 --- a/goal_task_tests/test_temporal_logistics_small.gd +++ b/goal_task_tests/test_temporal_logistics_small.gd @@ -87,11 +87,7 @@ func find_airport(state, l): func m_drive_truck(state, t, l): - if ( - t in state.trucks - and l in state.locations - and state.in_city[state.truck_at[t]] == state.in_city[l] - ): + if t in state.trucks and l in state.locations and state.in_city[state.truck_at[t]] == state.in_city[l]: return [["drive_truck", t, l]] @@ -124,11 +120,7 @@ func m_unload_plane(state, o, a): func move_within_city(state, o, l): - if ( - o in state.packages - and state.at[o] in state.locations - and state.in_city[state.at[o]] == state.in_city[l] - ): + if o in state.packages and state.at[o] in state.locations and state.in_city[state.at[o]] == state.in_city[l]: var t = find_truck(state, o) if t: return [["truck_at", t, state.at[o]], ["at", o, t], ["truck_at", t, l], ["at", o, l]] @@ -136,29 +128,15 @@ func move_within_city(state, o, l): func move_between_airports(state, o, a): - if ( - o in state.packages - and state.at[o] in state.airports - and a in state.airports - and state.in_city[state.at[o]] != state.in_city[a] - ): + if o in state.packages and state.at[o] in state.airports and a in state.airports and state.in_city[state.at[o]] != state.in_city[a]: var plane = find_plane(state, o) if plane: - return [ - ["plane_at", plane, state.at[o]], - ["at", o, plane], - ["plane_at", plane, a], - ["at", o, a] - ] + return [["plane_at", plane, state.at[o]], ["at", o, plane], ["plane_at", plane, a], ["at", o, a]] return false func move_between_city(state, o, l): - if ( - o in state.packages - and state.at[o] in state.locations - and state.in_city[state.at[o]] != state.in_city[l] - ): + if o in state.packages and state.at[o] in state.locations and state.in_city[state.at[o]] != state.in_city[l]: var a1 = find_airport(state, state.at[o]) var a2 = find_airport(state, l) if a1 and a2: @@ -168,43 +146,20 @@ func move_between_city(state, o, l): var state1: Dictionary + func test_move_goal_1(): state1.clear() planner._domains.push_back(the_domain) # If we've changed to some other domain, this will change us back. planner.current_domain = the_domain - planner.declare_actions( - [ - Callable(self, "drive_truck"), - Callable(self, "load_truck"), - Callable(self, "unload_truck"), - Callable(self, "fly_plane"), - Callable(self, "load_plane"), - Callable(self, "unload_plane") - ] - ) + planner.declare_actions([Callable(self, "drive_truck"), Callable(self, "load_truck"), Callable(self, "unload_truck"), Callable(self, "fly_plane"), Callable(self, "load_plane"), Callable(self, "unload_plane")]) - planner.declare_unigoal_methods( - "at", - [ - Callable(self, "m_load_truck"), - Callable(self, "m_unload_truck"), - Callable(self, "m_load_plane"), - Callable(self, "m_unload_plane") - ] - ) + planner.declare_unigoal_methods("at", [Callable(self, "m_load_truck"), Callable(self, "m_unload_truck"), Callable(self, "m_load_plane"), Callable(self, "m_unload_plane")]) planner.declare_unigoal_methods("truck_at", [Callable(self, "m_drive_truck")]) planner.declare_unigoal_methods("plane_at", [Callable(self, "m_fly_plane")]) - planner.declare_unigoal_methods( - "at", - [ - Callable(self, "move_within_city"), - Callable(self, "move_between_airports"), - Callable(self, "move_between_city") - ] - ) + planner.declare_unigoal_methods("at", [Callable(self, "move_within_city"), Callable(self, "move_between_airports"), Callable(self, "move_between_city")]) # planner.print_domain() @@ -218,23 +173,17 @@ func test_move_goal_1(): var load_plane2_duration = 2 var unload_plane2_duration = 3 - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, drive_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "drive_truck1"), - TemporalConstraint.new(0, 0, load_truck1_duration, TemporalConstraint.TemporalQualifier.AT_START, "load_truck1")) + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, drive_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "drive_truck1"), TemporalConstraint.new(0, 0, load_truck1_duration, TemporalConstraint.TemporalQualifier.AT_START, "load_truck1")) - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, load_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "load_truck1"), - TemporalConstraint.new(0, 0, drive_truck6_duration, TemporalConstraint.TemporalQualifier.AT_START, "drive_truck6")) + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, load_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "load_truck1"), TemporalConstraint.new(0, 0, drive_truck6_duration, TemporalConstraint.TemporalQualifier.AT_START, "drive_truck6")) - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, drive_truck6_duration, TemporalConstraint.TemporalQualifier.AT_END, "drive_truck6"), - TemporalConstraint.new(0, 0, unload_truck1_duration, TemporalConstraint.TemporalQualifier.AT_START, "unload_truck1")) + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, drive_truck6_duration, TemporalConstraint.TemporalQualifier.AT_END, "drive_truck6"), TemporalConstraint.new(0, 0, unload_truck1_duration, TemporalConstraint.TemporalQualifier.AT_START, "unload_truck1")) - stn.add_temporal_constraint(TemporalConstraint.new(0, 2, unload_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "unload_truck1"), - TemporalConstraint.new(0, 0, fly_plane2_duration, TemporalConstraint.TemporalQualifier.OVERALL, "fly_plane2")) + stn.add_temporal_constraint(TemporalConstraint.new(0, 2, unload_truck1_duration, TemporalConstraint.TemporalQualifier.AT_END, "unload_truck1"), TemporalConstraint.new(0, 0, fly_plane2_duration, TemporalConstraint.TemporalQualifier.OVERALL, "fly_plane2")) - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, fly_plane2_duration, TemporalConstraint.TemporalQualifier.AT_END, "fly_plane2"), - TemporalConstraint.new(0, 0, load_plane2_duration, TemporalConstraint.TemporalQualifier.AT_START, "load_plane2")) + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, fly_plane2_duration, TemporalConstraint.TemporalQualifier.AT_END, "fly_plane2"), TemporalConstraint.new(0, 0, load_plane2_duration, TemporalConstraint.TemporalQualifier.AT_START, "load_plane2")) - stn.add_temporal_constraint(TemporalConstraint.new(0, 0, load_plane2_duration, TemporalConstraint.TemporalQualifier.AT_END, "load_plane2"), - TemporalConstraint.new(0, 0, unload_plane2_duration, TemporalConstraint.TemporalQualifier.AT_START, "unload_plane2")) + stn.add_temporal_constraint(TemporalConstraint.new(0, 0, load_plane2_duration, TemporalConstraint.TemporalQualifier.AT_END, "load_plane2"), TemporalConstraint.new(0, 0, unload_plane2_duration, TemporalConstraint.TemporalQualifier.AT_START, "unload_plane2")) state1.packages = ["package1", "package2"] state1.trucks = ["truck1", "truck6"] @@ -246,20 +195,17 @@ func test_move_goal_1(): state1.at = {"package1": "location1", "package2": "location2"} state1.truck_at = {"truck1": "location3", "truck6": "location10"} state1.plane_at = {"plane2": "airport2"} - state1.in_city = { - "location1": "city1", - "location2": "city1", - "location3": "city1", - "airport1": "city1", - "location10": "city2", - "airport2": "city2" - } - - var plan = planner.find_plan(state1.duplicate(true), [ - ["at", "package1", "airport2"], - ], stn) - assert_eq( - plan, - [["drive_truck", "truck1", "location1"], ["load_truck", "package1", "truck1"], ["drive_truck", "truck1", "airport1"], ["unload_truck", "package1", "airport1"], ["fly_plane", "plane2", "airport1"], ["load_plane", "package1", "plane2"], ["fly_plane", "plane2", "airport2"], ["unload_plane", "package1", "airport2"]] + state1.in_city = {"location1": "city1", "location2": "city1", "location3": "city1", "airport1": "city1", "location10": "city2", "airport2": "city2"} + + var plan = ( + planner + . find_plan( + state1.duplicate(true), + [ + ["at", "package1", "airport2"], + ], + stn + ) ) + assert_eq(plan, [["drive_truck", "truck1", "location1"], ["load_truck", "package1", "truck1"], ["drive_truck", "truck1", "airport1"], ["unload_truck", "package1", "airport1"], ["fly_plane", "plane2", "airport1"], ["load_plane", "package1", "plane2"], ["fly_plane", "plane2", "airport2"], ["unload_plane", "package1", "airport2"]]) print(stn.to_dictionary())