// === Tranche 2a : QuikGraph 2.5.0 sur la MEME grille 11x11 (parite chiffree avec la tranche 1) ===
// Reconstruit la grille en BidirectionalGraph pondere : arete v -> n de poids CoutCase(n)
// (convention de cout D'ENTREE de la tranche 1, cf. cell 14). Puis BFS (BreadthFirstSearchAlgorithm,
// non pondere, parents via TreeEdge — pattern Search-2) et A* (AStarShortestPathAlgorithm, poids
// e.Tag, heuristique Manhattan de la cell 8, retrace via InEdges — pattern Search-3).
#r "nuget: QuikGraph, 2.5.0"
using QuikGraph;
using QuikGraph.Algorithms.Search;
using QuikGraph.Algorithms.ShortestPath;
BidirectionalGraph<GridState, STaggedEdge<GridState, double>> GrilleQuikGraph(
HashSet<GridState> mz, int cMarais)
{
var g = new BidirectionalGraph<GridState, STaggedEdge<GridState, double>>();
for (int x = 0; x < W; x++)
for (int y = 0; y < H; y++)
g.AddVertex(new GridState(x, y));
for (int x = 0; x < W; x++)
for (int y = 0; y < H; y++)
{
var v = new GridState(x, y);
foreach (var (dx, dy) in ACTIONS)
{
var n = new GridState(x + dx, y + dy);
if (n.X >= 0 && n.X < W && n.Y >= 0 && n.Y < H)
g.AddEdge(new STaggedEdge<GridState, double>(v, n, CoutCase(n, mz, cMarais)));
}
}
return g;
}
// BFS QuikGraph : le chemin reconstruit INCLUT le depart (convention CoutChemin.Skip(1), cell 14).
List<GridState> QuikBfsPath(BidirectionalGraph<GridState, STaggedEdge<GridState, double>> g,
GridState depart, GridState arrivee)
{
var parent = new Dictionary<GridState, GridState>();
var bfs = new BreadthFirstSearchAlgorithm<GridState, STaggedEdge<GridState, double>>(g);
bfs.TreeEdge += e => parent[e.Target] = e.Source;
bfs.Compute(depart);
var chemin = new List<GridState>();
var cur = arrivee;
while (!cur.Equals(depart))
{
chemin.Add(cur);
if (!parent.TryGetValue(cur, out cur)) break;
}
chemin.Add(depart);
chemin.Reverse();
return chemin;
}
// A* QuikGraph : poids = e.Tag, heuristique = Manhattan (meme fonction que la tranche 1).
// Retrace via InEdges : predecesseur u tel que d(u) + poids(u, v) = d(v) (pattern Search-3 cell 16).
// Le depart n'est ajoute qu'une fois (convention Count - 1 = nombre de pas, cf. cell 14).
(List<GridState> chemin, double cout) QuikAStarPath(
BidirectionalGraph<GridState, STaggedEdge<GridState, double>> g,
GridState depart, GridState arrivee)
{
var astar = new AStarShortestPathAlgorithm<GridState, STaggedEdge<GridState, double>>(
g, e => e.Tag, v => Manhattan(v, arrivee));
astar.Compute(depart);
double cout = astar.GetDistance(arrivee);
var chemin = new List<GridState>();
var cour = arrivee;
while (!cour.Equals(depart))
{
chemin.Add(cour);
double dCour = astar.GetDistance(cour);
GridState prec = null;
bool trouve = false;
foreach (var e in g.InEdges(cour))
if (Math.Abs(astar.GetDistance(e.Source) + e.Tag - dCour) < 1e-6)
{ prec = e.Source; trouve = true; break; }
if (!trouve) break;
cour = prec;
}
chemin.Add(depart);
chemin.Reverse();
return (chemin, cout);
}
// Parite chiffree : couts tranche 1 (cells 12/14) vs couts QuikGraph, sur les 2 instances.
int CoutQuik(List<GridState> path, HashSet<GridState> mz, int cM) => path.Skip(1).Sum(s => CoutCase(s, mz, cM));
var gUni = GrilleQuikGraph(noMarais, coutMarais);
var uniBfs = QuikBfsPath(gUni, start, goal);
var (uniAst, uniAstCost) = QuikAStarPath(gUni, start, goal);
var gPond = GrilleQuikGraph(marais, coutMaraisLourd);
var pondBfs = QuikBfsPath(gPond, start, goal);
var (pondAst, pondAstCost) = QuikAStarPath(gPond, start, goal);
int cUniBfs = CoutQuik(uniBfs, noMarais, coutMarais);
int cPondBfs = CoutQuik(pondBfs, marais, coutMaraisLourd);
Console.WriteLine("=== Tranche 2a : QuikGraph vs from-scratch (cells 12/14) — parite chiffree ===");
Console.WriteLine("Instance".PadRight(20) + "Algo".PadRight(8) + "Cout T1".PadRight(10) + "Cout QG".PadRight(10) + "Verdict");
Console.WriteLine(new string('-', 58));
Console.WriteLine($"{"Uniforme (cell 12)",-20}{"BFS",-8}{10,-10}{cUniBfs,-10}{(cUniBfs == 10 ? "OK" : "DIFF")}");
Console.WriteLine($"{"Uniforme (cell 12)",-20}{"A*",-8}{10,-10}{(int)uniAstCost,-10}{((int)uniAstCost == 10 ? "OK" : "DIFF")}");
Console.WriteLine($"{"Pondere (cell 14)",-20}{"BFS",-8}{55,-10}{cPondBfs,-10}{(cPondBfs == 55 ? "OK" : "DIFF")}");
Console.WriteLine($"{"Pondere (cell 14)",-20}{"A*",-8}{16,-10}{(int)pondAstCost,-10}{((int)pondAstCost == 16 ? "OK" : "DIFF")}");
Console.WriteLine();
Console.WriteLine($"BFS QuikGraph (pondere) : {pondBfs.Count - 1} pas, cout {cPondBfs} (traverse le marais, 5 cases).");
Console.WriteLine($"A* QuikGraph (pondere) : {pondAst.Count - 1} pas, cout {(int)pondAstCost} (contourne le marais).");
Console.WriteLine("Parite tranche 1 == tranche 2 : BFS et A* donnent les memes couts sur la meme instance.");