LI260: Course de voiture - Laboratoire d`Informatique de Paris 6
Transcription
LI260: Course de voiture - Laboratoire d`Informatique de Paris 6
LI260: Course de voiture Vincent Guigue [email protected] - 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: Organisation générale 1 1 2 3 Fichiers Organisation du code: package, interface Interactions basiques Géométrie 2 1 2 Point,Vecteur Modèle physique, inertie, déplacement, franchissement… 1er algorithme: radar + système expert 3 1 2 3 Architecture de la stratégie Codage du radar Système de décision 4 Algorithmes de plus court chemin 1 2 5 Optimisation automatique des trajectoires 1 2 6 7 8 Dijkstra Codage Algorithmes génétiques Plusieurs possibilités pour les utiliser Interfaces graphiques JAVA: SWING Moteur de rendu 3D … 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 [email protected] 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 actions: Simulation Fichier terrain: 1.0 0.5 1.0 0.0 -0.5 0.3 … ggggggggg… ggggrr… gggggrr… Score: 1256 coups 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: Dans la pratique, on préfère lire ligne par ligne: 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 … 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 = in.readLine(); for(int i=0; i<buf.length(); i++) buf.charAt(i); String buf = … Integer.parseInt(buf); En cas de pb, envoie: NumberFormatException 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 Sauvegarde d’un Point (attributs: double x, y) Il peut y avoir des problèmes avec les attributs static Il faut que les attributs soient sérializable (!) 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: Sauvegarde d’un objet Modification (même très légère) de la classe Chargement de l’objet précédent: 1. 2. 3. 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 utilise 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 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 Tools + static isRunnable(Terrain t): boolean + static terrainFromChar(char c): Terrain + static colorFromTerrain(Terrain t): int CircuitFactory -filename : String +CircuitFactory(String filename) +build() : Circuit utilise 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 circuit Vecteur 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: Savegarde: ImageIO.write(im, "png", outputfilename); Creation: BufferedImage im = ImageIO.read(new File(filename)) BufferedImage im = new BufferedImage(ncol,nligne, BufferedImage.TYPE_INT_ARGB); Manipulation: im.setRGB(i,j, color) int color = im.getRGB(i,j);