Teaching - 2i002 - (TME: sujets)


2i002 : Introduction à la programmation Objet

Extrait de l'examen 2007-2008 S1 : Aquarium

L'objet de ce problème consiste à réaliser un aquarium virtuel de taille 500\times500 dans lequel évoluent des thons et des requins. On suppose que l'on dispose de la classe Point ci-dessous :

public class Point {  
  public int x,y;  
  public Point(int x, int y){ this.x=x;this.y=y; }  
  public Point() {      /* initialise x et y entre 0 et 499 */      
    this((int)(Math.random() * 500),(int)(Math.random() * 500));  
  }  
  public String toString() { return "("+x+","+y+") "; }  
  public double distanceTo(Point p) {      
    int dx = p.x-x;      
    int dy = p.y-y;      
    return Math.sqrt(dx*dx+dy*dy);  
  }
}

La classe ArrayList est une une classe prédéfinie en java qui se trouve dans le package java.util . Elle permet de stocker des objets (un peu comme dans un tableau mais sans se préoccuper de la taille du tableau). Pour utiliser cette classe, il faut indiquer dans quel package elle se trouve, en rajoutant en haut de votre fichier : import java.util.ArrayList; L'utilisation de cette classe nécessite de préciser le type E des objets qui sont dans la liste. Pour cela, on indique le type des objets entre <...> .

Par exemple, voici un exemple d'utilisation des méthodes de cette classe quand ont veut créer une liste de chaines de caractères (String) :

ArrayList<String> liste=new ArrayList<String>();
liste.add(new String("Bonjour"));
String s=liste.get(0); // recuperation objet a la position zero
System.out.println("Taille du tableau : "+liste.size());

Vous utiliserez les deux classes ci-dessus pour ce problème.

Question 1 : Classe Poisson

Sous-question :

Définissez une classe abstraite Poisson qui possède un attribut protected position de type Point , avec son constructeur qui assigne à ce poisson une position aléatoire entre (0,0) et (499,499). Y mettre l'accesseur public de la position ainsi qu'une méthode abstraite void move(Point cible) qui sera définie dans des classes filles. Le point passé en paramètre est le point visé par le mouvement.

Sous-question :

Définissez dans la classe Poisson une méthode void verifPosition() qui replace le poisson dans l'aquarium s'il n'y est pas : si son abscisse ou son ordonnée sont en dehors de l'intervalle {0, 499$}, on l'y ramènera par un modulo 500.

Sous-question :

Définissez une autre classe PoissonInconnuException qui étend Exception et servira à gérer les cas où un poisson n'est pas d'un type connu.

Question 2 : Classe Requin

Sous-question :

Définissez une classe Requin qui hérite de Poisson et représente un requin. La méthode toString() rend la chaîne "requin" suivie des coordonnées du requin, par exemple "requin(450,200)" . Définissez-y la méthode void move(Point cible) qui assure le déplacement du requin. Le comportement de cette méthode consiste à parcourir la moitié du chemin qui le sépare du point cible, puis à vérifier que le requin est toujours dans l'aquarium (et si ce n'est pas le cas, de l'y replacer) en appelant la méthode définie plus haut verifPosition() .

Question 3 : Classe Thon

Sous-question :

Définissez une classe Thon qui hérite de Poisson et représente un thon. Écrivez-y, en plus du constructeur, la méthode void move(Point cible) , qui assure le déplacement du thon. Le comportement est le suivant : si le point cible est à une distance supérieure à 60, le thon se déplace aléatoirement en ajoutant à chaque coordonnée de sa position une valeur aléatoire comprise entre -15 et +15. Sinon, le thon parcourt la moitié du chemin qui le sépare du point. Puis on remet si besoin le poisson dans l'aquarium en appelant la méthode verifPosition() . La méthode toString() rend la chaîne "thon" suivie des coordonnées du thon, par exemple "thon(450,200)" .

Question 4 : Classe PoissonList

Cette classe PoissonList est destinée à gérer la liste des poissons présents dans l'aquarium.

Sous-question :

Définissez la classe PoissonList qui hérite de ArrayList , avec un constructeur sans paramètres et un constructeur de copie, comme dans la classe ArrayList dont elle hérite.

Sous-question :

Dans cette classe PoissonList ajouter une méthode nbThons() qui rend le nombre de thons dans cette liste.

Indication : on pourra utiliser l'opérateur instanceof qui permet de savoir si un objet est instance d'une classe : l'expression unObjet instanceof UneClasse rend true si et seulement si l'objet unObjet est une instance de la classe UneClasse .

Sous-question :

Dans cette classe, ajoutez une méthode int rangPoissonProche(int index) qui renvoie l'indice du poisson le plus proche dans l'aquarium du poisson dont l'indice est passé en paramètre.

Sous-question :

Dans cette classe, ajoutez une méthode void bougeTousPoissons() . Cette méthode déplace tous les poissons (thons et requins) en appelant leur méthode move(Point cible) , où cible est soit la position du poisson le plus proche (au sens de la question précédente) si celui-ci est un thon, soit le centre de l'aquarium - le point de coordonnées (250,250) - si le poisson le plus proche est un requin (autrement dit : tout poisson "fuit" les requins, tout poisson est attiré par les thons).

On dira que deux poissons sont "voisins" si la distance entre eux est inférieure à 2. Chaque fois qu'un requin est "voisin" d'un thon, il le mange et le thon disparaît. Chaque fois que deux thons sont "voisins" l'un de l'autre, ils se reproduisent et un nouveau thon est ajouté à une position aléatoire de l'aquarium entre (0,0) et (499,499). On veut gérer ces cas d'apparition et de disparition de poissons en créant une nouvelle liste de poissons. Ceci sera fait par la méthode faireUnPas() dont voici la description : Elle commence par mettre à jour les positions de tous les poissons en appelant la méthode bougeTousPoissons() et elle crée un double L2 de cette liste de poissons. Puis elle parcourt la liste originale, et pour chaque poisson de cette liste et son poisson le plus proche ( au sens de la question précédente) elle applique les règles d'ajout et de suppression décrites ci-dessous, et fait la modification dans la nouvelle liste. Lorsqu'elle a parcouru toute la liste elle renvoie la nouvelle PoissonList obtenue ainsi.

Règles d'ajout et de suppression :

  • S'il s'agit de deux thons, on ajoute un nouveau thon dans la nouvelle liste de poissons.
  • S'il s'agit d'un thon et d'un requin, on supprime le thon dans la nouvelle liste de poissons.
  • S'il s'agit de deux requins, il ne se passe rien.
  • S'il ne s'agit ni de thon ni de requin, on lève une instance de la classe PoissonInconnuException qui sera traitée dans le main.
Sous-question :

Toujours dans la classe PoissonList , complétez la méthode ci-dessous.

  public PoissonList faireUnPas() throws PoissonInconnuException {
    bougeTousPoissons();
    // creation d'un double de this :
    // parcours de this :
    for (int i=0;i<size();i++) {
      // on recupere le poisson courant et son plus proche dans l'aquarium :
      // traitement du couple si les deux poissons sont differents :

Question 5 : Classes Aquarium et TestAquarium

Sous-question :

Définissez une classe Aquarium qui contient un attribut liste de type PoissonList représentant la liste des poissons présents dans l'aquarium. Écrivez-y le constructeur Aquarium qui prend en argument deux entiers, nbThons et nbRequins , représentant le nombre initial de thons et de requins dans la simulation. Le constructeur remplit la liste de poissons avec le nombre adéquat de thons et de requins. Écrivez-y aussi la méthode toString() qui rend la liste des poissons contenus dans l'aquarium.

Sous-question :

Ecrivez dans une classe TestAquarium la méthode principale, public static void main(String [] args), qui récupère en arguments de la ligne de commande le nombre de thons et le nombre de requins, appelle le constructeur de Aquarium avec ces valeurs, affiche la liste des poissons avec leurs coordonnées, appelle la méthode faireUnPas et réaffiche la liste des poissons. Pensez à attraper les exceptions qui sont susceptibles d'être levées et à les traiter en affichant un message idoine.

Rappels de cours :

  • les arguments passés sur la ligne de commande sont récupérables par le tableau de chaînes de caractères args, paramètre de la méthode main.
  • la méthode int Integer.parseInt(String s) rend l'entier représenté par la chaîne s , ou bien lève une exception NumberFormatException si la chaîne s ne repésente pas un entier.

Voici deux exemples d'exécution possibles :

>java TestAquarium 5 6m
  donnee non entiere
>java TestAquarium 3 2
  la liste des poissons :
  [thon(474,286) , thon(30,301) , thon(27,417) , requin(181,127) , requin(98,400)]
  liste des poissons apres un pas:
  [thon(471,277) , thon(43,305) , thon(25,411) , requin(112,216) , requin(61,405)]
Sous-question :

Dans la classe Aquarium , ajoutez une méthode void run() qui réalise une boucle perpétuelle dans laquelle on met sans cesse à jour la liste des poissons (méthode faireUnPas() )avec une temporisation de 300 ms (utilisez la méthode sleep de la classe Thread ) et on affiche le nombre total de poissons ainsi que le nombre de thons et de requins. On traitera dans cette méthode les exceptions éventuellement levées.

Rappel de cours : la méthode static void sleep(long millis) de la classe Thread suspend l'exécution pendant millis millisecondes et peut lever l'exception InterruptedException , classe dérivée de Exception .