LI260: Course de voiture Vincent Guigue Vincent.guigue@lip6.fr - webia.lip6.fr/~guigue
Quelques images pour comprendre le contexte Une compétition algorithmique: http://www.dtek.chalmers.se/groups/icfpcontest/ Modèle (complexe ) de voiture imposé. Une course tour par tour 1 Algo -> commande 2 Application sur la voiture 3 MAJ voiture Optimiser le nombre de coups pour faire un tour
Objectifs de ce projet Gérer de nouveaux outils JAVA (fichiers, fenêtre ) Apprendre à mieux programmer (introduction aux design pattern) Coder (!) Gérer l interface entre mathématiques et informatique Algorithmique Géométrie dans l espace Algorithmes de plus court chemin Algorithmes génétiques
Dans le détail: 1 Organisation générale 1 Fichiers 2 Organisation du code: package, interface 3 Interactions basiques 2 Géométrie 1 Point, Vecteur 2 Modèle physique, inertie, déplacement, franchissement 3 1 er algorithme: radar + système expert 1 Architecture de la stratégie 2 Codage du radar 3 Système de décision
4 Algorithmes de plus court chemin 1 Dijkstra 2 Codage 5 Optimisation automatique des trajectoires 1 Algorithmes génétiques 2 Plusieurs possibilités pour les utiliser 6 Interfaces graphiques JAVA: SWING 7 Moteur de rendu 3D 8 En fonction des besoins
Fonctionnement de l UE 1h45 TD/Cours le lundi pendant 6 à 9 séances puis TME 3h30 TME le mercredi matin Double encadrement pour avancer plus vite Possibilité de tutorat pour les personnes qui se sentent perdues à partir de la semaine 4/5 Contacts par mail vincent.guigue@lip6.fr Evaluation: CC : 70% Partiel (0.25), Rapport et performance de votre code (0.6), participation (0.15) Exam : 30% Soutenance orale et modification de code individuel
Les fichiers
Les différents éléments du projet: Fichier terrain: ggggggggg ggggrr gggggrr Simulation Score: 1256 coups Fichier actions: 1.0 0.5 1.0 0.0-0.5 0.3 Simulateur externe
Fichiers: le dialogue entre les composants Besoins: Lire le fichier de terrain Ecrire un fichier action Pour le simulateur externe: lire le fichier action
3 manières de gérer les fichiers Lecture/écriture ASCII Serialization Externalization Syntaxes associées, usages Points forts et faibles des méthodes
ASCII: écriture dans un fichier Même syntaxe que pour l écriture dans la console System.out.println( Coucou ); Mais dans un autre stream Forme classique de codage: try { FileOutputStream output = new FileOutputStream(filename); PrintStream p = new PrintStream(output); p.print( ); [ ] output.close(); } catch (IOException e) { System.err.println("Can't open file " + filename + " for writting... Saving aborted"); }
ASCII: lecture dans un fichier try { FileReader fr = new FileReader(new File(filename)); BufferedReader in = new BufferedReader(fr); String buf = in.readline(); in.close(); }catch (FileNotFoundException e) { System.err.println("Unable to find "+ filename); } catch (IOException e) { System.err.println("Can't open file " + filename + " for reading... Loading aborted"); } catch (Exception e) { System.err.println("Invalid Format : " + filename + "... Loading aborted"); }
Trucs et usages classiques Pour lire les fichiers, plusieurs fonctions sont disponibles dans la classe BufferedReader: int read(): retourne le caractère lu ou -1 en fin de flux int read(cbuf, offset, length): lecture d un ensemble de caractères Dans la pratique, on préfère lire ligne par ligne: String readline() On évite ainsi tous les problèmes d encodage de retour chariot Gérer (au moins) 2 exceptions différentes pour les fichiers manquants ou les pbs de lecture
Fonctions complémentaires Comment séparer les différents mots de la ligne? String buf = in.readline(); StringTokenizer st = new StringTokenizer(buf, ", "); while (st.hasmoretokens()) { // traitements sur st.next() } Lire les caractères dans une String: Convertir une chaine vers un entier: String buf = Integer.parseInt(buf); En cas de pb, envoie: NumberFormatException String buf = in.readline(); for(int i=0; i<buf.length(); i++) buf.charat(i);
Bilan sur les fichiers ASCII Indispensable, surtout en lecture: on vous donne souvent des fichiers ASCII Très utile pour les grands projets impliquant beaucoup de gens: on comprend ce qu il y a dans les fichiers et on gagne du temps Assez lourds: Un double pèse 4 octets Un char pèse 1 octet Pour coder 1251.66679128, combien faut-il de place avec ce système? Pas très précis (les doubles sont tronqués)
Interface Serializable: sauvegarde Si un objet implements Serializable, il devient sauvegardable/chargeable automatiquement Exemple avec la classe Vector: public class Vector implements Serializable{ Il suffit ensuite d utiliser la syntaxe suivante: // sauvegarde try{ FileOutputStream fos = new FileOutputStream(filename); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeobject(new Vector(2,2)); fos.close(); }catch (java.io.filenotfoundexception e){ System.out.println(e.toString()); }catch (java.io.ioexception e){ System.out.println(e.toString()); }
Interface Serializable: chargement Usage symétrique: // chargement try{ FileInputStream fin = new FileInputStream(filename); ObjectInputStream oos = new ObjectInputStream(fin); Vector v = (Vector) oos.readobject(); fin.close(); }catch (java.io.filenotfoundexception e){ System.out.println(e.toString()); }catch (java.io.ioexception e){ System.out.println(e.toString()); } catch (ClassNotFoundException e) { e.printstacktrace(); } ATTENTION: on charge des Object -> il faut un cast pour avoir ce que vous voulez
Limites d usage La sérialization est une opération visant à automatiser la sauvegarde des attributs d une instance de classe Il peut y avoir des problèmes avec les attributs static Il faut que les attributs soient sérializable (!) Sauvegarde d un Point (attributs: double x, y) Tout se passe bien! Sauvegarde d une Voiture (position, vitesse, compteur static de voiture, stratégie de déplacement ) Il est possible d éliminer un attribut de la sauvegarde grace au mot clé transient. Problème de version très sensible (!)
Limites d usage (2) Lors de la sauvegarde, la Serialization enregistre le type exact de l objet Le cas suivant pose problème: 1. Sauvegarde d un objet 2. Modification (même très légère) de la classe 3. Chargement de l objet précédent: ClassNotFoundException JAVA vous indique qu il n est pas capable de charger l objet car cet objet est inconnu dans le cadre actuel du projet
Interface Serializable: bilan Très facile à utiliser On ne maitrise pas le format de sauvegarde Difficile à utiliser sur des projets en cours de développement
Interface Externalizable Même usage que Serializable: l ouverture/fermeture des flux est automatique Maitrise du format de sauvegarde à travers deux fonctions de lecture/écriture dans les flux: public class Vector implements Externalizable{ [ ] public void readexternal(objectinput in) throws IOException, ClassNotFoundException { x = in.readdouble(); y = in.readdouble(); } public void writeexternal(objectoutput out) throws IOException { out.writedouble(x); out.writedouble(y); }
Règles de développement
Règles de développement Attributs TOUJOURS private (+ accesseurs éventuels) Code d une classe < 2 pages en général, exceptionnellement un peu plus Penser à créer des sous-classe Nous y reviendrons régulièrement Regarder la documentation quand on a un doute: http://java.sun.com/javase/6/docs/api/ Utiliser les interfaces avant de coder les objets complexes cf transparents suivants. Utiliser les packages pour structurer le projet
Exemple: Circuit Exemple: le circuit Un circuit est une matrice: Chaque case de la matrice a un type Quelles sont les interactions entre le circuit et les autres éléments de la simulation? Quelles classes créer?
Circuit: le cahier des charges La voiture doit pouvoir interroger le circuit La voiture doit savoir si elle a le droit de rouler sur une case ou pas Le circuit doit pouvoir donner le point de départ, la direction de départ et le sens de franchissement de la ligne d arrivée Idées importantes: Séparer les différents concepts (Circuit / Terrain) Abstraire les objets complexes pour pouvoir proposer plusieurs implémentations Outil de base: Une interface = un objet abstrait = un cahier des charges
Proposition d architecture CircuitImpl -Terrain[][] matrice -Vecteur ptdepart -Vecteur sensdepart -Vecteur sensarrivee +CircuitImpl(Terrain[][], Vecteur, Vecteur, Vecteur) Tools + static isrunnable(terrain t): boolean + static terrainfromchar(char c): Terrain + static colorfromterrain(terrain t): int... +getterrain(int i, int j): Terrain +getterrain(point p): Terrain +getpointdepart(): Vecteur +getdirectiondepart(): Vecteur +getdirectionarrivee(): Vecteur + getwidth(): int + getheight(): int + getarrivees(): ArrayList<Vecteur> Vecteur - x: double - y: double Création manipulation opérateur... cf semaine prochaine Circuit <<INT>> +getterrain(int i, int j): Terrain +getterrain(vecteur p): Terrain +getpointdepart(): Vecteur +getdirectiondepart(): Vecteur +getdirectionarrivee(): Vecteur + getwidth(): int + getheight(): int + getarrivees(): ArrayList<Vecteur> utilise utilise Terrain <<ENUM>> Route, Herbe, Eau, Obstacle, BandeRouge, BandeBlanche, StartPoint, EndLine
Proposition d architecture (2) Dans notre projet, nous manipulerons toujours des Circuit: si l implémentation change, notre programme ne change pas Tous les concepts sont biens séparés des implémentations: terrain, circuit, aspects géométriques, outils de manipulation des circuits
Outils de manipulation des circuits Char -> Terrain (pour la lecture) Terrain -> RGB (pour la sauvegarde d image) Circuit -> Image Interrogation isrunnable
Comment créer un circuit? Reflexe classique mais mauvais: public Circuit(String filename){ } dans Circuit.java Bon reflexe: nouvelle classe Moins de code dans le même fichier, plus facile à lire et à corriger Si le format de fichier change Pas de problème, il suffit de faire une nouvelle classe CircuitFactory -filename : String +CircuitFactory(String filename) +build() : Circuit utilise Tools + static isrunnable(terrain t): boolean + static terrainfromchar(char c): Terrain + static colorfromterrain(terrain t): int Circuit <<INT>> +getterrain(int i, int j): Terrain +getterrain(point p): Terrain +getpointdepart(): Vecteur +getdirectiondepart(): Vecteur +getdirectionarrivee(): Vecteur
Structuration en package Afin de mieux gérer les gros projets, les fichiers sont répartis en arborescence: les packages geometry Vecteur circuit Circuit CircuitImpl Terrain ToolsTerrain CircuitFactory CircuitSaver voiture Voiture VoitureImpl VoitureFactory
Rappels de syntaxe Interface public interface Circuit { public Terrain getterrain(int i, int j); public Terrain getterrain(vecteur v); Classe implémentant une interface public class CircuitImpl implements Circuit { public Terrain getterrain(int i, int j) { } public Terrain getterrain(vecteur v) { }
Rappels de syntaxe (2) Enumération (dans le fichier Terrain.java): public enum Terrain { Route, Herbe, Eau, Obstacle, BandeRouge, BandeBlanche, StartPoint, EndLine} Déclaration/usage: Terrain t = Terrain.Herbe If( t == Terrain.Route)
Rappels de syntaxe (3) Que signifie public, private? Que signifie this? Définir les notions de constructeur, accesseur, setteur public class Vector { private double x,y; public Vector(double x, double y) { this.x = x; this.y = y; } public double getx() {return x;} public double gety() {return y;}
Exercice public Vector(Vecteur a, Vecteur b) Construit le vecteur du point a au point b Donner le code de la fonction NB: on considère qu un Point est un vecteur Rappeler l interprétation géométrique du produit scalaire Donner la signature et le code de la fonction calculant le produit scalaire.
Une correction formelle public Vector(Vecteur a, Vecteur b){ this(b.getx() - a.getx(), b.gety() - a.gety()); } public double scal(vector v){ return v.getx()*getx() + v.gety()*gety(); } A-t-on le droit de se passer des accesseurs? Pourquoi?
Exercice suivant Donner le code de la fonction qui permet d additionner deux vecteurs. Plusieurs versions sont possibles: on peut retourner void ou Vecteur Que choisir? On peut faire une version static ou pas Donner le code du programme principal permettant d additionner 2 vecteurs dans les différents cas Introduction à la classe Math: Donner le code de la fonction norme()
Gestion des images
Manipulation des images Image: classe abstraite Nous utiliserons la classe BufferedImage Chargement: BufferedImage im = ImageIO.read(new File(filename)) Savegarde: ImageIO.write(im, "png", outputfilename); Creation: BufferedImage im = new BufferedImage(ncol,nligne, BufferedImage.TYPE_INT_ARGB); Manipulation: im.setrgb(i,j, color) int color = im.getrgb(i,j);