#nullable enable
// === Port du §6.3 : les heuristiques guident-elles VRAIMENT la recherche ? ===
static List<(STRIPSAction action, HashSet<string> state)> Successeurs(STRIPSProblem problem, HashSet<string> state)
{
var res = new List<(STRIPSAction, HashSet<string>)>();
foreach (var a in problem.Actions)
if (a.Preconditions.All(p => state.Contains(p)))
{
var ns = new HashSet<string>(state);
ns.UnionWith(a.AddEffects);
if (!ns.SetEquals(state)) res.Add((a, ns));
}
return res;
}
static string CleEtat(HashSet<string> s) => string.Join(",", s.OrderBy(x => x));
static (int cost, int expansions) Recherche(STRIPSProblem problem, Func<STRIPSProblem, HashSet<string>, int>? hFn, string mode)
{
var frontier = new PriorityQueue<(HashSet<string> state, int g), (int p1, int p2, int ord)>();
int counter = 0;
var depart = new HashSet<string>(problem.InitialState);
int h0 = hFn != null ? hFn(problem, depart) : 0;
(int, int) pk(int g, int h) => mode switch { "ucs" => (g, 0), "astar" => (g + h, g), "greedy" => (h, g), _ => (g, 0) };
var k0 = pk(0, h0);
frontier.Enqueue((depart, 0), (k0.Item1, k0.Item2, counter++));
var fermes = new HashSet<string>();
int expansions = 0;
while (frontier.Count > 0)
{
var (etat, g) = frontier.Dequeue();
var cle = CleEtat(etat);
if (fermes.Contains(cle)) continue;
fermes.Add(cle); expansions++;
if (problem.Goal.All(gl => etat.Contains(gl))) return (g, expansions);
foreach (var (action, suivant) in Successeurs(problem, etat))
{
if (fermes.Contains(CleEtat(suivant))) continue;
int ng = g + action.Cost;
int nh = hFn != null ? hFn(problem, suivant) : 0;
var nk = pk(ng, nh);
frontier.Enqueue((suivant, ng), (nk.Item1, nk.Item2, counter++));
}
}
return (-1, expansions);
}
static int HMaxPur(STRIPSProblem p, HashSet<string> s) => HMax(p, s);
static int HAddPur(STRIPSProblem p, HashSet<string> s) => HAdd(p, s).hValue;
static int HFFPur(STRIPSProblem p, HashSet<string> s) => HFF(p, s).hValue;
static STRIPSProblem ProblemeChaineAvecDistracteurs(int M, int D)
{
var actions = new List<STRIPSAction>();
for (int j = 0; j < M; j++)
actions.Add(new STRIPSAction($"etape_but_{j+1}", new(){$"c{j}"}, new(){$"c{j+1}"}, 1));
for (int j = 0; j < D; j++)
{
actions.Add(new STRIPSAction($"leurre_{j}_1", new(){"c0"}, new(){$"d{j}"}, 1));
actions.Add(new STRIPSAction($"leurre_{j}_2", new(){$"d{j}"}, new(){$"dd{j}"}, 1));
}
return new STRIPSProblem(new(){"c0"}, new(){$"c{M}"}, actions);
}
var sb = new System.Text.StringBuilder();
sb.AppendLine("Benchmark : nombre de NOEUDS DEVELOPPES (deterministe, reproductible)");
sb.AppendLine("But = chaine de longueur M ; D = chaines leurres (faits non pertinents)");
sb.AppendLine();
sb.AppendLine(string.Format("{0,3} {1,3} | {2,14} {3,10} {4,10} {5,12}", "M", "D", "UCS (aveugle)", "A*+h^max", "A*+h^add", "Greedy+h^FF"));
sb.AppendLine(new string('-', 62));
foreach (var (M, D) in new[] {(3,0),(3,4),(3,8),(3,12),(3,16)})
{
var p = ProblemeChaineAvecDistracteurs(M, D);
int ucs = Recherche(p, null, "ucs").expansions;
int amax = Recherche(p, HMaxPur, "astar").expansions;
int aadd = Recherche(p, HAddPur, "astar").expansions;
int gff = Recherche(p, HFFPur, "greedy").expansions;
sb.AppendLine(string.Format("{0,3} {1,3} | {2,14} {3,10} {4,10} {5,12}", M, D, ucs, amax, aadd, gff));
}
sb.ToString().Display();