Teaching - 2i013

Enseignants


2i013 - Groupe 5 : Course de voiture

TME 3

Avant toutes choses, petite mise à jour de la structure du programme: assurez vous que vous séparez bien les différents éléments du programme en utilisant par exemple les packages suivants (les noms ne sont pas importants, il faut que les concepts soient représentés):

  • voiture
  • strategy
  • circuit
  • algo => pour les radars notamment
  • tools
  • ihm => à partir de la semaine 4
  • ihm.observer
  • main

NB: des variantes sont possibles mais il est nécessaire que votre projet soit structuré.

Partie 1: Code de base puis introduction de la Simulation

Le code suivant devrait donc être opérationnel (les noms de méthodes/classes pouvant évidemment être différents):

  • Chargement/création des éléments de jeu
  • Manipulation de l'image de la course
  • Course basique
        public static void main(String[] args) throws VoitureException, IOException {
                String filename = "track/1_safe.trk";

                Circuit track = CircuitFactoryFromFile.build(filename); // factory static
                Voiture v = VoitureFactory.build(track); // factory static

                BufferedImage im = ToolsTerrain.imageFromCircuit(track);

                ArrayList<Commande> coms = new ArrayList<Commande>();
                for(int i=0; i<50; i++) coms.add(new Commande(1,0)); // accel a fond
                for(int i=0; i<50; i++) coms.add(new Commande(1,0.05)); // accel a fond + droite
                for(int i=0; i<50; i++) coms.add(new Commande(1,0)); // accel a fond
                for(int i=0; i<50; i++) coms.add(new Commande(1,-0.05)); // accel a fond + gauche

                for(Commande c:coms){
                        v.drive(c);
                        System.out.println("position : "+ v.getPosition());
                        im.setRGB((int) v.getPosition().getX(), (int) v.getPosition().getY(), Color.orange.getRGB());
                }

                im.setRGB(0,0, Color.orange.getRGB());
                ImageIO.write(im, "png", new File("test.png"));
        }

Architecture générale coté modèle

 Définition de la stratégie (= pilote)
  • Strategy
 public interface Strategy {
        public Commande getCommande();
 }
  • StrategyToutDroit = première implémentation
 public class StrategyToutDroit implements Strategy {
        @Override
        public Commande getCommande() {
                return new Commande(1, 0);
        }
 }
  • StrategyListeCommande (à faire plus loin, pas utile dans un premier temps)

Introduction de la classe Simulation

Note: comme nous l'avons vu en cours, cette classe repose entièrement sur des concepts abstraits (Voiture, Circuit, Strategy). Il s'agit donc d'un très bel objet, qui s'adaptera à tous ce à quoi l'on n'a pas encore pensé!

Vérifier que l'on est capable d'afficher la trajectoire de la voiture sur une image avec la nouvelle architecture.

Partie 2: Radar

Interface Radar

Construire le cahier des charges du Radar suivant:

 public interface Radar {
        public double[] scores(); // score de chaque branche
        public double[] distancesInPixels(); // pour l'observer
        public int getBestIndex(); // meilleur indice
        public double[] thetas(); // angles de chaque faisceau
 }

Classe RadarClassique

Construire une première classe implémentant l'interface précédente et reprenant l'algorithme du cours 3.

Il est nécessaire de faire un main de validation du radar: positionnement du radar sur la voiture, au début du circuit 1_safe.trk puis calcul des différents faisceaux et vérification à l'oeil, sur l'image du circuit.

Partie 2: Classe StrategyRadar

Construire ensuite une Strategie basé sur cet algorithme.

Optimisation de la stratégie

Cette stratégie demande quelques ajustements pour bien fonctionner:

  • Trouver quelles actions effectuer en fonction des branches sélectionnées
  • Comment gérer la ligne d'arrivée?
    • Si vous ne faites pas attention, elle sera vue comme un mur et la voiture fera demi-tour en arrivant!

Partie 3: Sauvegarde d'une course et évaluation

OPTION 1 Modification de la classe Simulation (obligatoire dans un premier temps)

Ajout d'un attribut de sauvegarde:

  private ArrayList<Commande> record;

Ajout d'un accesseur:

  public ArrayList<Commande> getRecord(){
     return record;
  }

Toutes les commandes passées à la voiture seront sauvegardées dans cette liste.

OPTION 2 Creation d'un décorateur de stratégie (architecture amusante)

La classe StrategyRecord prend en argument une stratégie:

  • quand on lui demande une commande, elle récupère celle générée par son attribut
  • a chaque fois, elle enregistre la commande dans une liste

Construction d'un fichier de sauvegarde

La construction pose problème:

  • elle doit être standard (vous écrivez, une autre personne doit la lire pour le simulateur de référence)
  • elle ne doit pas dépendre de votre organisation du code (la sérialisation de la classe Commande est impossible, toutes les classes sont différentes dans vos projets et dans le simulateur de référence).
  • nous devons travailler en binaire (et non en ASCII) car il faut garder toute la précision des commandes.

Je vous propose donc un code standard de sauvegarde/chargement. Ce code pose une contrainte: vos classes commandes doivent posséder des accesseurs avec les noms suivant: c.getAcc(), c.getTurn()

        public static void saveListeCommande(ArrayList<Commande> liste, String filename){
                try {
                        DataOutputStream os = new DataOutputStream(new FileOutputStream(filename));
                        for(Commande c:liste){
                                os.writeDouble(c.getAcc());
                                os.writeDouble(c.getTurn());
                        }
                        os.close();
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }

        public static ArrayList<Commande> loadListeCommande(  String filename) throws IOException{
                ArrayList<Commande> liste = null;

                try {
                        DataInputStream os = new DataInputStream(new FileInputStream(filename));

                        liste = new ArrayList<Commande>();
                        double a,t;
                        while(true){ // on attend la fin de fichier
                                a = os.readDouble();
                                t = os.readDouble();
                                liste.add(new Commande(a,t));
                        }

                } catch (EOFException e){
                        return liste;
                }

        }

Usage du fichier de sauvegarde

Ce fichier sera compatible avec le simulateur de référence fournit cette séance. Il sera également compatible avec la version en ligne du simulateur.