This commit is contained in:
Joachim 2025-04-20 14:25:55 +02:00
parent fe21759e69
commit b4ac0db0f6
29 changed files with 724 additions and 0 deletions

BIN
lib/3uBkb7p-666847106.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 KiB

74
lib/maze.py Executable file
View file

@ -0,0 +1,74 @@
from random import shuffle, randint, choice
from lib.pile import *
def creation(x: int, y: int) -> list:
"""
Renvoie une liste de listes qui servent de création au labyrinthe.
In x, y: int
Out tab: list
"""
if x % 2 == 0:
x += 1
if y % 2 == 0:
y += 1
tab = [
["?" if i % 2 != 0 and j % 2 != 0 else "#" for j in range(y)] for i in range(x)
]
entrance_exit = choice(["horizontal", "vertical"])
if entrance_exit == "horizontal":
entry_point = (2 * randint(1, x // 2) - 1, 0)
exit_point = (2 * randint(1, x // 2) - 1, y - 1)
else:
entry_point = (0, 2 * randint(1, y // 2 - 1) - 1)
exit_point = (x - 1, 2 * randint(1, y // 2 - 1) - 1)
tab[entry_point[0]][entry_point[1]] = ">"
tab[exit_point[0]][exit_point[1]] = "O"
return tab
def gen_maze(sizex: int, sizey: int, lab=None, cells=None, x=None, y=None) -> list:
"""
Cree un labyrinthe avec un algorithme de recherche en profondeur (depth-first search).
Entree x, y, lab cells: int, int, list, Pile
Sortie lab: list
"""
if cells is None:
lab = creation(sizey, sizex)
valid_positions = [
(x, y)
for y in range(1, sizey - 2)
for x in range(1, sizex - 2)
if lab[y][x] != "#"
]
pos = randint(0, len(valid_positions) // 2 - 1) * 2
x, y = valid_positions[pos]
cells = Pile()
lab[y][x] = " "
cells.empiler((x, y))
while len(cells) > 0:
suiv = []
for coo in [(x - 2, y), (x + 2, y), (x, y - 2), (x, y + 2)]:
if 0 < coo[0] and 0 <= coo[1]:
if coo[0] < sizex and coo[1] < sizey:
if lab[coo[1]][coo[0]] == "?":
suiv.append(coo)
if suiv != []:
shuffle(suiv)
diff = ((suiv[0][0] - x) // 2, (suiv[0][1] - y) // 2)
x, y = suiv[0][0], suiv[0][1]
lab[y - diff[1]][x - diff[0]] = " "
lab[y][x] = " "
cells.empiler((x, y))
else:
coo = cells.depiler()
x, y = coo[0], coo[1]
return lab

70
lib/pile.py Executable file
View file

@ -0,0 +1,70 @@
class Cellule:
def __init__(self, x):
"""Constructeur de la classe Cellule :
valeur contient la valeur stockee dans la cellule.
suivant contient le pointeur vers la cellule suivante.
suivant est toujours initialise a None."""
self.valeur = x
self.suivant = None
def __del__(self):
"""Destructeur de la classe Cellule :
Supprime la valeur, le pointeur suivant et la cellule elle-meme."""
del self.valeur
del self.suivant
del self
class Pile:
def __init__(self):
"""Constructeur de la classe Pile :
sommet contient le pointeur vers la cellule suivante.
sommet est toujours initialise a None."""
self.sommet = None
self.__taille = 0
def est_vide(self):
"""Renvoie True si la pile est vide, False sinon.
Entree : aucune.
Sortie : booleen."""
return self.__taille == 0
def __len__(self):
"""Renvoie la taille de la pile.
Entree : aucune.
Sortie : entier positif."""
return self.__taille
def empiler(self, x):
"""Ajoute x en tete de pile.
On cree une instance de cellule avec la valeur x
et suivant pointe vers la tete.
Entree : x est une valeur possible.
Sortie : aucune."""
self.__taille += 1 # la taille augmente de 1
if self.est_vide():
self.sommet = Cellule(x)
else:
nouveau_sommet = Cellule(x)
nouveau_sommet.suivant = (
self.sommet
) # on relie la Cellule a la suite de la Pile
self.sommet = (
nouveau_sommet # le sommet de la pile devient la nouvelle Cellule
)
return None
def depiler(self):
"""Supprimer la Cellule de sommet et renvoie sa valeur.
Entree : aucune.
Sortie : aucune."""
if self.est_vide():
raise BrokenPipeError("La Pile est vide !")
cellule_a_depiler = self.sommet
self.sommet = (
cellule_a_depiler.suivant
) # on court-circuite la Cellule de sommet
retour = cellule_a_depiler.valeur # on recupere sa valeur
del cellule_a_depiler # on la supprime
self.__taille -= 1 # on met a jour la taille de la pile
return retour

235
lib/resolv.py Executable file
View file

@ -0,0 +1,235 @@
from lib.maze import gen_maze
from lib.pile import Pile
from random import shuffle
from tkinter import Canvas, Tk, Button
import tkinter.messagebox
from time import time
def find_maze_start(maze: list) -> tuple:
"""
Find the coo of the maze's entrance.
In maze: list
Out: tuple
"""
maze_end, mazex = len(maze), len(maze[0])
for i in range(maze_end):
for l in range(mazex):
if maze[i][l] == ">":
return (l, i)
# si le labyrinthe n'a pas d'entree
tkinter.messagebox.showinfo(
title="Amaze!", message="Il y a des erreurs dans le labyrinthe!"
)
raise Exception("Il y a des erreurs dans le labyrinthe!")
return (-1, -1)
def find_available_moves(
curr_x: int, curr_y: int, sizex: int, sizey: int, maze: list
) -> list:
"""
renvoit les coordonnees possible pour certain x et y
In curr_x, curry, sizex, sizey, maze: int, int, int, int, list
Out: list
"""
moves = [
(curr_x - 1, curr_y),
(curr_x + 1, curr_y),
(curr_x, curr_y - 1),
(curr_x, curr_y + 1),
]
return [
(x, y)
for x, y in moves
if 0 < x < sizex and 0 < y < sizey and maze[y][x] in [" ", "O"]
]
def rectangle(
x: int, y: int, taille_cells: int, couleur: str, canvas: Canvas, *, cailloux=None
) -> Pile:
"""
cree un carre au coordonnees x et y
In x, y, taille_cells, couleur, canvas, cailloux = int, int, int, str, Canvas(), Pile()
Out cailloux: Pile()
"""
if cailloux == None:
canvas.create_rectangle(
(x * taille_cells) - taille_cells / 2,
(y * taille_cells) - taille_cells / 2,
((x + 1) * taille_cells) - taille_cells / 2,
((y + 1) * taille_cells) - taille_cells / 2,
width=0,
fill=couleur,
tags=("path",),
)
else:
cailloux.empiler(
canvas.create_rectangle(
(x * taille_cells) - taille_cells / 2,
(y * taille_cells) - taille_cells / 2,
((x + 1) * taille_cells) - taille_cells / 2,
((y + 1) * taille_cells) - taille_cells / 2,
width=0,
fill=couleur,
tags=("path",),
)
)
return cailloux
def win(
x: int, y: int, taille_cells: int, start: int, canvas: Canvas, button: Button
) -> None:
"""
gere la fin du programme, quand la tete trouve la fin du labyrinthe.
In x, y, taille_cells, start, canvas, button: int, int, int, int, Canva(), Button()
Out: None
"""
# reactive le bouton de resolution
button["state"] = "active"
# affiche un carre vert sur la fin du labyrinthe
rectangle(x, y, taille_cells, "green", canvas)
# calcule le temps
end = time()
seconds = round(end) - round(start)
heures, minutes = 0, 0
while seconds > 59:
minutes += 1
seconds -= 60
if minutes > 59:
heures += 1
minutes -= 60
if minutes > 0:
m = f"{minutes}min "
else:
m = ""
if heures > 0:
h = f"{heures}h "
else:
h = ""
# affiche le temps
tkinter.messagebox.showinfo(
title="Amaze!", message=f"Labyrinthe résolu en {h}{m}{seconds}s!"
)
return None
def delete_old_path(canvas: Canvas) -> None:
"""
reset l'ancien chemin.
In: canvas: Canvas()
Out: None
"""
items = canvas.find_all()
for item_id in items:
tags = canvas.gettags(item_id)
if "path" in tags:
canvas.delete(item_id)
return None
def resolve(
maze: list,
canvas: Canvas,
root: Tk,
taille_cells: int,
*,
button=None,
poucet=None,
x=None,
y=None,
cailloux=None,
start=None,
) -> None:
"""
Resout le labyrinthe, compte le temps de resolution et grise le bouton de resolution
In :
maze (list)
canvas (Canvas())
root (Tk())
taille_cells(int)
button (Button())
poucet (Pile())
x (int)
y (int)
cailloux (Pile())
start (int)
Out :
None
"""
if poucet != None and poucet.est_vide():
try:
find_available_moves(x, y, maze) == []
except:
button["state"] = "active"
tkinter.messagebox.showinfo(
title="Amaze!", message=f"Labyrinthe impossible!"
)
del poucet, cailloux, x, y, start, maze
return None
if poucet == None: # initialisation
delete_old_path(canvas)
button["state"] = "disabled"
start = time()
x, y = find_maze_start(maze)
rectangle(x, y, taille_cells, "black", canvas)
sizex, sizey = len(maze[0]), len(maze)
poucet = Pile()
cailloux = Pile()
sizex, sizey = len(maze[0]), len(maze)
suiv = find_available_moves(x, y, sizex, sizey, maze)
if suiv != []: # si on peut avancer
shuffle(suiv)
x, y = suiv[0][0], suiv[0][1]
if maze[y][x] == "O":
win(x, y, taille_cells, start, canvas, button)
del poucet, cailloux, x, y, start, maze
return None
maze[y][x] = "V"
poucet.empiler((x, y))
cailloux = rectangle(x, y, taille_cells, "red", canvas, cailloux=cailloux)
else: # sinon on recule
coo = poucet.depiler()
x, y = coo[0], coo[1]
next_suiv = find_available_moves(x, y, sizex, sizey, maze)
for x2, y2 in next_suiv:
if maze[y][x] == "O":
win(x, y, taille_cells, end, canvas, button)
del poucet, cailloux, x, y, start, maze
return None
if next_suiv == []:
edit = cailloux.depiler()
canvas.itemconfig(edit, fill="#a6a6a6")
else:
poucet.empiler((x, y))
root.after(
5,
lambda: resolve(
maze,
canvas,
root,
taille_cells,
poucet=poucet,
x=x,
y=y,
cailloux=cailloux,
start=start,
button=button,
),
)

38
lib/txttobinary.py Executable file
View file

@ -0,0 +1,38 @@
def btt(dico_binaire, chaine_bin):
"""
Bin To Text
In dico_binaire, chaine_bin: dict, str
Out chaine_decomp: str
"""
chaine_decomp, current_code = "", ""
dico_inv = {valeur: clef for clef, valeur in dico_binaire.items()}
for bit in chaine_bin:
current_code += bit
try:
chaine_decomp += dico_inv[current_code]
current_code = ""
except KeyError:
pass
return chaine_decomp
def ttb(dico_bin, text_in):
"""
Text To Bin
In dico_bin, text_in : dict, str
out: text_bin: str
"""
text_bin_list = [dico_bin[elem] for elem in text_in if elem in dico_bin]
text_bin = "".join(text_bin_list)
return text_bin
def compression(maze):
"""
Convertit un labyrinthe str en un array de bits.
In maze: str
Out: bits array
"""
dico_decomp = {" ": "000", ">": "100", "O": "101", ":": "011", "#": "001"}
binmaze = ttb(dico_decomp, maze)
return bytes(int(binmaze[i : i + 3], 2) for i in range(0, len(binmaze), 3))