Update and some portion of code
This commit is contained in:
parent
f4f272c625
commit
f411dab924
38
main.py
38
main.py
@ -110,7 +110,7 @@ def mainmenu_option4(students_list):
|
||||
# On récupère l'élève associé à l'ID
|
||||
student = students_list[student_id]
|
||||
# On demande le siège où l'asseoir
|
||||
student_seat = interface.console_ask_student_seat()
|
||||
student_seat = interface.console_ask_student_seat(seatingplan)
|
||||
# Si on a pu récupérer une valeur
|
||||
if seatingplan.is_a_seat(student_seat):
|
||||
# On positionne l'élève
|
||||
@ -187,8 +187,8 @@ def mainmenu_option7(students_list):
|
||||
Plan de classe possible.
|
||||
:rtype: iterator
|
||||
"""
|
||||
number_of_proposals = input('Nombre maximal de propositions à faire : ')
|
||||
number_of_proposals = int(number_of_proposals)
|
||||
number_of_proposals = input('Nombre maximal de propositions à faire (5 par défaut) : ')
|
||||
number_of_proposals = int(number_of_proposals or "5")
|
||||
print('Presser Ctrl-C pour interrompre...')
|
||||
try:
|
||||
return iter(list(islice(engine.solve(seatingplan, students_list),
|
||||
@ -208,7 +208,8 @@ def mainmenu_option8():
|
||||
:return: None
|
||||
"""
|
||||
print('Effacement du plan de classe...')
|
||||
engine.flush_seatingplan(seatingplan)
|
||||
# engine.flush_seatingplan(seatingplan)
|
||||
seatingplan.flush()
|
||||
|
||||
|
||||
def mainmenu_option_next(solution):
|
||||
@ -225,14 +226,27 @@ def mainmenu_option_next(solution):
|
||||
"""
|
||||
if solution:
|
||||
try:
|
||||
engine.flush_seatingplan(seatingplan)
|
||||
engine.write_solution_to_seatingplan(next(solution), seatingplan)
|
||||
# engine.flush_seatingplan(seatingplan)
|
||||
seatingplan.flush()
|
||||
# engine.write_solution_to_seatingplan(next(solution), seatingplan)
|
||||
seatingplan.write_solution(next(solution))
|
||||
except StopIteration:
|
||||
print("Il n'y a plus de propositions de placement")
|
||||
else:
|
||||
print("Vous devez le calcul de solutions d'abord")
|
||||
|
||||
|
||||
def mainmenu_option9():
|
||||
"""Option 9 du menu principal.
|
||||
|
||||
|
||||
|
||||
:return: None
|
||||
"""
|
||||
print('Teste plan de classe...')
|
||||
print(engine.verify_solution(seatingplan))
|
||||
|
||||
|
||||
def settingsmenu():
|
||||
""" Menu paramètres.
|
||||
|
||||
@ -286,10 +300,15 @@ def mainmenu():
|
||||
|
||||
elif (command == '7'):
|
||||
proposals = mainmenu_option7(students_list)
|
||||
# On affiche la première solution
|
||||
mainmenu_option_next(proposals)
|
||||
|
||||
elif (command == '8'):
|
||||
mainmenu_option8()
|
||||
|
||||
elif (command == '9'):
|
||||
mainmenu_option9()
|
||||
|
||||
elif (command == 'n'):
|
||||
mainmenu_option_next(proposals)
|
||||
|
||||
@ -300,8 +319,11 @@ def mainmenu():
|
||||
return s
|
||||
|
||||
# DEBUG: print solutions
|
||||
for s in engine.solve(seatingplan, students_list):
|
||||
print({p: f(s) for p, s in s.items()})
|
||||
try:
|
||||
for s in engine.solve(seatingplan, students_list):
|
||||
print({p: f(s) for p, s in s.items()})
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
# DEBUG
|
||||
|
||||
elif (command == 's'):
|
||||
|
217
sage/engine.py
217
sage/engine.py
@ -46,72 +46,6 @@ class Engine:
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_4 = 2
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_5 = 2
|
||||
|
||||
def get_neighbourhood(self, seatingplan, seat, radius):
|
||||
"""Dans un plan de classe, retourne le voisinage dans un rayon donné
|
||||
d'une place.
|
||||
|
||||
:param seatingplan:
|
||||
Plan de classe.
|
||||
:type seatingplan: SeatingPlan
|
||||
:param seat:
|
||||
Coordonnées de la place dans le plan de classe.
|
||||
:type seat: tuple
|
||||
:param radius:
|
||||
Rayon du voisinage.
|
||||
:type radius: int
|
||||
|
||||
:return:
|
||||
Liste des places voisines.
|
||||
:rtype: list
|
||||
"""
|
||||
# La liste des voisins
|
||||
neighbourhood = []
|
||||
# 'position' est un tuple de coordonnées (i, j) où:
|
||||
# - i compris entre 0 et seatingplan.row-1
|
||||
# - j compris entre 0 et seatingplan.col-1
|
||||
#
|
||||
# Les voisins sont les élèves aux places de coordonnées situées
|
||||
# dans le carré passant par les points suivants :
|
||||
# (i, j-radius), (i, j+radius), (i-radius, j), (i+radius, j),
|
||||
# (i-radius, j-radius), (i-radius, j+radius),
|
||||
# (i+radius, j-radius) et (i+radius, j+radius)
|
||||
# On analyse ce voisinage et on n'ajoute un voisin que s'il y en a un
|
||||
for i in range(-radius, radius+1, 1):
|
||||
for j in range(-radius, radius+1, 1):
|
||||
# Traitons la place voisine
|
||||
neighbour_seat = (seat[0]+i, seat[1]+j)
|
||||
# print("Place: ", neighbour_seat) # DEBUG #
|
||||
# Cette place est-elle ?
|
||||
# - différente de la place autour de laquelle on regarde
|
||||
# - bien comprise dans le plan de classe (coordonnées de
|
||||
# dépassant pas celles du plan de classe)
|
||||
if (neighbour_seat != seat and
|
||||
0 <= neighbour_seat[0] <= seatingplan.row-1 and
|
||||
0 <= neighbour_seat[1] <= seatingplan.col-1):
|
||||
neighbourhood.append(neighbour_seat)
|
||||
# On retourne notre liste de places voisines
|
||||
return neighbourhood
|
||||
|
||||
def are_safe_neighbours(self, student, neighbour, delta_chat_lvl):
|
||||
"""Indique si deux élèves constituent de bons voisins.
|
||||
C'est-à-dire si la différence de leur coefficient de bavardage
|
||||
est d'une certaine valeur.
|
||||
|
||||
:param student:
|
||||
Élève.
|
||||
:type student: Student
|
||||
:param neighbour:
|
||||
Élève.
|
||||
:type neighbour: Student
|
||||
:param delta_chat_lvl:
|
||||
Différence entre les coefficients de bavardage.
|
||||
:type delta_chat_lvl: int
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return bool(abs(student.chat_lvl - neighbour.chat_lvl)
|
||||
>= delta_chat_lvl)
|
||||
|
||||
def respect_constraints(self, seat, student, seatingplan, solution):
|
||||
"""Indique si une place associée à une élève satisfait aux
|
||||
contraintes, à savoir si :
|
||||
@ -155,9 +89,8 @@ class Engine:
|
||||
else:
|
||||
# Si non, veŕifions le voisinage de la place
|
||||
# Récupérons le voisinage de la place en cours
|
||||
neighbourhood = self.get_neighbourhood(seatingplan,
|
||||
seat,
|
||||
self.NEIGHBOURHOOD_RADIUS)
|
||||
neighbourhood = seatingplan.get_seat_neighbourhood(seat,
|
||||
self.NEIGHBOURHOOD_RADIUS)
|
||||
|
||||
for neighbour_seat in neighbourhood:
|
||||
# On ne teste les contraintes que sur les sièges déjà occupés
|
||||
@ -174,34 +107,83 @@ class Engine:
|
||||
# Les contraintes portent sur la différence de
|
||||
# coefficient de bavardage entre deux voisins:
|
||||
if max_chat_lvl == 5:
|
||||
result = result and self.are_safe_neighbours(
|
||||
student,
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_5)
|
||||
|
||||
elif max_chat_lvl == 4:
|
||||
result = result and self.are_safe_neighbours(
|
||||
student,
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_4)
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_4)
|
||||
|
||||
elif max_chat_lvl == 3:
|
||||
result = result and self.are_safe_neighbours(
|
||||
student,
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_3)
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_3)
|
||||
|
||||
elif max_chat_lvl == 2:
|
||||
result = result and self.are_safe_neighbours(
|
||||
student,
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_2)
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_2)
|
||||
|
||||
elif max_chat_lvl == 1:
|
||||
result = result and self.are_safe_neighbours(
|
||||
student,
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_1)
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_1)
|
||||
|
||||
else:
|
||||
result = result and True
|
||||
# Le siège voisin n'est pas occupé, il est donc valide
|
||||
result = result and True
|
||||
# retournons la validité de l'association
|
||||
return result
|
||||
|
||||
def verify_solution(self, seatingplan):
|
||||
solution = seatingplan.mapping
|
||||
result = True
|
||||
for seat in list(solution.keys()):
|
||||
student = seatingplan.get_student(seat)
|
||||
neighbourhood = seatingplan.get_seat_neighbourhood(seat,
|
||||
self.NEIGHBOURHOOD_RADIUS)
|
||||
|
||||
for neighbour_seat in neighbourhood:
|
||||
# On ne teste les contraintes que sur les sièges déjà occupés
|
||||
# donc sur ceux faisant déjà partie des solutions
|
||||
if neighbour_seat in list(solution.keys()):
|
||||
# On récupère l'élève du siège voisin
|
||||
neighbour = solution[neighbour_seat]
|
||||
|
||||
# Lequel de l'élève en cours ou du voisin
|
||||
# est le plus bavard ?
|
||||
max_chat_lvl = max(student.chat_lvl,
|
||||
neighbour.chat_lvl)
|
||||
|
||||
# Les contraintes portent sur la différence de
|
||||
# coefficient de bavardage entre deux voisins:
|
||||
if max_chat_lvl == 5:
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_5)
|
||||
|
||||
elif max_chat_lvl == 4:
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_4)
|
||||
|
||||
elif max_chat_lvl == 3:
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_3)
|
||||
|
||||
elif max_chat_lvl == 2:
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_2)
|
||||
|
||||
elif max_chat_lvl == 1:
|
||||
result = result and student.has_safe_neighbour(
|
||||
neighbour,
|
||||
self.DELTA_FOR_MAX_CHAT_LVL_1)
|
||||
|
||||
else:
|
||||
result = result and True
|
||||
@ -320,64 +302,3 @@ class Engine:
|
||||
elif idx_student == 0:
|
||||
end = True
|
||||
|
||||
def write_solution_to_seatingplan(self, solution, seatingplan):
|
||||
"""Place les élèves de la solution dans un plan de classe.
|
||||
|
||||
:param solution:
|
||||
Solution de positionnement des élèves dans le plan de classe.
|
||||
:type solution: dict
|
||||
:param seatingplan:
|
||||
Plan de classe.
|
||||
:type seatingplan: SeatingPlan
|
||||
|
||||
:return: None
|
||||
"""
|
||||
for place, student in solution.items():
|
||||
if student is not None:
|
||||
seatingplan.place_student(student, place)
|
||||
else:
|
||||
seatingplan.mapping[place] = None
|
||||
|
||||
def flush_seatingplan(self, seatingplan):
|
||||
"""Vide le plan de classe. Supprime tous les élèves placés.
|
||||
|
||||
:param seatingplan:
|
||||
Plan de classe.
|
||||
:type seatingplan: SeatingPlan
|
||||
|
||||
:return: None
|
||||
"""
|
||||
for seat in list(seatingplan.mapping.keys()):
|
||||
student = seatingplan.get_student(seat)
|
||||
if student is not None:
|
||||
seatingplan.remove_student(student)
|
||||
|
||||
def get_corners(self, seatingplan):
|
||||
""" Renvoie, pour un plan de classe, la liste des places aux coins
|
||||
ou extrémités.
|
||||
C'est-à-dire une place située en : (0,0), (0,m-1),
|
||||
(n-1,0) ou (n-1,m-1) pour un plan de classe de dimensions n x m.
|
||||
|
||||
:param seatingplan:
|
||||
Plan de classe.
|
||||
:type seatingplan: SeatingPlan
|
||||
|
||||
:return:
|
||||
Liste des places aux coins/extrémités.
|
||||
:rtype: list
|
||||
"""
|
||||
# Dimensions du plan de classe
|
||||
n = seatingplan.raw
|
||||
m = seatingplan.col
|
||||
|
||||
# Le plan de classe est un tableau de dimensions n x m :
|
||||
# - si n>1 et m>1, il possède 4 coins
|
||||
# - si n=1 et m>1, il en possède 2
|
||||
# - si n>1 et m=1, il en possède 2
|
||||
# - si n=1 et m=1, il en possède 1
|
||||
#
|
||||
# On utilise donc un set qui va permettre d'éliminer les doublons de
|
||||
# coordonnées
|
||||
# Ce set est ensuite "traduit" en liste
|
||||
corners = list({(0, 0), (n-1, 0), (0, m-1), (n-1, m-1)})
|
||||
return corners
|
||||
|
@ -384,7 +384,7 @@ def console_ask_student_id():
|
||||
#
|
||||
# Option 4
|
||||
#
|
||||
def console_ask_student_seat():
|
||||
def console_ask_student_seat(seatingplan):
|
||||
"""Demande à l'utilisateur les coordonnées d'une place
|
||||
dans le plan de classe.
|
||||
|
||||
@ -401,10 +401,9 @@ def console_ask_student_seat():
|
||||
# sinon, on intercepte une exception
|
||||
student_row = int(student_row)
|
||||
# Si la valeur est entière mais négative
|
||||
if student_row <= 0:
|
||||
if student_row < 1 or student_row > seatingplan.row:
|
||||
# On lève une exception
|
||||
raise ValueError('Le numéro de rangée doit être strictement '
|
||||
'positif')
|
||||
raise ValueError('Mauvais range')
|
||||
|
||||
# Tout va bien, on passe au numéro de colonne
|
||||
student_col = input('\nVeuillez entrer le numéro de colonne où vous'
|
||||
@ -415,10 +414,10 @@ def console_ask_student_seat():
|
||||
# sinon, on intercepte une exception
|
||||
student_col = int(student_col)
|
||||
# Si la valeur est entière mais négative
|
||||
if student_col <= 0:
|
||||
if student_row < 1 or student_col > seatingplan.col:
|
||||
# On lève une exception
|
||||
raise ValueError('Le numéro de colonne doit être strictement '
|
||||
'positif')
|
||||
raise ValueError('Mauvais range')
|
||||
|
||||
except ValueError:
|
||||
print('La valeur saisie pour le numéro de colonne est invalide')
|
||||
student_col = None
|
||||
@ -429,7 +428,7 @@ def console_ask_student_seat():
|
||||
|
||||
# On renvoit les valeurs entrées mais ajustées à l'indexation
|
||||
# du plan de classe
|
||||
return student_row, student_col
|
||||
return student_row-1, student_col-1
|
||||
|
||||
|
||||
#
|
||||
|
@ -57,6 +57,36 @@ class SeatingPlan:
|
||||
self.mapping = {(i, j): None for i in range(row)
|
||||
for j in range(col)}
|
||||
|
||||
def get_corners(self):
|
||||
""" Renvoie, pour un plan de classe, la liste des places aux coins
|
||||
ou extrémités.
|
||||
C'est-à-dire une place située en : (0,0), (0,m-1),
|
||||
(n-1,0) ou (n-1,m-1) pour un plan de classe de dimensions n x m.
|
||||
|
||||
:param seatingplan:
|
||||
Plan de classe.
|
||||
:type seatingplan: SeatingPlan
|
||||
|
||||
:return:
|
||||
Liste des places aux coins/extrémités.
|
||||
:rtype: list
|
||||
"""
|
||||
# Dimensions du plan de classe
|
||||
n = self.raw
|
||||
m = self.col
|
||||
|
||||
# Le plan de classe est un tableau de dimensions n x m :
|
||||
# - si n>1 et m>1, il possède 4 coins
|
||||
# - si n=1 et m>1, il en possède 2
|
||||
# - si n>1 et m=1, il en possède 2
|
||||
# - si n=1 et m=1, il en possède 1
|
||||
#
|
||||
# On utilise donc un set qui va permettre d'éliminer les doublons de
|
||||
# coordonnées
|
||||
# Ce set est ensuite "traduit" en liste
|
||||
corners = list({(0, 0), (n-1, 0), (0, m-1), (n-1, m-1)})
|
||||
return corners
|
||||
|
||||
def is_a_seat(self, seat):
|
||||
"""Renvoie si une place est valide ou non.
|
||||
|
||||
@ -115,11 +145,59 @@ class SeatingPlan:
|
||||
:rtype: tuple or False
|
||||
"""
|
||||
try:
|
||||
return list(self.mapping.keys())[list(self.mapping.values())
|
||||
return self.get_seats()[list(self.mapping.values())
|
||||
.index(student)]
|
||||
# return list(self.mapping.keys())[list(self.mapping.values())
|
||||
# .index(student)]
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def get_seat_neighbourhood(self, seat, radius):
|
||||
"""Dans un plan de classe, retourne le voisinage dans un rayon donné
|
||||
d'une place.
|
||||
|
||||
:param seat:
|
||||
Coordonnées de la place dans le plan de classe.
|
||||
:type seat: tuple
|
||||
:param radius:
|
||||
Rayon du voisinage.
|
||||
:type radius: int
|
||||
|
||||
:return:
|
||||
Liste des places voisines.
|
||||
:rtype: list
|
||||
"""
|
||||
# La liste des voisins
|
||||
neighbourhood = []
|
||||
# 'position' est un tuple de coordonnées (i, j) où:
|
||||
# - i compris entre 0 et seatingplan.row-1
|
||||
# - j compris entre 0 et seatingplan.col-1
|
||||
#
|
||||
# Les voisins sont les élèves aux places de coordonnées situées
|
||||
# dans le carré passant par les points suivants :
|
||||
# (i, j-radius), (i, j+radius), (i-radius, j), (i+radius, j),
|
||||
# (i-radius, j-radius), (i-radius, j+radius),
|
||||
# (i+radius, j-radius) et (i+radius, j+radius)
|
||||
# On analyse ce voisinage et on n'ajoute un voisin que s'il y en a un
|
||||
for i in range(-radius, radius+1, 1):
|
||||
for j in range(-radius, radius+1, 1):
|
||||
# Traitons la place voisine
|
||||
neighbour_seat = (seat[0]+i, seat[1]+j)
|
||||
# print("Place: ", neighbour_seat) # DEBUG #
|
||||
# Cette place est-elle ?
|
||||
# - différente de la place autour de laquelle on regarde
|
||||
# - bien comprise dans le plan de classe (coordonnées de
|
||||
# dépassant pas celles du plan de classe)
|
||||
if (neighbour_seat != seat and
|
||||
0 <= neighbour_seat[0] <= self.row-1 and
|
||||
0 <= neighbour_seat[1] <= self.col-1):
|
||||
neighbourhood.append(neighbour_seat)
|
||||
# On retourne notre liste de places voisines
|
||||
return neighbourhood
|
||||
|
||||
def get_seats(self):
|
||||
return list(self.mapping.keys())
|
||||
|
||||
def is_empty_seat(self, seat):
|
||||
"""Indique si une place est libre.
|
||||
|
||||
@ -203,3 +281,28 @@ class SeatingPlan:
|
||||
and self.place_student(student_two, seat_one)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def flush(self):
|
||||
"""Vide le plan de classe. Supprime tous les élèves placés.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
for seat in self.get_seats():
|
||||
student = self.get_student(seat)
|
||||
if student is not None:
|
||||
self.remove_student(student)
|
||||
|
||||
def write_solution(self, solution):
|
||||
"""Place les élèves de la solution dans un plan de classe.
|
||||
|
||||
:param solution:
|
||||
Solution de positionnement des élèves dans le plan de classe.
|
||||
:type solution: dict
|
||||
|
||||
:return: None
|
||||
"""
|
||||
for place, student in solution.items():
|
||||
if student is not None:
|
||||
self.place_student(student, place)
|
||||
else:
|
||||
self.mapping[place] = None
|
@ -32,3 +32,20 @@ class Student:
|
||||
self.name = name
|
||||
self.chat_lvl = chat_lvl
|
||||
self.friends = friends
|
||||
|
||||
def has_safe_neighbour(self, neighbour, delta_chat_lvl):
|
||||
"""Indique si deux élèves constituent de bons voisins.
|
||||
C'est-à-dire si la différence de leur coefficient de bavardage
|
||||
est d'une certaine valeur.
|
||||
|
||||
:param neighbour:
|
||||
Élève.
|
||||
:type neighbour: Student
|
||||
:param delta_chat_lvl:
|
||||
Différence entre les coefficients de bavardage.
|
||||
:type delta_chat_lvl: int
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return bool(abs(self.chat_lvl - neighbour.chat_lvl)
|
||||
>= delta_chat_lvl)
|
7
tests/user_table_6.csv
Normal file
7
tests/user_table_6.csv
Normal file
@ -0,0 +1,7 @@
|
||||
"ID";"NAME";"CHAT LEVEL"
|
||||
0;"Jean NEYMAR";1
|
||||
1;"Sam DÉPASSE";0
|
||||
2;"Elsa DORSA";2
|
||||
3;"Sophie STICKÉ";3
|
||||
4;"Terry GOLO";4
|
||||
5;"Robin DIDON";5
|
|
Loading…
Reference in New Issue
Block a user