Bonjour
Suite à ce que j’ai fait pour les TCOs en Processing et les articles ("Un gestionnaire en C++ pour votre réseau 1 2 "), je suis en train de réécrire complètement le gestionnaire de mon réseau.
J’ai commencé par la partie TCO qui en avait le plus besoin, à force de modifications pour ajouter plein de choses ce n’était plus très propre et puis les courbes de Béziers c’est quand même plus joli.
Comme pour le Locodrome je fais circuler des trains (composés de plusieurs véhicules), mais pour l’instant sans gestionnaire (j’ai juste ajouté quelques informations sur le réseau dans le TCO, avec des méthodes " suivant…() " ).
C’est très pratique car je peux peaufiner le TCO et faire pas mal d’essais pour éliminer les bugs dans la gestion du TCO.
Comme je n’ai pas pour l’instant d'itinéraires, et pour le plaisir, j’ai expérimenté la méthode de Jean-Luc, je cherche les itinéraires entre une origine et une destination. Pour cela j’ai une méthode RECURSIVE qui fait une dizaine de lignes de code qui marche très bien, quand il y a plusieurs possibilités je prend la plus courte (ce n'est pas la meilleure façon, mais c'est la plus simple et il faut bien choisir !) et je trace l’itinéraire sur le TCO comme pour le Locodrome.
Par contre pour annuler ou mémoriser un itinéraire c’est plus délicat, et puis il il a l’enclenchement d’approche à réaliser et la prise en compte des autorisations, mais c'est faisable. Inversement j’économise l’écriture d’une centaine d’itinéraires.
La base de mon réseau est la ZONE, une zone est un ensemble de voies et/ou d'appareils de voie pour lequel il ne peut y avoir qu'un seul train à la fois
Pour la recherche des itinéraires j'ajoute dans les classes zones deux méthodes qui renvoient la liste des zones suivantes paires et la liste des suivantes impaires, et dans la classe zone deux méthodes pour la recherche des itinéraires (dont une récursive) ainsi que quelques variables. Comme les classes zones ont beaucoup de références croisées entre elles, on ne peut pas tout faire dans le constructeur et il faut attendre que tous les objets zones soient construits pour établir ces références, c'est le rôle de la méthode liens() de zone qui est appelée dans le setup().
J'ai choisi de séparer la recherche des itinéraires dans le sens pair de celle du sens impair, car en pratique, pour optimiser, je marque les zones avec les marques suivantes : origine-paire, origine-impaire, destination-paire et/ou destination-impaire. Comme je clique sur le TCO sur une zone d'origine puis sur une zone de destination, cela me permet d'éliminer d'emblée les itinéraires impossibles et dans certains cas de limiter la recherche (pair/impair).
Concrètement on trouvera ci dessous le programme de la recherche récursive automatique des itinéraires appliquée au Locodrome, écrit en Processing puis en C++. Le programme a été optimisé pour qu'il y ai le moins de choses possibles sur la pile d"exécution.
L'image du Locodrome pour l'identification des zones :
Le programme en Processing :
void setup() { // programme de test
for (Zone z : zones) z.liens(); // etablissement des liens entre zones (suivants)
print("Z3 vers z4 "); z3.itineraireVers(z4);
print("Z4 vers z3 "); z4.itineraireVers(z3);
z0.occupee=true; println("occupation de la zone Z0");
print("Z3 vers z4 "); z3.itineraireVers(z4);
print("Z4 vers z3 "); z4.itineraireVers(z3);
}
static Zone z0=new Z0(),z1=new Z1(),z2=new Z2(),z3=new Z3(),z4=new Z4(),z5=new Z5();
static Zone[] zones={z0,z1,z2,z3,z4,z5};
static abstract class Zone {
static Zone[] VIDE={};
boolean occupee=false; // la zone est occupee par un train
boolean utilisee=false; // la zone est utilisee par un autre itineraire
abstract Zone[] suivantesPair();
abstract Zone[] suivantesImpair();
Zone[] suivantesPair;
Zone[] suivantesImpair;
static Zone destination;
static Zone suivante;
static boolean sensRecherche;
static Zone[] itineraire=new Zone[10]; static int index; // itineraire courant
static Zone[] itineraireLePlusCourt=new Zone[10]; static int longueur; // itineraire le plus court
void liens() { suivantesPair=suivantesPair(); suivantesImpair=suivantesImpair(); }
void copie() { // copie de l'itineraire vers le plus court
for (longueur=0; longueur<index; longueur++) itineraireLePlusCourt[longueur]=itineraire[longueur];
}
void affichage() { // affichage de l'itineraire le plus court
println(); print("itineraire ");
for (int i=0; i<longueur; i++) print(itineraireLePlusCourt[i].getNom()+" "); println();
}
void itineraireVers(Zone d) { // methode preparatoire (non recursive)
destination=d;
itineraire[0]=this; index=1; // origine
longueur=0; // remise a zero de l'itineraire le plus court
sensRecherche=false; itineraire(); // recherche sens pair
sensRecherche=true; itineraire(); // recherche sens impair
if (longueur!=0) affichage(); // on a trouve un des itineraires les plus courts
}
void itineraire() { // vers destination (methode recursive)
if (occupee) return; // la zone est occupee (donc pas utilisable)
if (utilisee) return; // la zone est utilisee (par un autre itineraire)
if (this==destination) { // on est arrivé à destination donc on a trouvé un itineraire
if (longueur==0) copie(); // premier itineraire de la recherche (copie)
if (longueur>index) copie(); // itineraire plus court que le precedent (copie)
} else { // on continue a chercher
Zone[] suivantes=sensRecherche?suivantesPair:suivantesImpair; // suivant le sens de recherche
for (int i=0; i<suivantes.length; i++) { // pour tous les suivantes paires ou impaires
suivante=suivantes[i];
itineraire[index++]=suivante; // on ajoute une des zones suivantes a l'itineraire
suivante.itineraire(); // RECURSIVITE
index--; // on supprime la zone de l'itineraire
}
}
}
final String getNom() { return getClass().toString().substring(25) ; } // nom de zone
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static class Z3 extends Zone {
Zone[] suivantesPair() { return VIDE; }
Zone[] suivantesImpair() { return new Zone[] {z2}; }
}
static class Z2 extends Zone {
Zone[] suivantesPair() { return new Zone[] {z3}; }
Zone[] suivantesImpair() { return new Zone[] {z0,z1}; }
}
static class Z0 extends Zone {
Zone[] suivantesPair() { return new Zone[] {z2}; }
Zone[] suivantesImpair() { return new Zone[] {z5}; }
}
static class Z1 extends Zone {
Zone[] suivantesPair() { return new Zone[] {z2}; }
Zone[] suivantesImpair() { return new Zone[] {z5}; }
}
static class Z5 extends Zone {
Zone[] suivantesPair() { return new Zone[] {z0,z1}; }
Zone[] suivantesImpair() { return new Zone[] {z4}; }
}
static class Z4 extends Zone {
Zone[] suivantesPair() { return new Zone[] {z5}; }
Zone[] suivantesImpair() { return VIDE; }
}
Le programme en C++ :
class Zone;
static Zone* destination;
static Zone* suivante;
static boolean sensRecherche;
static Zone* itineraireCourant[10]; static int index; // itineraire courant
static Zone* itineraireLePlusCourt[10]; static int longueur; // itineraire le plus court
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Zone {
public:
boolean occupee=false; // la zone est occupee par un train
boolean utilisee=false; // la zone est utilisee par un autre itineraire
Zone** suivsPair;
Zone** suivsImpair;
int nbPair;
int nbImpair;
virtual Zone** suivantesPair()=0;
virtual Zone** suivantesImpair()=0;
Zone(int np,int ni) { // constructeur
nbPair=np; nbImpair=ni;
}
void liens() { suivsPair=suivantesPair(); suivsImpair=suivantesImpair(); }
void copie() { // copie de l'itineraire vers le plus court
for (longueur=0; longueur<index; longueur++) itineraireLePlusCourt[longueur]=itineraireCourant[longueur];
}
void affichage() { // affichage de l'itineraire le plus court
Serial.println(); Serial.print("itineraire ");
for (int i=0; i<longueur; i++) Serial.print(itineraireLePlusCourt[i]->getNom()+" "); Serial.println();
}
void itineraireVers(Zone* d) { // methode preparatoire (non recursive)
destination=d;
itineraireCourant[0]=this; index=1; // origine
longueur=0; // remise a zero de l'itineraire le plus court
sensRecherche=false; itineraire(); // recherche sens pair
sensRecherche=true; itineraire(); // recherche sens impair
if (longueur!=0) affichage(); // on a trouve un des itineraires les plus courts
}
void itineraire() { // vers destination (methode recursive)
if (occupee) return; // la zone est occupee (donc pas utilisable)
if (utilisee) return; // la zone est utilisee (par un autre itineraire)
if (this==destination) { // on est arrivé à destination donc on a trouvé un itineraire
if (longueur==0) copie(); // premier itineraire de la recherche (copie)
if (longueur>index) copie(); // itineraire plus court que le precedent (copie)
} else { // on continue a chercher
Zone** suivantes=sensRecherche?suivsPair:suivsImpair; // suivant le sens de recherche
int nb=sensRecherche?nbPair:nbImpair;
for (int i=0; i<nb; i++) { // pour tous les suivantes paires ou impaires
suivante=suivantes[i];
itineraireCourant[index++]=suivante; // on ajoute une des zones suivantes a l'itineraire
suivante->itineraire(); // RECURSIVITE
index--; // on supprime la zone de l'itineraire
}
}
}
virtual String getNom(); // nom de zone
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Z3:public Zone {
public:
Z3():Zone(0,1) {}
Zone** suivantesPair();
Zone** suivantesImpair();
String getNom() { return "Z3"; }
};
class Z2:public Zone {
public:
Z2():Zone(1,2) {}
Zone** suivantesPair();
Zone** suivantesImpair();
String getNom() { return "Z2"; }
};
class Z0:public Zone {
public:
Z0():Zone(1,1) {}
Zone** suivantesPair();
Zone** suivantesImpair();
String getNom() { return "Z0"; }
};
class Z1:public Zone {
public:
Z1():Zone(1,1) {}
Zone** suivantesPair();
Zone** suivantesImpair();
String getNom() { return "Z1"; }
};
class Z5:public Zone {
public:
Z5():Zone(2,1) {}
Zone** suivantesPair();
Zone** suivantesImpair();
String getNom() { return "Z5"; }
};
class Z4:public Zone {
public:
Z4():Zone(1,0) {}
Zone** suivantesPair();
Zone** suivantesImpair();
String getNom() { return "Z4"; }
};
static Zone *z0=new Z0(),*z1=new Z1(),*z2=new Z2(),*z3=new Z3(),*z4=new Z4(),*z5=new Z5();
static Zone* zones[]={z0,z1,z2,z3,z4,z5};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
for (int i=0; i<6; i++) zones[i]->liens(); // etablissement des liens entre zones (suivants)
Serial.begin(9600);
Serial.print("Z3 vers z4 "); z3->itineraireVers(z4);
Serial.print("Z4 vers z3 "); z4->itineraireVers(z3);
z0->occupee=true; Serial.println("occupation de la zone Z0");
Serial.print("Z3 vers z4 "); z3->itineraireVers(z4);
Serial.print("Z4 vers z3 "); z4->itineraireVers(z3);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static Zone** VIDE=new Zone*[0]{};
Zone** Z3::suivantesPair() { return VIDE; }
Zone** Z3::suivantesImpair() { return new Zone*[1] {z2}; }
Zone** Z2::suivantesPair() { return new Zone*[1] {z3}; }
Zone** Z2::suivantesImpair() { return new Zone*[2] {z0,z1}; }
Zone** Z0::suivantesPair() { return new Zone*[1] {z2}; }
Zone** Z0::suivantesImpair() { return new Zone*[1] {z5}; }
Zone** Z1::suivantesPair() { return new Zone*[1] {z2}; }
Zone** Z1::suivantesImpair() { return new Zone*[1] {z5}; }
Zone** Z5::suivantesPair() { return new Zone*[2] {z0,z1}; }
Zone** Z5::suivantesImpair() { return new Zone*[1] {z4}; }
Zone** Z4::suivantesPair() { return new Zone*[1] {z5}; }
Zone** Z4::suivantesImpair() { return VIDE; }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {}
Affichages des deux programmes (c'est le même pour les deux) :
Z3 vers z4
itineraire Z3 Z2 Z0 Z5 Z4
Z4 vers z3
itineraire Z4 Z5 Z0 Z2 Z3
occupation de la zone Z0
Z3 vers z4
itineraire Z3 Z2 Z1 Z5 Z4
Z4 vers z3
itineraire Z4 Z5 Z1 Z2 Z3
Et pour finir ce que cela donne avec la grande gare de mon réseau :
Pierre