Projets : structure par projet (jeu_de_la_vie, labyrinthe, mastermind, wator)

This commit is contained in:
2026-06-15 12:57:25 +02:00
parent 47ccdbb255
commit b17a21b394
75 changed files with 4304 additions and 0 deletions

72
Projets/SUIVI_TEMPLATE.md Normal file
View File

@@ -0,0 +1,72 @@
# Suivi de projet — [Nom du projet]
**Élève(s) :**
**Classe :**
**Projet :**
**Date de début :**
---
## Journal de bord
*Compléter après chaque séance.*
### Séance 1 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
### Séance 2 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
*(Dupliquer le bloc ci-dessus pour chaque séance)*
---
## Bilan du projet
### Ce qui fonctionne
### Ce qui ne fonctionne pas encore (et pourquoi)
### Choix techniques importants
*Quelles décisions as-tu prises ? Pourquoi ce choix plutôt qu'un autre ?*
### Ce que j'ai appris
---
## Préparation au Grand Oral
*Le Grand Oral dure 20 minutes. Tu dois pouvoir présenter ton projet et répondre à des questions. Prépare-toi en répondant à ces questions :*
**1. En quoi consiste ton projet ? Explique-le en 2 minutes à quelqu'un qui ne connaît pas l'informatique.**
**2. Quelles notions du cours as-tu utilisées ? Donne un exemple concret dans ton code.**
**3. Quelle a été la difficulté principale ? Comment l'as-tu résolue ?**
**4. Qu'aurais-tu fait différemment si tu recommençais ?**
**5. Quelle extension aimerais-tu implémenter si tu avais plus de temps ?**

596
Projets/fichier_suivi.md Normal file
View File

@@ -0,0 +1,596 @@
## Les algorithmes génétiques
* Présentation
- Les *algorithmes génétiques* font partie des algorithmes
évolutionnistes.
- S'inspirent des mécanismes de sélection naturelle pour approcher
une solution d'un problème *d'optimisation*.
- S'appliquent bien dans le cas où l'espace des solutions est grand.
* Darwin
- /De l'origine des espèces/
- les individus les mieux adaptés à leur environnement survivent et
se reproduisent davantage en transmettant les variations utiles à
la survie.
- L'espèce évolue en s'adaptant à son environnement.
- C'est le principe de *sélection naturelle*
* Sélection naturelle
Trois principes :
- Variation : les individus diffèrent les uns des autres et d'une
génération à l'autre ;
- Adaptation : les individus les mieux adaptés survivent et se
reproduisent davantage ;
- Hérédité : les caractéristiques sont héréditaires et se
transmettent de génération en génération.
* Principes
** Problèmes et solution
On dispose d'un problème d'optimisation :
$$\max_{s\in R}\lbrace f(s) \rbrace$$
- $R$ est l'espace des solutions *réalisables*
- $f$ est la fonction *objectif*, où encore :
- fonction d'évaluation ,
- fonction d'adaptation ,
- fonction de fitness.
** Le principe des algorithmes génétiques
- travailler avec un sous-ensemble $P$ de $R$ appelé *population* ;
- On fait évoluer $P$ en lui appliquent des opérateurs génétiques
pour produire une nouvelle génération ;
- On favorise les individus les mieux adaptés (valeur de $f$ plus
élevée) lors de la production de la génération suivante ;
- Après $n$ générations, on choisit un individu dans la population.
** génome
- Les individus sont représentés par leur génôme : suite finie de
$k$ symboles ;
- Ces symboles représentent les caractéristiques d'un individu,
c'est-à-dire un gène ;
Chercher la solution optimale consiste à chercher une séquence de
symboles optimale.
Idée à respecter : plus l'adaptation est élevée, plus l'individu
est proche de la solution.
** Opérateurs génétiques
Il existe trois opérateurs génétiques :
*** la *sélection*
choix des individus d'une population qui vont survivre à la
génération suivante :
- Les individus les mieux adaptés ont plus de
chance de survivre ;
- Méthode du /tournoi/ .
*** le *croisement*
Combine les gènes de deux individus pour en produire deux
nouveaux :
- croisement **1 point** : découpe des deux génome au même
endroit, puis échange ,
- croisement **2 points** : découpe chacun des génomes en trois
portions , on échange les gènes des parties médianes.
*** La *mutation*
- Mutation /aléatoire/ des caractéristiques d'un individu ;
- Réalisée avec une probabilité faible ;
- Permet de faire apparaitre dans une population de nouvelles
caractéristiques
* L'algorithme génétique
Notre algorithme travaillera avec une population constante d'une
génération à l'autre.
** Entrées
- la fonction de /fitness/ du problème ;
- le nombre d'individus $n$ pair ;
- le nombre de génération $g$ ;
- la probabilité de mutation $p$.
** L'algorithme
1. population $\leftarrow$ $n$ individus créés aléatoirement.
2. calculer pour chaque individu de population son degré
d'adaptation.
3. répéter $g$ fois
4. $\quad$ next_gen_1 $\leftarrow$ sélectionner par tournoi
$\frac{n}{2}$ individus de la population
5. $\quad$ next_gen_2 $\leftarrow$ créer $\frac{n}{2}$ individus
par croisement entre individus de population
6. $\quad$ next_gen $\leftarrow$ next_gen_1 + next_gen_2
7. $\quad$ faire muter chacun des individus de next_gen
8. $\quad$ calculer pour chaque individu de next_gen son degré
d'adaptation
9. $\quad$ meilleurs $\leftarrow$ les 5 individus les mieux adaptés de population
10. $\quad$ retirer de next_gen les 5 individus les moins adaptés
11. $\quad$ population $\leftarrow$ neilleurs + next_gen
12. solution $\leftarrow$ individu le mieux adapté de population
** Sélection par tournoi
- La sélection par tournoi dans une population de taille $n$
consiste à choisir aléatoirement deux individus de la population
pour ne garder que l'individu le mieux adapté des deux.
- On répète ce processus de sorte que chaque individu est choisi
exactement une fois. On obtient donc $\frac{n}{2}$ individus à la
fin de cette phase.
** Croisements
- Croisement à un point
- On choisit aléatoirement deux individus de la population pour
construire deux nouveaux individus
- On applique ensuite un tournoi entre les deux individus produits
pour ne garder que le meilleur
- On répète ce processus de sorte que chaque individu est choisi
exactement une fois. On obtient donc $\frac{n}{2}$ individus à la
fin de cette phase.
** Mutations
- Pour chacun des individus considérés, il y a une probabilité $p$
que chacune de ses caractéristiques mute. C'est-à-dire que la
valeur de cette caractéristique est modifiée
aléatoirement. L'individu est donc remplacé par sa /version
mutée/.
- À la fin de la population la taille de la population est la même.
* Programmation
Plusieurs classes :
- l'algorithme génétique,
- le problème traité et
- les individus.
** l'algorithme génétique
- classe `AlgoGen`.
#+begin_src python :tangle src/algo_gen.py :exports none
import random
BEST_KEPT = 5
class AlgoGen(object):
def __init__(self, problem, population_size, crossover_rate, mutation_probability):
'''
build an genetic algorithm to solve problem using a population of size population_size and a probability of muation of mutation_probability
:param problem: the problem to solve
:type problem: a Problem object
:param population_size: the size of the population (must be even)
:type population_size: int
:param mutation_probability: the mutation probability
:type mutation_probability: float
:UC: population_size must be even and mutation_probability must be 0<= and <1
'''
self.__problem = problem
self.__population_size = population_size
self.__population = []
self.__nb_crossover = crossover_rate
self.__mutation_probability = mutation_probability
def get_population(self):
return self.__population
def genesis(self):
"""
create the population
"""
pass
def evaluate_population(self, population):
"""
evalue la population passée en paramètre
:param population: (list) la population
"""
pass
def next_generation_tournament(self, population):
'''
organise un tournoi entre les individu d'une population .
chaque individu choisi une et une seule fois, dans un tournoi ,
vainqueur conservé taille résultat = 1/2 taille population
:param population: (list) la population
:return: (list) les vainqueurs du tournoi
'''
pass
def crossover(self, population):
'''Croise les individus d'une population.
Chaque individu est sélectionné une et une seule fois, on
garde le meilleur descendant à chaque fois.
taille résultat = 1/2 taille population
:param population: (list) a population
:return: (list) new individuals
'''
pass
def mutate(self, individuals):
"""
mute les individus
:param individuals: (list) une liste des individus
"""
pass
def one_round(self):
'''
applique successivement les opéréateurs
- selection
- croisement
- mutation
'''
pass
def solve(self, nb_rounds):
"""
tente d'approcher une solution en faisant muter la population
:param nb_rounds: (int) nombre de génération
"""
self.genesis()
self.evaluate_population(self.__population)
self.display_stat('INITIAL')
for step in range(nb_rounds):
self.one_round()
#self.display_population(step=step+1, number = 20)
self.display_stat(step)
#self.display_population(number = 20) # display 20 best individuals
return self.__problem.best_individual(self.__population)
def display_stat(self, step):
best = self.__problem.best_individual(self.__population)
all_scores = [ individual.get_score() for individual in self.__population ]
average = sum (all_scores) / self.__population_size
print ( ' STEP = {} -- best = {} - avg = {} '.format(step, best, average) )
def display_population(self, step = ' ', number = 0):
if number <= 0:
individuals = self.__population
else:
individuals = self.__population[:min(number, len(self.__population))]
print( ' **** STEP : {} ********* '.format(step) )
for individual in individuals :
print ( individual )
print( )
# tool function
def biased_wheel(alea,scores):
'''
return smallest i such that alea < sum(scores[k], 0 <= k < i)
CU : 0 <= alea < sum(scores)
'''
current_sum = 0
result_index = -1
while alea > current_sum :
result_index = result_index + 1
current_sum = current_sum + scores[result_index]
return result_index
#+end_src
** les problèmes
Comme indiqué, les classes pour représenter les problèmes devront
partager certaines méthodes. Nous suggérons les méthodes suivantes
comme méthodes communes aux problèmes. Evidemment, il peut exister
d'autres méthodes spécifiques à chacun des problèmes. Notamment la
manière de calculer l'adaptation d'un individu variera.
#+begin_src python :tangle src/problem_interface.py :exports none
class Problem_Interface(object):
'''
a prototype class for "problems" in genetic algorithm
an Individual class must be associated to a problem to represent the individuals
'''
def create_individual(self):
'''
create a randomly generated indidivual for this problem
:return: a randomly generated individual gfor this problem
:rtype: an Individual object
'''
pass
def evaluate_fitness(self, individual):
'''
compute the fitness of individual for this problem
:param individual: the individual to consider
:type individual: an Individual object
:return: the fitness of individual for this problem
:rtype: an Individual object
'''
pass
def sort_population(self, population):
'''
sort population from best fitted to worst fitted individuals.
Depending on the problem, it can correspond to ascending or descending order with respect to the fitness function.
side effect : population is modified by this method
:param population: the list of individuals to sort
:type population: list(Individual)
'''
pass
def best_individual(self, population):
'''
return the best fitted individual from population.
Depending on the problem, it can correspond to the individual with the highest or the lowest fitness value.
:param population: the list of individuals to sort
:type population: list(Individual)
:return: the best fitted individual of population
:rtype: an Individual object
'''
pass
def tournament(self, first, second):
'''
perform a rounament between two individuals, the winner is the most fitted one, it is returned
:param first: an individual
:type first: an Indivdual object
:param second: an individual
:type second: an Individual object
:return: the winner of the tournament
:rtype: Individual object
'''
pass
#+end_src
** les individus
Les classes pour les individus devront partager certaines
méthodes. Nous suggérons les méthodes suivantes comme méthodes
communes aux individus. Evidemment, il peut exister d'autres méthodes
spécifiques à chacun des individus, et notamment, les valeurs des
génomes vont différer.
#+begin_src python :tangle src/individual_interface.py :exports none
import random
class Individual_Interface(object):
'''
an Individual in genteic algorithm problem
the value (or genome) of an indal is a sequence (e.g string or list) of a fixed size
an individual has a fitness score
'''
def __init__(self, size):
'''
create an Individual object, its genome value is randomly built. Initially score is not set.
:param size: size of the genome
:type size: int
'''
self.__score = None
self.__size = size
self.__value = self.init_value()
def copy(self):
'''
build a copy of self, the genome is a copy of self's genome
:return: a new Individual which is a "clone" of self
:rtype: an Individual object
'''
return None
def get_size(self):
'''
:return: the size of self's genome
:rtype: int
'''
return self.__size
def get_score(self):
'''
:return: the fitness score of self
:rtype: number
'''
return self.__score
def set_score(self, new_score):
'''
change the fitness score of self
:param new_score: the new fitness score
:type new_score: number
'''
self.__score = new_score
def __str__(self):
return "{} ({})".format(self.__value, self.__score)
def __repr__(self):
return self.__str__()
def get_value(self):
'''
:return: the genome of self
:rtype: sequence
'''
return self.__value
def set_value(self, new_value):
'''
change the genome value of self
:param new_score: the new genome value
:type new_score: sequence
'''
self.__value = new_value
def init_value(self):
'''
randomly initialize the genome value of self
'''
return None
def evaluate(self,problem):
'''
set the fitness score with the fitness computed by problem for self
:param problem: the problem
:type problem: a Problem object
'''
self.set_score(problem.evaluate_fitness(self))
def cross_with(self, other):
'''
perform a 1 point crossover between self and other, two new built individuals are returned
:param other: the individual to croww with
:type other: an Idnividual object
:return: the two new Individuals built by 1 point crossover operation
:rtype: 2-uple of individuals
'''
pass
def mutate(self, probability):
'''
apply mutation operation to self : each element of the geome sequence is randomly changed with given probabiliy
side effect : self's genome is modified
:param probability: the probability of mutation for every gene
:type probability: float
:UC: probability in [0,1[
'''
pass
def mutate_gene(self, gene):
pass
#+end_src
* Problème 1 : Calcul de la valeur maximale d'une fonction
Problème : trouver dans un intervalle $[x_\mbox{min};x_\mbox{max}]$
une valeur de $x$ pour laquelle $f(x)$ est maximale.
* Problème 2 : Recherche d'un message "secret"
** Description
Cette fois, il ne s'agit pas à proprement parler d'un problème à
résoudre, puisqu'on en connaît la solution au départ... Ce second
exemple a pour objectif d'illustrer que les algorithmes génétiques
peuvent trouver une solution dans un très grand espace de recherche.
** Problème
Le problème est celui de faire trouver une chaîne de caractères à un
algorithme génétique. On suppose cette chaîne construite à partir du
caractère espace et des 26 lettres de l'alphabet (minuscules et non
accentuées). Si elle est le longueur 30, il existe alors *27^30*
chaînes possibles.
Nous allons donc vérifier qu'en explorant qu'une petite partie de
toutes ces possibilités, un
algorithme génétique est capable de trouver l'une d'entre elles.
Par exemple, nous allons considérer la chaine
*secret* = ~'ceci est un secret a retrouver'~.
* Problème 3 : Recherche d'un chemin dans un labyrinthe
** Labyrinthe
#+begin_example
7;7
+-+-+-+-+-+-+-+
| | |
+ + +-+ + +-+ +
| | | | |
+ + +-+ + + + +
| | | | |
+ +-+ +-+-+ + +
| | | |
+ + +-+-+ +-+-+
| | | |
+ +-+-+ + +-+ +
| | | | |
+ + + +-+-+ +-+
| | |
+-+-+-+-+-+-+-+
#+end_example
** Problème
Le problème consiste à trouver dans un tel labyrinthe de taille *N*
(ici *N=7x7=49*) donné, un chemin entre la case d'entrée située en
haut à gauche et celle de sortie située en bas à droite.
Pour simplifier on considère que les largeur et hauteur du
labyrinthe sont identiques.
** Matériel
- une classe ~Maze~ permettant de gérer les labyrinthes (fichier ~maze.py~);
- des exemples de labyrinthe (fichier ~maze.zip~)
* Problème 4 : le TSP
Utiliser un algorithme génétique pour approcher une solution du
Problème du voyageur de commerce.

View File

@@ -0,0 +1,80 @@
# Jalons — Le Jeu de la Vie
> Progression suggérée sur ~10 séances (septembre → février)
---
## Phase 1 — Analyse et modélisation (Séances 1-2)
**Objectif :** Comprendre le problème et choisir les structures de données.
- Lire les règles de Conway et les reformuler avec ses propres mots
- Dessiner à la main quelques générations d'une configuration simple (ex. 3 cellules en ligne)
- Choisir une représentation de la grille : liste 2D, dictionnaire `{(i,j): état}` ?
- Écrire la fonction `creer_grille(n, m)` → grille vide de n lignes et m colonnes
- Écrire la fonction `afficher_grille(grille)` → affichage terminal
**Questions à se poser :**
- Comment représenter une cellule morte / vivante en Python ?
- Que se passe-t-il aux bords de la grille ?
---
## Phase 2 — Règles du jeu (Séances 3-4)
**Objectif :** Implémenter le cœur de la simulation.
- Écrire `compter_voisins(grille, i, j)` → nombre de voisins vivants de la cellule (i, j)
- Écrire `etat_suivant(etat, nb_voisins)` → applique les 4 règles, renvoie le nouvel état
- Écrire `generation_suivante(grille)` → calcule la grille à l'étape suivante
- Tester manuellement sur un petit exemple (3×3)
**Point d'attention :** la prochaine génération doit être calculée **simultanément** pour toutes les cellules — on ne modifie pas la grille en cours de calcul.
---
## Phase 3 — Simulation et configurations (Séances 5-6)
**Objectif :** Lancer une simulation complète et observer des comportements.
- Écrire `simuler(grille, nb_generations)` → boucle principale
- Tester sur les configurations classiques :
- **Bloc** (stable)
- **Clignotant** (oscillateur période 2)
- **Planeur** (se déplace)
- Observer : certaines configurations sont-elles stables ? Oscillantes ? Chaotiques ?
---
## Phase 4 — Affichage graphique (Séances 7-8)
**Objectif :** Rendre la simulation visuelle et interactive.
- Choisir une bibliothèque graphique : Pyxel, matplotlib (animation), tkinter
- Afficher la grille avec des cellules colorées
- Animer la simulation pas à pas avec un délai configurable
- (Extension) Permettre à l'utilisateur de cliquer pour activer/désactiver des cellules
---
## Phase 5 — Finitions et documentation (Séances 9-10)
**Objectif :** Préparer le rendu et le Grand Oral.
- Nettoyer le code, ajouter des docstrings
- Rédiger un README personnel décrivant le projet et les choix techniques
- Compléter le `SUIVI.md`
- Préparer 2-3 questions pour le Grand Oral (voir `SUIVI.md`)
---
## Idées d'extension avancée
- **Grille torique** : `i % n` et `j % m` pour que les bords se rejoignent
- **Statistiques** : tracer le nombre de cellules vivantes par génération (matplotlib)
- **Chargement de patterns** : lire des fichiers `.rle` (format standard des patterns Conway)
- **Détection de stabilité** : arrêter la simulation quand la grille ne change plus
---
Auteur : Florian Mathieu — Licence CC BY NC

View File

@@ -0,0 +1,54 @@
# Projet — Le Jeu de la Vie
> En 1970, le mathématicien John Horton Conway invente un automate cellulaire capable de générer une vie artificielle à partir de quatre règles simples. Cinquante ans plus tard, des chercheurs utilisent encore ce modèle pour simuler des phénomènes biologiques, sociaux et physiques.
## Contexte
Le **Jeu de la Vie** est un automate cellulaire qui se déroule sur une grille infinie (ou torique) de cellules. Chaque cellule est soit **vivante**, soit **morte**. À chaque génération, l'état de chaque cellule évolue selon quatre règles appliquées simultanément :
1. Une cellule vivante avec **moins de 2 voisins vivants** meurt (sous-population).
2. Une cellule vivante avec **2 ou 3 voisins vivants** survit.
3. Une cellule vivante avec **plus de 3 voisins vivants** meurt (surpopulation).
4. Une cellule morte avec **exactement 3 voisins vivants** devient vivante (reproduction).
Ces règles font émerger des comportements complexes : structures stables, oscillateurs, planeurs...
## Compétences mobilisées
| Notion | Chapitre du cours |
|--------|-------------------|
| Listes 2D (grille) | Programmation |
| Fonctions pures | Paradigmes |
| Classes et objets | POO |
| Récursivité / itération | Récursivité |
| Visualisation (matplotlib / Pyxel) | — |
## Cahier des charges
### Fonctionnalités minimales
- [ ] Représenter la grille sous forme de liste 2D (ou dictionnaire)
- [ ] Calculer le nombre de voisins vivants d'une cellule
- [ ] Appliquer les 4 règles pour calculer la génération suivante
- [ ] Afficher la grille dans le terminal (caractères `█` / `.`)
- [ ] Lancer la simulation sur N générations
### Extensions possibles
- [ ] Affichage graphique animé (Pyxel, matplotlib, tkinter)
- [ ] Grille torique (les bords se rejoignent)
- [ ] Charger des configurations célèbres (planeur, canon à planeurs, pulsar...)
- [ ] Compter et tracer l'évolution du nombre de cellules vivantes
- [ ] Permettre à l'utilisateur de dessiner une configuration initiale
- [ ] Détecter automatiquement les structures stables et les oscillateurs
## Ressources
- `5_projet_le_jeu_de_la_vie.pdf` — consignes détaillées (dans le dossier parent)
- [Conway's Game of Life — Wikipedia](https://fr.wikipedia.org/wiki/Jeu_de_la_vie)
- [LifeWiki — patterns célèbres](https://conwaylife.com/wiki/)
- [Pyxel — moteur graphique Python](https://github.com/kitao/pyxel)
---
Auteur : Florian Mathieu — Licence CC BY NC

View File

@@ -0,0 +1,72 @@
# Suivi de projet — [Nom du projet]
**Élève(s) :**
**Classe :**
**Projet :**
**Date de début :**
---
## Journal de bord
*Compléter après chaque séance.*
### Séance 1 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
### Séance 2 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
*(Dupliquer le bloc ci-dessus pour chaque séance)*
---
## Bilan du projet
### Ce qui fonctionne
### Ce qui ne fonctionne pas encore (et pourquoi)
### Choix techniques importants
*Quelles décisions as-tu prises ? Pourquoi ce choix plutôt qu'un autre ?*
### Ce que j'ai appris
---
## Préparation au Grand Oral
*Le Grand Oral dure 20 minutes. Tu dois pouvoir présenter ton projet et répondre à des questions. Prépare-toi en répondant à ces questions :*
**1. En quoi consiste ton projet ? Explique-le en 2 minutes à quelqu'un qui ne connaît pas l'informatique.**
**2. Quelles notions du cours as-tu utilisées ? Donne un exemple concret dans ton code.**
**3. Quelle a été la difficulté principale ? Comment l'as-tu résolue ?**
**4. Qu'aurais-tu fait différemment si tu recommençais ?**
**5. Quelle extension aimerais-tu implémenter si tu avais plus de temps ?**

View File

@@ -0,0 +1,88 @@
# Jalons — Labyrinthe
> Progression suggérée sur ~10 séances (septembre → février)
---
## Phase 1 — Modélisation (Séances 1-2)
**Objectif :** Représenter un labyrinthe en Python.
- Étudier la classe `Maze` dans `../src/maze.py` : comment est codé le labyrinthe ? Quelles méthodes sont disponibles ?
- Choisir une représentation : dictionnaire `{cellule: [voisins accessibles]}` ou grille 2D avec murs
- Écrire `creer_grille(n, m)` → grille n×m avec tous les murs fermés
- Écrire `afficher(grille)` → affichage terminal avec `+`, `-`, `|`, ` `
**Exemple de représentation :**
```
+--+--+--+
| | |
+ +--+ +
| | |
+--+--+--+
```
---
## Phase 2 — Génération (Séances 3-4)
**Objectif :** Générer un labyrinthe parfait aléatoirement.
**Algorithme DFS récursif (backtracking) :**
1. Partir d'une cellule aléatoire, la marquer comme visitée
2. Choisir aléatoirement un voisin non visité
3. Supprimer le mur entre les deux cellules
4. Appeler récursivement depuis le voisin
5. Si aucun voisin non visité : revenir en arrière (backtrack)
- Implémenter `generer_dfs(grille, cellule)` de manière récursive
- Tester sur un labyrinthe 5×5, puis 20×20
- Observer : le labyrinthe est-il toujours parfait (sans cycle) ?
---
## Phase 3 — Résolution (Séances 5-6)
**Objectif :** Trouver le chemin de l'entrée à la sortie.
- Implémenter `resoudre_bfs(grille, entree, sortie)` → plus court chemin (largeur d'abord)
- Implémenter `resoudre_dfs(grille, entree, sortie)` → premier chemin trouvé (profondeur d'abord)
- Comparer les deux : même longueur de chemin ? Même rapidité ?
- Afficher le chemin dans le labyrinthe (ex. cellules marquées `*`)
**Point clé :** dans un labyrinthe parfait, BFS et DFS trouvent tous les deux le chemin (unique), mais BFS garantit qu'il est le plus court.
---
## Phase 4 — Affichage graphique (Séances 7-8)
**Objectif :** Visualiser la génération et la résolution.
- Choisir une bibliothèque graphique (Pyxel, tkinter, pygame)
- Afficher le labyrinthe avec des rectangles
- Animer la génération cellule par cellule
- Afficher le chemin solution en couleur
---
## Phase 5 — Finitions et documentation (Séances 9-10)
**Objectif :** Préparer le rendu et le Grand Oral.
- Nettoyer le code, ajouter des docstrings
- Mesurer les performances : temps de génération selon la taille (10×10, 50×50, 100×100)
- Compléter le `SUIVI.md`
- Préparer 2-3 questions pour le Grand Oral
---
## Idées d'extension avancée
- **A\*** : heuristique distance de Manhattan pour guider la recherche vers la sortie
- **Kruskal** : algorithme de génération alternatif (union-find)
- **Résolution par algo génétique** : utiliser le framework du projet `mastermind/`
- **Labyrinthe 3D** : extension vers une grille à 3 dimensions
---
Auteur : Florian Mathieu — Licence CC BY NC

View File

@@ -0,0 +1,51 @@
# Projet — Labyrinthe
> Générer un labyrinthe parfait (sans boucles, avec un unique chemin entre deux cases) et le résoudre automatiquement — un problème classique qui mobilise graphes, récursivité et algorithmique de recherche.
## Contexte
Un **labyrinthe parfait** est un graphe dans lequel il existe exactement un chemin entre n'importe quelle paire de cellules. Le générer revient à construire un arbre couvrant d'une grille ; le résoudre revient à parcourir ce graphe de l'entrée à la sortie.
Ce projet relie directement le chapitre **Graphes** et le chapitre **Récursivité** du programme de Terminale NSI.
Une base de code est disponible dans `../src/maze.py` : la classe `Maze` lit un labyrinthe depuis un fichier texte et permet de tester un chemin.
## Compétences mobilisées
| Notion | Chapitre du cours |
|--------|-------------------|
| Graphes, arbres couvrants | Graphes |
| Parcours DFS / BFS | Graphes, Récursivité |
| Récursivité et pile d'appels | Récursivité |
| Classes et encapsulation | POO |
| Représentation par dictionnaire | Programmation |
## Cahier des charges
### Fonctionnalités minimales
- [ ] Représenter un labyrinthe sous forme de graphe (dictionnaire de voisins)
- [ ] Générer un labyrinthe parfait aléatoire (algorithme DFS récursif ou union-find/Kruskal)
- [ ] Afficher le labyrinthe dans le terminal (avec `+`, `-`, `|`, ` `)
- [ ] Résoudre le labyrinthe (trouver un chemin de l'entrée à la sortie) avec BFS ou DFS
- [ ] Afficher le chemin solution
### Extensions possibles
- [ ] Affichage graphique (Pyxel, tkinter)
- [ ] Visualisation animée de la génération et de la résolution
- [ ] Comparer BFS (plus court chemin) et DFS (premier chemin trouvé)
- [ ] Implémenter l'algorithme A* (heuristique distance de Manhattan)
- [ ] Résolution par algorithme génétique (en lien avec le projet `mastermind/`)
- [ ] Mesurer les performances selon la taille du labyrinthe
## Ressources
- `../src/maze.py` — classe `Maze` de base (lecture depuis fichier texte)
- `../src/maze.zip` — archive complète avec exemples de labyrinthes
- [Maze generation algorithms — Wikipedia](https://en.wikipedia.org/wiki/Maze_generation_algorithm)
- [Algorithme A* — Wikipedia](https://fr.wikipedia.org/wiki/Algorithme_A*)
---
Auteur : Florian Mathieu — Licence CC BY NC

View File

@@ -0,0 +1,72 @@
# Suivi de projet — [Nom du projet]
**Élève(s) :**
**Classe :**
**Projet :**
**Date de début :**
---
## Journal de bord
*Compléter après chaque séance.*
### Séance 1 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
### Séance 2 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
*(Dupliquer le bloc ci-dessus pour chaque séance)*
---
## Bilan du projet
### Ce qui fonctionne
### Ce qui ne fonctionne pas encore (et pourquoi)
### Choix techniques importants
*Quelles décisions as-tu prises ? Pourquoi ce choix plutôt qu'un autre ?*
### Ce que j'ai appris
---
## Préparation au Grand Oral
*Le Grand Oral dure 20 minutes. Tu dois pouvoir présenter ton projet et répondre à des questions. Prépare-toi en répondant à ces questions :*
**1. En quoi consiste ton projet ? Explique-le en 2 minutes à quelqu'un qui ne connaît pas l'informatique.**
**2. Quelles notions du cours as-tu utilisées ? Donne un exemple concret dans ton code.**
**3. Quelle a été la difficulté principale ? Comment l'as-tu résolue ?**
**4. Qu'aurais-tu fait différemment si tu recommençais ?**
**5. Quelle extension aimerais-tu implémenter si tu avais plus de temps ?**

View File

@@ -0,0 +1,80 @@
# Jalons — Mastermind & Algorithmes Génétiques
> Progression suggérée sur ~10 séances (septembre → février)
---
## Phase 1 — Comprendre les algorithmes génétiques (Séances 1-2)
**Objectif :** Saisir l'analogie entre évolution biologique et optimisation.
- Lire la fiche `../src/src_mastermind_fiche.md` du début jusqu'à la section "Les individus"
- Répondre aux questions : qu'est-ce qu'un individu ? une population ? le fitness ? la sélection ?
- Schématiser à la main le cycle d'un algorithme génétique :
```
Génèse → Évaluation → Sélection → Croisement → Mutation → [retour Évaluation]
```
- Expliquer en une phrase comment cet algorithme peut résoudre le Mastermind
---
## Phase 2 — L'individu : la classe Combinaison (Séances 3-4)
**Objectif :** Implémenter la brique de base.
- Implémenter `init_value(self)` → génère une combinaison aléatoire de couleurs
- Implémenter `cross_with(self, other)` → croisement en 1 point (renvoie 2 enfants)
- Implémenter `mutate_gene(self, gene)` → remplace un gène par une autre couleur aléatoire
- Tester chaque méthode indépendamment avant de passer à la suite
**Point clé :** le croisement en 1 point prend un indice aléatoire et échange les parties.
---
## Phase 3 — Le problème : la classe Mastermind (Séances 5-6)
**Objectif :** Définir comment évaluer une combinaison.
- Implémenter `create_individual(self)` → crée un individu aléatoire du bon type
- Implémenter `evaluate_fitness(self, individual)` → compte les gènes bien placés
- Implémenter `best_individual(self, population)` → renvoie le meilleur de la population
- Implémenter `tournament(self, first, second)` → compare deux individus, garde le meilleur
- Tester avec une combinaison cible connue et vérifier que le score est correct
---
## Phase 4 — L'algorithme : la classe AlgoGen (Séances 7-8)
**Objectif :** Assembler toutes les briques.
- Implémenter `genesis(self)` → crée la population initiale
- Implémenter `evaluate_population(self, population)` → évalue tous les individus
- Implémenter `next_generation_tournament(self, population)` → sélection par tournoi
- Implémenter `crossover(self, population)` → croisements entre les meilleurs
- Implémenter `mutate(self, individuals)` → applique les mutations
- Implémenter `solve(self, nb_generations)` → boucle principale
---
## Phase 5 — Tests, ajustement et documentation (Séances 9-10)
**Objectif :** Valider et optimiser.
- Tester avec la combinaison exemple : `Mastermind(["bleu", "blanc", "blanc", "rouge"])`
- Faire varier les paramètres : taille de population, probabilité de mutation, taux de croisement
- Observer : combien de générations faut-il en moyenne pour trouver la solution ?
- Tracer la courbe de convergence (fitness max par génération)
- Compléter le `SUIVI.md` et préparer les questions pour le Grand Oral
---
## Idées d'extension avancée
- **Nouveau problème** : adapter le framework à un autre problème (labyrinthe, sac à dos, TSP...)
- **Roulette vs tournoi** : implémenter une sélection par roulette et comparer les performances
- **Mastermind jouable** : ajouter une interface où un humain joue contre l'IA
- **Parallélisation** : tester plusieurs populations en parallèle et garder la meilleure
---
Auteur : Florian Mathieu — Licence CC BY NC

View File

@@ -0,0 +1,53 @@
# Projet — Mastermind & Algorithmes Génétiques
> Darwin rencontre la programmation : faire évoluer une population de combinaisons pour qu'elle converge vers la solution cachée du Mastermind — sans jamais la connaître à l'avance.
## Contexte
Le **Mastermind** est un jeu de déduction : un joueur choisit une combinaison secrète de 4 couleurs parmi 8, l'autre doit la deviner en un minimum d'essais.
Plutôt que de coder une IA par force brute, ce projet applique un **algorithme génétique** : on fait évoluer une population de combinaisons candidates en s'inspirant de la sélection naturelle (tournoi, croisement, mutation) jusqu'à trouver la solution.
Une base de code complète est disponible dans `../src/` :
- `algo_gen.py` — framework de l'algorithme génétique
- `combinaison.py` — représentation d'un individu (une combinaison)
- `src_mastermind_fiche.md` — fiche TP détaillée avec toutes les consignes
## Compétences mobilisées
| Notion | Chapitre du cours |
|--------|-------------------|
| Classes, héritage, encapsulation | POO |
| Paradigme fonctionnel (fonctions pures) | Paradigmes |
| Algorithmes d'optimisation | Algorithmique |
| Listes, compréhensions de listes | Programmation |
| Probabilités appliquées | — |
## Cahier des charges
### Fonctionnalités minimales
- [ ] Implémenter la classe `Combinaison` (individu) : création aléatoire, croisement, mutation
- [ ] Implémenter la classe `Mastermind` (problème) : évaluation du fitness, tournoi
- [ ] Implémenter la classe `AlgoGen` : génèse, sélection, croisement, mutation, solve
- [ ] Résoudre une combinaison cible en un nombre raisonnable de générations
- [ ] Afficher l'évolution du meilleur individu à chaque génération
### Extensions possibles
- [ ] Adapter le framework à un autre problème (labyrinthe, sac à dos...)
- [ ] Comparer plusieurs stratégies de sélection (tournoi vs roulette)
- [ ] Tracer la courbe de convergence (fitness max par génération)
- [ ] Implémenter un Mastermind jouable par un humain vs l'IA
## Ressources
- `../src/src_mastermind_fiche.md` — fiche TP complète avec toutes les consignes
- `../src/algo_gen.py` — squelette du framework générique
- `../src/combinaison.py` — classe individu à compléter
- [Algorithme génétique — Wikipedia](https://fr.wikipedia.org/wiki/Algorithme_génétique)
- [Mastermind — Wikipedia](https://fr.wikipedia.org/wiki/Mastermind)
---
Auteur : Florian Mathieu — Licence CC BY NC

View File

@@ -0,0 +1,72 @@
# Suivi de projet — [Nom du projet]
**Élève(s) :**
**Classe :**
**Projet :**
**Date de début :**
---
## Journal de bord
*Compléter après chaque séance.*
### Séance 1 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
### Séance 2 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
*(Dupliquer le bloc ci-dessus pour chaque séance)*
---
## Bilan du projet
### Ce qui fonctionne
### Ce qui ne fonctionne pas encore (et pourquoi)
### Choix techniques importants
*Quelles décisions as-tu prises ? Pourquoi ce choix plutôt qu'un autre ?*
### Ce que j'ai appris
---
## Préparation au Grand Oral
*Le Grand Oral dure 20 minutes. Tu dois pouvoir présenter ton projet et répondre à des questions. Prépare-toi en répondant à ces questions :*
**1. En quoi consiste ton projet ? Explique-le en 2 minutes à quelqu'un qui ne connaît pas l'informatique.**
**2. Quelles notions du cours as-tu utilisées ? Donne un exemple concret dans ton code.**
**3. Quelle a été la difficulté principale ? Comment l'as-tu résolue ?**
**4. Qu'aurais-tu fait différemment si tu recommençais ?**
**5. Quelle extension aimerais-tu implémenter si tu avais plus de temps ?**

329
Projets/pyxel_text.py Normal file
View File

@@ -0,0 +1,329 @@
"""
Module de gestion de texte pour Pyxel
Permet d'afficher du texte et de capturer la saisie utilisateur
"""
import pyxel
# Mapping des touches Pyxel vers les caractères
KEY_MAP = {
pyxel.KEY_A: ('a', 'A'), pyxel.KEY_B: ('b', 'B'), pyxel.KEY_C: ('c', 'C'),
pyxel.KEY_D: ('d', 'D'), pyxel.KEY_E: ('e', 'E'), pyxel.KEY_F: ('f', 'F'),
pyxel.KEY_G: ('g', 'G'), pyxel.KEY_H: ('h', 'H'), pyxel.KEY_I: ('i', 'I'),
pyxel.KEY_J: ('j', 'J'), pyxel.KEY_K: ('k', 'K'), pyxel.KEY_L: ('l', 'L'),
pyxel.KEY_M: ('m', 'M'), pyxel.KEY_N: ('n', 'N'), pyxel.KEY_O: ('o', 'O'),
pyxel.KEY_P: ('p', 'P'), pyxel.KEY_Q: ('q', 'Q'), pyxel.KEY_R: ('r', 'R'),
pyxel.KEY_S: ('s', 'S'), pyxel.KEY_T: ('t', 'T'), pyxel.KEY_U: ('u', 'U'),
pyxel.KEY_V: ('v', 'V'), pyxel.KEY_W: ('w', 'W'), pyxel.KEY_X: ('x', 'X'),
pyxel.KEY_Y: ('y', 'Y'), pyxel.KEY_Z: ('z', 'Z'),
pyxel.KEY_0: ('0', ')'), pyxel.KEY_1: ('1', '!'), pyxel.KEY_2: ('2', '@'),
pyxel.KEY_3: ('3', '#'), pyxel.KEY_4: ('4', '$'), pyxel.KEY_5: ('5', '%'),
pyxel.KEY_6: ('6', '^'), pyxel.KEY_7: ('7', '&'), pyxel.KEY_8: ('8', '*'),
pyxel.KEY_9: ('9', '('),
pyxel.KEY_SPACE: (' ', ' '),
pyxel.KEY_MINUS: ('-', '_'),
pyxel.KEY_PERIOD: ('.', '>'),
pyxel.KEY_COMMA: (',', '<'),
pyxel.KEY_SLASH: ('/', '?'),
pyxel.KEY_SEMICOLON: (';', ':'),
pyxel.KEY_APOSTROPHE: ("'", '"'),
}
class TextInput:
"""
Champ de saisie de texte pour Pyxel
Exemple d'utilisation:
input_field = TextInput(x=10, y=50, max_length=20)
def update():
input_field.update()
if input_field.submitted:
print(f"Texte saisi: {input_field.text}")
input_field.reset()
def draw():
input_field.draw()
"""
def __init__(self, x=0, y=0, max_length=32, width=None, color=7, bg_color=0, cursor_color=8):
"""
Initialise un champ de saisie
Args:
x, y: Position du champ
max_length: Nombre maximum de caractères
width: Largeur du champ en pixels (auto si None)
color: Couleur du texte (0-15)
bg_color: Couleur de fond (0-15, None pour transparent)
cursor_color: Couleur du curseur (0-15)
"""
self.x = x
self.y = y
self.max_length = max_length
self.width = width if width else (max_length * 4 + 4)
self.color = color
self.bg_color = bg_color
self.cursor_color = cursor_color
self.text = ""
self.cursor_pos = 0
self.active = True
self.submitted = False
self._cursor_visible = True
self._cursor_timer = 0
def update(self):
"""Met à jour le champ de saisie (à appeler dans update())"""
if not self.active:
return
self.submitted = False
# Clignotement du curseur
self._cursor_timer += 1
if self._cursor_timer >= 15:
self._cursor_timer = 0
self._cursor_visible = not self._cursor_visible
# Validation avec Entrée
if pyxel.btnp(pyxel.KEY_RETURN):
self.submitted = True
return
# Suppression avec Backspace
if pyxel.btnp(pyxel.KEY_BACKSPACE, hold=10, repeat=3):
if self.cursor_pos > 0:
self.text = self.text[:self.cursor_pos-1] + self.text[self.cursor_pos:]
self.cursor_pos -= 1
# Suppression avec Delete
if pyxel.btnp(pyxel.KEY_DELETE, hold=10, repeat=3):
if self.cursor_pos < len(self.text):
self.text = self.text[:self.cursor_pos] + self.text[self.cursor_pos+1:]
# Navigation avec flèches
if pyxel.btnp(pyxel.KEY_LEFT, hold=10, repeat=3):
self.cursor_pos = max(0, self.cursor_pos - 1)
if pyxel.btnp(pyxel.KEY_RIGHT, hold=10, repeat=3):
self.cursor_pos = min(len(self.text), self.cursor_pos + 1)
if pyxel.btnp(pyxel.KEY_HOME):
self.cursor_pos = 0
if pyxel.btnp(pyxel.KEY_END):
self.cursor_pos = len(self.text)
# Saisie de caractères
if len(self.text) < self.max_length:
shift = pyxel.btn(pyxel.KEY_LSHIFT) or pyxel.btn(pyxel.KEY_RSHIFT)
for key, chars in KEY_MAP.items():
if pyxel.btnp(key, hold=15, repeat=3):
char = chars[1] if shift else chars[0]
self.text = self.text[:self.cursor_pos] + char + self.text[self.cursor_pos:]
self.cursor_pos += 1
break
def draw(self):
"""Dessine le champ de saisie (à appeler dans draw())"""
# Fond
if self.bg_color is not None:
pyxel.rect(self.x, self.y, self.width, 9, self.bg_color)
# Bordure si actif
if self.active:
pyxel.rectb(self.x - 1, self.y - 1, self.width + 2, 11, self.color)
# Texte
pyxel.text(self.x + 2, self.y + 1, self.text, self.color)
# Curseur
if self.active and self._cursor_visible:
cursor_x = self.x + 2 + self.cursor_pos * 4
pyxel.rect(cursor_x, self.y + 1, 1, 7, self.cursor_color)
def reset(self):
"""Réinitialise le champ"""
self.text = ""
self.cursor_pos = 0
self.submitted = False
def set_text(self, text):
"""Définit le texte du champ"""
self.text = text[:self.max_length]
self.cursor_pos = len(self.text)
class TextBox:
"""
Zone de texte multiligne avec retour à la ligne automatique
Exemple d'utilisation:
textbox = TextBox(x=10, y=10, width=100, color=7)
textbox.set_text("Ceci est un long texte qui sera automatiquement découpé en plusieurs lignes.")
def draw():
textbox.draw()
"""
def __init__(self, x=0, y=0, width=100, color=7, line_spacing=8):
"""
Initialise une zone de texte
Args:
x, y: Position de la zone
width: Largeur en pixels
color: Couleur du texte (0-15)
line_spacing: Espacement entre les lignes
"""
self.x = x
self.y = y
self.width = width
self.color = color
self.line_spacing = line_spacing
self.lines = []
def set_text(self, text):
"""Définit le texte avec retour à la ligne automatique"""
self.lines = []
chars_per_line = self.width // 4
paragraphs = text.split('\n')
for paragraph in paragraphs:
words = paragraph.split(' ')
current_line = ""
for word in words:
test_line = current_line + (" " if current_line else "") + word
if len(test_line) <= chars_per_line:
current_line = test_line
else:
if current_line:
self.lines.append(current_line)
# Mot trop long, on le coupe
while len(word) > chars_per_line:
self.lines.append(word[:chars_per_line])
word = word[chars_per_line:]
current_line = word
if current_line:
self.lines.append(current_line)
def draw(self):
"""Dessine la zone de texte"""
for i, line in enumerate(self.lines):
pyxel.text(self.x, self.y + i * self.line_spacing, line, self.color)
def get_height(self):
"""Retourne la hauteur totale du texte"""
return len(self.lines) * self.line_spacing
class TypeWriter:
"""
Effet machine à écrire pour afficher du texte progressivement
Exemple d'utilisation:
typewriter = TypeWriter(x=10, y=10, speed=3)
typewriter.set_text("Bienvenue dans le jeu...")
def update():
typewriter.update()
def draw():
typewriter.draw()
"""
def __init__(self, x=0, y=0, width=100, color=7, speed=2, line_spacing=8):
"""
Args:
x, y: Position
width: Largeur en pixels
color: Couleur du texte
speed: Frames entre chaque caractère (plus bas = plus rapide)
line_spacing: Espacement entre les lignes
"""
self.textbox = TextBox(x, y, width, color, line_spacing)
self.speed = speed
self.full_text = ""
self.displayed_chars = 0
self.timer = 0
self.finished = False
def set_text(self, text):
"""Définit le texte à afficher progressivement"""
self.full_text = text
self.displayed_chars = 0
self.timer = 0
self.finished = False
def update(self):
"""Met à jour l'animation"""
if self.finished:
return
self.timer += 1
if self.timer >= self.speed:
self.timer = 0
self.displayed_chars += 1
if self.displayed_chars >= len(self.full_text):
self.displayed_chars = len(self.full_text)
self.finished = True
self.textbox.set_text(self.full_text[:self.displayed_chars])
def draw(self):
"""Dessine le texte"""
self.textbox.draw()
def skip(self):
"""Affiche tout le texte immédiatement"""
self.displayed_chars = len(self.full_text)
self.finished = True
self.textbox.set_text(self.full_text)
def is_finished(self):
"""Retourne True si tout le texte est affiché"""
return self.finished
# --- Exemple d'utilisation ---
if __name__ == "__main__":
class Demo:
def __init__(self):
pyxel.init(200, 150, title="Demo TextInput")
self.input_field = TextInput(x=10, y=60, max_length=25, color=7)
self.typewriter = TypeWriter(x=10, y=10, width=180, speed=2)
self.typewriter.set_text("Bienvenue ! Entrez votre nom ci-dessous et appuyez sur Entree.")
self.messages = []
pyxel.run(self.update, self.draw)
def update(self):
self.typewriter.update()
self.input_field.update()
# Skip le typewriter avec espace
if pyxel.btnp(pyxel.KEY_SPACE) and not self.input_field.active:
self.typewriter.skip()
# Validation de la saisie
if self.input_field.submitted and self.input_field.text:
self.messages.append(f"Bonjour, {self.input_field.text} !")
self.input_field.reset()
def draw(self):
pyxel.cls(0)
self.typewriter.draw()
pyxel.text(10, 50, "Votre nom:", 6)
self.input_field.draw()
# Afficher les messages
for i, msg in enumerate(self.messages[-3:]):
pyxel.text(10, 90 + i * 10, msg, 11)
Demo()

View File

@@ -0,0 +1,6 @@
{
"cells": [],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 4
}

559
Projets/src/Activité.md Normal file
View File

@@ -0,0 +1,559 @@
# TP d'introduction aux algorithmes génétiques
Durée : 2h00
Objectifs à atteindre :
- Comprendre le fonctionnement d'un algorithme génétique
- Parvenir à tester son algorithme
- Niveau des connaissances : Première
- Pré requis :
- Avoir suivi les cours sur les algorithmes
- Être bien réveillé et suivre la fiche d'activité !
- Systèmes mis en œuvre : PC connecté au réseau
- Logiciels utilisés : Thonny.
## Type de document
Il s'agit d'un TP de découverte des algorithmes dits "génétiques"
## Table des matières
1. Introduction
2. Le coffre-fort
3. Consignes
4. Au boulot !
### Introduction
Dans le dense monde des algorithmes, le génétique est un type particulier. Il ne s'appuie sur aucune forme particulière, et propose de résoudre un problème de manière *évolutionniste* (comprendre à la manière de Darwin ) et cherche à trouver une solution à l'aide de tests de comparaison.
Ce type d'algorithme necessite un jeu de données, lequel servira de source d'échantillons afin d'effectuer les tests et de stocker les résultats.
Un peu comme lorsque nous avons vu les algorithmes gloutons, il s'agira d'effectuer le meilleur cas optimal mais ici ça ne sera pas localement mais globalement, à l'aide du principe de **selection naturelle** :
À l'aide d'une classe particulière, nous allons pouvoir procéder à des mesures entre plusieurs *individus* (on parle d'individus peu importe le type de données comparé, n'oubliez pas qu'on se base sur les travaux de Darwin) et ne garder que les meilleurs éléments selon un score établi.
Nous créerons donc de nouveaux jeux de données avec ces meilleurs éléments qui devraient nous rapprocher du résultat recherché.
En somme, un algorithme génétique compare des éléments pour affiner une recherche et se rapprocher d'un résultat optimal.
--------------------
### Le coffre-fort
En informatique, on connait le principe de bruteforce ou d'attaque par dictionnaire, qui se rapproche du principe d'algorithme générique.
Mais on peut appliquer aussi cet algorithme en *réel*, par exemple pour trouver une chaîne de caractère la plus proche possible d'un mot donné, - un peu comme si vous jouiez à Motus - à des jeux type mastermind, sobokan, ou à un parcours de labyrinthe, trouver un code pin...etc.
Après tout, tous ces exemples ont une solution et il est possible de tester différentes combinaisons afin de s'en approcher voir même de trouver cette solution.
Songez à un cas pratique qui vous demanderait une combinaison, un code...Et vous pourriez faire quelques essais pour trouver...Une combinaison de coffre fort !
![coffre.jpg](coffre.jpg)
Après tout, nous disposons d'un jeu de données : les chiffres [0 - 9], il manque juste une donnée : la longueur du code à trouver.
Notre but sera donc de proposer un code python permettant de tester un nombre à **n** chiffres, en regardant combien d'essais sont necessaires pour trouver la bonne formule qui ouvrira ce coffre.
Pour simplifier la chose, nous fixerons **n** nous mêmes afin de réduire le nombre de possibilités : en admettant un code de longueur n avec tous les chiffres possibles, nous aurions 10^n possibilités...
Par exemple, un code de longueur n = 4 qui utiliserait tous les chiffres (0 à 9) aurait 10^4 (10 000) combinaisons possibles ! Imaginez le temps que celà prendra, même avec une bonne machine, si n valait 20, 30, 100 !
Il est donc temps de s'y mettre car cela risque de durer un moment.
---------------
### Consignes
Avant de commencer à programmer, rappelons les besoins :
1. Décider de quel type seront les individus que nous allons comparer
2. Un jeu de données
3. Une classe permettant de traiter ces données
1. Comme indiqué plus haut, nous allons rechercher une suite numérique : il semble logique que le type d'individus que nous allons comparer se trouve être des chiffres. Nous devrons donc indiquer une liste de chiffres possiblement utilisables pour la combinaison.
2. Quel type de structure utiliser ?
Une liste comprenant les nombres sera plus facile à employer, d'autant qu'il s'agit de stocker des nombres.
3. Les fichiers individual_interface.py et problem_interface.py vous sont fournis : les classes que vous devez écrire en héritent.
Il s'agira donc de créer un fichier de classe héritant d'individual_interface et un autre héritant de problem_interface.
Il faudra définir les méthodes de ces classes et adapter le code fournit pour notre problème.
N'oubliez pas d'importer les bibliothèques necessaires ainsi que les fichiers python dont vous avez besoin.
---------------------------------
### Au boulot !
Nous allons travailler sur deux fichiers différents : dans le but d'être clair et de ne pas se perdre, mieux vaut en faire un (1) à la fois.
Commençons par le fichier combinaison.py, qui hérite d'individual_interface : ce fichier nous permettra d'écrire le comportement de notre algorithme.
Avant de créer la classe, déclarons la liste des chiffres susceptibles d'être utilisés :
```python
CHIFFRE = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
```
Maintenant, il va s'agir de remplir ces espaces vides
1. Copier notre individu
```python
class Combinaison(Individual_Interface):
def copy(self):
'''
créé une copie d'un individu (appelé génome)
:return: un individu nouveau, qui est son propre clone
:rtype: un type d'individu
'''
copy = ....(self.get_size())
copy.set_value(self.get_value())
return ....
```
Ici, il est question de copier l'individu sur lequel nous allons travailler : rappelez vous, notre jeu de données sera modifié et remplacé par un nouveau, après les tests. Donc nous avons besoin de cloner nos données.
<br>
2. Initialiser un nouvel individu <br>
<br>
Pour remplir notre jeu de données avec de nouveaux individus, il nous faut bien une initialisation.
```python
def init_value(self):
'''
randomly initialize the genome value of self
'''
return [choice(....) for .... in .... (self.get_size())]
```
**Rappel** : choice est une méthode permettant de retourner un nombre aléatoire dans une séquence spécifique. Il faut donc importer une bibliothèque et choisir la séquence indiqué plus tôt.
<br>
3. Croiser deux individus afin de générer un tuple de deux nouveaux individus.
<br>
Les deux individus passés en paramètres de la fonction seront appelés *self* et *other*.
Pour les besoins de cette fonction, il sera necessaire de créer un "pivot" qui sera un nombre aléatoire compris entre 0 et le nombre d'élements dans la séquence CHIFFRE (donc sa longueur...)
Ce pivot permettra de créer des individus aléatoire grâce au croisement.
On appellera test_1 et test_2 nos deux nouveaux individus obtenus depuis self et other ainsi qu'à l'aide de pivot.
Finalement, nous aurons besoin de définir la valeur de nos deux nouveaux amis
test_1.set_value(param1[:...] + param2[...:])
test_2.set_value(param2[:...] + param1[...:])
```python
def cross_with(self, other):
'''
Croiser self et other, grâce à un point pivot, puis retourner deux nouveaux individus créés.
:param other: le second individu
:type other: un individu
:return: Les deux individus crées par le croisement, test_1 et test_2
:rtype: tuple de test_1 et test_2
'''
value = self.
other_value = other.
#générer le nombre pivot ici
#initialiser test_1 et test_2 (comme vous avez créé les individus de base)
#modifier la valeur de test_1
#modifier la valeur de test_2
return (...)
```
4. Enfin, puisqu'on parle de *génétique*, de *croisement* : lorsque l'on va tester notre combinaison de coffre fort, et bien si le test n'est pas concluant du premier coup, il faudra changer les chiffres utilisés et pour cela, on parlera de **mutation**
Chaque individu est susceptible d'être transformé par un autre individu, qui fera partie de la séquence génétique que nous avons déclaré au tout début...
```python
def mutate(self, probability):
'''
Appliquer la mutation à l'individu self passé en paramètre : chaque élément de la séquence (combinaison ici) est changé aléatoirement selon une probabilité donnée
Effet de bord : l'individu va être modifié
:param probability: la probabilité de mutation de chaque génome
:type probability: float
:UC: probability entre [0,1[
'''
for .... in (len(self.get_value())):
#variable = uniform (nombre_min, nombre_max)
#si variable < param probabilité :
self.mutate_gene(gene)
```
On vous donnera la fonction mutate_gene que l'on utilise ci dessus afin de muter un individu
```python
def mutate_gene(self, gene):
temp = self.get_value()
self.set_value(temp[:gene] + [choice(COLOR)] + temp [gene+1:])
```
Ici, notre fichier combinaison.py est terminé. Il nous reste à travailler sur le fichier de la combinaison : celui qui va créer le problème auquel notre algorithme devra répondre.
----------------------------
Maintenant, attaquons nous à problem.py, qui hérite de problem_interface.py
<br>
Ne pas oublier d'importer le fichier dont on hérite, ainsi que le fichier que l'on vient de créer.
C'est parti :
Pour pouvoir résoudre un problème de combinaison à trouver, il faudra tout d'abord....créer le problème à résoudre !
Ainsi, il va falloir initialiser le calcul que nous souhaitons régler par notre algorithme et se faisant, générer une longueur de combinaison à trouver.
```python
class Problem(Problem_Interface) :
"""
classe probleme : Combinaison Coffre-Fort
"""
def __init__(self, calcul):
self.__size = #longueur du paramètre calcul
self.__combinaison = #paramètre calcul
```
Dans le fichier combinaison.py nous clonions des individus.
Mais ici, nous allons implémenter la fonction qui permettra de générer cet individu pour un problème donné.
```python
def create_individual(self):
'''
create a randomly generated indidivual for this problem
:return: a randomly generated individual gfor this problem
:rtype: an Individual object
'''
#retourne un individu crée par la classe que nous avons définie dans combinaison.py
```
Un point important non abordé jusqu'ici : on parle de comparer les individus entre eux pour en selectionner les meilleurs.
Mais comment les compare t-on ? Sur quel(s) critère(s) ?
Et bien tout simplement en leur attribuant un score, selon leur positionnement par rapport à la bonne réponse :
Plus un individu ressemblera au résultat recherché (donc à l'individu parfait dans notre résultat) plus son score sera élevé.
Au contraire, un individu éloigné de la bonne réponse aura un score plus faible.
```python
def evaluate_fitness(self, individual):
'''
Calcule le score d'un individu pour un problème donné.
:param individual: l'individu à évaluer
:type individual: un individu
:return: le score de l'individu évalué
:rtype: un individu
'''
#retourner la somme de ([1 si calcul[element] est équivalent à self.__combinaison[element] sinon 0 pour chaque element dans la fourchette (longueur(calcul))])
```
Une fois que chaque individu aura obtenu un score, nous allons pouvoir ranger la population d'individu qui compose notre génétique.
Pour cela, il suffit de trier par un ordre de score.
```python
def sort_population(self, population):
'''
:param population : la liste d'individus à trier
effet souhaité : on obtient une liste triée d'individus en sortie
'''
#utiliser la méthode sort de la liste en paramètre afin de trier par score.
```
En supposant que notre liste soit bien triée, desormais, on va souhaiter garder le meilleur de la liste afin de ne conserver que les meilleurs gènes, comme la selection naturelle le fait.
```python
def best_individual(self, population):
'''
renvoie le meilleur individu d'une liste.
à vous de voir si c'est celui avec le plus grand score ou le plus petit
'''
#trier la population
#renvoie l'élement en tête de liste ou en bout
```
Maintenant que l'on a nos meilleurs individus, il est temps d'organiser un Mortal Kombat pour décider qui gagnera !
```python
def tournament(self, first, second):
'''
On organise un match entre deux individus, celui qui a le plus gros score remporte la victoire
:param first: un individu
:param second: un individu
:return: le vainqueur
'''
#si tel individu est plus grand qu'un autre, alors le resultat = individu_plusfort
#sinon, resultat = l'autre individu
#renvoie le resultat
```
----------------------
Et maintenant j'ai terminé ? Et bien non.
Si vous avez bien suivi il manquait en fait une chose importante : le fichier algo_gen.py, qui vous est fournit, n'est pas complet.
Ce fichier va nous permettre d'initialiser le probleme, de créer une population d'individus, et de tester notre algorithme solution.
Allez, on y est presque !
<br>
Comme d'habitude, on n'oublie pas d'importer nos fichiers précédemment crées.
<br>
```python
BEST_KEPT = 5
class AlgoGen(object):
def __init__(self, problem, population_size, crossover_rate, mutation_probability):
'''
build an genetic algorithm to solve problem using a population of size population_size and a probability of muation of mutation_probability
:param problem: Le probleme que l'on souhaite ré
:type problem: a Problem object
:param population_size: the size of the population (must be even)
:type population_size: int
:param mutation_probability: the mutation probability
:type mutation_probability: float
:UC: population_size must be even and mutation_probability must be 0<= and <1
'''
self.__problem = problem
self.__population_size = population_size
self.__population = []
self.__nb_crossover = crossover_rate
self.__mutation_probability = mutation_probability
def get_population(self):
return self.__population
```
Cette partie vous est donnée complète, elle permet de voir comment on initialise un problème à résoudre, et obtenir la population d'un problème - qui doit être paire, afin de pouvoir la diviser par deux.
Nous allons commencer à modifier les fonctions qui sont incomplètes dans votre fichier algo_gen.py
Tout d'abord, la fonction qui va se charger de créer la population, la genèse.
Sachant qu'au dessus, nous avons noté
```
self.__population = []
```
Que peut-on en conclure de population ?
Du coup, pour générer la population, il suffira de compter chaque élément dans cette structure et d'utiliser la méthode permettant d'ajouter un élément à cette une structure de même type, que vous trouverez dans votre fichier problem.py
```python
def genesis(self):
"""
générer la population
"""
```
Pour la fonction suivante, il s'agit d'évaluer non pas un individu spécifique, mais toute une population. C'est à dire chaque individu qui la compose.
```python
def evaluate_population(self, population):
"""
evalue la population passée en paramètre
:param population: (list) la population
"""
#Pour chaque individu, on evalue.
```
Une fois évalué, que fait - on ? On relance un Mortal Kombat !
C'est ici que l'on exploitera la parité de votre population : On compare toujours deux individus entre eux. Sur une population complète, on peut rapporter cela à un test d'une moitié de la population envers une autre. Il nous restera donc...une moitié.
```python
def next_generation_tournament(self, population):
'''
organise un tournoi entre les individu d'une population .
chaque individu choisi une et une seule fois, dans un tournoi ,
vainqueur conservé taille résultat = 1/2 taille population
:param population: (list) la population
:return: (list) les vainqueurs du tournoi
'''
```
Comme fait précédemment, une fois le tournoi effectué, il faut croiser les individus pour, encore une fois,ne garder que les meilleurs des meilleurs.
```python
def crossover(self, population):
'''Croise les individus d'une population.
Chaque individu est sélectionné une et une seule fois, on
garde le meilleur descendant à chaque fois.
taille résultat = 1/2 taille population
:param population: (list) a population
:return: (list) new individuals
'''
#Une liste
#pour chaque individu dans une fourchette de la moitié de population
#individu_1 et individu_2 = croisement d'un individu dans la #liste population avec l'individu qui le précède
#Puis on évalue les deux individus
#On les ajoute à la liste initialisée plus haut
#et on suit les consignes jusqu'au bout
```
Enfin, il ne vous reste plus qu'à intégrer une fonction qui fera muter chaque individu dans une liste donnée en paramètre, qu'on appellera "individuals".
Rappel : Au début de votre fichier se trouve "self.__mutation_probability" qui se révélera utile ici.
Et c'est terminé.
Une dernière chose : ça serait bien de tester.
Alors allons y :
```python
if __name__ =="__main__":
probleme = problem('1','3','3','7')
#On initialise le probleme avec une combinaison
solution = AlgoGen(p,100,0,0.1)
solution.solve(100)
```

BIN
Projets/src/Hamming.pdf Normal file

Binary file not shown.

BIN
Projets/src/Images/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
Projets/src/Images/10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
Projets/src/Images/11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
Projets/src/Images/12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
Projets/src/Images/13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
Projets/src/Images/14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
Projets/src/Images/15.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
Projets/src/Images/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
Projets/src/Images/17.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
Projets/src/Images/18.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
Projets/src/Images/19.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
Projets/src/Images/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
Projets/src/Images/20.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
Projets/src/Images/21.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
Projets/src/Images/22.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
Projets/src/Images/23.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Projets/src/Images/24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
Projets/src/Images/25.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Projets/src/Images/26.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Projets/src/Images/27.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Projets/src/Images/28.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Projets/src/Images/29.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
Projets/src/Images/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
Projets/src/Images/30.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Projets/src/Images/31.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Projets/src/Images/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Projets/src/Images/33.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Projets/src/Images/34.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
Projets/src/Images/35.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
Projets/src/Images/36.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
Projets/src/Images/37.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
Projets/src/Images/38.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
Projets/src/Images/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
Projets/src/Images/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
Projets/src/Images/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
Projets/src/Images/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
Projets/src/Images/8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

BIN
Projets/src/Images/9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
Projets/src/Readme.pdf Normal file

Binary file not shown.

View File

@@ -0,0 +1,551 @@
# TP d'introduction aux systèmes Unix
Durée : 1h30
<br>
Objectifs à atteindre : Créer / configurer une machine virtuelle VirtualBox, installer et découvrir le système dexploitation linux Ubuntu, utiliser les commandes de base.
<br>
Niveau des connaissances : Première <br>
Pré requis : <li>Connaissance de larchitecture PC.
<li>Connaissance de lOS Windows.
Systèmes mis en œuvre : PC connecté au réseau <br>
Logiciels utilisés : VirtualBox. <br>
Dernière version stable de Linux Ubuntu
## Type de document
Il s'agit d'un TP de découverte de l'environnement Linux via le système d'exploitation Ubuntu.
## Programme
Il s'agit d'étudier ce point du programme de première, et plus particulièrement les commandes de bases.
![Programme 1ere NSI](Images/Programme.png)
## Contexte
<li>TP à faire en solo ou en binôme, une machine par binôme.
<li>1H30 pour faire le tp, voir 1H selon le temps d'installation des machines virtuelles.
<li>TP à compléter avec un support word, afin que les élèves puissent répondre à des questions sur le libre et sur Linux en général pendant l'installation.
<li> TP donné sous forme de markdown pour utiliser les commandes, via un git crée pour la classe, à utiliser en ligne donc.
## Evaluation du TP et Correction
<li>Passage dans la classe régulièrement afin de s'assurer de l'avancée et de le compréhension des élèves.
<li> Préparation du prochain TP avec des commandes à réutiliser.
<li> Possiblement ajout d'une partie dans le TP : desactivation de l'interface graphique puis exercices sur les commandes.
## Table des matières
1. Création de la nouvelle machine.
2. Installation du système d'exploitation
3. Arborescence d'un système Linux
4. Commandes
### Création de la nouvelle machine.
Votre machine devra avoir les caractéristiques suivantes :
<li>Système d'exploitation --> Ubuntu
<li>Nom de la machine --> Ubuntu - monprénom
<li>Nombre de processeur --> 2
<li>Mémoire vive --> entre 2 Go et 4 Go (2048 Mo ou 4096 mo)
<li>Type de réseau --> NAT
<li>Nouveau disque dur virtuel 20 Go devraient suffire.
----------
Dans VirtualBox, cliquez sur New Virtual Machine
![Nouvelle machine virtuelle](Images/1.png)
Ici, choisissez le type Linux, et le système d'exploitation Ubuntu 64 bits. N'oubliez pas de donner un nom à votre machine, en respectant la consigne située en haut.
![Choix type de machine](Images/2.png)
Le choix de la taille de la mémoire vive dépendra des ressources de votre machine.
![Mémoire vive](Images/3.png)
C'est le moment de créer votre disque dur virtuel.
![Choix type de disque dur](Images/4.png)
Choisissez le type VDI.
<br> Pourquoi ça ? <br> Parce qu'il permet de fonctionner dynamiquement, c'est à dire de modifier l'espace du disque en fonction des besoins, mais également car il offre la possibilité de sauvegarder son disque dur et de l'importer n'importe où.
![Type disque dur](Images/5.png)
Si vous avez bien suivi, vous savez quoi choisir à cette étape :
la taille dynamique, pour ne pas avoir de souci.
![Allocation Taille du disque dur](Images/6.png)
Mais alors, pourquoi selectionner une taille de disque dur si on a décidé de l'attribuer dynamiquement ?
Et bien parce qu'ici, on a la possibilité d'indiquer la taille maximale du disque dur ! C'est à dire que votre disque ne doit jamais dépasser 20 Go par exemple.
![Taille maximale du disque dur](Images/7.png)
Maintenant qu'on est de retour sur le menu de VirtualBox,il ne nous reste plus qu'à selectionner notre VM (Virtual Machine) et à partir à la découverte de Linux, Youpi !
![Let's go !](Images/8.png)
--------------------
### Installation du système d'exploitation
Double cliquez sur votre machine pour la lancer.
Puis, selectionnez l'image du système d'exploitation à installer.
![Selection Iso](9.png)
Si tout se passe bien, vous devriez avoir cet écran ensuite :
![Ubuntu début de l'installation](Images/10.png)
Dès le début de l'installation, le système vous proposera de suivre cela dans une multitude de langues.
Dans la mesure où nous ne sommes pas bilingues, mieux vaut selectionner le français.
![Selection Langue](Images/11.png)
Parmis les deux options, veuillez choisir ***Installer Ubuntu***
Pourquoi ? Parce que la première option vous permet d'utiliser le système mais dès lors que vous quitterez votre machine, il n'y aura plus rien dessus !
On poursuit, en selectionnant le clavier français.
![Clavier](Images/12.png)
Puis, on va décider quoi installer.
N'oubliez pas de décocher la case ***télécharger les mises à jour pendant l'installation d'Ubuntu*** ! Sinon, nous ne sommes pas prêts de voir le bout du TP !
![Choix installation](Images/13.png)
![Case décochée](Images/14.png)
Enfin, le dernier choix : Doit-on effacer le disque dur ou partitionner ?
<br>
Pour le moment, on va se contenter de formater entièrement l'espace disque **virtuel**
Il ne s'agit pas du disque dur de votre machine physique, mais bien des 20 go que nous avons attribué tout à l'heure, pas de panique !
![Effacer disque dur](Images/15.png)
Pas de panique donc quand cet écran apparaîtra devant vous :
![Attention disque](Images/16.png)
Enfin, j'en ai terminé...Et bien ***NON**
Il vous reste à choisir votre fuseau horaire (C'est toujours pratique d'avoir la bonne heure, histoire de terminer dans les temps ! )
![Fuseau Horaire](Images/17.png)
Enfin, indiquez votre prénom, le nom de votre machine, et le mot de passe en respectant cette consigne :
<li>prénom --> mon prénom
<li>nom de la machine --> machine de mon prénom
<li>mot de passe --> mon prénom_mon nom_jour et mois de naissance_*
Habituez vous à taper ce mot de passe, car cela va souvent être necessaire.
![Nom et Mot de passe](Images/18.png)
Si tout est en ordre, alors le système commencera véritablement à s'installer.
![Installation en cours](Images/19.png)
Pendant l'installation, petite refllexion à faire :
Est ce que l'installation d'un système Windows me pousse autant à reflechir ? Pourquoi ?
En attendant l'installation, faites des recherches sur le principe de logiciel libre, et notez ce que vous en avez retenu.
------------
### Arborescence d'un système Linux
Avant de commencer à travailler sur le système, il est necessaire de revoir certaines choses:
Un système Unix, dont font partie les systèmes Linux, MacOs, Android, iOS etc etc fonctionnent via une arborescence de fichiers.
Cela signifie que le système est un arbre, et où chaque dossier est une branche, contenant des feuilles, les fichiers.
Voilà à quoi cela ressemble.
![Arborescence Linux](Images/20.png)
On y trouvera donc des repertoires, indiqués dans le schéma ci dessus entourés d'un rectangle, et des fichiers.
***Attention : sous un système Unix, il n'y a pas forcément d'extension à un fichier.***
On parle d'arborescence, car ce système de fichier ressemble à un arbre à l'envers.
La racine de l'arbre, et donc le point le plus haut dans le système, est appelé "***/***"
#### Chemin absolu et chemin relatif
Pour indiquer la position d'un fichier (ou d'un répertoire) dans l'arborescence, il existe 2 méthodes : indiquer un chemin absolu ou indiquer un chemin relatif.
Le chemin absolu doit indiquer l'adresse du fichier ou dossier depuis la racine.
Le chemin relatif indiquera l'adresse à partir d'un point quelconque.
Par exemple, ici, j'utilise le chemin absolu pour créer un fichier toto.txt
![Chemin absolu](Images/21.png)
Mais après, je peux aussi utiliser un chemin relatif selon l'endroit où je me trouve.
![Chemin relatif](Images/22.png)
Et pourtant, j'ai crée le fichier au même endroit !
Selon vous, quel sera le chemin absolu permettant d'accéder aux fichiers suivants :
<li>**cat***
<li>**rapport.odt**
Et le chemin relatif pour accéder à :
<li>**rapport.odt* depuis le répertoire **elsa**
<li>**fiche.ods** depuis le répertoire **boulot**
?
### Commandes
Si vous avez déjà regardé de vieux films traitant de l'informatique, ou si vous avez bien suivi le cours, vous devez savoir qu'à l'origine, il n'existait aucune interface graphique pour les ordinateurs!
Tout se faisait par des lignes de commandes.
Et bien c'est une chance pour nous, les sytèmes Unix et Linux permettent toujours cela!
Préparez vous à vous prendre pour de (gentils) pirates !
Tout d'abord, il va falloir ouvrir un outil qui deviendra votre meilleur ami, le terminal.
Vous avez la possibilité de l'ouvrir via le menu des applications situé en bas à gauche
![Terminal via applications](Images/24.png)
ou alors si vous êtes chauds du clavier, en appuyant sur ctrl - alt - t en même temps.
![Terminal clavier](Images/23.png)
Et si vous voyez que votre machine virtuelle galère, alors votre professeur a une super solution :
Depuis votre TP, en dessous de chaque explication de commande, vous aurez la possibilité de taper cette commande et d'essayer directement, comme ceci :
![Test commande dans le TP](Images/29.png)
Pour cela, il vous suffira de taper la commande, et d'appuyer ensuite sur entrée et shift en même temps.
```python
print("Coucou le monde!")
```
Coucou le monde!
Nous allons donc pouvoir découvrir quelques commandes de base dans ce premier TP.
<br>
Tout d'abord, la commande **PWD**
Elle vous permet de savoir où vous vous situez dans l'arborescence Unix.
![Commande PWD](Images/25.png)
```python
pwd
```
'C:\\Users\\flori\\Desktop\\Master 2020\\Cours\\UE6 - Culture numérique - TICE\\TP Florian Mathieu\\Cours UE CNT'
--------------
La commande **CD**
<br>
CD, pour *Current Directory*, permet de changer le répertoire courant.
Il suffit d'indiquer le chemin (relatif ou absolu, rappelez vous en !) qui permet d'atteindre le nouveau répertoire.
Par exemple, je peux me promener dans mes différents dossiers.
![CD Boulot](Images/26.png)
![CD Jeux](Images/27.png)
![CD Baccalauréat](Images/28.png)
Vous remarquerez que la commande **PWD** m'a confirmé que je me trouvais bien là où je voulais.
*Mais qu'est ce que c'est que ces deux points au début du chemin ?*
Et bien les deux points, **..** au début de l'adresse que l'on cherche à atteindre, indique au système que l'on souhaite revenir dans le repertoire du dessus !
Ainsi, pour passer de /Bureau/Jeux à /Bureau/Boulot, je peux indiquer :
**CD ../Boulot**
Le système sait que je dois remonter d'un cran, puis repartir vers le dossier Boulot.
En utilisant l'arborescence vue précèdemment, quelle est la commande à saisir si le répertoire courant est le répertoire "home" et que vous souhaitez vous rendre dans le répertoire "boulot" (vous utiliserez d'abord un chemin absolu puis un chemin relatif)
```python
cd ../Travail/
```
[WinError 2] Le fichier spécifié est introuvable: '../Travail/'
C:\Users\flori\Desktop\Master 2020\Cours\UE6 - Culture numérique - TICE\TP Florian Mathieu\Cours UE CNT
-----------------
La commande **LS**
**ls** permet de lister le contenu du repertoire courant.
![Commande LS](Images/30.png)
Après avoir ouvert une console, utilisez la commande ls depuis votre répertoire personnel.
```python
ls
```
------------
La commande **MKDIR**
***mkdir***, pour MaKe DIRectory, permet de créer un répertoire dans le répertoire courant. La commande est de la forme "mkdir nom_du_répertoire"
![Commande mkdir](Images/31.png)
Attention: Il vaut mieux ne pas utiliser de caractères accentués dans les noms de répertoire (ou de fichier). Il en est de même pour les espaces (à remplacer par des caractères tirets bas ou underscore "_")
Comme vous pouvez le voir, le nom des dossiers apparait en bleu dans le terminal.
Après avoir ouvert une console, utilisez la commande "mkdir" afin de créer un répertoire "**NSI**" dans votre répertoire personnel.
```python
mkdir toto
```
```python
ls
```
-----------------
La commande **RM**
**rm** permet de supprimer un fichier.
La commande est de la forme **rm** nom_du_fichier.extension
![Commande rm](Images/32.png)
Et voilà, c'en est terminé de toto.txt, fini les blagues !
Si vous souhaitez supprimer un dossier, alors c'est la variante **rmdir** qu'il vous faut.
Celle-ci s'utilise comme sa copine, sous la forme **rmdir** _nom_du_repertoire
**Attention**
Cette commande peut se révéler devastatrice pour votre système si vous utilisez mal les options !
**Attention**
```python
ls
```
```python
rmdir toto
```
```python
ls
```
-------------------
La commande **TOUCH**
**touch** vous permet de créer un fichier initialement vide.
La commande est de la forme **touch** nom_du_fichier
![Commande touch](Images/33.png)
```python
ls
```
```python
!touch toto.txt
```
```python
ls
```
--------------
La commande **CP**
<br>
**cp**, ou copy, permet de copier un fichier.
La commande est de la forme "cp /répertoire_source/nom_fichier_à_copier /répertoire_destination/nom_fichier"
![Commande cp](Images/34.png)
Vous n'êtes pas obligé de donner le même nom au fichier source et au fichier de destination.
De même, la commande **cp** peut vous permettre de renommer un fichier !
Il vous suffit de copier le fichier dans le même repertoire mais avec un nouveau nom, pratique non ?
![Commande cp 2](Images/35.png)
<br>
Placez-vous dans le répertoire **NSI** créé aprécédemment avec **mkdir**.
<br>
Créez un fichier *coucou.txt*. Créez un répertoire *Première_NSI*.
<br>
Copiez le fichier "coucou.txt" dans le répertoire *Première_NSI*.
<br>
Effacez le répertoire *Première_NSI* ainsi que son contenu.
```python
ls
```
```python
cp toto.txt UE\ CN/coucou.txt
```
File "<ipython-input-14-bebeb8ed501b>", line 1
cp toto.txt UE\ CN/coucou.txt
^
SyntaxError: invalid syntax
```python
cd UE\ CN
```
```python
ls
```
-----------
La commande **MV**
**mv**, pour move, permet de déplacer fichiers et repertoires.
Tout comme **cp**, **mv** permet également de renommer un fichier.
![Commande mv](Images/36.png)
```python
ls
```
```python
mv coucou.txt /home/poueteuh/Documents/Cours\ UE\ CNT
```
```python
cd /home/poueteuh/Documents/Cours\ UE\ CNT
```
```python
ls
```
---------------
*Monsieur, vous vous êtes embêté à chaque fois à ouvrir un nouveau terminal pour virer les commandes d'avant ?*
Et bien non, car il existe une commande magique :
**clear**
Il vous suffit de l'utiliser pour nettoyer votre terminal.
![Commande clear](Images/37.png)
Et voilà !
![Commande clear 2](Images/38.png)
*Suis-je devenu un-e pirate après tout ça ?*
Pas du tout ! Mais au moins, vous savez utiliser les commandes de bases Unix, c'est toujours ça de pris !

View File

@@ -0,0 +1,32 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

156
Projets/src/algo_gen.py Normal file
View File

@@ -0,0 +1,156 @@
from problem_sup import *
BEST_KEPT = 5
class AlgoGen(object):
def __init__(self, problem, population_size, crossover_rate, mutation_probability):
'''
build an genetic algorithm to solve problem using a population of size population_size and a probability of muation of mutation_probability
:param problem: the problem to solve
:type problem: a Problem object
:param population_size: the size of the population (must be even)
:type population_size: int
:param mutation_probability: the mutation probability
:type mutation_probability: float
:UC: population_size must be even and mutation_probability must be 0<= and <1
'''
self.__problem = problem
self.__population_size = population_size
self.__population = []
self.__nb_crossover = crossover_rate
self.__mutation_probability = mutation_probability
def get_population(self):
return self.__population
def genesis(self):
"""
create the population
"""
for _ in range(self.__population_size):
self.__population.append(self.__problem.create_individual())
def evaluate_population(self, population):
"""
evalue la population passée en paramètre
:param population: (list) la population
"""
for individu in population:
individu.evaluate(self.__problem)
def next_generation_tournament(self, population):
'''
organise un tournoi entre les individu d'une population .
chaque individu choisi une et une seule fois, dans un tournoi ,
vainqueur conservé taille résultat = 1/2 taille population
:param population: (list) la population
:return: (list) les vainqueurs du tournoi
'''
vainqueur = []
for individu in range(len(population)//2):
vainqueur.append(self.__problem.tournament(population[individu], population[len(population) - 1 - individu]))
return vainqueur
def crossover(self, population):
'''Croise les individus d'une population.
Chaque individu est sélectionné une et une seule fois, on
garde le meilleur descendant à chaque fois.
taille résultat = 1/2 taille population
:param population: (list) a population
:return: (list) new individuals
'''
meilleur = []
for individu in range(len(population)//2):
individu_1, individu_2 = population[individu].cross_with(population[len(population) - 1 - individu])
individu_1.evaluate(self.__problem)
individu_2.evaluate(self.__problem)
meilleur.append(self.__problem.tournament(individu_1, individu_2))
return meilleur
def mutate(self, individuals):
"""
mute les individus
:param individuals: (list) une liste des individus
"""
for el in individuals:
el.mutate(self.__mutation_probability)
def one_round(self):
'''
applique successivement les opérateurs
- selection
- croisement
- mutation
'''
popu = self.next_generation_tournament(self.__population)
popu_2 = self.crossover(self.__population)
popu_croisée = popu + popu_2
self.mutate(popu_croisée)
self.evaluate_population(popu_croisée)
self.__population = popu_croisée
def solve(self, nb_rounds):
"""
tente d'approcher une solution en faisant muter la population
:param nb_rounds: (int) nombre de génération
"""
self.genesis()
self.evaluate_population(self.__population)
self.display_stat('INITIAL')
for step in range(nb_rounds):
self.one_round()
#self.display_population(step=step+1, number = 20)
self.display_stat(step)
#self.display_population(number = 20) # display 20 best individuals
return self.__problem.best_individual(self.__population)
def display_stat(self, step):
best = self.__problem.best_individual(self.__population)
all_scores = [ individual.get_score() for individual in self.__population ]
average = sum (all_scores) / self.__population_size
print ( ' STEP = {} -- best = {} - avg = {} '.format(step, best, average) )
def display_population(self, step = ' ', number = 0):
if number <= 0:
individuals = self.__population
else:
individuals = self.__population[:min(number, len(self.__population))]
print( ' **** STEP : {} ********* '.format(step) )
for individual in individuals :
print ( individual )
print( )
# tool function
def biased_wheel(alea,scores):
'''
return smallest i such that alea < sum(scores[k], 0 <= k < i)
CU : 0 <= alea < sum(scores)
'''
current_sum = 0
result_index = -1
while alea > current_sum :
result_index = result_index + 1
current_sum = current_sum + scores[result_index]
return result_index
if __name__ =="__main__":
p = problem_sup('1','3','3','7')
a = AlgoGen(p,100,0,0.1)
a.solve(100)

BIN
Projets/src/coffre.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,75 @@
from individual_interface import *
from random import *
from math import *
CHIFFRE = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
class Combinaison(Individual_Interface):
def copy(self):
'''
build a copy of self, the genome is a copy of self's genome
:return: a new Individual which is a "clone" of self
:rtype: an Individual object
'''
copy = Combinaison(self.get_size())
copy.set_value(self.get_value())
return copy
def init_value(self):
'''
randomly initialize the genome value of self
'''
return [choice(CHIFFRE) for _ in range (self.get_size())]
def cross_with(self, other):
'''
perform a 1 point crossover between self and other, two new built individuals are returned
:param other: the individual to cross with
:type other: an Individual object
:return: the two new Individuals built by 1 point crossover operation
:rtype: 2-uple of individuals
'''
value = self.get_value()
other_value = other.get_value()
pivot = randint (0, self.get_size())
test_1, test_2 = Combinaison(self.get_size()), Combinaison(self.get_size())
test_1.set_value(value[:pivot] + other_value[pivot:])
test_2.set_value(other_value[:pivot] + value[pivot:])
return (test_1, test_2)
def mutate(self, probability):
'''
apply mutation operation to self : each element of the genome sequence is randomly changed with given probabiliy
side effect : self's genome is modified
:param probability: the probability of mutation for every gene
:type probability: float
:UC: probability in [0,1[
'''
for gene in range(len(self.get_value())):
proba = uniform(0, 1)
if proba < probability :
self.mutate_gene(gene)
def mutate_gene(self, gene):
temp = self.get_value()
self.set_value(temp[:gene] + [choice(CHIFFRE)] + temp [gene+1:])

View File

@@ -0,0 +1,120 @@
import random
class Individual_Interface(object):
'''
an Individual in genteic algorithm problem
the value (or genome) of an indal is a sequence (e.g string or list) of a fixed size
an individual has a fitness score
'''
def __init__(self, size):
'''
create an Individual object, its genome value is randomly built. Initially score is not set.
:param size: size of the genome
:type size: int
'''
self.__score = None
self.__size = size
self.__value = self.init_value()
def copy(self):
'''
build a copy of self, the genome is a copy of self's genome
:return: a new Individual which is a "clone" of self
:rtype: an Individual object
'''
return None
def get_size(self):
'''
:return: the size of self's genome
:rtype: int
'''
return self.__size
def get_score(self):
'''
:return: the fitness score of self
:rtype: number
'''
return self.__score
def set_score(self, new_score):
'''
change the fitness score of self
:param new_score: the new fitness score
:type new_score: number
'''
self.__score = new_score
def __str__(self):
return "{} ({})".format(self.__value, self.__score)
def __repr__(self):
return self.__str__()
def get_value(self):
'''
:return: the genome of self
:rtype: sequence
'''
return self.__value
def set_value(self, new_value):
'''
change the genome value of self
:param new_score: the new genome value
:type new_score: sequence
'''
self.__value = new_value
def init_value(self):
'''
randomly initialize the genome value of self
'''
return None
def evaluate(self,problem):
'''
set the fitness score with the fitness computed by problem for self
:param problem: the problem
:type problem: a Problem object
'''
self.set_score(problem.evaluate_fitness(self))
def cross_with(self, other):
'''
perform a 1 point crossover between self and other, two new built individuals are returned
:param other: the individual to croww with
:type other: an Idnividual object
:return: the two new Individuals built by 1 point crossover operation
:rtype: 2-uple of individuals
'''
pass
def mutate(self, probability):
'''
apply mutation operation to self : each element of the geome sequence is randomly changed with given probabiliy
side effect : self's genome is modified
:param probability: the probability of mutation for every gene
:type probability: float
:UC: probability in [0,1[
'''
pass
def mutate_gene(self, gene):
pass

140
Projets/src/maze.py Normal file
View File

@@ -0,0 +1,140 @@
class Maze(object):
DIRECTIONS = ['N', 'E', 'S', 'W']
def __init__(self, filename):
'''
each cell of a maze receives a number from 0 to the number of cells -1, cells by cells, line by line
maze is represented by a dictionnary whose keys are the cell numbers and values the list of possible DIRECTIONS from this cell
start cell is number 0 (top, left) and exit cell is the last one (bottom, right)
:param filename: the name file containing the maze description
:type filename; string
'''
self.init_cells(filename)
self.__start = 0 # index of starting cell
self.__exit = len(self.__cells) - 1 # index of exit cell
def init_cells(self, filename):
'''
read a text file describing the maze from a "picture" made of | + - and spaces
cells are stored in a dictionnary, the key is the cell number (built line by line) the value is the string made of
N,S,E,W corresponding to available directions for leave the cell
:param filename: the name of the file containing the aze description
:type filename: string
:return: the dictionnary corresponding to the maze description
:rtype: dictionnary (int : list(DIRECTIONS))
'''
CSV_SEPARATOR = ';'
def read_content(input_stream):
HOLE = ' ' # a HOLE means a path
def handle_horizontal_path(chaine):
for col in range(1, self.__width):
car = chaine[2*col]
if car == HOLE:
cells[line*self.__width + col - 1] += 'E'
cells[line*self.__width + col ] += 'W'
def handle_vertical_path(chaine):
for col in range(0, self.__width):
car = chaine[2*col+1]
if car == HOLE:
cells[line*self.__width + col ] += 'S'
cells[(line+1)*self.__width + col ] += 'N'
# read_content body
line = input_stream.readline().rstrip().split(CSV_SEPARATOR) # first line of file
self.__width, self.__height = int(line[0]), int(line[1]) # define __width and __height fields
cells = dict((i,'') for i in range (self.__width* self.__height))
lines = input_stream.readlines() # all other lines
for line in range(self.__height):
handle_horizontal_path(lines[2*line+1]) # odd lines => horizontal path
handle_vertical_path(lines[2*line+2]) # even lines => vertical path
return cells
# init_cells body
try :
with open(filename, 'r', encoding="utf-8") as input_stream :
self.__cells = read_content(input_stream)
except FileNotFoundError :
raise FileNotFoundError('fichier inconnu')
def get_size(self):
'''
:return: the number of cells of this maze
:rtype: int
'''
return self.__width * self.__height
def try_path(self, path):
'''
path is tried from entry point, step by step.
Course stops either as soon as a wall is met, or it returns to an already visited cell, or reaches the exit.
Successful steps are steps until stop.
:param path: a sequence of DIRECTIONS corresponding to moves.
:type path: an sequence (actually a string but could be list) of DIRECTIONS
:return: a tuple containing number of successful steps using path and manhattan distance between reached cell exit
:rtype: (int, int)
'''
visited = []
nb_steps = 0
distance = -1
current_cell = self.__start
while current_cell != self.__exit and\
nb_steps < len(path) and\
current_cell not in visited and\
path[nb_steps] in self.__cells[current_cell] :
visited.append(current_cell)
current_cell = current_cell + self.offset(path[nb_steps])
nb_steps = nb_steps + 1
return (nb_steps, self.manhattan_distance(current_cell, self.__exit))
def offset(self,direction):
'''
consider a move towards DIRECTION,
and compute the offset to apply to the current cell number to obtain the destination cell number
:param direction: the direction to convert
:type direction: an element of DIRECTIONS
:return: the offset corresponding to direction
:rtype:int
'''
if direction == 'E':
return + 1
elif direction == 'N':
return - self.__width
elif direction == 'W':
return - 1
elif direction == 'S':
return + self.__width
def manhattan_distance(self, first, second):
'''
:param first: coordinate of first cell
:type first: (int, int)
:param second: coordinate of second cell
:type second: (int, int)
:return: the manhattan distance between coordinates first and second
:rtype: int
'''
col_first, line_first = first % self.__width, first // self.__width
col_second, line_second = second % self.__width, second // self.__width
return abs(col_first - col_second) + abs (line_first - line_second)
if __name__ == "__main__":
m = Maze('maze2.txt')
print(m.try_path('EEESWWSESWSEESENEESENENNNS'))
print(m.try_path('EEESWWSESWSEESENEES'))
print(m.try_path('SSENEESENNEESWSSE'))

BIN
Projets/src/maze.zip Normal file

Binary file not shown.

94
Projets/src/mot.py Normal file
View File

@@ -0,0 +1,94 @@
from individual_interface import *
from random import *
ALPHABET = "abcdefghijklmnopqrstuvwxyz "
class Mot(Individual_Interface):
def __init__(self, size):
'''
create an Individual object, its genome value is randomly built. Initially score is not set.
:param size: size of the genome
:type size: int
'''
super().__init__(size)
def copy(self):
'''
build a copy of self, the genome is a copy of self's genome
:return: a new Individual which is a "clone" of self
:rtype: an Individual object
'''
copy = Mot(self.__size)
copy.set_value(self.get_value())
return copy
def init_value(self):
'''
randomly initialize the genome value of self
'''
return "".join([choice(ALPHABET) for _ in range(self.get_size())])
def cross_with(self, other):
'''
perform a 1 point crossover between self and other, two new built individuals are returned
:param other: the individual to croww with
:type other: an Idnividual object
:return: the two new Individuals built by 1 point crossover operation
:rtype: 2-uple of individuals
'''
value = self.get_value()
other_value = other.get_value()
pivot = randint (0, self.get_size())
test_1, test_2 = Mot(self.get_size()), Mot(self.get_size())
test_1.set_value(value[:pivot] + other_value[pivot:])
test_2.set_value(other_value[:pivot] + value[pivot:])
return (test_1, test_2)
def mutate(self, probability):
'''
apply mutation operation to self : each element of the geome sequence is randomly changed with given probabiliy
side effect : self's genome is modified
:param probability: the probability of mutation for every gene
:type probability: float
:UC: probability in [0,1[
'''
for gene in range(len(self.get_value())):
proba = uniform(0, 1)
if proba < probability :
self.mutate_gene(gene)
def mutate_gene(self, gene):
temp = self.get_value()
self.set_value = temp[:gene] + choice(ALPHABET) + temp [gene+1:]
if __name__ == "__main__":
mot_1 = Mot(5)
mot_2 = Mot(5)
mot_3, mot_4 = mot_1.cross_with(mot_2)
print("""
mot_1 : {}
mot_2 : {}
mot_3 : {}
mot_4 : {}
""".format(mot_1.get_value(), mot_2.get_value(), mot_3.get_value(), mot_4.get_value()))

92
Projets/src/nombre.py Normal file
View File

@@ -0,0 +1,92 @@
from individual_interface import *
from random import *
ENSEMBLE = "01"
class Nombre(Individual_Interface):
def __init__(self, size):
'''
create an Individual object, its genome value is randomly built. Initially score is not set.
:param size: size of the genome
:type size: int
'''
super().__init__(size)
def copy(self):
'''
build a copy of self, the genome is a copy of self's genome
:return: a new Individual which is a "clone" of self
:rtype: an Individual object
'''
copy = (self.get_size())
copy.set_value(self.get_value())
return copy
def init_value(self):
'''
randomly initialize the genome value of self
'''
return "".join([choice(ENSEMBLE) for _ in range(self.get_size())])
def cross_with(self, other):
'''
perform a 1 point crossover between self and other, two new built individuals are returned
:param other: the individual to croww with
:type other: an Idnividual object
:return: the two new Individuals built by 1 point crossover operation
:rtype: 2-uple of individuals
'''
value = self.get_value()
other_value = other.get_value()
pivot = randint (0, self.get_size())
test_1, test_2 = Nombre(self.get_size()), Nombre(self.get_size())
test_1.set_value(value[:pivot] + other_value[pivot:])
test_2.set_value(other_value[:pivot] + value[pivot:])
return (test_1, test_2)
def mutate(self, probability):
'''
apply mutation operation to self : each element of the geome sequence is randomly changed with given probabiliy
side effect : self's genome is modified
:param probability: the probability of mutation for every gene
:type probability: float
:UC: probability in [0,1[
'''
for gene in range(len(self.get_value())):
proba = uniform(0, 1)
if proba < probability :
self.mutate_gene(gene)
def mutate_gene(self, gene):
temp = self.get_value()
self.set_value = temp[:gene] + choice(ENSEMBLE) + temp [gene+1:]
if __name__ == "__main__":
nombre = Nombre(5)
nombre_2 = Nombre(5)
nombre_3, nombre_4 = nombre.cross_with(nombre_2)
print("""
nombre : {}
nombre_2 : {}
nombre_3 : {}
nombre_4 : {}
""".format(nombre.get_value(), nombre_2.get_value(), nombre_3.get_value(), nombre_4.get_value()))

84
Projets/src/problem_1.py Normal file
View File

@@ -0,0 +1,84 @@
from problem_interface import *
from nombre import *
class Problem_1(Problem_Interface) :
"""
classe probleme n°1 : nombre à trouver pour f(x) = x_max
"""
def __init__(self, size, f):
self.__size = size
self.__f = f
def create_individual(self):
'''
create a randomly generated indidivual for this problem
:return: a randomly generated individual gfor this problem
:rtype: an Individual object
'''
return Nombre((self.__size))
def evaluate_fitness(self, individual):
'''
compute the fitness of individual for this problem
:param individual: the individual to consider
:type individual: an Individual object
:return: the fitness of individual for this problem
:rtype: an Individual object
'''
return self.__f(bin_deci(individual.get_value()))
def sort_population(self, population):
'''
sort population from best fitted to worst fitted individuals.
Depending on the problem, it can correspond to ascending or descending order with respect to the fitness function.
side effect : population is modified by this method
:param population: the list of individuals to sort
:type population: list(Individual)
'''
population.sort(key = lambda x: x.get_score())
def best_individual(self, population):
'''
return the best fitted individual from population.
Depending on the problem, it can correspond to the individual with the highest or the lowest fitness value.
:param population: the list of individuals to sort
:type population: list(Individual)
:return: the best fitted individual of population
:rtype: an Individual object
'''
self.sort_population(population)
return population[len(population) -1 ]
def tournament(self, first, second):
'''
perform a rounament between two individuals, the winner is the most fitted one, it is returned
:param first: an individual
:type first: an Indivdual object
:param second: an individual
:type second: an Individual object
:return: the winner of the tournament
:rtype: Individual object
'''
if first.get_score() > second.get_score():
res = first
else:
res = second
return res
return first if first.get_score() > second.get_score() else second
def bin_deci(chaine):
return int(chaine,2)

118
Projets/src/problem_2.py Normal file
View File

@@ -0,0 +1,118 @@
from problem_interface import *
from mot import *
class Problem_2(Problem_Interface) :
"""
classe probleme n°2 : message secret
"""
def __init__(self, mot_cherche):
self.__size = len(mot_cherche)
self.__mot_cherche = mot_cherche
def get_size(self):
return self.__size
def get_mot_cherche(self):
return self.__mot_cherche
def create_individual(self):
'''
create a randomly generated indidivual for this problem
:return: a randomly generated individual gfor this problem
:rtype: an Individual object
'''
return Mot(self.get_size())
def evaluate_fitness(self, individual):
'''
compute the fitness of individual for this problem
:param individual: the individual to consider
:type individual: an Individual object
:return: the fitness of individual for this problem
:rtype: an Individual object
'''
mot = individual.get_value()
cherche = self.get_mot_cherche()
res = 0
for lettre in range(len(mot)):
if mot[lettre] == cherche[lettre]:
res += 1
return res
# return sum([1 if mot[letter]==cherche[letter] else 0 for letter in range(len(mot))])
def sort_population(self, population):
'''
sort population from best fitted to worst fitted individuals.
Depending on the problem, it can correspond to ascending or descending order with respect to the fitness function.
side effect : population is modified by this method
:param population: the list of individuals to sort
:type population: list(Individual)
'''
population.sort(key = lambda x: x.get_score())
def best_individual(self, population):
'''
return the best fitted individual from population.
Depending on the problem, it can correspond to the individual with the highest or the lowest fitness value.
:param population: the list of individuals to sort
:type population: list(Individual)
:return: the best fitted individual of population
:rtype: an Individual object
'''
self.sort_population(population)
return population[len(population) -1 ]
def tournament(self, first, second):
'''
perform a rounament between two individuals, the winner is the most fitted one, it is returned
:param first: an individual
:type first: an Indivdual object
:param second: an individual
:type second: an Individual object
:return: the winner of the tournament
:rtype: Individual object
'''
if first.get_score() > second.get_score():
res = first
else:
res = second
return res
return first if first.get_score() > second.get_score() else second
if __name__ == "__main__":
probleme = Problem_2("toto")
mot = Mot(4)
mot_2 = Mot(4)
mot_3 = Mot(4)
mot.set_value("toto")
mot_2.set_value("tata")
mot_3.set_value("titi")
liste = [mot_2, mot, mot_3]
for el in liste:
el.evaluate(probleme)
print (liste)
probleme.sort_population(liste)
print (liste)
print(probleme.evaluate_fitness(mot))

85
Projets/src/problem_4.py Normal file
View File

@@ -0,0 +1,85 @@
from problem_interface import *
from ville import *
class Problem_4(Problem_Interface) :
"""
classe probleme n°4 : TSP
"""
def __init__(self, villes):
self.__size = len(villes)
self.__villes = villes
def create_individual(self):
'''
create a randomly generated indidivual for this problem
:return: a randomly generated individual gfor this problem
:rtype: an Individual object
'''
return Ville(self.__villes)
def evaluate_fitness(self, individual):
'''
compute the fitness of individual for this problem
:param individual: the individual to consider
:type individual: an Individual object
:return: the fitness of individual for this problem
:rtype: an Individual object
'''
return individual.distance_totale()
# return sum([1 if mot[letter]==cherche[letter] else 0 for letter in range(len(mot))])
def sort_population(self, population):
'''
sort population from best fitted to worst fitted individuals.
Depending on the problem, it can correspond to ascending or descending order with respect to the fitness function.
side effect : population is modified by this method
:param population: the list of individuals to sort
:type population: list(Individual)
'''
population.sort(key = lambda x: x.get_score())
def best_individual(self, population):
'''
return the best fitted individual from population.
Depending on the problem, it can correspond to the individual with the highest or the lowest fitness value.
:param population: the list of individuals to sort
:type population: list(Individual)
:return: the best fitted individual of population
:rtype: an Individual object
'''
self.sort_population(population)
return population[len(population) -1 ]
def tournament(self, first, second):
'''
perform a rounament between two individuals, the winner is the most fitted one, it is returned
:param first: an individual
:type first: an Indivdual object
:param second: an individual
:type second: an Individual object
:return: the winner of the tournament
:rtype: Individual object
'''
if first.get_score() < second.get_score():
res = first
else:
res = second
return res
return first if first.get_score() < second.get_score() else second

View File

@@ -0,0 +1,65 @@
class Problem_Interface(object):
'''
a prototype class for "problems" in genetic algorithm
an Individual class must be associated to a problem to represent the individuals
'''
def create_individual(self):
'''
create a randomly generated indidivual for this problem
:return: a randomly generated individual gfor this problem
:rtype: an Individual object
'''
pass
def evaluate_fitness(self, individual):
'''
compute the fitness of individual for this problem
:param individual: the individual to consider
:type individual: an Individual object
:return: the fitness of individual for this problem
:rtype: an Individual object
'''
pass
def sort_population(self, population):
'''
sort population from best fitted to worst fitted individuals.
Depending on the problem, it can correspond to ascending or descending order with respect to the fitness function.
side effect : population is modified by this method
:param population: the list of individuals to sort
:type population: list(Individual)
'''
pass
def best_individual(self, population):
'''
return the best fitted individual from population.
Depending on the problem, it can correspond to the individual with the highest or the lowest fitness value.
:param population: the list of individuals to sort
:type population: list(Individual)
:return: the best fitted individual of population
:rtype: an Individual object
'''
pass
def tournament(self, first, second):
'''
perform a rounament between two individuals, the winner is the most fitted one, it is returned
:param first: an individual
:type first: an Indivdual object
:param second: an individual
:type second: an Individual object
:return: the winner of the tournament
:rtype: Individual object
'''
pass

View File

@@ -0,0 +1,84 @@
from problem_interface import *
from combinaison import *
class Problem_5(Problem_Interface) :
"""
classe probleme n°5 : Combinaison Coffre-Fort
"""
def __init__(self, calcul):
self.__size = len(calcul)
self.__combinaison = calcul
def create_individual(self):
'''
create a randomly generated indidivual for this problem
:return: a randomly generated individual gfor this problem
:rtype: an Individual object
'''
return Combinaison(self.__size)
def evaluate_fitness(self, individual):
'''
compute the fitness of individual for this problem
:param individual: the individual to consider
:type individual: an Individual object
:return: the fitness of individual for this problem
:rtype: an Individual object
'''
return sum([1 if calcul[chiffre]==self.__combinaison[chiffre] else 0 for chiffre in range(len(calcul))])
def sort_population(self, population):
'''
sort population from best fitted to worst fitted individuals.
Depending on the problem, it can correspond to ascending or descending order with respect to the fitness function.
side effect : population is modified by this method
:param population: the list of individuals to sort
:type population: list(Individual)
'''
population.sort(key = lambda x: x.get_score())
def best_individual(self, population):
'''
return the best fitted individual from population.
Depending on the problem, it can correspond to the individual with the highest or the lowest fitness value.
:param population: the list of individuals to sort
:type population: list(Individual)
:return: the best fitted individual of population
:rtype: an Individual object
'''
self.sort_population(population)
return population[len(population) -1 ]
def tournament(self, first, second):
'''
perform a rounament between two individuals, the winner is the most fitted one, it is returned
:param first: an individual
:type first: an Individual object
:param second: an individual
:type second: an Individual object
:return: the winner of the tournament
:rtype: Individual object
'''
if first.get_score() < second.get_score():
res = first
else:
res = second
return res

View File

@@ -0,0 +1,135 @@
# **Algorithmes génétiques**
## **Le Mastermind**
### **Le jeu**
Le Mastermind est un jeu de société pour deux joueurs dont le but est de trouver une combinaison de pions avec pour chaque pion associe une couleur parmis une liste de 8 couleurs.
La combinaison de pion peut avoir plusieurs fois une même couleur en son sein.
*source :* [wikipédia](https://fr.wikipedia.org/wiki/Mastermind "lien vers la page wikipédia")
Pour ce TP, la combinaison caché sera définit par 4 pions et la liste des couleurs sera définit par une liste :
```python
COLOR = ["rouge", "jaune", "bleu", "orange", "vert", "blanc", "violet", "rose"]
```
---
### **Les individus**
#### Création
Un individu pour ce problème aura comme valeur une liste de couleur. Le nombre de couleur / la taille de la liste dépendra donc de la taille donnée ors de sa création. De plus lors de la création, de l'individu, les couleurs seront choisis de façon aléatoire.
**Consigne** - Implémenter la méthode ```init_value(self)``` de la classe **Combinaison**.
#### Croisement
Afin de simplifier, nous utiliserons uniquement des croisements en **1 Points**.
Lorsque l'on voudra croiser 2 individus
- on prendra aléatoirement un indice compris entre 0 et la taille max de la liste de couleur
- on cré 2 nouveaux individus :
- new_ind_1 <- individu_1\[:indice\] + individu_2\[indice:\]
- new_ind_2 <- individu_2\[:indice\] + individu_1\[indice:\]
- renvoi new_ind_1 et new_ind_2
**Consigne** - Implémenter la méthode ```cross_with(self, other)``` de la classe **Combinaison**.
#### Mutation
Pour un individu, un gêne est représenté par une couleur (qui appartient à la variable ```COLOR```). Muter un gêne revient alors à remplacer ce gêne par un autre, c'est à dire par une autre couleur.
La fonction ```mutate(self, probability)``` de la classe **Combinaison**, va parcourir tous les gênes de l'individu et va aléatoirement lancer une mutation en fonction de la probabilité qui lui est donné en paramètre.
**Consigne** - Implémenter la méthode ```mutate_gene(self, gene)``` de la classe **Combinaison**.
---
### **Le problème**
#### Création
Lorsque l'on va instancier le problème, on lui donnera une combinaison de couleur, cette combinaison est ce que l'algorithme doit trouver.
Pour la résolution du problème, nous avons besoin de définir une méthode qui va générer des individus de façon aléatoire en fonction de la taille de la combinaison solution.
**Consigne** - Implémenter la méthode ```create_individual(self)``` de la classe **Mastermind**.
#### Evaluation
Pour évaluer un individu, on va comparé gêne par gêne sa combinaison avec la combinaison solution. Le résultat de l'évaluation sera alors le nombre de gêne valide et correctement bien placé.
**Consigne** - Implémenter la méthode ```evaluate_fitness(self, individual)``` de la classe **Mastermind**.
#### Tournoi
L'algorithme va progressivement faire des tournois entre les individus afin de garder les meilleurs.
La méthode ```sort_population(self, population)``` prend en paramètre une population de combinaison et va la triée dans l'ordre décroissant de score.
**Consigne** - Implémenter la méthode ```best_individual(self, population)``` de la classe **Mastermind**.
**Consigne** - Implémenter la méthode ```tournament(self, first, second)``` de la classe **Mastermind** qui compare 2 individus et renvoie le meilleur des 2.
---
### **Algo_gen**
#### Population
Parmi les attributs de la classe AlgoGen, se trouve un attribut pour la population qui est vide au départ ainsi qu'un attribut pour la taille de la population.
**Consigne** - Implémenter la méthode ```genesis(self)``` de la classe **AlgoGen** qui aura comme effet de bord la création de la population.
#### Evaluation
Un individu à sa création voit son score à ```None```. Il va donc falloir, après avoir générer la population, évaluer chaque individu afin que tous ai un score mis à jour.
**Consigne** - Implémenter la méthode ```evaluate_population(self, population)``` de la classe **AlgoGen** qui prend une population et évalue chaque individu de cette dernière.
#### Tournoi
Afin d'améliorer la population, nous allons effectué une multitude de tournoi entre la première moitié de la population avec l'autre moitié. Ainsi on obtiendra une population avec les ```n//2``` meilleur individu de la population avec *n* étant la taille de la population.
**Consigne** - Implémenter la méthode ```next_generation_tournament(self, population)``` de la classe **AlgoGen**.
#### Croisement
La population d'une étape *i* (avec *i* non nulle), est la concaténation entre le résultat de la méthode des tournois et de la méthode des croisements.
La méthode de croisement prendra les ```n//2``` (avec *n* la taille de la population) premiers individus de la populations et va les croiser avec les ```n//2``` derniers individus de la population. Ainsi nous aurons une liste d'individus de taille ```n//2```.
**Consigne** - Implémenter la méthode ```crossover(self, population)``` de la classe **AlgoGen**.
#### Mutation
Après avoir créé la population, la dernière étape consiste à faire les mutations de chaque individu selon la probabilité se trouvant dans les attributs de la classe.
**Consigne** - Implémenter la méthode ```mutate(self, individuals)``` de la classe **AlgoGen**.
---
### **Test**
Maintenant que les classes sont implémentées, il nous reste plus qu'à tester notre algorithme génétique :
```python
# on importe le module de l'algorithme génétique ainsi que la classe pour définir un problème
from algo_gen import *
from mastermind import *
if __name__=="__main__":
# création du problème
p = Mastermind(["bleu", "blanc", "blanc", "rouge"])
# création d'algogen
a = AlgoGen(p, 12, 0.1)
# on lance la résolution au problème
a.solve(15)
```
**Consigne** - Tester le programme.

16
Projets/src/test.py Normal file
View File

@@ -0,0 +1,16 @@
from algo_gen import *
from problem_4 import *
if __name__=="__main__":
p = Problem_4([
['Lille', 15, 15],
['Tourcoing', 10, 20],
['Paris', 5, 20],
['Alsace', 5, 30],
['Londre', 30, 15],
['Strasburg', 30, 30],
['Moskou', 5, 5],
['San Francisco', 10, 15]
])
a = AlgoGen(p, 1000, 0, 0.1)
a.solve(20)

119
Projets/src/ville.py Normal file
View File

@@ -0,0 +1,119 @@
from individual_interface import *
from random import *
from math import *
def distance(ville_1, ville_2) :
return sqrt((ville_1[1]-ville_2[1])**2 + (ville_1[2]-ville_2[2])**2)
class Ville(Individual_Interface):
def __init__(self, villes):
'''
create an Individual object, its genome value is randomly built. Initially score is not set.
:param size: size of the genome
:type size: int
'''
self.__villes = villes.copy()
super().__init__(len(villes))
def copy(self):
'''
build a copy of self, the genome is a copy of self's genome
:return: a new Individual which is a "clone" of self
:rtype: an Individual object
'''
copy = Ville(self.__size)
copy.set_value(self.get_value())
return copy
def init_value(self):
'''
randomly initialize the genome value of self
'''
alea = self.__villes
shuffle(alea)
return alea
def cross_with(self, other):
'''
perform a 1 point crossover between self and other, two new built individuals are returned
:param other: the individual to croww with
:type other: an Idnividual object
:return: the two new Individuals built by 1 point crossover operation
:rtype: 2-uple of individuals
'''
value = self.get_value()
other_value = other.get_value()
pivot = randint (0, self.get_size())
test_1, test_2 = Ville(self.__villes), Ville(self.__villes)
test_1.set_value(value[:pivot] + other_value[pivot:])
test_2.set_value(other_value[:pivot] + value[pivot:])
test_1.eliminer_doublons()
test_2.eliminer_doublons()
return (test_1, test_2)
def mutate(self, probability):
'''
apply mutation operation to self : each element of the geome sequence is randomly changed with given probabiliy
side effect : self's genome is modified
:param probability: the probability of mutation for every gene
:type probability: float
:UC: probability in [0,1[
'''
for gene in range(len(self.get_value())):
proba = uniform(0, 1)
if proba < probability :
self.mutate_gene(gene)
def mutate_gene(self, gene):
temp = self.get_value()
self.set_value(temp[:gene] + [choice(self.__villes)] + temp [gene+1:])
self.eliminer_doublons()
def eliminer_doublons(self):
dico = {}
redondantes = self.get_value()
liste_finale = []
for ville in redondantes :
if ville[0] not in dico:
liste_finale.append(ville)
dico[ville[0]] = ville
for el in self.__villes:
if el[0] not in dico:
liste_finale.append(el)
self.set_value(liste_finale)
def distance_totale(self):
i = 0
total = 0
while i < self.get_size()-1 :
total = total + distance(self.get_value()[i],self.get_value()[i+1])
i+=1
total = total + distance(self.get_value()[0],self.get_value()[-1])
return total

82
Projets/wator/JALONS.md Normal file
View File

@@ -0,0 +1,82 @@
# Jalons — Wa-Tor
> Progression suggérée sur ~10 séances (septembre → février)
---
## Phase 1 — Modélisation (Séances 1-2)
**Objectif :** Comprendre le système proie-prédateur et choisir les structures de données.
- Lire le `README.md` et les règles du Wa-Tor
- Identifier les entités : grille, thon, requin — quels attributs ont-ils ?
- Choisir une représentation de la grille : liste 2D, dictionnaire `{(i,j): animal}` ?
- Écrire `creer_grille(n, m, nb_thons, nb_requins)` → grille initialisée aléatoirement
- Écrire `afficher(grille)` → affichage terminal (ex. `T` thon, `R` requin, `.` vide)
**Variables globales à définir :**
- `GESTATION_THON` : nombre d'étapes avant reproduction
- `GESTATION_REQUIN` : nombre d'étapes avant reproduction
- `ENERGIE_REQUIN` : énergie initiale d'un requin (perd 1 par étape, +1 en mangeant)
---
## Phase 2 — Les thons (Séances 3-4)
**Objectif :** Implémenter le comportement des proies.
- Écrire `voisins_libres(grille, i, j)` → liste des cases vides adjacentes (N, S, E, O)
- Écrire `deplacer_thon(grille, i, j)` → le thon se déplace vers une case libre aléatoire
- Gérer la gestation : si `age >= GESTATION_THON`, un thon se reproduit en laissant un jeune sur la case quittée
- Tester sur une grille avec uniquement des thons : se déplacent-ils ? Se reproduisent-ils ?
---
## Phase 3 — Les requins (Séances 5-6)
**Objectif :** Implémenter les prédateurs.
- Écrire `voisins_thons(grille, i, j)` → liste des cases adjacentes contenant un thon
- Écrire `deplacer_requin(grille, i, j)` :
- Si un thon est adjacent → le manger (gain d'énergie, déplacement)
- Sinon → se déplacer vers une case libre
- Gérer la gestation (reproduction comme le thon)
- Si énergie ≤ 0 → le requin meurt
- Tester sur une grille mixte
---
## Phase 4 — Simulation complète (Séances 7-8)
**Objectif :** Assembler et observer.
- Écrire `etape(grille)` → applique une étape à tous les animaux (attention à l'ordre !)
- Écrire `simuler(grille, nb_etapes)` → boucle principale avec affichage
- Observer les dynamiques : les populations oscillent-elles ? L'une disparaît-elle ?
- Faire varier les paramètres et noter les effets
**Point d'attention :** chaque animal ne doit agir qu'une seule fois par étape — utiliser un marqueur "déjà déplacé" pour éviter qu'un animal soit traité deux fois.
---
## Phase 5 — Visualisation et finitions (Séances 9-10)
**Objectif :** Affichage graphique et préparation du Grand Oral.
- Ajouter un affichage graphique (Pyxel, matplotlib, tkinter)
- Tracer l'évolution des populations (courbe thons / requins par étape)
- Nettoyer le code, ajouter des docstrings
- Compléter le `SUIVI.md` et préparer les questions pour le Grand Oral
---
## Idées d'extension avancée
- **Cycles de Lotka-Volterra** : comparer les courbes simulées aux équations différentielles théoriques
- **Grille torique** : les bords se rejoignent (comme dans le TP d'origine)
- **Espèce supplémentaire** : ajouter un super-prédateur
- **Paramétrage dynamique** : interface pour modifier les paramètres en cours de simulation
---
Auteur : Florian Mathieu — Licence CC BY NC

72
Projets/wator/SUIVI.md Normal file
View File

@@ -0,0 +1,72 @@
# Suivi de projet — [Nom du projet]
**Élève(s) :**
**Classe :**
**Projet :**
**Date de début :**
---
## Journal de bord
*Compléter après chaque séance.*
### Séance 1 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
### Séance 2 — [Date]
**Objectif de la séance :**
**Ce que j'ai fait :**
**Difficultés rencontrées :**
**Décisions prises et pourquoi :**
**Ce qu'il reste à faire :**
---
*(Dupliquer le bloc ci-dessus pour chaque séance)*
---
## Bilan du projet
### Ce qui fonctionne
### Ce qui ne fonctionne pas encore (et pourquoi)
### Choix techniques importants
*Quelles décisions as-tu prises ? Pourquoi ce choix plutôt qu'un autre ?*
### Ce que j'ai appris
---
## Préparation au Grand Oral
*Le Grand Oral dure 20 minutes. Tu dois pouvoir présenter ton projet et répondre à des questions. Prépare-toi en répondant à ces questions :*
**1. En quoi consiste ton projet ? Explique-le en 2 minutes à quelqu'un qui ne connaît pas l'informatique.**
**2. Quelles notions du cours as-tu utilisées ? Donne un exemple concret dans ton code.**
**3. Quelle a été la difficulté principale ? Comment l'as-tu résolue ?**
**4. Qu'aurais-tu fait différemment si tu recommençais ?**
**5. Quelle extension aimerais-tu implémenter si tu avais plus de temps ?**