# --- Exemple guide : Resolution complete du domaine Gripper ---
# Cet exemple montre le workflow complet : modelisation unified-planning -> resolution -> analyse du plan
from collections import OrderedDict
from unified_planning.shortcuts import *
from unified_planning.engines import PlanGenerationResultStatus
from unified_planning.environment import get_environment
get_environment().credits_stream = None # silencer les credits pyperplan (sortie pedagogique propre)
# 1. Definition des types
Room = UserType('Room')
Ball = UserType('Ball')
Gripper = UserType('Gripper')
# 2. Definition des fluents (predicats dynamiques)
at_robby = Fluent('at-robby', BoolType(), OrderedDict([('r', Room)]))
at_ball = Fluent('at', BoolType(), OrderedDict([('b', Ball), ('r', Room)]))
free_gripper = Fluent('free', BoolType(), OrderedDict([('g', Gripper)]))
carry_ball = Fluent('carry', BoolType(), OrderedDict([('b', Ball), ('g', Gripper)]))
# 3. Definition des actions
# Action move : deplacer le robot d'une piece a l'autre
move = InstantaneousAction('move', OrderedDict([('from', Room), ('to', Room)]))
from_m = move.parameter('from')
to_m = move.parameter('to')
move.add_precondition(at_robby(from_m))
move.add_effect(at_robby(to_m), True)
move.add_effect(at_robby(from_m), False)
# Action pick : ramasser une balle avec une pince
pick = InstantaneousAction('pick', OrderedDict([('b', Ball), ('r', Room), ('g', Gripper)]))
b_p = pick.parameter('b')
r_p = pick.parameter('r')
g_p = pick.parameter('g')
pick.add_precondition(at_ball(b_p, r_p))
pick.add_precondition(at_robby(r_p))
pick.add_precondition(free_gripper(g_p))
pick.add_effect(carry_ball(b_p, g_p), True)
pick.add_effect(at_ball(b_p, r_p), False)
pick.add_effect(free_gripper(g_p), False)
# Action drop : poser une balle
drop = InstantaneousAction('drop', OrderedDict([('b', Ball), ('r', Room), ('g', Gripper)]))
b_d = drop.parameter('b')
r_d = drop.parameter('r')
g_d = drop.parameter('g')
drop.add_precondition(carry_ball(b_d, g_d))
drop.add_precondition(at_robby(r_d))
drop.add_effect(at_ball(b_d, r_d), True)
drop.add_effect(free_gripper(g_d), True)
drop.add_effect(carry_ball(b_d, g_d), False)
# 4. Creation du probleme
gripper_problem = Problem('gripper-2rooms-3balls')
# Objets : 2 pieces, 3 balles, 2 pinces (left et right)
room_a = Object('room_a', Room)
room_b = Object('room_b', Room)
ball1 = Object('ball1', Ball)
ball2 = Object('ball2', Ball)
ball3 = Object('ball3', Ball)
left = Object('left', Gripper)
right = Object('right', Gripper)
gripper_problem.add_objects([room_a, room_b, ball1, ball2, ball3, left, right])
# Etat initial : toutes les balles dans room_a, robot dans room_a, pinces libres
gripper_problem.set_initial_value(at_robby(room_a), True)
for ball in [ball1, ball2, ball3]:
gripper_problem.set_initial_value(at_ball(ball, room_a), True)
for gripper_obj in [left, right]:
gripper_problem.set_initial_value(free_gripper(gripper_obj), True)
# But : deplacer toutes les balles vers room_b
for ball in [ball1, ball2, ball3]:
gripper_problem.add_goal(at_ball(ball, room_b))
# Ajout des actions au probleme
gripper_problem.add_action(move)
gripper_problem.add_action(pick)
gripper_problem.add_action(drop)
print("Probleme Gripper cree avec unified-planning")
print(f" Pieces: room_a, room_b")
print(f" Balles: ball1, ball2, ball3 (toutes dans room_a)")
print(f" Pinces: left, right (toutes libres)")
print(f" But: deplacer les 3 balles vers room_b")
print(f" Actions disponibles: {[a.name for a in gripper_problem.actions]}")
# 5. Resolution avec pyperplan
print("\nResolution en cours...")
try:
with OneshotPlanner(name='pyperplan') as planner:
result = planner.solve(gripper_problem)
if result.status == PlanGenerationResultStatus.SOLVED_SATISFICING:
plan = result.plan
print(f"\nPlan trouve ({len(plan.actions)} actions) :")
print("=" * 55)
for i, action in enumerate(plan.actions):
params = ', '.join(str(p.object()) for p in action.actual_parameters)
print(f" {i+1:2d}. {action.action.name}({params})")
print("=" * 55)
# Analyse du plan : compter les deplacements
move_count = sum(1 for a in plan.actions if a.action.name == 'move')
pick_count = sum(1 for a in plan.actions if a.action.name == 'pick')
drop_count = sum(1 for a in plan.actions if a.action.name == 'drop')
print(f"\nStatistiques du plan :")
print(f" Deplacements (move) : {move_count}")
print(f" Ramassages (pick) : {pick_count}")
print(f" Depots (drop) : {drop_count}")
# Pinces effectivement utilisees (3eme param de pick/drop) + trajets productifs
grippers = sorted({str(a.actual_parameters[2].object()) for a in plan.actions if a.action.name in ('pick', 'drop')})
trips_to_b = sum(1 for a in plan.actions if a.action.name == 'move' and str(a.actual_parameters[1].object()) == 'room_b')
print(f"\nAnalyse du plan trouve :")
print(f" Pinces utilisees : {len(grippers)} ({', '.join(grippers)})")
print(f" Trajets vers room_b : {trips_to_b}")
if len(grippers) == 1:
print(f"\npyperplan est un planificateur SATISFICING (non optimal) : il renvoie ici un")
print(f"plan sous-optimal valide utilisant 1 seule pince, 1 balle par trajet")
print(f"({trips_to_b} allers + {move_count - trips_to_b} retours = {move_count} moves, {len(plan.actions)} actions).")
print(f"\nLe plan OPTIMAL exploiterait les 2 pinces (left+right) : 2 balles au premier")
print(f"trajet puis 1 au second = 9 actions (3 pick + 3 drop + 3 move). Cf. le plan")
print(f"theorique ci-dessus (affiche si pyperplan est indisponible).")
else:
print(f"\nLe plan utilise les 2 pinces (left+right) pour porter 2 balles par trajet :")
print(f"2 trajets aller-retour (2 balles puis 1 balle) = {len(plan.actions)} actions, strategie optimale.")
else:
print(f"Statut: {result.status}")
except Exception as e:
print(f"Planificateur non disponible ({e})")
print("\nPlan theorique (optimal) :")
print(" 1. pick(ball1, room_a, left) - prendre ball1 avec pince gauche")
print(" 2. pick(ball2, room_a, right) - prendre ball2 avec pince droite")
print(" 3. move(room_a, room_b) - aller en room_b")
print(" 4. drop(ball1, room_b, left) - poser ball1")
print(" 5. drop(ball2, room_b, right) - poser ball2")
print(" 6. move(room_b, room_a) - retour en room_a")
print(" 7. pick(ball3, room_a, left) - prendre ball3")
print(" 8. move(room_a, room_b) - aller en room_b")
print(" 9. drop(ball3, room_b, left) - poser ball3")
print(" => 9 actions au total (3 pick + 3 drop + 3 move)")