Ça démarre insidieusement par la création d'une variable booléenne pour mémoriser une situation et régler un cas particulier.
Puis une deuxième variable pour régler un autre cas.
Puis on passe à une variable entière pour un troisième cas…
Au bout d'un moment, ça devient suspect
et on se rend compte qu'on vient de lever un lièvre : il faut revoir en profondeur l'analyse du problème.
Les cas singuliers sont en fait des cas particuliers d'une vision plus générale.
J'ai donc revu ma copie et posé les bases d'une nouvelle façon de voir les choses.
La variable "numero_train" : Le grand changement, c'est d'avoir exploité au maximum la variable "numero_train" dans les pavés.
J'avais déjà entrevu la solution, mais je n'avais pas traité à fond toutes les possibilités de cette variable.
Ce qui a profondément transformé mon programme qui a maintenant moins de variables, moins de lignes, plus de clarté et de lisibilité.
Voilà mon réseau d'essais, avec des numérotations liées à cet article.
Je rappelle qu'on n'a jamais besoin de ces numéros de zone ni dans l'exploitation du réseau, ni dans le programme lui même (voir précédents posts)
En premier, le pupitre avec les numéros de train, qui commencent à 1, pas à 0. C'est important.
En bleu, le train n°2 et on voit son orientation sur le pupitre, orientation qui suivra la vraie orientation du train (merci à Pierre (=Pierre59) pour cette idée).
En jaune, le train n°3.
Voilà comment les numero_train sont présents dans les pavés :
N est le numéro de train.
Si numero_train est négatif, c'est qu'on n'a pas de train sur l'itinéraire
Si numero_train est positif, c'est qu'on a bien un train sur l'itinéraire
Pas de train = -1000, ce qui laisse 999 numéros de trains !
Ce numero_train existe aussi pour les itinéraires.
Il peut y avoir plusieurs itinéraires qui ont le même numero_train, en négatif : tous les itinéraires suivants, recopiés, (voir plus loin)
Mais il n'y a qu'un seul itinéraire ayant un numero_train positif : celui sur lequel est vraiment le train à un moment donné.
Comme je ne voulais pas détailler tous les pavés, on raisonnera sur les zones dans ce post.
En réalité, ça se passe au niveau des pavés.
Cas particulier de la couleur SABLE des itinéraires (itinéraires temporaires) : La couleur SABLE ne correspond pas à un numero_train.
Elle est gérée par une variable booléenne "iti_tempo".
Pendant qu'on affiche la couleur SABLE (qui est dessinée au dessus de tous les tracés, avec iti_tempo à true) les autres couleurs continuent leur vie "en dessous", les trains continuent à rouler, etc.
Dès qu'on a choisi un itinéraire, on met iti_tempo à false et on retourne aux couleurs normales qui ne sont désormais plus masquées.
Les couleurs affichées avant qu'on dessine des itinéraires temporaires ont pu évoluer entre temps.
La couleur SABLE ne masque pas les trains.
Évolution des couleurs : Quand on ouvre le réseau, tous les numero_trains sont à 0 et donc tout est affiché en GRIS.
Ce qui explique pourquoi on démarre les numero_train à 1.
Sur l'exemple précédent, j'ai défini un premier itinéraire.
(Un clic sur le point de départ, un clic sur le point d'arrivée)
iti1 : Z1, Z4, Z5, Z6, Z9, Z10, Z11 pour le train 3.
Puis, toujours pour le train 3, iti2 : Z11, Z6, Z5, Z4, Z2, Z1
Si on regarde bien, ça fait un "8" couché.
Puis, pour le train 2, iti3 : Z3, Z4, Z5, Z6, Z7, Z8.
Là, on a affaire à un point à point.
Pour le train 3, avec, entre parenthèses, le numero_train du pavé :
Z1(3) est en ROUGE à cause de l'occupation.
Z4(3), Z5(3), Z6(3), Z7(3), Z9(3), Z10(3) et Z11(3) sont en VERT. Comme c'est le premier itinéraire demandé, il est complètement VERT car rien ne l'empêchait.
Quand je fais le deuxième itinéraire du train 3, Z11(3) est déjà VERT et le reste.
Z6(3) est déjà VERT, mais on ne peut plus changer la position du branchement, réservée par iti1.
Z5(3) et Z4(3) sans changement, avec les positions du premier itinéraire.
Z2 ne fait pas partie du premier itinéraire et on a donc Z2(-3).
Le signe – signifie qu'il n'y a pas de train dessus. C'est l'itinéraire "suivant" de iti1.
Z1(3) à cause du premier itinéraire.
Pour le train 2 qui vient après les deux autres itinéraires, on a :
Z3(2), Z4(3), Z5(3), Z6(3), Z7(3), Z8(-2)
Le train 2, bloqué par un carré en sortie de Z3 (voir son cab-signal) n'est pas gêné que les numero_train soient d'un numéro différent du sien sur les zones suivantes puisqu'il ne peut pas y aller…
Vous voyez ainsi que, pour un même itinéraire, on peut avoir des pavés ayant des numero_train différents.
En effet, à un instant donné, un pavé ne peut avoir qu'un seul numero_train. C'est la phrase clé.
Quand on a réservé le pavé P pour le train 3, il reste réservé pour le train 3.
Dans tous les itinéraires utilisant le pavé P, il restera affecté au train 3.
Calcul des points : Un véhicule (loco ou wagon) ne peut avancer que s'il est sur une zone où on a des points. Il avance par sauts minuscules. Par exemple, l'itinéraire 1 ci-dessus compte … 10 304 points !
Dès le début, on se dit qu'il va falloir calculer les points le moins souvent possible sous peine de saturation processeur.
Autre remarque : Il ne faut pas, à chaque fois, calculer tous les points d'un itinéraire, mais seulement ceux qui servent.
Exemple : Pour le premier itinéraire du train 2, je n'ai calculé que les points de Z3. Ce n'est pas la peine d'aller plus loin car il est bloqué par un CARRE.
En fait, je ne m'occupe pas du CARRE, je regarde simplement les numero_train de cet itinéraire :
Z3(2), Z4(3), etc.
Dès l'instant où la Z4 est affectée au train 3, je sais que le train 2 ne pourra pas y aller. Et j'arrête de calculer les points.
Quand a-t-on vraiment besoin de calculer les points ? Dans seulement 3 cas :
1°) Quand on pose un train :
L'itinéraire était BLANC et devient VERT, puis ROUGE pour les zones occupées.
2°) Quand on relance un train arrêté :Exemple : le train 2 est actuellement arrêté devant son CARRE.
Quand il pourra rouler, par passage de CARRE à RAPPEL_RALENTISSEMENT_60, il aura fallu, auparavant calculer les points de la suite de l'itinéraire
3°) Lors d'un transfert d'itinéraire : Ex : quand il sera entièrement sur la Z11, le train 3 sera en train de ralentir devant un CARRE de fin d'itinéraire. Dès que l'itinéraire suivant sera possible, le train passera de dernière zone de l'itinéraire 1 à première zone de l'itinéraire 2. Et, là, il faudra calculer les points de l'itinéraire 2.
Donc le calcule se fait très rarement, en "one shot" (comme disent les Brexitois) à chaque fois.
Depuis le début de l'itinéraire jusqu'à ce qu'on trouve une zone qui n'a pas le bon numero_train.
Pour le train 3, on calcule les points tant que la zone a le numero_train = 3, -3, 0 ou -1000.
3, c'est évident, -3 car on peut le transformer en 3, 0 pour une première affectation d'une zone entièrement vide et -1000 uniquement quand on pose le train la première fois.
Et quand le calcul des points arrive sur un pavé ayant le mauvais numero_train, il s'arrête et décrète que le train est "en attente".
"iti_en_attente = true" pour cet itinéraire.
Quand les numero_train des pavés évoluent-ils ? Là, par contre, très souvent.
Le moment clé est la sortie d'un train d'une zone. On est en PRS, je le rappelle.
S'il n'y a qu'un seul train sur le réseau, au fur et à mesure que le train avance, on met le numero_train à 0 chaque fois qu'on sort d'une zone.
Mais j'ai voulu améliorer l'aspect visuel quand il y a plusieurs trains. Et aussi améliorer la compréhension de façon "parlante".
Dans l'exemple, seul le train 3 peut avancer.
Il va libérer Z1 qui va passer à 0 (GRIS).
Puis il va libérer Z4 qui va passer à -2 (BLANC). Pourquoi ?
Quand on libère Z1, personne n'en a plus besoin et on la libère complètement en passant à 0.
Par contre, quand on libère la Z4, le train 2, lui, va en avoir besoin et on la lui réserve en la mettant à numero_train = -2.
Puis, en avançant, le train 3 va libérer Z5 qui devrait, par le même raisonnement, passer à -2 et donc apparaitre en BLANC.
Oui, mais, maintenant, le canton formé par Z4 et Z5 est maintenant entièrement libre.
Temps mort : un canton, c'est une ou plusieurs zones avec un feu à chaque bout (en banalisé). Ne pas confondre zone et canton. Ce n'est pas la même chose.Donc, le train 2, ayant un canton libre devant lui, va pouvoir avancer.
Calcul des points jusqu'à Z5 inclus, les -2 deviennent des 2, et passage à VERT pour les zones.
Le train 3, sur Z6, empêche le train 2 d'aller plus loin.
Libération de Z6 qui passe à -2 (BLANC), de Z7 qui passe à -2 (BLANC) et Z8 est déjà à -2 (BLANC)
Donc libération totale du train 2, toutes les zones Z6, Z7 et Z8 passent à 2 (VERT) et le train 2 peut aller jusqu'au bout.
A noter aussi que cette libération du train 2 va maintenant bloquer le train 3. C'est lui qui, maintenant, va avoir iti_en_attente à true !
Quand le train 3 est entièrement sur Z1, abandon du premier itinéraire, transfert du train 3 au début de l'itinéraire 3 qui commence à Z11.
Le train 2 libère Z3 qui passe à 0 (GRIS), puis Z4 qui passe à –3 (BLANC), puis Z5 qui passe à -3 (BLANC), puis Z6 qui passe à -3 (BLANC).
Le train 3 est libéré, calcul des points et on a :
Z11(3), Z6(-3->3), Z5(-3->3), Z4(-3->3), Z2(-3->3), Z1(0->3). Tout est VERT et il peut avancer.
Si vous avez bien saisi cette mécanique, vous remarquerez que les points ne sont calculés que pour les zones VERT et ROUGE.
Pour être plus exact, c'est parce qu'on a des points que les zones sont VERT ou ROUGE. Signal CARRE : Je reviens sur le signal le plus complexe : le CARRE.
Complexe parce qu'il peut être modifié manuellement.
Gérer automatiquement une chose manuelle est impossible dans tous les cas.
Par contre, si on y arrive dans trois situations, cela couvre une très grande majorité des cas possibles.
1°) le signal est devant un branchement pris en talon et dans la mauvaise position .
On a le CARRE puisqu'on a pris comme règle que le branchement n'est pas talonnable.
(Je ne désespère pas de gérer les branchements talonnables, mais c'est une autre histoire)
2°) le dernier pavé d'un itinéraire se termine forcément par un CARRE s'il n'a pas d'itinéraire suivant. Lancer un train, c'est lui trouver un itinéraire suivant.
3°) le pavé suivant dans l'itinéraire n'a pas le même sens.Ce cas survient si on a deux trains face à face et il faut bien en arrêter un.
A tester car j'ai encore quelques bugs.
Itinéraires répétitifs: Un itinéraire répétitif, c'est un itinéraire qui se détruit, comme les autres, mais qui se reconstruira plus tard.
A partir du moment où on sait transférer un train d'un itinéraire sur un autre, c'est une suite assez logique.
Il "suffit" de recopier l'itinéraire qu'on est en train de parcourir en bout de liste.
Pour qu'on identifie dès le départ que tel itinéraire sera répétitif, j'ai dû créer un autre bouton au menu.
Dans l'exemple, j'ai crée, avec le bouton de recherche d'itinéraire, un premier itinéraire pour le train 3 qui va de Z1 à Z11, puis un deuxième itinéraire qui va de Z11 à Z1.
On a iti1 = Z1->Z11 et iti2 = Z11->Z1. Et iti2 suivant de iti1.
Le train 3 parcoure iti1, arrive en Z11, effectue un transfert de iti1->iti2 et parcoure iti2 jusqu'à Z1 et il s'arrête.
Si je crée iti1 à partir du bouton "itinéraire répétitif" et iti2 à partir du même bouton, que va-t-il se passer ?
Le train 3 parcoure iti1.
En fait, dès qu'il a démarré, il a recopié iti1' = iti1. Et dit que suivant(iti2) = iti1'.
Il y a maintenant dans "tous_iti" : iti1, iti2, iti1'.
Le train 3 arrive en Z11, sait que suivant(iti1) = iti2, et il y a transfert du train 3 sur iti2.
A cet instant, iti1 a disparu et il reste iti2, iti1'.
Dès que le train 3 a basculé, on ajoute iti2' = iti2 et on dit que suivant(iti1') = iti2'.
Il y a maintenant dans "tous_iti" : iti2, iti1', iti2'
Quand le train 3 arrive en Z1, on efface iti2, on transfère le train sur iti1'.
Il reste iti1' et iti2'.
Dès que le train 3 a basculé, on ajoute iti1" = iti1' et on dit que suivant(iti2') = iti1".
Quand le train 3 arrive en Z11, on efface iti1', on transfère le train sur iti2'.
Etc.
Donc on peut abouter autant d'itinéraires que l'on veut et ils se recopient à l'infini.
Paradoxe : Après plus de deux ans de recherches et de développement, je suis en mesure de poser un train sur un ovale, mettre le curseur à fond et le regarder tourner !!!
Tout ça pour ça….
Navette : Comme le transfert peut aussi avoir lieu avec le changement loco en tire -> loco en pousse et réciproquement, on peut faire une navette infinie aussi, sans difficulté particulière.
Exemple sur la vidéo suivante ou j'exploite avec un train les 4 terminaisons possibles.
VIDEO Et pour arrêter ? Eh oui, l'infini c'est long (surtout sur la fin) et il faudrait pouvoir arrêter quand on veut.
C'était déjà fait avant (voir mon post du 14 septembre).
On arrête manuellement le train là où on veut.
Un double clic sur l'un des signaux de la zone occupée et tous les itinéraires qui ont le même numero_train, mais en négatif, sont supprimés et le seul itinéraire qui a le numero_train en positif est réduit à la seule zone occupée.
C'est là qu'on voit la puissance des numero_train. Simple et diablement efficace.
Deux itinéraires répétitifs sécants : Pour bien monter que ce sont les itinéraires répétitifs, je vous montre la vidéo des itinéraires du début, mais en répétition pour chaque train ! Et tout s'enchaîne sans heurts !
VIDEO Et l'éditeur, dans tout ça ? Ayant atteint une étape cruciale dans le gestionnaire, je vais faire une pause et m'attaquer à l'éditeur.
Il existe déjà, mais mal programmé.
J'ai fait de gros progrès dans la gestion du temps machine, par contrainte de calculs à faire en temps réel pour le gestionnaire.
Je vais appliquer ces innovations à l'éditeur qui va devenir beaucoup plus rapide. C'est fondamental.
En plus, je vais très vraisemblablement intégrer certaines modifications apportées par Pierre pour le dessin des formes qui composent le réseau dans son article du 14 octobre
http://www.locoduino.org/spip.php?article231 Le gestionnaire est fini ? Que nenni !
Il manque :
1°) La gestion des zones de manœuvre, avec de vraies spécificités
2°) La gestion du nombre de wagons. Là,
le train 1 a 1 loco et 3 wagons
le train 2 a 1 loco et 4 wagons
le train 3 a 1 loco et 8 wagons
le train 4 est une loco HLP (Haut Le Pied = seule)
On ne peut rien y changer, sauf à "mettre les mains dans le cambouis".
Il faut pouvoir modifier le nombre de wagons très facilement, en détacher un ou plusieurs, etc.
3°) Pouvoir choisir que le pupitre soit en regroupé (comme maintenant) ou tout à droite du réseau (ce doit être facile)
4°) Améliorer l'accélération.
Pour l'instant, la partie pour l'accélération c'est :
void acceleration() { vitesse = consigne; }
Difficile de faire moins…
Alors que l'arrêt, par exemple fait 53 lignes et fait un beau ralenti type décharge de condensateur (voir mon post du 9 août).
Et, surtout, ce n'est pour l'instant qu'un jeu, sophistiqué, certes, mais sans lien avec le monde réel.
Je compte beaucoup sur mes collègues Orléanais pour m'expliquer ce qu'ils ont fait et qui est exactement ce qui me manque.
Complémentaires, je disais.
Piste pour un décor sur mesure : Comme on peut mettre n'importe quelle image comme fond de décor, je me suis amusé à ajouter deux rectangles. C'est moche, mais ça marche. Mais on peut dessiner ce qu'on veut.
Il suffit d'avoir Paint.net qui gère les calques. C'est gratuit.
1°) Vous faites une copie d'écran de votre réseau
2°) Vous redimensionnez l'image à 1366 x 768 (la définition donnée dans le setup)
3°) Vous ouvrez un nouveau calque
4°) Vous dessinez ce que vous voulez (bâtiments, photos, chemins, …). C'est facile parce que vous avez le réseau en transparence. Vous pouvez être très précis.
5°) Vous ne sauvez QUE le calque (sans le réseau) et vous appelez votre image "Fond ecran.png" rangée dans "data/Icones"
Aucune incidence sur le temps de traitement car elle n'est dessinée qu'une seule fois.
Nota : on ne peut pas prendre une photo de son vrai réseau et la mettre en fond pour une raison toute simple : les rails ne sont pas les mêmes (les branchements, traversées ont un angle de 45°, en particulier). On a fait le maximum pour que ça ressemble, mais c'est un schéma…
Pour ceux que ça intéresse : le lien vers le TCO V7_0 :
http://www.locoduino.org/IMG/zip/train_tco_v1_7_0.zip