#r "nuget: pythonnet,3.0.5"
using System;
using Python.Runtime;
// Pont .NET -> CPython -> unified_planning + moteur aries (Tranche 2, lib-vs-lib #10382).
// unified_planning (AIPlan4EU) = la lib SOTA de planification du jumeau Python (section 5.2).
// Pont via pythonnet (axe 5 #10459 : binding Python) -- patron GT-6 (axelrod) / Search-7 (pyspiel).
Runtime.PythonDLL = Environment.GetEnvironmentVariable("PYTHONNET_PYDLL") ?? @"C:\Users\jsboi\AppData\Local\Programs\Python\Python313\python313.dll";
PythonEngine.Initialize();
Console.WriteLine($"Runtime Python : {PythonEngine.Version}");
using (Py.GIL())
{
dynamic scope = Py.CreateScope();
scope.Exec(@"
import unified_planning as up
from unified_planning.shortcuts import (
UserType, BoolType, Fluent, InstantaneousAction, Object, OneshotPlanner, Not, Equals,
)
from unified_planning.model.htn import HierarchicalProblem
from unified_planning.model.htn import Task as UPTask
from unified_planning.model.htn import Method as UPMethod
up.shortcuts.get_environment().credits_stream = None
# --- Types et fluents (meme etat du monde que les sections 3-5) ---
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)
up_at_veh = Fluent('at_veh', BoolType(), v=Veh_T, l=Loc_T)
up_in_veh = Fluent('in_veh', BoolType(), p=Pkg_T, v=Veh_T)
# --- Actions primitives load / unload / drive ---
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)))
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])
# --- Tache composee goto(v, dst) : noop si deja sur place, sinon drive (repositionnement) ---
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))
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)))
m_move.add_subtask(up_drive, mv, mfrm, mdst)
up_prob.add_method(m_move)
# --- Tache composee deliver(p, src, dst) : goto(src) -> load -> goto(dst) -> 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)
s1 = m_del.add_subtask(up_load, mp, mv, msrc)
g2 = m_del.add_subtask(up_goto, mv, mdst)
s2 = m_del.add_subtask(up_unload, mp, mv, mdst)
m_del.set_ordered(g1, s1, g2, s2)
up_prob.add_method(m_del)
# --- Instance a DEUX colis (celle du jumeau Python 5.2) : 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 le VRAI moteur HTN (aries, selectionne automatiquement) ---
with OneshotPlanner(problem_kind=up_prob.kind) as planner:
result = planner.solve(up_prob)
engine_name = planner.name
status = str(result.status)
up_actions = result.plan.action_plan.actions
lines = [f'unified_planning {up.__version__} -- moteur {engine_name} -- statut {status}']
lines.append(f'Plan trouve ({len(up_actions)} actions) :')
for i, a in enumerate(up_actions, 1):
lines.append(f' {i}. {a}')
report = chr(10).join(lines)
");
Console.WriteLine((string)scope.report);
}
Console.WriteLine("PONT .NET -> unified_planning/aries : OK");