Algorithmique de la mobilité
Dans cette séance, nous allons réaliser graduellement un scénario complexe impliquant des entités fixes et des entités mobiles. Les interactions se feront par envoi de messages. À tout moment, vous pouvez vous référer à la vidéo suivante pour visualiser le genre de résultat que vous êtes censés obtenir (à titre indicatif seulement).
Créez un projet en copiez-y les six classes données plus bas (Canadair
, Fire
, Lake
, Sensor
, Station
et MainCanadair
) ainsi que la classe WaypointNode
que vous avez peut-être déjà (sinon ce lien).
Node
(autrement dit toutes les classes sauf MainCanadair
). Notez que la majorité du code consiste à surcharger les méthodes de la classe Node
.Sensor
) en surchargeant onSensingIn()
. Utilisez instanceof pour être sûr qu’il s’agit de feux et non pas d’une autre sorte de noeud. Lorsqu’un feu est détecté, le capteur doit se mettre en mode alerte (couleur rouge, setColor(Color.red)
).onSensingIn()
). Lorsqu'un feu est détecté, le canadair l'éteint (méthode die()
sur le feu).Sensor
) détecte s'il n'y a plus aucun feu dans son entourage et si c'est le cas, il se remet en mode normal. Vous pouvez maintenir la liste des feux actuellement captés par ce capteur via la liste sensedFires
prévue à cet effet. Pour détecter qu'un feu a disparu, utilisez onSensingOut()
, et pour supprimer la couleur actuelle, utiliser une couleur null
.onMessage
, utilisez parent
, utilisez getCommonLinkWith
, utilisez setWidth
).onClock
, utilisez getLocation
et des messages de type Point
).getTime()
lors de la réception d'un message pour récupérer le numéro du round, et testez-le dans le onClock()
).sendAll
). Ces derniers doivent se mettre en route vers le capteur (souvenez vous que Canadair
hérite de WaypointNode
, qui possède une méthode addDestination()
...)onArrival()
, testez alors si la cible courante était différente du parking, auquel cas reprenez l'emplacement de parking comme nouvelle cible.)Les objectifs suivants admettent un certain degré de liberté.
sensedFire
d'un capteur lorsqu'il arrive sur le capteur.onSensingIn
, instanceof
, ...) avant d'éteindre les feux. En fait, il ne peut éteindre qu'un seul feu (ou disons un nombre donné) à chaque fois qu'il va au lac !sensedFire
, leur trajectoire doit couvrir la zone concernée (c.f. la vidéo).MainCanadair
et la classe Canadair
en les renommant MainCanadair2
et Canadair2
. Dans le nouveau main, commentez l'appel à deploySensors
, ce qui aura pour effet de supprimer la station et les capteurs. Dans le nouveau canadair, enlevez tout ce qui a trait à la station et aux capteurs. Vos nouveaux canadairs doivent patrouiller en permanence sur le terrain en éteignant les feux rencontrés (en refaisant le plein d'eau au lac de temps en temps).onClock()
et onMessage()
).Canadair
import io.jbotsim.core.Point;
import io.jbotsim.ui.icons.Icons;
public class Canadair extends WaypointNode {
Point parking;
public Canadair(){
setIcon(Icons.CANADAIR);
setIconSize(16);
setCommunicationRange(120);
setSensingRange(30);
}
@Override
public void onStart() {
parking = getLocation();
super.setSpeed(2);
}
}
Fire
import java.util.ArrayList;
import java.util.Random;
import io.jbotsim.core.Node;
import io.jbotsim.ui.icons.Icons;
/* Gardez cette classe telle quelle */
public class Fire extends Node {
static ArrayList<Fire> allFires = new ArrayList<Fire>();
Random r=new Random();
@Override
public void onStart(){
allFires.add(this);
disableWireless();
setIcon(Icons.FIRE);
}
@Override
public void onClock(){
if (Math.random() < 0.01 && allFires.size() < 100)
propagate();
}
public void propagate(){
double x = getX() + r.nextDouble() * 20 - 10;
double y = getY() + r.nextDouble() * 20 - 10;
for (Fire fire : allFires)
if (fire.distance(x, y) < 10)
return;
getTopology().addNode(x, y, new Fire());
}
@Override
public void die(){
super.die();
allFires.remove(this);
}
}
Lake
import io.jbotsim.core.Node;
import io.jbotsim.ui.icons.Icons;
public class Lake extends Node {
public Lake(){
setIcon(Icons.LAKE);
setIconSize(40);
disableWireless();
}
}
Sensor
import io.jbotsim.core.Node;
import io.jbotsim.ui.icons.Icons;
import java.util.ArrayList;
import java.util.List;
public class Sensor extends Node {
Node parent = null;
List<Fire> sensedFires = new ArrayList<>();
@Override
public void onStart() {
setIcon(Icons.SENSOR);
setIconSize(16);
setCommunicationRange(120);
setSensingRange(60);
}
}
Station
import io.jbotsim.core.Message;
import io.jbotsim.core.Node;
import io.jbotsim.ui.icons.Icons;
public class Station extends Node {
public Station() {
setIcon(Icons.STATION);
setIconSize(25);
setCommunicationRange(120);
}
@Override
public void onStart() {
sendAll(new Message());
}
}
MainCanadair
import io.jbotsim.core.*;
import io.jbotsim.ui.JViewer;
import io.jbotsim.core.DelayMessageEngine;
/**
* Il n'est pas essentiel de comprendre ce qu'il y a dans cette classe.
*/
public class MainCanadair {
public static void deploySensors(Topology topology){
for (int i=0; i<6; i++)
for (int j=0; j<4; j++)
topology.addNode(i*100+180-(j%2)*30, j*100+100, new Sensor());
topology.addNode(50, 400, new Station());
for (Link link : topology.getLinks())
link.setColor(Color.gray);
}
public static void main(String[] args) {
Topology topology = new Topology(800,600);
topology.setLinkResolver(new LinkResolver(){
@Override
public boolean isHeardBy(Node n1, Node n2) {
if ((n1 instanceof Sensor && n2 instanceof Canadair) ||
(n2 instanceof Sensor && n1 instanceof Canadair))
return false;
return (n1.isWirelessEnabled() && n2.isWirelessEnabled()
&& n1.distance(n2) < n1.getCommunicationRange());
}
});
topology.setMessageEngine(new DelayMessageEngine(topology, 10));
deploySensors(topology);
topology.addNode(50, 500, new Canadair());
topology.addNode(100, 500, new Canadair());
topology.addNode(50, 50, new Lake());
topology.setTimeUnit(30);
topology.setDefaultNodeModel(Fire.class);
new JViewer(topology);
topology.start();
}
}