From 163afd8615738f75008f1e9742b69c4361c23e5a Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Wed, 27 Dec 2023 23:52:47 -0800 Subject: [PATCH] Run gdformat. --- addons/task_goal/core/domain.gd | 40 ++- addons/task_goal/core/plan.gd | 59 +++- .../task_goal/core/simple_temporal_network.gd | 62 +++- addons/task_goal/core/temporal_constraint.gd | 14 +- goal_task_tests/core/test_constraints.gd | 18 +- goal_task_tests/core/test_performance.gd | 40 ++- .../core/test_simple_temporal_network.gd | 50 ++- .../core_problems/test_logistics.gd | 120 ++++++- .../core_problems/test_simple_gtn.gd | 39 ++- .../core_problems/test_simple_htn.gd | 30 +- goal_task_tests/domains/3d_artwork_domain.gd | 100 +++++- goal_task_tests/domains/anime_domain.gd | 94 +++++- .../domains/college_town_domain.gd | 50 ++- .../game_problems/test_3d_avatar.gd | 21 +- goal_task_tests/game_problems/test_anime.gd | 299 ++++++++++++++++-- .../test_magical_town_planner.gd | 86 ++++- 16 files changed, 952 insertions(+), 170 deletions(-) diff --git a/addons/task_goal/core/domain.gd b/addons/task_goal/core/domain.gd index 3c594cb..73c7eb5 100644 --- a/addons/task_goal/core/domain.gd +++ b/addons/task_goal/core/domain.gd @@ -29,21 +29,44 @@ var _unigoal_method_dict: Dictionary = {} var _multigoal_method_list: Array = [] -func _m_verify_g(state: Dictionary, method: String, state_var: String, arg: String, desired_val: Variant, depth: int) -> Variant: + +func _m_verify_g( + state: Dictionary, + method: String, + state_var: String, + arg: String, + desired_val: Variant, + depth: int +) -> Variant: if state[state_var][arg] != desired_val: if verbose >= 3: - print("Depth %s: method %s didn't achieve\nGoal %s[%s] = %s" % [depth, method, state_var, arg, desired_val]) + print( + ( + "Depth %s: method %s didn't achieve\nGoal %s[%s] = %s" + % [depth, method, state_var, arg, desired_val] + ) + ) return false - + if state.has("stn"): for p in state["stn"].keys(): if not state["stn"][p].is_consistent(): if verbose >= 3: - print("Depth %s: method %s resulted in inconsistent STN for %s" % [depth, method, p]) + print( + ( + "Depth %s: method %s resulted in inconsistent STN for %s" + % [depth, method, p] + ) + ) return false if verbose >= 3: - print("Depth %s: method %s achieved\nGoal %s[%s] = %s" % [depth, method, state_var, arg, desired_val]) + print( + ( + "Depth %s: method %s achieved\nGoal %s[%s] = %s" + % [depth, method, state_var, arg, desired_val] + ) + ) return [] @@ -70,7 +93,12 @@ func _m_verify_mg(state: Dictionary, method: String, multigoal: Multigoal, depth for p in state["stn"].keys(): if not state["stn"][p].is_consistent(): if verbose >= 3: - print("Depth %s: method %s resulted in inconsistent STN for %s" % [depth, method, p]) + print( + ( + "Depth %s: method %s resulted in inconsistent STN for %s" + % [depth, method, p] + ) + ) return false if verbose >= 3: diff --git a/addons/task_goal/core/plan.gd b/addons/task_goal/core/plan.gd index a0f9e28..bc32f8c 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 @@ -40,6 +40,7 @@ var _next_domain_number: int = 0 const _domain_const = preload("domain.gd") + ## Print domain's actions, commands, and methods. The optional 'domain' ## argument defaults to the current domain func print_domain(domain: Object = null) -> void: @@ -284,7 +285,9 @@ var verify_goals: bool = true ## Actions in HTN are atomic units of work, representing the simplest tasks that can't be further broken down. Actions often called primitives. -func _apply_action_and_continue(state: Dictionary, task1: Array, todo_list: Array, plan: Array, depth: int) -> Variant: +func _apply_action_and_continue( + state: Dictionary, task1: Array, todo_list: Array, plan: Array, depth: int +) -> Variant: var action: Callable = current_domain._action_dict[task1[0]] if verbose >= 2: print("Depth %s, Action %s: " % [depth, str([action.get_method()] + task1.slice(1))]) @@ -300,11 +303,15 @@ func _apply_action_and_continue(state: Dictionary, task1: Array, todo_list: Arra print("Task: ", task1) print("State: ", state) if verbose >= 2: - print("Recursive call: Not applicable action: ", str([action.get_method()] + task1.slice(1))) + print( + "Recursive call: Not applicable action: ", str([action.get_method()] + task1.slice(1)) + ) return false -func _refine_task_and_continue(state: Dictionary, task1: Array, todo_list: Array, plan: Array, depth: int) -> Variant: +func _refine_task_and_continue( + state: Dictionary, task1: Array, todo_list: Array, plan: Array, depth: int +) -> Variant: var relevant: Array = current_domain._task_method_dict[task1[0]] if verbose >= 3: var string_array: PackedStringArray = [] @@ -314,12 +321,14 @@ func _refine_task_and_continue(state: Dictionary, task1: Array, todo_list: Array for method in relevant: if verbose >= 2: print("Depth %s, Trying method %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) + ) if subtasks is Array: if verbose >= 3: print("Intermediate computation: Method applicable.") print("Depth %s, Subtasks: %s" % [depth, subtasks]) - + var result: Variant = seek_plan(state, subtasks + todo_list, plan, depth + 1) if result is Array: return result @@ -328,7 +337,9 @@ func _refine_task_and_continue(state: Dictionary, task1: Array, todo_list: Array return false -func _refine_unigoal_and_continue(state: Dictionary, goal1: Array, todo_list: Array, plan: Array, depth: int) -> Variant: +func _refine_unigoal_and_continue( + state: Dictionary, goal1: Array, todo_list: Array, plan: Array, depth: int +) -> Variant: if verbose >= 3: print("Depth %s, Goal %s: " % [depth, goal1]) @@ -362,7 +373,9 @@ func _refine_unigoal_and_continue(state: Dictionary, goal1: Array, todo_list: Ar 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 = [] @@ -378,7 +391,9 @@ func _refine_unigoal_and_continue(state: Dictionary, goal1: Array, todo_list: Ar return false -func _refine_multigoal_and_continue(state: Dictionary, goal1: Multigoal, todo_list: Array, plan: Array, depth: int) -> Variant: +func _refine_multigoal_and_continue( + state: Dictionary, goal1: Multigoal, todo_list: Array, plan: Array, depth: int +) -> Variant: if verbose >= 3: print("Depth %s, Multigoal %s: " % [depth, goal1]) @@ -489,7 +504,12 @@ func _item_to_string(item): ## no corresponding command definition, it uses the action definition instead. func run_lazy_lookahead(state: Dictionary, todo_list: Array, max_tries: int = 10) -> Dictionary: if verbose >= 1: - print("RunLazyLookahead> run_lazy_lookahead, verbose = %s, max_tries = %s" % [verbose, max_tries]) + print( + ( + "RunLazyLookahead> run_lazy_lookahead, verbose = %s, max_tries = %s" + % [verbose, max_tries] + ) + ) print("RunLazyLookahead> initial state: %s" % [state.keys()]) print("RunLazyLookahead> To do:", todo_list) @@ -500,12 +520,20 @@ func run_lazy_lookahead(state: Dictionary, todo_list: Array, max_tries: int = 10 print("RunLazyLookahead> %s%s call to find_plan:" % [tries, ordinals.get(tries, "")]) var plan = find_plan(state, todo_list) - if plan == null or (typeof(plan) == TYPE_ARRAY and plan.is_empty()) or (typeof(plan) == TYPE_DICTIONARY and !plan): + if ( + plan == null + or (typeof(plan) == TYPE_ARRAY and plan.is_empty()) + or (typeof(plan) == TYPE_DICTIONARY and !plan) + ): if verbose >= 1: print("run_lazy_lookahead: find_plan has failed") return state - if plan == null or (typeof(plan) == TYPE_ARRAY and plan.is_empty()) or (typeof(plan) == TYPE_DICTIONARY and !plan): + if ( + plan == null + or (typeof(plan) == TYPE_ARRAY and plan.is_empty()) + or (typeof(plan) == TYPE_DICTIONARY and !plan) + ): if verbose >= 1: print("RunLazyLookahead> Empty plan => success\nafter {tries} calls to find_plan.") if verbose >= 2: @@ -525,7 +553,12 @@ func run_lazy_lookahead(state: Dictionary, todo_list: Array, max_tries: int = 10 state = new_state else: if verbose >= 1: - print("RunLazyLookahead> WARNING: action %s failed; will call find_plan." % [action_name]) + print( + ( + "RunLazyLookahead> WARNING: action %s failed; will call find_plan." + % [action_name] + ) + ) break if verbose >= 1 and state != null: diff --git a/addons/task_goal/core/simple_temporal_network.gd b/addons/task_goal/core/simple_temporal_network.gd index 623e9ee..cdb9d98 100644 --- a/addons/task_goal/core/simple_temporal_network.gd +++ b/addons/task_goal/core/simple_temporal_network.gd @@ -14,6 +14,7 @@ var node_indices: Dictionary = {} var node_index_cache = {} + func _to_string() -> String: if resource_name.is_empty(): return "SimpleTemporalNetwork" @@ -40,12 +41,20 @@ var outgoing_edges: Dictionary = {} func check_overlap(new_constraint: TemporalConstraint) -> bool: for constraint in constraints: if constraint.resource_name == new_constraint.resource_name: - if (constraint.time_interval.x < new_constraint.time_interval.y) and (new_constraint.time_interval.x < constraint.time_interval.y): + if ( + (constraint.time_interval.x < new_constraint.time_interval.y) + and (new_constraint.time_interval.x < constraint.time_interval.y) + ): return true return false -func add_temporal_constraint(from_constraint: TemporalConstraint, to_constraint: TemporalConstraint = null, min_gap: float = 0, max_gap: float = 0) -> bool: +func add_temporal_constraint( + from_constraint: TemporalConstraint, + to_constraint: TemporalConstraint = null, + min_gap: float = 0, + max_gap: float = 0 +) -> bool: if not validate_constraints(from_constraint, to_constraint, min_gap, max_gap): print("Failed to validate constraints") return false @@ -85,11 +94,18 @@ func update_constraints_list(constraint: TemporalConstraint, node: TemporalConst ## This function validates the constraints and returns a boolean value. func validate_constraints(from_constraint, to_constraint, min_gap: float, max_gap: float) -> bool: - if not from_constraint or not from_constraint.get("time_interval") or not from_constraint.get("duration"): + if ( + not from_constraint + or not from_constraint.get("time_interval") + or not from_constraint.get("duration") + ): print("Invalid from_constraint %s" % from_constraint) return false - if from_constraint['duration'] > (from_constraint['time_interval'][1] - from_constraint['time_interval'][0]): + if ( + from_constraint["duration"] + > (from_constraint["time_interval"][1] - from_constraint["time_interval"][0]) + ): print("Duration is longer than time interval for from_constraint %s" % from_constraint) return false @@ -99,12 +115,19 @@ func validate_constraints(from_constraint, to_constraint, min_gap: float, max_ga print("Invalid to_constraint %s" % to_constraint) return false - if to_constraint['duration'] > (to_constraint['time_interval'][1] - to_constraint['time_interval'][0]): + if ( + to_constraint["duration"] + > (to_constraint["time_interval"][1] - to_constraint["time_interval"][0]) + ): print("Duration is longer than time interval for to_constraint %s" % to_constraint) return false # Check if min_gap and max_gap are valid - if typeof(min_gap) != TYPE_FLOAT or min_gap < 0 or (typeof(max_gap) != TYPE_FLOAT and max_gap != INF): + if ( + typeof(min_gap) != TYPE_FLOAT + or min_gap < 0 + or (typeof(max_gap) != TYPE_FLOAT and max_gap != INF) + ): print("Invalid gap values") return false @@ -112,7 +135,9 @@ func validate_constraints(from_constraint, to_constraint, min_gap: float, max_ga ## This function adds the constraints to the list. -func add_constraints_to_list(from_constraint: TemporalConstraint, to_constraint: TemporalConstraint): +func add_constraints_to_list( + from_constraint: TemporalConstraint, to_constraint: TemporalConstraint +): if from_constraint: constraints.append(from_constraint) if to_constraint: @@ -153,8 +178,18 @@ func is_consistent() -> bool: constraints.sort_custom(TemporalConstraint.sort_func) for i in range(constraints.size()): for j in range(i + 1, constraints.size()): - if constraints[i].time_interval.y > constraints[j].time_interval.x and constraints[i].time_interval.x < constraints[j].time_interval.y: - print("Overlapping constraints: " + str(constraints[i]) + " and " + str(constraints[j])) + if ( + constraints[i].time_interval.y > constraints[j].time_interval.x + and constraints[i].time_interval.x < constraints[j].time_interval.y + ): + print( + ( + "Overlapping constraints: " + + str(constraints[i]) + + " and " + + str(constraints[j]) + ) + ) return false var decompositions = enumerate_decompositions(constraints[i]) if decompositions.is_empty(): @@ -171,7 +206,6 @@ func enumerate_decompositions(vertex: TemporalConstraint) -> Array[Array]: var leafs: Array[Array] = [[]] if is_leaf(vertex): - leafs.append([vertex]) else: if is_or(vertex): @@ -226,5 +260,11 @@ func update_state(state: Dictionary) -> void: for key in state: var value = state[key] if value is TemporalConstraint: - var constraint = TemporalConstraint.new(value.time_interval.x, value.time_interval.y, value.duration, value.temporal_qualifier, value.resource_name) + var constraint = TemporalConstraint.new( + value.time_interval.x, + value.time_interval.y, + value.duration, + value.temporal_qualifier, + value.resource_name + ) add_temporal_constraint(constraint) diff --git a/addons/task_goal/core/temporal_constraint.gd b/addons/task_goal/core/temporal_constraint.gd index ca05f00..d64a9c9 100644 --- a/addons/task_goal/core/temporal_constraint.gd +++ b/addons/task_goal/core/temporal_constraint.gd @@ -26,8 +26,18 @@ func _init(start: int, end: int, duration: int, qualifier: TemporalQualifier, re func _to_string() -> String: - return str({"resource_name": resource_name, "time_interval": time_interval, "duration": duration, "temporal_qualifier": temporal_qualifier}) + return str( + { + "resource_name": resource_name, + "time_interval": time_interval, + "duration": duration, + "temporal_qualifier": temporal_qualifier + } + ) static func sort_func(a: TemporalConstraint, b: TemporalConstraint) -> bool: - return a.time_interval.x < b.time_interval.x or (a.time_interval.x == b.time_interval.x and a.time_interval.y < b.time_interval.y) + return ( + a.time_interval.x < b.time_interval.x + or (a.time_interval.x == b.time_interval.x and a.time_interval.y < b.time_interval.y) + ) diff --git a/goal_task_tests/core/test_constraints.gd b/goal_task_tests/core/test_constraints.gd index cbbb7c9..13831f2 100644 --- a/goal_task_tests/core/test_constraints.gd +++ b/goal_task_tests/core/test_constraints.gd @@ -7,6 +7,7 @@ extends "res://addons/gut/test.gd" var stn = null + func test_is_consistent_with_no_constraints() -> void: stn = SimpleTemporalNetwork.new() assert_true(stn.is_consistent(), "An empty STN should be consistent") @@ -14,15 +15,24 @@ func test_is_consistent_with_no_constraints() -> void: func test_is_consistent_with_one_constraint() -> void: stn = SimpleTemporalNetwork.new() - var constraint = TemporalConstraint.new(0, 10, 5, TemporalConstraint.TemporalQualifier.AT_START, "some_resource") + var constraint = TemporalConstraint.new( + 0, 10, 5, TemporalConstraint.TemporalQualifier.AT_START, "some_resource" + ) stn.add_temporal_constraint(constraint) assert_true(stn.is_consistent(), "An STN with one constraint should be consistent") func test_is_consistent_with_inconsistent_constraints() -> void: stn = SimpleTemporalNetwork.new() - var constraint1 = TemporalConstraint.new(0, 10, 5, TemporalConstraint.TemporalQualifier.AT_START, "resource1") - var constraint2 = TemporalConstraint.new(5, 15, 10, TemporalConstraint.TemporalQualifier.AT_START, "resource2") + var constraint1 = TemporalConstraint.new( + 0, 10, 5, TemporalConstraint.TemporalQualifier.AT_START, "resource1" + ) + var constraint2 = TemporalConstraint.new( + 5, 15, 10, TemporalConstraint.TemporalQualifier.AT_START, "resource2" + ) stn.add_temporal_constraint(constraint1) assert_true(stn.is_consistent(), "An STN with overlapping constraints should not be consistent") - assert_false(stn.check_overlap(constraint2), "An STN with overlapping constraints should not be consistent") + assert_false( + stn.check_overlap(constraint2), + "An STN with overlapping constraints should not be consistent" + ) diff --git a/goal_task_tests/core/test_performance.gd b/goal_task_tests/core/test_performance.gd index 9bfc4a8..127a2aa 100644 --- a/goal_task_tests/core/test_performance.gd +++ b/goal_task_tests/core/test_performance.gd @@ -25,10 +25,14 @@ func test_performance_with_large_number_of_constraints_fixme() -> void: for i in range(800): var qualifier_1 = TemporalConstraint.TemporalQualifier.AT_START var interval_1 = calculate_time_interval(i, qualifier_1) - var from_constraint = TemporalConstraint.new(interval_1[0], interval_1[1], 5, qualifier_1, "from" + str(i)) + var from_constraint = TemporalConstraint.new( + interval_1[0], interval_1[1], 5, qualifier_1, "from" + str(i) + ) var qualifier_2 = TemporalConstraint.TemporalQualifier.AT_END var interval_2 = calculate_time_interval(i + 1, qualifier_2) - var to_constraint = TemporalConstraint.new(interval_2[0], interval_2[1], 5, qualifier_2, "to" + str(i)) + var to_constraint = TemporalConstraint.new( + interval_2[0], interval_2[1], 5, qualifier_2, "to" + str(i) + ) # Add constraints and propagate them. stn.add_temporal_constraint(from_constraint, to_constraint, 0, 10) @@ -37,7 +41,9 @@ func test_performance_with_large_number_of_constraints_fixme() -> void: var end_time = Time.get_ticks_msec() var time_taken = end_time - start_time gut.p("Time taken for constraints: " + str(time_taken) + " ms") - assert_false(time_taken < 500, "Performance test failed: Time taken should be faster than 0.5 seconds") + assert_false( + time_taken < 500, "Performance test failed: Time taken should be faster than 0.5 seconds" + ) func test_performance_with_large_number_of_constraints_failure_fixme() -> void: @@ -46,23 +52,37 @@ func test_performance_with_large_number_of_constraints_failure_fixme() -> void: for i in range(200): var qualifier_1 = TemporalConstraint.TemporalQualifier.AT_START var interval_1 = calculate_time_interval(i, qualifier_1) - var from_constraint = TemporalConstraint.new(interval_1[0], interval_1[1], 5, qualifier_1, "from" + str(i)) + var from_constraint = TemporalConstraint.new( + interval_1[0], interval_1[1], 5, qualifier_1, "from" + str(i) + ) var qualifier_2 = TemporalConstraint.TemporalQualifier.AT_END var interval_2 = calculate_time_interval(i + 1, qualifier_2) - var to_constraint = TemporalConstraint.new(interval_2[0], interval_2[1], 5, qualifier_2, "to" + str(i + 1)) + var to_constraint = TemporalConstraint.new( + interval_2[0], interval_2[1], 5, qualifier_2, "to" + str(i + 1) + ) # Add constraints and propagate them. stn.add_temporal_constraint(from_constraint, to_constraint, 0, 10) var qualifier_3 = TemporalConstraint.TemporalQualifier.AT_START var interval_3 = calculate_time_interval(0, qualifier_3) - var _from_constraint = TemporalConstraint.new(interval_3[0], interval_3[1], 5, qualifier_3, "from" + str(0)) + var _from_constraint = TemporalConstraint.new( + interval_3[0], interval_3[1], 5, qualifier_3, "from" + str(0) + ) var qualifier_4 = TemporalConstraint.TemporalQualifier.AT_END var interval_4 = calculate_time_interval(0 + 1, qualifier_4) - var _to_constraint = TemporalConstraint.new(interval_4[0], interval_4[1], 5, qualifier_4, "to" + str(1)) - assert_true(stn.check_overlap(_from_constraint), "Consistency test failed: STN should not be consistent") - assert_true(stn.check_overlap(_to_constraint), "Consistency test failed: STN should not be consistent") + var _to_constraint = TemporalConstraint.new( + interval_4[0], interval_4[1], 5, qualifier_4, "to" + str(1) + ) + assert_true( + stn.check_overlap(_from_constraint), "Consistency test failed: STN should not be consistent" + ) + assert_true( + stn.check_overlap(_to_constraint), "Consistency test failed: STN should not be consistent" + ) var end_time = Time.get_ticks_msec() var time_taken = end_time - start_time gut.p("Time taken for constraints: " + str(time_taken) + " ms") - assert_true(time_taken < 500, "Performance test failed: Time taken should be faster than 0.5 seconds") + assert_true( + time_taken < 500, "Performance test failed: Time taken should be faster than 0.5 seconds" + ) diff --git a/goal_task_tests/core/test_simple_temporal_network.gd b/goal_task_tests/core/test_simple_temporal_network.gd index 5f18d39..a81cd75 100644 --- a/goal_task_tests/core/test_simple_temporal_network.gd +++ b/goal_task_tests/core/test_simple_temporal_network.gd @@ -13,16 +13,22 @@ func before_each(): func test_add_temporal_constraint(): - var from_constraint = TemporalConstraint.new(1, 5, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource") + var from_constraint = TemporalConstraint.new( + 1, 5, 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, 5, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource") + var constraint = TemporalConstraint.new( + 1, 5, 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") + assert_eq( + result, constraint, "get_temporal_constraint_by_name should return the correct constraint" + ) func test_is_consistent(): @@ -31,13 +37,20 @@ func test_is_consistent(): func test_update_state(): - var state = {"constraint": TemporalConstraint.new(1, 5, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource")} + var state = { + "constraint": + TemporalConstraint.new(1, 5, 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") + assert_eq( + stn.constraints.size(), 1, "update_state should add the constraint to the constraints array" + ) func test_print_constraints_and_check_consistency(): - var constraint = TemporalConstraint.new(1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource") + var constraint = TemporalConstraint.new( + 1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource" + ) stn.add_temporal_constraint(constraint) for i in range(stn.constraints.size()): gut.p("Constraint " + str(i) + ": " + str(stn.constraints[i]), gut.LOG_LEVEL_ALL_ASSERTS) @@ -46,18 +59,31 @@ func test_print_constraints_and_check_consistency(): func test_validate_constraints(): - var from_constraint = TemporalConstraint.new(1, 5, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource") - var to_constraint = TemporalConstraint.new(4, 10, 6, TemporalConstraint.TemporalQualifier.AT_END, "resource") - assert_true(stn.validate_constraints(from_constraint, to_constraint, 0, 0), "validate_constraints should return true when constraints are valid") + var from_constraint = TemporalConstraint.new( + 1, 5, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource" + ) + var to_constraint = TemporalConstraint.new( + 4, 10, 6, TemporalConstraint.TemporalQualifier.AT_END, "resource" + ) + assert_true( + stn.validate_constraints(from_constraint, to_constraint, 0, 0), + "validate_constraints should return true when constraints are valid" + ) func test_add_constraints_to_list(): - var from_constraint = TemporalConstraint.new(1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource") + var from_constraint = TemporalConstraint.new( + 1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource" + ) stn.add_constraints_to_list(from_constraint, null) - assert_eq(stn.constraints.size(), 1, "add_constraints_to_list should add one constraint to the list") + assert_eq( + stn.constraints.size(), 1, "add_constraints_to_list should add one constraint to the list" + ) func test_process_constraint(): - var from_constraint = TemporalConstraint.new(1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource") + var from_constraint = TemporalConstraint.new( + 1, 2, 3, TemporalConstraint.TemporalQualifier.AT_START, "resource" + ) var node_index = stn.process_constraint(from_constraint) assert_true(node_index != null, "process_constraint should return a valid node") diff --git a/goal_task_tests/core_problems/test_logistics.gd b/goal_task_tests/core_problems/test_logistics.gd index c54c2fd..93ef6f6 100644 --- a/goal_task_tests/core_problems/test_logistics.gd +++ b/goal_task_tests/core_problems/test_logistics.gd @@ -89,7 +89,11 @@ func find_airport(state, l) -> Variant: func m_drive_truck(state, t, l) -> Variant: - 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]] return false @@ -99,11 +103,13 @@ func m_load_truck(state, o, t) -> Variant: return [["load_truck", o, t]] return false + func m_unload_truck(state, o, l) -> Variant: if o in state.packages and state.at[o] in state.trucks and l in state.locations: return [["unload_truck", o, l]] return false + func m_fly_plane(state, plane, a) -> Variant: if plane in state.airplanes and a in state.airports: return [["fly_plane", plane, a]] @@ -126,7 +132,11 @@ func m_unload_plane(state, o, a) -> Variant: func move_within_city(state, o, l) -> Variant: - 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]] @@ -134,15 +144,29 @@ func move_within_city(state, o, l) -> Variant: func move_between_airports(state, o, a) -> Variant: - 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) -> Variant: - 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: @@ -159,13 +183,37 @@ func before_each() -> void: # 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() @@ -179,18 +227,54 @@ func before_each() -> void: 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() -> void: - 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() -> void: 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) @@ -202,4 +286,12 @@ func test_move_goal_3() -> void: ## Goal 4: package1 is at location2 func test_move_goal_4() -> void: 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/core_problems/test_simple_gtn.gd b/goal_task_tests/core_problems/test_simple_gtn.gd index fb733de..e9f667d 100644 --- a/goal_task_tests/core_problems/test_simple_gtn.gd +++ b/goal_task_tests/core_problems/test_simple_gtn.gd @@ -31,7 +31,11 @@ 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"}}) @@ -93,6 +97,7 @@ func walk(state, p, x, y) -> Variant: return state return false + func call_taxi(state, p, x) -> Variant: if is_a(p, "person") and is_a(x, "location"): state.loc["taxi1"] = x @@ -100,6 +105,7 @@ func call_taxi(state, p, x) -> Variant: return state return false + func ride_taxi(state, p, y) -> Variant: # if p is a person, p is in a taxi, and y is a location: if is_a(p, "person") and is_a(state.loc[p], "taxi") and is_a(y, "location"): @@ -111,6 +117,7 @@ func ride_taxi(state, p, y) -> Variant: return state return false + func pay_driver(state: Dictionary, p: String, y: String) -> Variant: if is_a(p, "person"): if state.cash[p] >= state.owe[p]: @@ -120,6 +127,7 @@ func pay_driver(state: Dictionary, p: String, y: String) -> Variant: return state return false + ############################################################################### # Methods: @@ -131,6 +139,7 @@ func do_nothing(state, p, y) -> Variant: return [] return false + func travel_by_foot(state, p, y) -> Variant: if is_a(p, "person") and is_a(y, "location"): var x = state.loc[p] @@ -146,15 +155,25 @@ func travel_by_taxi(state, p, y) -> Variant: return [["call_taxi", p, x], ["ride_taxi", p, y], ["pay_driver", p, y]] return false + func _ready() -> void: planner.domains.push_back(the_domain) planner.current_domain = the_domain 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 @@ -254,8 +273,18 @@ func test_simple_gtn() -> void: state1 = state0.duplicate(true) plan = planner.find_plan(state1, [goal3]) gut.p("Plan %s" % [plan]) - assert_eq(plan, [["call_taxi", "alice", "home_a"], ["ride_taxi", "alice", "park"], ["pay_driver", "alice", "park"], ["walk", "bob", "home_b", "park"]]) - var new_plan = planner.run_lazy_lookahead(state1, [Multigoal.new("goal1", {"loc": {"alice": "park"}})]) + assert_eq( + plan, + [ + ["call_taxi", "alice", "home_a"], + ["ride_taxi", "alice", "park"], + ["pay_driver", "alice", "park"], + ["walk", "bob", "home_b", "park"] + ] + ) + var new_plan = planner.run_lazy_lookahead( + state1, [Multigoal.new("goal1", {"loc": {"alice": "park"}})] + ) assert_ne_deep(new_plan, []) gut.p("Alice is now at the park, so the planner will return an empty plan:") plan = planner.find_plan(state1, [Multigoal.new("goal1", {"loc": {"alice": "park"}})]) diff --git a/goal_task_tests/core_problems/test_simple_htn.gd b/goal_task_tests/core_problems/test_simple_htn.gd index ce21539..72ac3bb 100644 --- a/goal_task_tests/core_problems/test_simple_htn.gd +++ b/goal_task_tests/core_problems/test_simple_htn.gd @@ -34,7 +34,11 @@ 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: @@ -88,6 +92,7 @@ func walk(state, p, x, y) -> Variant: return state return false + func call_taxi(state, p, x) -> Variant: if is_a(p, "person") and is_a(x, "location"): state.loc["taxi1"] = x @@ -145,11 +150,26 @@ func travel_by_taxi(state, p, y) -> Variant: return [["call_taxi", p, x], ["ride_taxi", p, y], ["pay_driver", p, y]] return false + func test_simple_gtn() -> void: 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 @@ -194,7 +214,9 @@ We'll do it several times with different values for 'verbose'. gut.p("-- If verbose=3, the planner will print even more information.") gut.p("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"]] + ) gut.p("Plan %s" % [plan]) assert_eq( diff --git a/goal_task_tests/domains/3d_artwork_domain.gd b/goal_task_tests/domains/3d_artwork_domain.gd index 49b8fe5..e50d5a2 100644 --- a/goal_task_tests/domains/3d_artwork_domain.gd +++ b/goal_task_tests/domains/3d_artwork_domain.gd @@ -5,15 +5,53 @@ extends "res://addons/task_goal/core/domain.gd" -# Brainstorm and sketch out what you want your avatar to look like. Consider things like body proportions, clothing, and accessories. -# Create a 3D model of your avatar using software like Blender. Add textures to give your avatar color and detail. -# This is done using software like Krita. Add bones to your model so it can move. This is usually done in the same software as the modeling. +# Brainstorm and sketch out what you want your avatar to look like. Consider things like body proportions, clothing, and accessories. +# Create a 3D model of your avatar using software like Blender. Add textures to give your avatar color and detail. +# This is done using software like Krita. Add bones to your model so it can move. This is usually done in the same software as the modeling. # Animate your avatar to make it move. This can be done using keyframes in the modeling software, or with a physics engine in the game server. @export var types = { "character": ["Mia", "Frank", "Chair", "Hero", "Villain", "user1", "target1"], - "location": ["home_Mia", "home_Frank", "cinema", "station", "mall", "park", "restaurant", "school", "office", "gym", "library", "hospital", "beach", "supermarket", "museum", "zoo", "airport"], - "door": ["home_Mia", "home_Frank", "cinema", "station", "mall", "park", "restaurant", "school", "office", "gym", "library", "hospital", "beach", "supermarket", "museum", "zoo", "airport"], + "location": + [ + "home_Mia", + "home_Frank", + "cinema", + "station", + "mall", + "park", + "restaurant", + "school", + "office", + "gym", + "library", + "hospital", + "beach", + "supermarket", + "museum", + "zoo", + "airport" + ], + "door": + [ + "home_Mia", + "home_Frank", + "cinema", + "station", + "mall", + "park", + "restaurant", + "school", + "office", + "gym", + "library", + "hospital", + "beach", + "supermarket", + "museum", + "zoo", + "airport" + ], "vehicle": ["car1", "car2"], "owe": [], "stn": [], @@ -50,6 +88,7 @@ extends "res://addons/task_goal/core/domain.gd" ["airport", "home_Frank"]: 17, } + func _init() -> void: set_name("romantic") @@ -66,8 +105,15 @@ func close_door(state, person, location, status) -> Variant: return state return false + func handle_temporal_constraint(state, person, current_time, goal_time, constraint_name) -> Variant: - var constraint = TemporalConstraint.new(current_time, goal_time, goal_time - current_time, TemporalConstraint.TemporalQualifier.AT_END, constraint_name) + var constraint = TemporalConstraint.new( + current_time, + goal_time, + goal_time - current_time, + TemporalConstraint.TemporalQualifier.AT_END, + constraint_name + ) if state["stn"][person].check_overlap(constraint): if verbose > 0: print("Error: Temporal constraint overlaped %s" % [str(constraint)]) @@ -80,13 +126,14 @@ func handle_temporal_constraint(state, person, current_time, goal_time, constrai print("Error: Failed to add temporal constraint %s" % str(constraint)) return false + @export var moves = { "Tackle": {"power": 40, "type": "Normal", "category": "Physical"}, "Growl": {"power": 0, "type": "Normal", "category": "Status"}, "Ember": {"power": 40, "type": "Fire", "category": "Special"}, "Tail Whip": {"power": 0, "type": "Normal", "category": "Status"} } - + func apply_status_move(state, user, target, move) -> Variant: if move == "Growl": @@ -126,6 +173,7 @@ func use_move(state, user, target, move, time) -> Variant: return apply_damage_move(state, user, target, move) return false + func idle(state, person, time) -> Variant: if is_a(person, "character"): var current_time = state["time"][person] @@ -160,7 +208,12 @@ func ride_car(state, p, y, time) -> Variant: if is_a(x, "location") and x != y: var current_time = state.time[p] if current_time >= time: - print("ride_car error: Current time %s is bigger than ride car time %s" % [current_time, time]) + print( + ( + "ride_car error: Current time %s is bigger than ride car time %s" + % [current_time, time] + ) + ) return false var constraint_name = "%s_ride_car_from_%s_to_%s" % [p, x, y] state = handle_temporal_constraint(state, p, current_time, time, constraint_name) @@ -170,6 +223,7 @@ func ride_car(state, p, y, time) -> Variant: return state return false + func call_car(state, p, x, goal_time) -> Variant: if is_a(p, "character") and is_a(x, "location"): var current_time = state.time[p] @@ -188,6 +242,7 @@ func call_car(state, p, x, goal_time) -> Variant: return state return false + func pay_driver(state, p, y, goal_time) -> Variant: if is_a(p, "character"): if state.cash[p] >= state.owe[p]: @@ -198,7 +253,9 @@ func pay_driver(state, p, y, goal_time) -> Variant: current_time = goal_time var post_payment_time = current_time + payment_time var constraint_name = "%s_pay_driver_at_%s" % [p, y] - state = handle_temporal_constraint(state, p, current_time, post_payment_time, constraint_name) + state = handle_temporal_constraint( + state, p, current_time, post_payment_time, constraint_name + ) if state: state.cash[p] = state.cash[p] - state.owe[p] state.owe[p] = 0 @@ -209,6 +266,7 @@ func pay_driver(state, p, y, goal_time) -> Variant: print("Temporal constraint could not be added") return false + func walk(state, p, x, y, time) -> Variant: if is_a(p, "character") and is_a(x, "location") and is_a(y, "location"): if x == y: @@ -228,10 +286,11 @@ func walk(state, p, x, y, time) -> Variant: print("Invalid parameters.") return false + func do_mia_close_door(state, location, status) -> Variant: var person = "Mia" if is_a(person, "character") and is_a(location, "location") and is_a(location, "door"): - if state["door"][location] == 'closed': + if state["door"][location] == "closed": if verbose > 0: print("Door at location: %s is already closed" % location) return [] @@ -288,6 +347,7 @@ func do_walk(state, person, x, y, time) -> Variant: return [["walk", person, x, y, time]] return false + func travel_time(x, y, mode) -> int: var _distance = distance_to(x, y) if mode == "foot": @@ -360,6 +420,7 @@ func find_path(state, p, destination) -> Variant: return path + func travel_by_car(state, p, y) -> Variant: if is_a(p, "character") and is_a(y, "location"): var x = state.loc[p] @@ -367,20 +428,31 @@ func travel_by_car(state, p, y) -> Variant: return false if x != y and state.cash[p] >= taxi_rate(distance_to(x, y)): var call_car_goal_time = state.time[p] + 1 # Assuming calling a car takes 1 unit of time - var ride_car_goal_time = max(call_car_goal_time + travel_time(x, y, "car") + 1, call_car_goal_time + 2) - var actions = [["call_car", p, x, call_car_goal_time], ["ride_car", p, y, ride_car_goal_time]] + var ride_car_goal_time = max( + call_car_goal_time + travel_time(x, y, "car") + 1, call_car_goal_time + 2 + ) + var actions = [ + ["call_car", p, x, call_car_goal_time], ["ride_car", p, y, ride_car_goal_time] + ] if actions[0][0] == "call_car" and actions[1][0] == "ride_car": var pay_driver_goal_time = ride_car_goal_time + 1 # Assuming payment takes 1 unit of time actions.append(["pay_driver", p, y, pay_driver_goal_time]) return actions return false + func compare_goal_times(a, b) -> bool: return a[1] > b[1] + func check_location_and_action(state, person, location, action) -> bool: - return is_a(location, "location") and is_a(person, "character") and state["loc"][person] == location and state[action][location] != "closed" - + return ( + is_a(location, "location") + and is_a(person, "character") + and state["loc"][person] == location + and state[action][location] != "closed" + ) + func path_has_location(path, location) -> bool: for step in path: diff --git a/goal_task_tests/domains/anime_domain.gd b/goal_task_tests/domains/anime_domain.gd index 894a53f..19a8872 100644 --- a/goal_task_tests/domains/anime_domain.gd +++ b/goal_task_tests/domains/anime_domain.gd @@ -7,8 +7,46 @@ extends "res://addons/task_goal/core/domain.gd" @export var types = { "character": ["Mia", "Frank", "Chair", "Hero", "Villain", "user1", "target1"], - "location": ["home_Mia", "home_Frank", "cinema", "station", "mall", "park", "restaurant", "school", "office", "gym", "library", "hospital", "beach", "supermarket", "museum", "zoo", "airport"], - "door": ["home_Mia", "home_Frank", "cinema", "station", "mall", "park", "restaurant", "school", "office", "gym", "library", "hospital", "beach", "supermarket", "museum", "zoo", "airport"], + "location": + [ + "home_Mia", + "home_Frank", + "cinema", + "station", + "mall", + "park", + "restaurant", + "school", + "office", + "gym", + "library", + "hospital", + "beach", + "supermarket", + "museum", + "zoo", + "airport" + ], + "door": + [ + "home_Mia", + "home_Frank", + "cinema", + "station", + "mall", + "park", + "restaurant", + "school", + "office", + "gym", + "library", + "hospital", + "beach", + "supermarket", + "museum", + "zoo", + "airport" + ], "vehicle": ["car1", "car2"], "owe": [], "stn": [], @@ -45,6 +83,7 @@ extends "res://addons/task_goal/core/domain.gd" ["airport", "home_Frank"]: 17, } + func _init() -> void: set_name("romantic") @@ -61,8 +100,15 @@ func close_door(state, person, location, status) -> Variant: return state return false + func handle_temporal_constraint(state, person, current_time, goal_time, constraint_name) -> Variant: - var constraint = TemporalConstraint.new(current_time, goal_time, goal_time - current_time, TemporalConstraint.TemporalQualifier.AT_END, constraint_name) + var constraint = TemporalConstraint.new( + current_time, + goal_time, + goal_time - current_time, + TemporalConstraint.TemporalQualifier.AT_END, + constraint_name + ) if state["stn"][person].check_overlap(constraint): if verbose > 0: print("Error: Temporal constraint overlaped %s" % [str(constraint)]) @@ -75,13 +121,14 @@ func handle_temporal_constraint(state, person, current_time, goal_time, constrai print("Error: Failed to add temporal constraint %s" % str(constraint)) return false + @export var moves = { "Tackle": {"power": 40, "type": "Normal", "category": "Physical"}, "Growl": {"power": 0, "type": "Normal", "category": "Status"}, "Ember": {"power": 40, "type": "Fire", "category": "Special"}, "Tail Whip": {"power": 0, "type": "Normal", "category": "Status"} } - + func apply_status_move(state, user, target, move) -> Variant: if move == "Growl": @@ -121,6 +168,7 @@ func use_move(state, user, target, move, time) -> Variant: return apply_damage_move(state, user, target, move) return false + func idle(state, person, time) -> Variant: if is_a(person, "character"): var current_time = state["time"][person] @@ -155,7 +203,12 @@ func ride_car(state, p, y, time) -> Variant: if is_a(x, "location") and x != y: var current_time = state.time[p] if current_time >= time: - print("ride_car error: Current time %s is bigger than ride car time %s" % [current_time, time]) + print( + ( + "ride_car error: Current time %s is bigger than ride car time %s" + % [current_time, time] + ) + ) return false var constraint_name = "%s_ride_car_from_%s_to_%s" % [p, x, y] state = handle_temporal_constraint(state, p, current_time, time, constraint_name) @@ -165,6 +218,7 @@ func ride_car(state, p, y, time) -> Variant: return state return false + func call_car(state, p, x, goal_time) -> Variant: if is_a(p, "character") and is_a(x, "location"): var current_time = state.time[p] @@ -183,6 +237,7 @@ func call_car(state, p, x, goal_time) -> Variant: return state return false + func pay_driver(state, p, y, goal_time) -> Variant: if is_a(p, "character"): if state.cash[p] >= state.owe[p]: @@ -193,7 +248,9 @@ func pay_driver(state, p, y, goal_time) -> Variant: current_time = goal_time var post_payment_time = current_time + payment_time var constraint_name = "%s_pay_driver_at_%s" % [p, y] - state = handle_temporal_constraint(state, p, current_time, post_payment_time, constraint_name) + state = handle_temporal_constraint( + state, p, current_time, post_payment_time, constraint_name + ) if state: state.cash[p] = state.cash[p] - state.owe[p] state.owe[p] = 0 @@ -204,6 +261,7 @@ func pay_driver(state, p, y, goal_time) -> Variant: print("Temporal constraint could not be added") return false + func walk(state, p, x, y, time) -> Variant: if is_a(p, "character") and is_a(x, "location") and is_a(y, "location"): if x == y: @@ -223,10 +281,11 @@ func walk(state, p, x, y, time) -> Variant: print("Invalid parameters.") return false + func do_mia_close_door(state, location, status) -> Variant: var person = "Mia" if is_a(person, "character") and is_a(location, "location") and is_a(location, "door"): - if state["door"][location] == 'closed': + if state["door"][location] == "closed": if verbose > 0: print("Door at location: %s is already closed" % location) return [] @@ -283,6 +342,7 @@ func do_walk(state, person, x, y, time) -> Variant: return [["walk", person, x, y, time]] return false + func travel_time(x, y, mode) -> int: var _distance = distance_to(x, y) if mode == "foot": @@ -355,6 +415,7 @@ func find_path(state, p, destination) -> Variant: return path + func travel_by_car(state, p, y) -> Variant: if is_a(p, "character") and is_a(y, "location"): var x = state.loc[p] @@ -362,20 +423,31 @@ func travel_by_car(state, p, y) -> Variant: return false if x != y and state.cash[p] >= taxi_rate(distance_to(x, y)): var call_car_goal_time = state.time[p] + 1 # Assuming calling a car takes 1 unit of time - var ride_car_goal_time = max(call_car_goal_time + travel_time(x, y, "car") + 1, call_car_goal_time + 2) - var actions = [["call_car", p, x, call_car_goal_time], ["ride_car", p, y, ride_car_goal_time]] + var ride_car_goal_time = max( + call_car_goal_time + travel_time(x, y, "car") + 1, call_car_goal_time + 2 + ) + var actions = [ + ["call_car", p, x, call_car_goal_time], ["ride_car", p, y, ride_car_goal_time] + ] if actions[0][0] == "call_car" and actions[1][0] == "ride_car": var pay_driver_goal_time = ride_car_goal_time + 1 # Assuming payment takes 1 unit of time actions.append(["pay_driver", p, y, pay_driver_goal_time]) return actions return false + func compare_goal_times(a, b) -> bool: return a[1] > b[1] + func check_location_and_action(state, person, location, action) -> bool: - return is_a(location, "location") and is_a(person, "character") and state["loc"][person] == location and state[action][location] != "closed" - + return ( + is_a(location, "location") + and is_a(person, "character") + and state["loc"][person] == location + and state[action][location] != "closed" + ) + func path_has_location(path, location) -> bool: for step in path: diff --git a/goal_task_tests/domains/college_town_domain.gd b/goal_task_tests/domains/college_town_domain.gd index bd4441f..494bbe8 100644 --- a/goal_task_tests/domains/college_town_domain.gd +++ b/goal_task_tests/domains/college_town_domain.gd @@ -5,28 +5,67 @@ extends "res://addons/task_goal/core/domain.gd" - @export var distance: Dictionary = { - # ["home_Mia", "cinema"]: 12, +# ["home_Mia", "cinema"]: 12, } + func _init() -> void: set_name("college_town") + @export var types = { "character": ["Mia", "Frank", "Chair", "Hero", "Villain", "user1", "target1"], - "location": ["home_Mia", "home_Frank", "cinema", "station", "mall", "park", "restaurant", "school", "office", "gym", "library", "hospital", "beach", "supermarket", "museum", "zoo", "airport"], - "door": ["home_Mia", "home_Frank", "cinema", "station", "mall", "park", "restaurant", "school", "office", "gym", "library", "hospital", "beach", "supermarket", "museum", "zoo", "airport"], + "location": + [ + "home_Mia", + "home_Frank", + "cinema", + "station", + "mall", + "park", + "restaurant", + "school", + "office", + "gym", + "library", + "hospital", + "beach", + "supermarket", + "museum", + "zoo", + "airport" + ], + "door": + [ + "home_Mia", + "home_Frank", + "cinema", + "station", + "mall", + "park", + "restaurant", + "school", + "office", + "gym", + "library", + "hospital", + "beach", + "supermarket", + "museum", + "zoo", + "airport" + ], "vehicle": ["car1", "car2"], "owe": [], "stn": [], } + func is_a(variable, type) -> bool: return variable in types[type] - func travel_time(x, y, mode) -> int: var _distance = distance_to(x, y) if mode == "foot": @@ -37,6 +76,7 @@ func travel_time(x, y, mode) -> int: print("Error: Invalid mode of transportation") return -1 + func find_path(state, p, destination) -> Variant: var current_location = state["loc"][p] var pq = [[0, current_location]] # Initialize priority queue with current location and distance as 0 diff --git a/goal_task_tests/game_problems/test_3d_avatar.gd b/goal_task_tests/game_problems/test_3d_avatar.gd index e52ddbe..48c2a6c 100644 --- a/goal_task_tests/game_problems/test_3d_avatar.gd +++ b/goal_task_tests/game_problems/test_3d_avatar.gd @@ -5,34 +5,37 @@ extends GutTest - -# Brainstorm and sketch out what you want your avatar to look like. Consider things like body proportions, clothing, and accessories. -# Create a 3D model of your avatar using software like Blender. Add textures to give your avatar color and detail. -# This is done using software like Krita. -# Add bones to your model so it can move. This is usually done in the same software as the modeling. +# Brainstorm and sketch out what you want your avatar to look like. Consider things like body proportions, clothing, and accessories. +# Create a 3D model of your avatar using software like Blender. Add textures to give your avatar color and detail. +# This is done using software like Krita. +# Add bones to your model so it can move. This is usually done in the same software as the modeling. var the_domain = preload("res://goal_task_tests/domains/anime_domain.gd").new() var planner = null + func before_each(): planner = preload("res://addons/task_goal/core/plan.gd").new() planner.verbose = 0 var new_domain = the_domain.duplicate(true) planner.domains.push_back(new_domain) planner.current_domain = new_domain - - planner.declare_multigoal_methods([planner.m_split_multigoal]) + + planner.declare_multigoal_methods([planner.m_split_multigoal]) #planner.declare_actions([Callable(planner.current_domain, "use_move")]) #planner.declare_unigoal_methods("loc", [Callable(planner.current_domain, "travel_by_foot")]) #planner.declare_task_methods("travel", [Callable(planner.current_domain, "travel_by_foot")]) + func test_create_existing_avatar(): planner.verbose = 0 var state0: Dictionary = {"time": {}, "stn": {}} state0.avatar = {} state0.avatar["Nova"] = "rigged" var state1 = state0.duplicate(true) - var expected = [] - var result = planner.find_plan(state1, [Multigoal.new("Create an avatar", { "avatar": {"Nova": "rigged"}})]) + var expected = [] + var result = planner.find_plan( + state1, [Multigoal.new("Create an avatar", {"avatar": {"Nova": "rigged"}})] + ) assert_eq_deep(result, expected) diff --git a/goal_task_tests/game_problems/test_anime.gd b/goal_task_tests/game_problems/test_anime.gd index 1ed0388..3461ff0 100644 --- a/goal_task_tests/game_problems/test_anime.gd +++ b/goal_task_tests/game_problems/test_anime.gd @@ -9,31 +9,84 @@ var the_domain = preload("res://goal_task_tests/domains/anime_domain.gd").new() var planner = null + func before_each(): planner = preload("res://addons/task_goal/core/plan.gd").new() planner.verbose = 0 var new_domain = the_domain.duplicate(true) planner.domains.push_back(new_domain) planner.current_domain = new_domain - - - planner.declare_actions([Callable(planner.current_domain, "use_move"), Callable(planner.current_domain, "apply_status_move"), Callable(planner.current_domain, "apply_damage_move"), Callable(planner.current_domain, "walk"), Callable(planner.current_domain, "close_door"), Callable(planner.current_domain, "call_car"), Callable(planner.current_domain, "do_nothing"), Callable(planner.current_domain, "ride_car"), Callable(planner.current_domain, "pay_driver"), Callable(planner.current_domain, "do_nothing"), Callable(planner.current_domain, "idle"), Callable(planner.current_domain, "wait_for_everyone")]) - planner.declare_unigoal_methods("loc", [Callable(planner.current_domain, "travel_by_foot"), Callable(planner.current_domain, "travel_by_car"), Callable(planner.current_domain, "find_path")]) - planner.declare_task_methods("travel", [Callable(planner.current_domain, "travel_by_foot"), Callable(planner.current_domain, "travel_by_car"), Callable(planner.current_domain, "find_path")]) - planner.declare_task_methods("travel_by_car", [Callable(planner.current_domain, "travel_by_car")]) + + planner.declare_actions( + [ + Callable(planner.current_domain, "use_move"), + Callable(planner.current_domain, "apply_status_move"), + Callable(planner.current_domain, "apply_damage_move"), + Callable(planner.current_domain, "walk"), + Callable(planner.current_domain, "close_door"), + Callable(planner.current_domain, "call_car"), + Callable(planner.current_domain, "do_nothing"), + Callable(planner.current_domain, "ride_car"), + Callable(planner.current_domain, "pay_driver"), + Callable(planner.current_domain, "do_nothing"), + Callable(planner.current_domain, "idle"), + Callable(planner.current_domain, "wait_for_everyone") + ] + ) + planner.declare_unigoal_methods( + "loc", + [ + Callable(planner.current_domain, "travel_by_foot"), + Callable(planner.current_domain, "travel_by_car"), + Callable(planner.current_domain, "find_path") + ] + ) + planner.declare_task_methods( + "travel", + [ + Callable(planner.current_domain, "travel_by_foot"), + Callable(planner.current_domain, "travel_by_car"), + Callable(planner.current_domain, "find_path") + ] + ) + planner.declare_task_methods( + "travel_by_car", [Callable(planner.current_domain, "travel_by_car")] + ) planner.declare_unigoal_methods("time", [Callable(planner.current_domain, "do_idle")]) - planner.declare_multigoal_methods([planner.m_split_multigoal]) + planner.declare_multigoal_methods([planner.m_split_multigoal]) planner.declare_unigoal_methods("door", [Callable(planner.current_domain, "do_mia_close_door")]) planner.declare_task_methods("find_path", [Callable(planner.current_domain, "find_path")]) - planner.declare_task_methods("do_close_door", [Callable(planner.current_domain, "do_close_door")]) + planner.declare_task_methods( + "do_close_door", [Callable(planner.current_domain, "do_close_door")] + ) planner.declare_task_methods("do_walk", [Callable(planner.current_domain, "do_walk")]) planner.declare_multigoal_methods([planner.m_split_multigoal]) + func test_walk(): planner.verbose = 0 - var state0: Dictionary = {"loc": {"Mia": "home_Mia", "Chair": "home_Mia", "Frank": "home_Frank", "car1": "cinema", "car2": "station"}, "cash": {"Mia": 30, "Frank": 35}, "owe": {"Mia": 0, "Frank": 0}, "time": {"Mia": 0, "Frank": 0}, "stn": {"Mia": SimpleTemporalNetwork.new(), "Frank": SimpleTemporalNetwork.new(), "Chair": SimpleTemporalNetwork.new()}, "door": {}} + var state0: Dictionary = { + "loc": + { + "Mia": "home_Mia", + "Chair": "home_Mia", + "Frank": "home_Frank", + "car1": "cinema", + "car2": "station" + }, + "cash": {"Mia": 30, "Frank": 35}, + "owe": {"Mia": 0, "Frank": 0}, + "time": {"Mia": 0, "Frank": 0}, + "stn": + { + "Mia": SimpleTemporalNetwork.new(), + "Frank": SimpleTemporalNetwork.new(), + "Chair": SimpleTemporalNetwork.new() + }, + "door": {} + } var state1 = state0.duplicate(true) - var expected = [["walk", "Mia", "home_Mia", "mall", 8]] + var expected = [["walk", "Mia", "home_Mia", "mall", 8]] var result = planner.find_plan(state1, [["walk", "Mia", "home_Mia", "mall", 8]]) assert_eq_deep(result, expected) assert_eq(state1["time"]["Mia"], 8) @@ -42,24 +95,87 @@ func test_walk(): func test_find_path(): planner.verbose = 0 - var state0: Dictionary = {"loc": {"Mia": "home_Mia", "Chair": "home_Mia", "Frank": "home_Frank", "car1": "cinema", "car2": "station"}, "cash": {"Mia": 30, "Frank": 35}, "owe": {"Mia": 0, "Frank": 0}, "time": {"Mia": 0, "Frank": 0}, "stn": {"Mia": SimpleTemporalNetwork.new(), "Frank": SimpleTemporalNetwork.new(), "Chair": SimpleTemporalNetwork.new()}, "door": {}} - var expected = [["walk", "Mia", "home_Mia", "mall", 8]] + var state0: Dictionary = { + "loc": + { + "Mia": "home_Mia", + "Chair": "home_Mia", + "Frank": "home_Frank", + "car1": "cinema", + "car2": "station" + }, + "cash": {"Mia": 30, "Frank": 35}, + "owe": {"Mia": 0, "Frank": 0}, + "time": {"Mia": 0, "Frank": 0}, + "stn": + { + "Mia": SimpleTemporalNetwork.new(), + "Frank": SimpleTemporalNetwork.new(), + "Chair": SimpleTemporalNetwork.new() + }, + "door": {} + } + var expected = [["walk", "Mia", "home_Mia", "mall", 8]] var result = planner.find_plan(state0.duplicate(true), [["find_path", "Mia", "mall"]]) assert_eq_deep(result, expected) - + + func test_find_path_next_node(): planner.verbose = 0 - var state0: Dictionary = {"loc": {"Mia": "home_Mia", "Chair": "home_Mia", "Frank": "home_Frank", "car1": "cinema", "car2": "station"}, "cash": {"Mia": 30, "Frank": 35}, "owe": {"Mia": 0, "Frank": 0}, "time": {"Mia": 0, "Frank": 0}, "stn": {"Mia": SimpleTemporalNetwork.new(), "Frank": SimpleTemporalNetwork.new(), "Chair": SimpleTemporalNetwork.new()}, "door": {}} - var expected = [["walk", "Mia", "home_Mia", "park", 5], ["walk", "Mia", "park", "museum", 13], ["walk", "Mia", "museum", "zoo", 14], ["walk", "Mia", "zoo", "airport", 15]] + var state0: Dictionary = { + "loc": + { + "Mia": "home_Mia", + "Chair": "home_Mia", + "Frank": "home_Frank", + "car1": "cinema", + "car2": "station" + }, + "cash": {"Mia": 30, "Frank": 35}, + "owe": {"Mia": 0, "Frank": 0}, + "time": {"Mia": 0, "Frank": 0}, + "stn": + { + "Mia": SimpleTemporalNetwork.new(), + "Frank": SimpleTemporalNetwork.new(), + "Chair": SimpleTemporalNetwork.new() + }, + "door": {} + } + var expected = [ + ["walk", "Mia", "home_Mia", "park", 5], + ["walk", "Mia", "park", "museum", 13], + ["walk", "Mia", "museum", "zoo", 14], + ["walk", "Mia", "zoo", "airport", 15] + ] var result = planner.find_plan(state0, [["find_path", "Mia", "airport"]]) assert_eq_deep(result, expected) assert_eq(state0["time"]["Mia"], 15) assert_eq(state0["loc"]["Mia"], "airport") - - + + func test_isekai_anime_01(): planner.verbose = 0 - var state0: Dictionary = {"loc": {"Mia": "home_Mia", "Chair": "home_Mia", "Frank": "home_Frank", "car1": "cinema", "car2": "station"}, "cash": {"Mia": 30, "Frank": 35}, "owe": {"Mia": 0, "Frank": 0}, "time": {"Mia": 0, "Frank": 0}, "stn": {"Mia": SimpleTemporalNetwork.new(), "Frank": SimpleTemporalNetwork.new(), "Chair": SimpleTemporalNetwork.new()}, "door": {}} + var state0: Dictionary = { + "loc": + { + "Mia": "home_Mia", + "Chair": "home_Mia", + "Frank": "home_Frank", + "car1": "cinema", + "car2": "station" + }, + "cash": {"Mia": 30, "Frank": 35}, + "owe": {"Mia": 0, "Frank": 0}, + "time": {"Mia": 0, "Frank": 0}, + "stn": + { + "Mia": SimpleTemporalNetwork.new(), + "Frank": SimpleTemporalNetwork.new(), + "Chair": SimpleTemporalNetwork.new() + }, + "door": {} + } var expected = [["walk", "Mia", "home_Mia", "mall", 8]] var result = planner.find_plan(state0.duplicate(true), [["travel", "Mia", "mall"]]) assert_eq_deep(result, expected) @@ -67,36 +183,121 @@ func test_isekai_anime_01(): func test_isekai_anime_02(): planner.verbose = 0 - var state0: Dictionary = {"loc": {"Mia": "home_Mia", "Chair": "home_Mia", "Frank": "home_Frank", "car1": "cinema", "car2": "station"}, "cash": {"Mia": 30, "Frank": 35}, "owe": {"Mia": 0, "Frank": 0}, "time": {"Mia": 0, "Frank": 0}, "stn": {"Mia": SimpleTemporalNetwork.new(), "Frank": SimpleTemporalNetwork.new(), "Chair": SimpleTemporalNetwork.new()}, "door": {}} + var state0: Dictionary = { + "loc": + { + "Mia": "home_Mia", + "Chair": "home_Mia", + "Frank": "home_Frank", + "car1": "cinema", + "car2": "station" + }, + "cash": {"Mia": 30, "Frank": 35}, + "owe": {"Mia": 0, "Frank": 0}, + "time": {"Mia": 0, "Frank": 0}, + "stn": + { + "Mia": SimpleTemporalNetwork.new(), + "Frank": SimpleTemporalNetwork.new(), + "Chair": SimpleTemporalNetwork.new() + }, + "door": {} + } var goal2 = Multigoal.new("goal2", {"loc": {"Mia": "mall", "Frank": "mall"}}) var plan = planner.find_plan(state0.duplicate(true), [goal2]) - assert_eq_deep(plan, [["walk", "Mia", "home_Mia", "mall", 8], ["walk", "Frank", "home_Frank", "mall", 10]]) + assert_eq_deep( + plan, [["walk", "Mia", "home_Mia", "mall", 8], ["walk", "Frank", "home_Frank", "mall", 10]] + ) func test_visit_all_the_doors() -> void: planner.verbose = 0 - var state0: Dictionary = {"loc": {"Mia": "home_Mia", "Chair": "home_Mia", "Frank": "home_Frank", "car1": "cinema", "car2": "station"}, "cash": {"Mia": 30, "Frank": 35}, "owe": {"Mia": 0, "Frank": 0}, "time": {"Mia": 0, "Frank": 0}, "stn": {"Mia": SimpleTemporalNetwork.new(), "Frank": SimpleTemporalNetwork.new(), "Chair": SimpleTemporalNetwork.new()}, "door": {}} + var state0: Dictionary = { + "loc": + { + "Mia": "home_Mia", + "Chair": "home_Mia", + "Frank": "home_Frank", + "car1": "cinema", + "car2": "station" + }, + "cash": {"Mia": 30, "Frank": 35}, + "owe": {"Mia": 0, "Frank": 0}, + "time": {"Mia": 0, "Frank": 0}, + "stn": + { + "Mia": SimpleTemporalNetwork.new(), + "Frank": SimpleTemporalNetwork.new(), + "Chair": SimpleTemporalNetwork.new() + }, + "door": {} + } var door_goals = [] for location in the_domain.types["location"]: var task = ["travel", "Mia", location] gut.p(task) door_goals.append(task) var result = planner.find_plan(state0, door_goals) - assert_eq_deep(result, [["call_car", "Mia", "cinema", 13], ["ride_car", "Mia", "home_Frank", 15], ["pay_driver", "Mia", "home_Frank", 16], ["walk", "Mia", "home_Frank", "cinema", 21], ["call_car", "Mia", "home_Mia", 34], ["ride_car", "Mia", "station", 37], ["pay_driver", "Mia", "station", 38], ["walk", "Mia", "station", "home_Mia", 40], ["walk", "Mia", "home_Mia", "mall", 47], ["call_car", "Mia", "home_Mia", 60], ["ride_car", "Mia", "park", 62], ["pay_driver", "Mia", "park", 63], ["walk", "Mia", "park", "restaurant", 69], ["walk", "Mia", "restaurant", "school", 75], ["walk", "Mia", "school", "office", 82], ["walk", "Mia", "office", "gym", 90], ["walk", "Mia", "gym", "library", 99], ["walk", "Mia", "library", "hospital", 109], ["walk", "Mia", "hospital", "beach", 120], ["walk", "Mia", "beach", "supermarket", 132], ["call_car", "Mia", "park", 146], ["ride_car", "Mia", "museum", 149], ["pay_driver", "Mia", "museum", 150], ["walk", "Mia", "museum", "zoo", 165], ["walk", "Mia", "zoo", "airport", 180]]) + assert_eq_deep( + result, + [ + ["call_car", "Mia", "cinema", 13], + ["ride_car", "Mia", "home_Frank", 15], + ["pay_driver", "Mia", "home_Frank", 16], + ["walk", "Mia", "home_Frank", "cinema", 21], + ["call_car", "Mia", "home_Mia", 34], + ["ride_car", "Mia", "station", 37], + ["pay_driver", "Mia", "station", 38], + ["walk", "Mia", "station", "home_Mia", 40], + ["walk", "Mia", "home_Mia", "mall", 47], + ["call_car", "Mia", "home_Mia", 60], + ["ride_car", "Mia", "park", 62], + ["pay_driver", "Mia", "park", 63], + ["walk", "Mia", "park", "restaurant", 69], + ["walk", "Mia", "restaurant", "school", 75], + ["walk", "Mia", "school", "office", 82], + ["walk", "Mia", "office", "gym", 90], + ["walk", "Mia", "gym", "library", 99], + ["walk", "Mia", "library", "hospital", 109], + ["walk", "Mia", "hospital", "beach", 120], + ["walk", "Mia", "beach", "supermarket", 132], + ["call_car", "Mia", "park", 146], + ["ride_car", "Mia", "museum", 149], + ["pay_driver", "Mia", "museum", 150], + ["walk", "Mia", "museum", "zoo", 165], + ["walk", "Mia", "zoo", "airport", 180] + ] + ) assert_ne_deep(result, false) func test_close_all_the_door_goal() -> void: planner.verbose = 0 - var state0: Dictionary = {"loc": {"Mia": "home_Mia", "Chair": "home_Mia"}, "cash": {"Mia": 30, "Frank": 35}, "owe": {"Mia": 0, "Frank": 0}, "time": {"Mia": 0, "Frank": 0}, "stn": {"Mia": SimpleTemporalNetwork.new(), "Frank": SimpleTemporalNetwork.new(), "Chair": SimpleTemporalNetwork.new()}, "door": {}} - var state1 = state0.duplicate(true) + var state0: Dictionary = { + "loc": {"Mia": "home_Mia", "Chair": "home_Mia"}, + "cash": {"Mia": 30, "Frank": 35}, + "owe": {"Mia": 0, "Frank": 0}, + "time": {"Mia": 0, "Frank": 0}, + "stn": + { + "Mia": SimpleTemporalNetwork.new(), + "Frank": SimpleTemporalNetwork.new(), + "Chair": SimpleTemporalNetwork.new() + }, + "door": {} + } + var state1 = state0.duplicate(true) for location in planner.current_domain.types["location"]: state1["door"][location] = "opened" var goals = [] for location in planner.current_domain.types["location"]: if not planner.current_domain.is_a(location, "location"): continue - goals.append(Multigoal.new("goal_%s" % location, {"loc": {"Mia": location}, "door" : {location: "closed"}})) + goals.append( + Multigoal.new( + "goal_%s" % location, {"loc": {"Mia": location}, "door": {location: "closed"}} + ) + ) state1 = planner.run_lazy_lookahead(state1, goals) var is_doors_closed = true for location in planner.current_domain.types["location"]: @@ -112,35 +313,65 @@ func test_close_all_the_door_goal() -> void: func test_travel_by_car(): planner.verbose = 0 var state: Dictionary = { - "loc": {"Mia": "home_Mia", "Chair": "home_Mia", "Frank": "home_Frank", "car1": "cinema", "car2": "station"}, + "loc": + { + "Mia": "home_Mia", + "Chair": "home_Mia", + "Frank": "home_Frank", + "car1": "cinema", + "car2": "station" + }, "cash": {"Mia": 30, "Frank": 35}, "owe": {"Mia": 0, "Frank": 0}, "time": {"Mia": 0, "Frank": 0}, - "stn": {"Mia": SimpleTemporalNetwork.new(), "Frank": SimpleTemporalNetwork.new(), "Chair": SimpleTemporalNetwork.new()}, + "stn": + { + "Mia": SimpleTemporalNetwork.new(), + "Frank": SimpleTemporalNetwork.new(), + "Chair": SimpleTemporalNetwork.new() + }, "door": {} } - - var expected = [["call_car", "Mia", "home_Mia", 1], ["ride_car", "Mia", "mall", 3], ["pay_driver", "Mia", "mall", 4]] - + + var expected = [ + ["call_car", "Mia", "home_Mia", 1], + ["ride_car", "Mia", "mall", 3], + ["pay_driver", "Mia", "mall", 4] + ] + var result = planner.find_plan(state, [["travel_by_car", "Mia", "mall"]]) assert_eq_deep(result, expected) + func test_do_walk(): planner.verbose = 0 var state: Dictionary = { - "loc": {"Mia": "home_Mia", "Chair": "home_Mia", "Frank": "home_Frank", "car1": "cinema", "car2": "station"}, + "loc": + { + "Mia": "home_Mia", + "Chair": "home_Mia", + "Frank": "home_Frank", + "car1": "cinema", + "car2": "station" + }, "cash": {"Mia": 30, "Frank": 35}, "owe": {"Mia": 0, "Frank": 0}, "time": {"Mia": 0, "Frank": 0}, - "stn": {"Mia": SimpleTemporalNetwork.new(), "Frank": SimpleTemporalNetwork.new(), "Chair": SimpleTemporalNetwork.new()}, + "stn": + { + "Mia": SimpleTemporalNetwork.new(), + "Frank": SimpleTemporalNetwork.new(), + "Chair": SimpleTemporalNetwork.new() + }, "door": {} } - + var expected = [["walk", "Mia", "home_Mia", "mall", 8]] - + var result = planner.find_plan(state, [["do_walk", "Mia", "home_Mia", "mall", 8]]) assert_eq_deep(result, expected) + func test_use_move(): planner.verbose = 0 var state = { diff --git a/goal_task_tests/game_problems/test_magical_town_planner.gd b/goal_task_tests/game_problems/test_magical_town_planner.gd index abd37a8..17b71cf 100644 --- a/goal_task_tests/game_problems/test_magical_town_planner.gd +++ b/goal_task_tests/game_problems/test_magical_town_planner.gd @@ -9,28 +9,82 @@ var the_domain = preload("res://goal_task_tests/domains/college_town_domain.gd") var planner = null + func before_each(): planner = preload("res://addons/task_goal/core/plan.gd").new() planner.verbose = 0 var new_domain = the_domain.duplicate(true) planner.domains.push_back(new_domain) planner.current_domain = new_domain - - planner.declare_actions([Callable(planner.current_domain, "create_room"), Callable(planner.current_domain, "place_furniture"), Callable(planner.current_domain, "change_layout")]) - planner.declare_task_methods("design_room", [Callable(planner.current_domain, "create_room"), Callable(planner.current_domain, "place_furniture")]) - planner.declare_task_methods("redesign_apartment", [Callable(planner.current_domain, "change_layout")]) + + planner.declare_actions( + [ + Callable(planner.current_domain, "create_room"), + Callable(planner.current_domain, "place_furniture"), + Callable(planner.current_domain, "change_layout") + ] + ) + planner.declare_task_methods( + "design_room", + [ + Callable(planner.current_domain, "create_room"), + Callable(planner.current_domain, "place_furniture") + ] + ) + planner.declare_task_methods( + "redesign_apartment", [Callable(planner.current_domain, "change_layout")] + ) + func test_college_town_plan(): planner.verbose = 0 - var town_state: Dictionary = {} # TODO - var result = planner.find_plan(town_state.duplicate(true), [ - ["design_room", "bedroom1", "medium", "modern", "sleep"], - ["design_room", "bedroom2", "small", "vintage", "study"] - ]); - - assert_ne_deep(result, [ - ["create_room", "bedroom1", "medium", "modern", "sleep", {"pivot": [0, 0, 0], "footprint": [5, 5, 3]}, 10], - ["place_furniture", "bed", "bedroom1", "north-wall", "facing-south", {"pivot": [2, 2, 0], "footprint": [3, 2, 1]}, 15], - ["create_room", "bedroom2", "small", "vintage", "study", {"pivot": [6, 0, 0], "footprint": [4, 4, 3]}, 30], - ["place_furniture", "desk", "bedroom2", "west-wall", "facing-east", {"pivot": [7, 2, 0], "footprint": [2, 1, 1]}, 35] - ]); + var town_state: Dictionary = {} # TODO + var result = planner.find_plan( + town_state.duplicate(true), + [ + ["design_room", "bedroom1", "medium", "modern", "sleep"], + ["design_room", "bedroom2", "small", "vintage", "study"] + ] + ) + + assert_ne_deep( + result, + [ + [ + "create_room", + "bedroom1", + "medium", + "modern", + "sleep", + {"pivot": [0, 0, 0], "footprint": [5, 5, 3]}, + 10 + ], + [ + "place_furniture", + "bed", + "bedroom1", + "north-wall", + "facing-south", + {"pivot": [2, 2, 0], "footprint": [3, 2, 1]}, + 15 + ], + [ + "create_room", + "bedroom2", + "small", + "vintage", + "study", + {"pivot": [6, 0, 0], "footprint": [4, 4, 3]}, + 30 + ], + [ + "place_furniture", + "desk", + "bedroom2", + "west-wall", + "facing-east", + {"pivot": [7, 2, 0], "footprint": [2, 1, 1]}, + 35 + ] + ] + )