// Graphe des villes francaises (13 villes, reutilise du jumeau Search-02-CSharp)
// + coordonnees lat/lon + heuristique Haversine.
var franceGraph = new Dictionary<string, Dictionary<string, double>> {
["Bordeaux"] = new() { ["Paris"] = 585, ["Toulouse"] = 245, ["Nantes"] = 350 },
["Paris"] = new() { ["Bordeaux"] = 585, ["Lille"] = 225, ["Strasbourg"] = 490, ["Lyon"] = 465, ["Nantes"] = 385 },
["Lille"] = new() { ["Paris"] = 225, ["Rennes"] = 510 },
["Rennes"] = new() { ["Lille"] = 510, ["Nantes"] = 110, ["Brest"] = 245 },
["Brest"] = new() { ["Rennes"] = 245 },
["Nantes"] = new() { ["Rennes"] = 110, ["Paris"] = 385, ["Bordeaux"] = 350, ["Toulouse"] = 590 },
["Strasbourg"] = new() { ["Paris"] = 490, ["Lyon"] = 490 },
["Lyon"] = new() { ["Paris"] = 465, ["Strasbourg"] = 490, ["Marseille"] = 315, ["Toulouse"] = 535, ["Grenoble"] = 115 },
["Grenoble"] = new() { ["Lyon"] = 115, ["Marseille"] = 305 },
["Marseille"] = new() { ["Lyon"] = 315, ["Grenoble"] = 305, ["Toulouse"] = 405, ["Nice"] = 200, ["Montpellier"] = 165 },
["Toulouse"] = new() { ["Bordeaux"] = 245, ["Nantes"] = 590, ["Marseille"] = 405, ["Lyon"] = 535, ["Montpellier"] = 245 },
["Montpellier"] = new() { ["Toulouse"] = 245, ["Marseille"] = 165 },
["Nice"] = new() { ["Marseille"] = 200 },
};
// Coordonnees (latitude, longitude) en degres decimaux.
var franceCoords = new Dictionary<string, (double lat, double lon)> {
["Paris"] = (48.86, 2.35),
["Lyon"] = (45.76, 4.83),
["Marseille"] = (43.30, 5.37),
["Toulouse"] = (43.60, 1.44),
["Bordeaux"] = (44.84, -0.57),
["Nantes"] = (47.22, -1.55),
["Rennes"] = (48.11, -1.68),
["Lille"] = (50.63, 3.06),
["Strasbourg"] = (48.57, 7.75),
["Nice"] = (43.70, 7.26),
["Montpellier"] = (43.61, 3.88),
["Grenoble"] = (45.19, 5.72),
["Brest"] = (48.39, -4.49),
};
// Distance Haversine (arc de grand cercle) entre deux villes — heuristique admissible.
double Haversine(string a, string b) {
const double R = 6371.0; // rayon de la Terre en km
var (la, lo) = franceCoords[a];
var (lb, lb2) = franceCoords[b];
double dLat = (lb - la) * Math.PI / 180.0;
double dLon = (lb2 - lo) * Math.PI / 180.0;
double x = Math.Sin(dLat / 2) * Math.Sin(dLat / 2)
+ Math.Cos(la * Math.PI / 180.0) * Math.Cos(lb * Math.PI / 180.0)
* Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
return 2.0 * R * Math.Asin(Math.Min(1.0, Math.Sqrt(x)));
}
// Fabrique une fonction heurique h_etat(s) = Haversine(s, goal).
Func<string, double> MakeH(string goal) => s => Haversine(s, goal);
// Probleme de reference : Lille -> Toulouse (la ou Greedy et A* divergent).
var problemLT = new GraphProblem("Lille", "Toulouse", franceGraph);
Func<string, double> hLilleToulouse = MakeH("Toulouse");
Console.WriteLine($"Carte chargee : {franceGraph.Count} villes");
Console.WriteLine($"Probleme de reference : {problemLT.Initial} -> {problemLT.Goal}");
Console.WriteLine();
Console.WriteLine("Heuristique h(n) = Haversine vers Toulouse (vol d'oiseau, admissible) :");
foreach (var v in new[] { "Lille", "Paris", "Rennes", "Nantes", "Bordeaux", "Toulouse" }) {
Console.WriteLine($" {v,-12} h = {hLilleToulouse(v),6:F0} km");
}