// --- Poursuite-evasion 2D en RK4 (from-scratch) ---
// Etat x = [px, py, ex, ey]. Strategie de P = pure pursuit (vers E).
// Strategie de E : (a) radiale : fuit droit devant P ; (b) perpendiculaire.
public static double Distance(double[] p, double[] e)
{
double dx = p[0] - e[0], dy = p[1] - e[1];
return Math.Sqrt(dx * dx + dy * dy);
}
// Mode d'evasion de E : "radial" (fuit sur la ligne P->E) ou "perpendiculaire".
public static double SimulatePursuit(double vp, double ve, double[] startP, double[] startE,
string mode, double dt, double eps, double tMax,
out double captureTime)
{
double[] x = { startP[0], startP[1], startE[0], startE[1] };
double t = 0.0;
double dInit = Distance(startP, startE);
// Dynamique : P -> E a vitesse vp ; E dans sa direction d'evasion a vitesse ve.
double[] Dyn(double tt, double[] s)
{
double dx = s[2] - s[0], dy = s[3] - s[1];
double d = Math.Sqrt(dx * dx + dy * dy);
if (d < 1e-12) return new double[4];
double ux = dx / d, uy = dy / d; // unite P -> E
double vx, vy; // direction de fuite de E
if (mode == "radial") { vx = ux; vy = uy; } // fuit droit devant
else { vx = -uy; vy = ux; } // perpendiculaire (norme a gauche)
return new double[] { vp * ux, vp * uy, ve * vx, ve * vy };
}
while (t < tMax)
{
x = Rk4Step(Dyn, t, x, dt);
t += dt;
if (Distance(new double[] { x[0], x[1] }, new double[] { x[2], x[3] }) < eps) break;
}
captureTime = t;
return dInit;
}
double vp = 2.0, ve = 1.0;
var p0 = new double[] { 0.0, 0.0 };
var e0 = new double[] { 10.0, 0.0 };
SimulatePursuit(vp, ve, p0, e0, "radial", dt: 0.001, eps: 0.05, tMax: 50.0, out double tRad);
SimulatePursuit(vp, ve, p0, e0, "perpendiculaire", dt: 0.001, eps: 0.05, tMax: 200.0, out double tPerp);
// --- Cas 3 : evasion vers un refuge (zone sure) ---
// E ne fuie pas P directement : il file vers un point de refuge R hors de l'axe P->E.
// Sa trajectoire n'est alors NI radiale NI perpendiculaire (composantes melangees),
// et celle de P (pure pursuit) devient courbe. Le temps de capture/echappement n'admet
// PLUS de formule analytique simple (d0/(vp-ve) ou d0/vp) : seule l'integration RK4 le donne.
double[] refuge = new double[] { 10.0, 8.0 }; // au-dessus de l'axe P->E
public static double SimulatePursuitRefuge(double vp, double ve, double[] startP, double[] startE,
double[] refuge, double dt, double eps, double tMax,
out bool escaped)
{
double[] x = { startP[0], startP[1], startE[0], startE[1] };
double t = 0.0;
double[] Dyn(double tt, double[] s)
{
double dpx = s[2] - s[0], dpy = s[3] - s[1]; // P -> E
double d = Math.Sqrt(dpx * dpx + dpy * dpy);
if (d < 1e-12) return new double[4];
double ux = dpx / d, uy = dpy / d; // P : pure pursuit vers E
double drx = refuge[0] - s[2], dry = refuge[1] - s[3]; // E -> refuge
double dr = Math.Sqrt(drx * drx + dry * dry);
if (dr < 1e-12) return new double[] { vp * ux, vp * uy, 0, 0 };
double wx = drx / dr, wy = dry / dr; // E : file vers le refuge
return new double[] { vp * ux, vp * uy, ve * wx, ve * wy };
}
while (t < tMax)
{
x = Rk4Step(Dyn, t, x, dt);
t += dt;
double dPE = Distance(new double[] { x[0], x[1] }, new double[] { x[2], x[3] });
double dER = Distance(new double[] { x[2], x[3] }, refuge);
if (dPE < eps) { escaped = false; return t; } // P capture E
if (dER < eps) { escaped = true; return t; } // E atteint le refuge (echappe)
}
escaped = true;
return tMax;
}
double tRefuge = SimulatePursuitRefuge(vp, ve, p0, e0, refuge, dt: 0.001, eps: 0.05, tMax: 200.0, out bool eRefugeEscaped);
double dRefuge = Distance(e0, refuge); // distance E0 -> refuge (borne inferieure du temps d'E : dRefuge/ve)
double tRadTheory = 10.0 / (vp - ve); // d0 / (vp - ve) = 10
var sb7 = new StringBuilder();
sb7.AppendLine("Poursuite-evasion (vp=2, ve=1, d0=10, eps=0.05) en RK4");
sb7.AppendLine(new string('=', 60));
sb7.AppendLine($" evasion radiale : t* = {tRad:F3} (theorie d0/(vp-ve) = {tRadTheory:F3}) <- meilleur cas pour E");
sb7.AppendLine($" evasion perpendiculaire: t* = {tPerp:F3} (theorie d0/vp = {10.0/vp:F3}) <- pire cas pour E");
sb7.AppendLine($" evasion vers refuge : t* = {tRefuge:F3} (E atteint refuge a {dRefuge:F1}, borne dRefuge/ve = {dRefuge/ve:F3}) {(eRefugeEscaped ? "<- E ECHAPPE (atteint le refuge)" : "<- P capture E en chemin")}");
sb7.AppendLine($" (refuge hors axe P->E : trajectoire courbe, PAS de formule analytique -> RK4 necessaire)");
sb7.AppendLine();
sb7.AppendLine($" ecart radial vs theorie : {Math.Abs(tRad - tRadTheory):F4}");
sb7.AppendLine(" -> vp > ve garantit la capture en temps fini (Isaacs 1965).");
sb7.AppendLine(" -> radial maximise t* (E oppose toute sa vitesse) ; perpendiculaire le minimise (E gaspille sa vitesse).");
Show(sb7.ToString());