if UP_OK:
# Tous les noms sont prefixes (up_*, *_T) pour ne pas ecraser le domaine jouet.
# --- Types et fluents (l'etat du monde) ---
Loc_T = UserType("Location"); Pkg_T = UserType("Package"); Veh_T = UserType("Vehicle")
up_at_pkg = Fluent("at_pkg", BoolType(), p=Pkg_T, l=Loc_T) # colis p est en l
up_at_veh = Fluent("at_veh", BoolType(), v=Veh_T, l=Loc_T) # vehicule v est en l
up_in_veh = Fluent("in_veh", BoolType(), p=Pkg_T, v=Veh_T) # colis p est dans v
# --- Actions primitives (mêmes sémantiques que le domaine HDDL des sections 3-5) ---
up_load = InstantaneousAction("load", p=Pkg_T, v=Veh_T, l=Loc_T)
p, v, l = up_load.parameter("p"), up_load.parameter("v"), up_load.parameter("l")
up_load.add_precondition(up_at_pkg(p, l)); up_load.add_precondition(up_at_veh(v, l))
up_load.add_effect(up_in_veh(p, v), True); up_load.add_effect(up_at_pkg(p, l), False)
up_unload = InstantaneousAction("unload", p=Pkg_T, v=Veh_T, l=Loc_T)
p, v, l = up_unload.parameter("p"), up_unload.parameter("v"), up_unload.parameter("l")
up_unload.add_precondition(up_in_veh(p, v)); up_unload.add_precondition(up_at_veh(v, l))
up_unload.add_effect(up_at_pkg(p, l), True); up_unload.add_effect(up_in_veh(p, v), False)
up_drive = InstantaneousAction("drive", v=Veh_T, f=Loc_T, t=Loc_T)
v, f, t = up_drive.parameter("v"), up_drive.parameter("f"), up_drive.parameter("t")
up_drive.add_precondition(up_at_veh(v, f))
up_drive.add_precondition(Not(Equals(f, t))) # un trajet doit vraiment deplacer
up_drive.add_effect(up_at_veh(v, t), True); up_drive.add_effect(up_at_veh(v, f), False)
up_prob = HierarchicalProblem()
up_prob.add_fluent(up_at_pkg, default_initial_value=False)
up_prob.add_fluent(up_at_veh, default_initial_value=False)
up_prob.add_fluent(up_in_veh, default_initial_value=False)
up_prob.add_actions([up_load, up_unload, up_drive])
# --- Tâche composee goto(v, dst) : déjà sur place (noop) OU rouler (récursif) ---
# C'est ce qui apporte le REPOSITIONNEMENT automatique absent du solveur jouet.
up_goto = UPTask("goto", v=Veh_T, dst=Loc_T)
up_prob.add_task(up_goto)
m_here = UPMethod("m_goto_noop", v=Veh_T, dst=Loc_T)
mv, mdst = m_here.parameter("v"), m_here.parameter("dst")
m_here.set_task(up_goto, mv, mdst)
m_here.add_precondition(up_at_veh(mv, mdst)) # déjà a destination : 0 sous-tâche
up_prob.add_method(m_here)
m_move = UPMethod("m_goto_move", v=Veh_T, dst=Loc_T, frm=Loc_T)
mv, mdst, mfrm = m_move.parameter("v"), m_move.parameter("dst"), m_move.parameter("frm")
m_move.set_task(up_goto, mv, mdst)
m_move.add_precondition(up_at_veh(mv, mfrm))
m_move.add_precondition(Not(Equals(mfrm, mdst))) # eviter un goto vide vers sa propre case
m_move.add_subtask(up_drive, mv, mfrm, mdst)
up_prob.add_method(m_move)
# --- Tâche composee deliver(p, src, dst) : reposition -> load -> reposition -> unload ---
up_deliver = UPTask("deliver", p=Pkg_T, src=Loc_T, dst=Loc_T)
up_prob.add_task(up_deliver)
m_del = UPMethod("m_deliver", p=Pkg_T, src=Loc_T, dst=Loc_T, v=Veh_T)
mp, msrc, mdst, mv = m_del.parameter("p"), m_del.parameter("src"), m_del.parameter("dst"), m_del.parameter("v")
m_del.set_task(up_deliver, mp, msrc, mdst)
g1 = m_del.add_subtask(up_goto, mv, msrc) # amener le camion au colis
s1 = m_del.add_subtask(up_load, mp, mv, msrc)
g2 = m_del.add_subtask(up_goto, mv, mdst) # amener le camion a destination
s2 = m_del.add_subtask(up_unload, mp, mv, mdst)
m_del.set_ordered(g1, s1, g2, s2)
up_prob.add_method(m_del)
# --- Problème concret : 2 colis, 1 camion au depot (pkg2 EXIGE un repositionnement) ---
o_depot = Object("depot", Loc_T); o_store = Object("store", Loc_T)
o_wh = Object("warehouse", Loc_T); o_office = Object("office", Loc_T)
o_pkg1 = Object("pkg1", Pkg_T); o_pkg2 = Object("pkg2", Pkg_T)
o_truck = Object("truck", Veh_T)
up_prob.add_objects([o_depot, o_store, o_wh, o_office, o_pkg1, o_pkg2, o_truck])
up_prob.set_initial_value(up_at_pkg(o_pkg1, o_depot), True)
up_prob.set_initial_value(up_at_pkg(o_pkg2, o_wh), True)
up_prob.set_initial_value(up_at_veh(o_truck, o_depot), True)
up_prob.task_network.add_subtask(up_deliver, o_pkg1, o_depot, o_store)
up_prob.task_network.add_subtask(up_deliver, o_pkg2, o_wh, o_office)
# --- Resolution par un VRAI moteur HTN (aries, selectionne automatiquement) ---
with OneshotPlanner(problem_kind=up_prob.kind) as planner:
result = planner.solve(up_prob)
print("Moteur selectionne :", planner.name)
print("Statut :", result.status)
if result.plan is not None:
up_actions = result.plan.action_plan.actions # plan hiérarchique -> plan séquentiel
print(f"\nPlan trouve ({len(up_actions)} actions) :")
for i, a in enumerate(up_actions, 1):
print(f" {i}. {a}")