diff --git a/assets/brain.png b/assets/brain.png new file mode 100755 index 0000000..e950d62 Binary files /dev/null and b/assets/brain.png differ diff --git a/assets/cross.png b/assets/cross.png new file mode 100755 index 0000000..3db817b Binary files /dev/null and b/assets/cross.png differ diff --git a/assets/down_left.png b/assets/down_left.png new file mode 100755 index 0000000..091c7dc Binary files /dev/null and b/assets/down_left.png differ diff --git a/assets/down_right.png b/assets/down_right.png new file mode 100755 index 0000000..65f597b Binary files /dev/null and b/assets/down_right.png differ diff --git a/assets/empty.png b/assets/empty.png new file mode 100755 index 0000000..2d4c3a8 Binary files /dev/null and b/assets/empty.png differ diff --git a/assets/end.png b/assets/end.png new file mode 100755 index 0000000..1b58102 Binary files /dev/null and b/assets/end.png differ diff --git a/assets/end_down.png b/assets/end_down.png new file mode 100755 index 0000000..9a70c17 Binary files /dev/null and b/assets/end_down.png differ diff --git a/assets/end_left.png b/assets/end_left.png new file mode 100755 index 0000000..d465599 Binary files /dev/null and b/assets/end_left.png differ diff --git a/assets/end_right.png b/assets/end_right.png new file mode 100755 index 0000000..945b424 Binary files /dev/null and b/assets/end_right.png differ diff --git a/assets/end_up.png b/assets/end_up.png new file mode 100755 index 0000000..0847c16 Binary files /dev/null and b/assets/end_up.png differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..6e61a94 Binary files /dev/null and b/assets/logo.png differ diff --git a/assets/return.png b/assets/return.png new file mode 100755 index 0000000..0765454 Binary files /dev/null and b/assets/return.png differ diff --git a/assets/save.png b/assets/save.png new file mode 100755 index 0000000..30d15fb Binary files /dev/null and b/assets/save.png differ diff --git a/assets/start.png b/assets/start.png new file mode 100755 index 0000000..890e638 Binary files /dev/null and b/assets/start.png differ diff --git a/assets/straight.png b/assets/straight.png new file mode 100755 index 0000000..95a6a1f Binary files /dev/null and b/assets/straight.png differ diff --git a/assets/straight_down.png b/assets/straight_down.png new file mode 100755 index 0000000..96b2627 Binary files /dev/null and b/assets/straight_down.png differ diff --git a/assets/three_down.png b/assets/three_down.png new file mode 100755 index 0000000..7708657 Binary files /dev/null and b/assets/three_down.png differ diff --git a/assets/three_left.png b/assets/three_left.png new file mode 100755 index 0000000..4709b16 Binary files /dev/null and b/assets/three_left.png differ diff --git a/assets/three_right.png b/assets/three_right.png new file mode 100755 index 0000000..fdb28e2 Binary files /dev/null and b/assets/three_right.png differ diff --git a/assets/three_up.png b/assets/three_up.png new file mode 100755 index 0000000..d1504b0 Binary files /dev/null and b/assets/three_up.png differ diff --git a/assets/up_left.png b/assets/up_left.png new file mode 100755 index 0000000..a0116aa Binary files /dev/null and b/assets/up_left.png differ diff --git a/assets/up_right.png b/assets/up_right.png new file mode 100755 index 0000000..397c809 Binary files /dev/null and b/assets/up_right.png differ diff --git a/example.png b/example.png new file mode 100755 index 0000000..79b10b0 Binary files /dev/null and b/example.png differ diff --git a/lib/3uBkb7p-666847106.png b/lib/3uBkb7p-666847106.png new file mode 100755 index 0000000..3832493 Binary files /dev/null and b/lib/3uBkb7p-666847106.png differ diff --git a/lib/maze.py b/lib/maze.py new file mode 100755 index 0000000..add9fd1 --- /dev/null +++ b/lib/maze.py @@ -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 diff --git a/lib/pile.py b/lib/pile.py new file mode 100755 index 0000000..626a4aa --- /dev/null +++ b/lib/pile.py @@ -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 diff --git a/lib/resolv.py b/lib/resolv.py new file mode 100755 index 0000000..bcf4c65 --- /dev/null +++ b/lib/resolv.py @@ -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, + ), + ) diff --git a/lib/txttobinary.py b/lib/txttobinary.py new file mode 100755 index 0000000..344522e --- /dev/null +++ b/lib/txttobinary.py @@ -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)) diff --git a/main.py b/main.py new file mode 100755 index 0000000..8cb390b --- /dev/null +++ b/main.py @@ -0,0 +1,307 @@ +from lib.txttobinary import btt, compression +from lib.maze import gen_maze +from lib.resolv import resolve +from tkinter import * +import tkinter.messagebox +from PIL import ImageTk, Image +from copy import deepcopy + + +def save(maze: list, bouton: Button) -> None: + """ + Cree un fichier binaire avec les donnees du labyrinthe. + In tab_maze, bouton : list, Button() + Out : None + """ + bouton["state"] = "disable" # evite que l'utilisateur spam le bouton ensuite + + try: # permet de connaitre la derniere sauvegarde + with open("save/savelist.txt", "r") as savelist: + savefilestr = int(savelist.readline().split(";")[-2]) + 1 + except FileNotFoundError: + savefilestr = "0" + + str_maze = ":".join("".join(row) for row in maze) # convertit le labyrinthe en str + bin_maze = compression(str_maze) # convert str_maze en un array de 0 et 1 + + with open(f"save/save{savefilestr}.bin", "wb") as binfile: + binfile.write(bin_maze) + + with open("save/savelist.txt", "a") as savelist: + savelist.write(f"{savefilestr};") # ajoute le numero de la sauvegarde + + tkinter.messagebox.showinfo( + title="Amaze!", + message=f"Labyrinthe sauvegardé!\n (~/save/save{savefilestr}.bin)", + ) + return None + + +def resize_image(image_path: str, width: int, height: int) -> ImageTk.PhotoImage: + """ + Resize and return a image with a given path to a given size. + In image_path, width, height: str, int, int + Out :PhotoImage() + """ + original_image = Image.open(image_path) + # resampling_mode = Image.Resampling.NEAREST if width > 50 else Image.Resampling.BICUBIC # pas possible avec windows + # resized_image = original_image.resize((width, height), resample=resampling_mode) + resized_image = original_image.resize((width, height)) + return ImageTk.PhotoImage(resized_image) + + +def display(tab: list, activ_save=False) -> None: + """ + Display the maze with sweet sweet handmade tiles. + In tab: list + Out : None + """ + root = Tk() + root.title("Amaze!") + root.configure(bg="white") + root.resizable(False, False) + root.iconphoto(False, PhotoImage(file="assets/logo.png")) + + # definit la taille d'une cellule + screen_height = root.winfo_screenheight() - 100 + tcellx = round(screen_height / len(tab)) + tcelly = round(screen_height / len(tab[0])) + if tcellx > tcelly: + tcell = tcelly + else: + tcell = tcellx + del tcellx, tcelly + + maze_canva = Canvas( + root, + width=(len(tab[0]) - 1) * tcell, + height=(len(tab) - 1) * tcell, + bg="white", + bd=0, + ) + + images = { # doit etre dans la loop tk pour ne pas etre garbage collected + "a": resize_image("assets/end_up.png", tcell, tcell), + "b": resize_image("assets/end_down.png", tcell, tcell), + "c": resize_image("assets/end_left.png", tcell, tcell), + "d": resize_image("assets/end_right.png", tcell, tcell), + "ab": resize_image("assets/straight.png", tcell, tcell), + "cd": resize_image("assets/straight_down.png", tcell, tcell), + "da": resize_image("assets/down_right.png", tcell, tcell), + "ca": resize_image("assets/down_left.png", tcell, tcell), + "cb": resize_image("assets/up_left.png", tcell, tcell), + "db": resize_image("assets/up_right.png", tcell, tcell), + "cab": resize_image("assets/three_left.png", tcell, tcell), + "dab": resize_image("assets/three_right.png", tcell, tcell), + "cda": resize_image("assets/three_down.png", tcell, tcell), + "cdb": resize_image("assets/three_up.png", tcell, tcell), + "cdab": resize_image("assets/cross.png", tcell, tcell), + " ": resize_image("assets/empty.png", tcell, tcell), + "end": resize_image("assets/end.png", tcell, tcell), + "start": resize_image("assets/start.png", tcell, tcell), + "return": resize_image("assets/return.png", 20, 20), + "brain": resize_image("assets/brain.png", 20, 20), + "save": resize_image("assets/save.png", 20, 20), + } + + # applique les images + for i in range(len(tab)): + for j in range(len(tab[i])): + name = "" + if tab[i][j] == " ": + for coo in [ + (j - 1, i, "c"), + (j + 1, i, "d"), + (j, i - 1, "a"), + (j, i + 1, "b"), + ]: + if 0 <= coo[0] and 0 <= coo[1]: + if tab[coo[1]][coo[0]] in [" ", "O", ">"]: + name += coo[2] + maze_canva.create_image( + j * tcell - 1, i * tcell - 1, image=images[name] + ) + elif tab[i][j] == "O": + maze_canva.create_image( + j * tcell - 1, i * tcell - 1, image=images["end"] + ) + elif tab[i][j] == ">": + maze_canva.create_image( + j * tcell - 1, i * tcell - 1, image=images["start"] + ) + else: + maze_canva.create_image(j * tcell - 1, i * tcell - 1, image=images[" "]) + maze_canva.pack(side=LEFT) + + # cree les boutons + buttons_canva = Canvas(root, background="white") + save_button = Button( + buttons_canva, + text="Sauvegarder", + command=lambda: save(tab, save_button), + image=images["save"], + compound=LEFT, + ) + resolv_button = Button( + buttons_canva, + text="R\u00e9soudre", + command=lambda: resolve( + deepcopy(tab), maze_canva, root, tcell, button=resolv_button + ), + image=images["brain"], + compound=LEFT, + ) + return_button = Button( + buttons_canva, + text="Retour", + command=lambda: start(root), + image=images["return"], + compound=LEFT, + ) + + # desactive le bouton "Sauvegarder" + if not activ_save: + save_button["state"] = "disabled" + + resolv_button.pack(padx=20, pady=10) + save_button.pack(padx=20, pady=10) + return_button.pack(padx=20, pady=10) + buttons_canva.pack(side=LEFT) + + root.mainloop() + + +def load_save(file_nb: int, to_close: Tk) -> None: + """ + Charge une sauvegarde .bin l'affiche + In file_nb, to_close: int + Out: None + """ + if to_close is not None: + to_close.destroy() + + with open(f"save/save{file_nb}.bin", "rb") as file: + binary_data = file.read() + + bin_string = "".join( + format(byte, "03b") for byte in binary_data + ) # Les fichiers de sauvegarde sont encodes sur 3 bits + + dico_decomp = {" ": "000", ">": "100", "O": "101", ":": "011", "#": "001"} + deco_list = btt(dico_decomp, bin_string).split(":") + maze = [] + for i in deco_list: + tmp = [] + for l in i: + tmp.append(l) + maze.append(tmp) + + display(maze) + + return None + + +def valid_values(varx: StringVar, vary: StringVar, to_close: Tk, info: Label) -> None: + """ + verifie des les valeurs x et y de creation du labyrinthe sont correctes + In varx, vary, to_close, info = StringVar(), StringVar(), Tk(), Label() + Out : None + """ + try: + x, y = int(varx.get()), int(vary.get()) + if x < 5 or x > 500 or y < 5 or y > 500: + info.config( + text="La taille du labyrinthe doit \u00eatre\n entre 5x5 et 500x500 (\u25d4\u005f\u25d4)", + foreground="red", + ) + else: + to_close.destroy() + maze = gen_maze(x, y) + display(maze, True) + except: + info.config( + text="Vous devriez essayer avec\n des chiffres (\uffe2\u005f\uffe2)", + foreground="red", + ) + + return None + + +def start(to_close=None) -> None: + """ + Affiche la fenetre de selection du labyrinthe + In to_close: Tk() + Out: None + """ + if to_close is not None: + to_close.destroy() + + root = Tk() + root.title("Amaze!") + root.resizable(False, False) + root.configure() + root.iconphoto(False, PhotoImage(file="assets/logo.png")) + + top_frame = Frame(root) + entry_frame = Frame(top_frame) + bottom_frame = Frame(root) + + info_label = Label( + top_frame, + text="Super Labyrinthe Mega Ultra HD\n 4K Premium ChatPGT IA 14.5", + font=("Times New Roman", 15, "bold"), + justify=CENTER, + foreground="blue", + ) + info_label.pack_propagate(0) + info_label.pack(side=TOP) + txtx = Label(entry_frame, text="x", font=("Times New Roman", 11, "bold")) + + sizex_var = StringVar(root) + sizex_var.set("10") + sizey_var = StringVar(root) + sizey_var.set("10") + + sizex_entry = Entry(entry_frame, textvariable=sizex_var) + sizex_entry.pack(padx=5, pady=10, side="left") + + txtx.pack(side=LEFT) + + sizey_entry = Entry(entry_frame, textvariable=sizey_var) + sizey_entry.pack(padx=5, pady=10, side="left") + + create_button = Button( + top_frame, + text="G\u00e9n\u00e9rer un labyrinthe", + command=lambda: valid_values(sizex_var, sizey_var, root, info_label), + ) + create_button.pack(pady=10, side="bottom") + + try: + with open("save/savelist.txt", "r") as savelist: + option_save = savelist.readline().split(";")[:-1] + + save_var = StringVar(root) + save_var.set(option_save[0]) + + option_menu_save = OptionMenu(bottom_frame, save_var, *option_save) + save_button = Button( + bottom_frame, + text="Charger un labyrinthe", + command=lambda: load_save(save_var.get(), root), + ) + save_button.pack(pady=10, side="bottom") + option_menu_save.pack(pady=10, side="bottom") + + entry_frame.pack() + top_frame.pack(padx=20, pady=20, side="top") + bottom_frame.pack(padx=20, pady=20) + except FileNotFoundError: + top_frame.pack(padx=20, pady=20, side="top") + entry_frame.pack() + create_button.pack(pady=10) + + root.mainloop() + + +start()