diff --git a/fltk.py b/fltk.py new file mode 100644 index 0000000..9957508 --- /dev/null +++ b/fltk.py @@ -0,0 +1,844 @@ +import subprocess +import sys +import tkinter as tk +from collections import deque +from os import system +from pathlib import Path +from time import sleep, time +from tkinter import PhotoImage +from tkinter.font import Font +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Deque, + Dict, + List, + Optional, + Set, + Tuple, + TypeVar, + Union, +) + + +try: + # noinspection PyUnresolvedReferences + from PIL import Image, ImageTk + + print("Bibliothèque PIL chargée.", file=sys.stderr) + PIL_AVAILABLE = True +except ImportError as e: + PIL_AVAILABLE = False + +if TYPE_CHECKING: + from typing_extensions import Literal + + Anchor = Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] + TkEvent = tk.Event[tk.BaseWidget] +else: + Anchor = str + TkEvent = tk.Event +FltkEvent = Tuple[str, Optional[TkEvent]] + +__all__ = [ + # gestion de fenêtre + "cree_fenetre", + "ferme_fenetre", + "redimensionne_fenetre", + "mise_a_jour", + # dessin + "ligne", + "fleche", + "polygone", + "rectangle", + "cercle", + "point", + "image", + "texte", + "taille_texte", + # effacer + "efface_tout", + "efface", + # utilitaires + "attente", + "capture_ecran", + "touche_pressee", + "abscisse_souris", + "ordonnee_souris", + "hauteur_fenetre", + "largeur_fenetre", + # événements + "donne_ev", + "attend_ev", + "attend_clic_gauche", + "attend_fermeture", + "type_ev", + "abscisse", + "ordonnee", + "touche", +] + + +class CustomCanvas: + """ + Classe qui encapsule tous les objets tkinter nécessaires à la création + d'un canevas. + """ + + _on_osx = sys.platform.startswith("darwin") + + _ev_mapping = { + "ClicGauche": "", + "ClicMilieu": "", + "ClicDroit": "" if _on_osx else "", + "Deplacement": "", + "Touche": "", + "Redimension": "", + } + + _default_ev = ["ClicGauche", "ClicDroit", "Touche"] + + def __init__( + self, + width: int, + height: int, + refresh_rate: int = 100, + events: Optional[List[str]] = None, + resizing: bool = False, + ) -> None: + # width and height of the canvas + self.width = width + self.height = height + self.interval = 1 / refresh_rate + + # root Tk object + self.root = tk.Tk() + + # canvas attached to the root object + self.canvas = tk.Canvas( + self.root, width=width, height=height, highlightthickness=0 + ) + + # adding the canvas to the root window and giving it focus + self.canvas.pack(fill=tk.BOTH, expand=tk.YES) + self.root.resizable(width=resizing, height=resizing) + self.canvas.focus_set() + self.first_resize = True + + # binding events + self.ev_queue: Deque[FltkEvent] = deque() + self.pressed_keys: Set[str] = set() + self.events = CustomCanvas._default_ev if events is None else events + self.bind_events() + + # update for the first time + self.last_update = time() + self.root.update() + + if CustomCanvas._on_osx: + system( + """/usr/bin/osascript -e 'tell app "Finder" \ + to set frontmost of process "Python" to true' """ + ) + + def update(self) -> None: + t = time() + self.root.update() + sleep(max(0.0, self.interval - (t - self.last_update))) + self.last_update = time() + + def resize(self, width: int, height: int) -> None: + self.root.geometry(f"{int(width)}x{int(height)}") + + def bind_events(self) -> None: + self.root.protocol("WM_DELETE_WINDOW", self.event_quit) + self.canvas.bind("", self.event_resize) + self.canvas.bind("", self.register_key) + self.canvas.bind("", self.release_key) + for name in self.events: + self.bind_event(name) + + # noinspection PyUnresolvedReferences + def register_key(self, ev: TkEvent) -> None: + self.pressed_keys.add(ev.keysym) + + # noinspection PyUnresolvedReferences + def release_key(self, ev: TkEvent) -> None: + if ev.keysym in self.pressed_keys: + self.pressed_keys.remove(ev.keysym) + + def event_quit(self) -> None: + self.ev_queue.append(("Quitte", None)) + + # noinspection PyUnresolvedReferences + def event_resize(self, event: TkEvent) -> None: + if event.widget.widgetName == "canvas": + if self.width != event.width or self.height != event.height: + self.width, self.height = event.width, event.height + if not self.ev_queue or self.ev_queue[-1][0] != "Redimension": + self.ev_queue.append(("Redimension", event)) + + def bind_event(self, name: str) -> None: + e_type = CustomCanvas._ev_mapping.get(name, name) + + def handler(event: TkEvent, _name: str = name) -> None: + self.ev_queue.append((_name, event)) + + self.canvas.bind(e_type, handler, "+") + + def unbind_event(self, name: str) -> None: + e_type = CustomCanvas._ev_mapping.get(name, name) + self.canvas.unbind(e_type) + + +__canevas: Optional[CustomCanvas] = None +__img: Dict[Tuple[Path, int, int], PhotoImage] = {} + + +############################################################################# +# Exceptions +############################################################################# + + +class TypeEvenementNonValide(Exception): + pass + + +class FenetreNonCree(Exception): + pass + + +class FenetreDejaCree(Exception): + pass + + +Ret = TypeVar("Ret") + + +def _fenetre_cree(func: Callable[..., Ret]) -> Callable[..., Ret]: + def new_func(*args: Any, **kwargs: Any) -> Ret: + if __canevas is None: + raise FenetreNonCree( + 'La fenêtre n\'a pas été crée avec la fonction "cree_fenetre".' + ) + return func(*args, **kwargs) + + return new_func + + +############################################################################# +# Initialisation, mise à jour et fermeture +############################################################################# + + +def cree_fenetre( + largeur: int, hauteur: int, frequence: int = 100, + redimension: bool = False +) -> None: + """ + Crée une fenêtre de dimensions ``largeur`` x ``hauteur`` pixels. + :rtype: + """ + global __canevas + if __canevas is not None: + raise FenetreDejaCree( + 'La fenêtre a déjà été crée avec la fonction "cree_fenetre".' + ) + __canevas = CustomCanvas(largeur, hauteur, frequence, resizing=redimension) + + +@_fenetre_cree +def ferme_fenetre() -> None: + """ + Détruit la fenêtre. + """ + global __canevas + assert __canevas is not None + __canevas.root.destroy() + __canevas = None + + +@_fenetre_cree +def redimensionne_fenetre(largeur: int, hauteur: int) -> None: + """ + Fixe les dimensions de la fenêtre à (``hauteur`` x ``largeur``) pixels. + + Le contenu du canevas n'est pas automatiquement mis à l'échelle et doit + être redessiné si nécessaire. + """ + assert __canevas is not None + __canevas.resize(width=largeur, height=hauteur) + + +@_fenetre_cree +def mise_a_jour() -> None: + """ + Met à jour la fenêtre. Les dessins ne sont affichés qu'après + l'appel à cette fonction. + """ + assert __canevas is not None + __canevas.update() + + +############################################################################# +# Fonctions de dessin +############################################################################# + + +# Formes géométriques + + +@_fenetre_cree +def ligne( + ax: float, + ay: float, + bx: float, + by: float, + couleur: str = "black", + epaisseur: float = 1, + tag: str = "", +) -> int: + """ + Trace un segment reliant le point ``(ax, ay)`` au point ``(bx, by)``. + + :param float ax: abscisse du premier point + :param float ay: ordonnée du premier point + :param float bx: abscisse du second point + :param float by: ordonnée du second point + :param str couleur: couleur de trait (défaut 'black') + :param float epaisseur: épaisseur de trait en pixels (défaut 1) + :param str tag: étiquette d'objet (défaut : pas d'étiquette) + :return: identificateur d'objet + """ + assert __canevas is not None + return __canevas.canvas.create_line( + ax, ay, bx, by, fill=couleur, width=epaisseur, tags=tag + ) + + +@_fenetre_cree +def fleche( + ax: float, + ay: float, + bx: float, + by: float, + couleur: str = "black", + epaisseur: float = 1, + tag: str = "", +) -> int: + """ + Trace une flèche du point ``(ax, ay)`` au point ``(bx, by)``. + + :param float ax: abscisse du premier point + :param float ay: ordonnée du premier point + :param float bx: abscisse du second point + :param float by: ordonnée du second point + :param str couleur: couleur de trait (défaut 'black') + :param float epaisseur: épaisseur de trait en pixels (défaut 1) + :param str tag: étiquette d'objet (défaut : pas d'étiquette) + :return: identificateur d'objet + """ + x, y = (bx - ax, by - ay) + n = (x ** 2 + y ** 2) ** 0.5 + x, y = x / n, y / n + points = [ + bx, + by, + bx - x * 5 - 2 * y, + by - 5 * y + 2 * x, + bx - x * 5 + 2 * y, + by - 5 * y - 2 * x, + ] + assert __canevas is not None + return __canevas.canvas.create_polygon( + points, fill=couleur, outline=couleur, width=epaisseur, tags=tag + ) + + +@_fenetre_cree +def polygone( + points: List[float], + couleur: str = "black", + remplissage: str = "", + epaisseur: float = 1, + tag: str = "", +) -> int: + """ + Trace un polygone dont la liste de points est fournie. + + :param list points: liste de couples (abscisse, ordonnee) de points + :param str couleur: couleur de trait (défaut 'black') + :param str remplissage: couleur de fond (défaut transparent) + :param float epaisseur: épaisseur de trait en pixels (défaut 1) + :param str tag: étiquette d'objet (défaut : pas d'étiquette) + :return: identificateur d'objet + """ + assert __canevas is not None + return __canevas.canvas.create_polygon( + points, fill=remplissage, outline=couleur, width=epaisseur, tags=tag + ) + + +@_fenetre_cree +def rectangle( + ax: float, + ay: float, + bx: float, + by: float, + couleur: str = "black", + remplissage: str = "", + epaisseur: float = 1, + tag: str = "", +) -> int: + """ + Trace un rectangle noir ayant les point ``(ax, ay)`` et ``(bx, by)`` + comme coins opposés. + + :param float ax: abscisse du premier coin + :param float ay: ordonnée du premier coin + :param float bx: abscisse du second coin + :param float by: ordonnée du second coin + :param str couleur: couleur de trait (défaut 'black') + :param str remplissage: couleur de fond (défaut transparent) + :param float epaisseur: épaisseur de trait en pixels (défaut 1) + :param str tag: étiquette d'objet (défaut : pas d'étiquette) + :return: identificateur d'objet + """ + assert __canevas is not None + return __canevas.canvas.create_rectangle( + ax, ay, bx, by, + outline=couleur, fill=remplissage, width=epaisseur, tags=tag + ) + + +@_fenetre_cree +def cercle( + x: float, + y: float, + r: float, + couleur: str = "black", + remplissage: str = "", + epaisseur: float = 1, + tag: str = "", +) -> int: + """ + Trace un cercle de centre ``(x, y)`` et de rayon ``r`` en noir. + + :param float x: abscisse du centre + :param float y: ordonnée du centre + :param float r: rayon + :param str couleur: couleur de trait (défaut 'black') + :param str remplissage: couleur de fond (défaut transparent) + :param float epaisseur: épaisseur de trait en pixels (défaut 1) + :param str tag: étiquette d'objet (défaut : pas d'étiquette) + :return: identificateur d'objet + """ + assert __canevas is not None + return __canevas.canvas.create_oval( + x - r, + y - r, + x + r, + y + r, + outline=couleur, + fill=remplissage, + width=epaisseur, + tags=tag, + ) + + +@_fenetre_cree +def arc( + x: float, + y: float, + r: float, + ouverture: float = 90, + depart: float = 0, + couleur: str = "black", + remplissage: str = "", + epaisseur: float = 1, + tag: str = "", +) -> int: + """ + Trace un arc de cercle de centre ``(x, y)``, de rayon ``r`` et + d'angle d'ouverture ``ouverture`` (défaut : 90 degrés, dans le sens + contraire des aiguilles d'une montre) depuis l'angle initial ``depart`` + (défaut : direction 'est'). + + :param float x: abscisse du centre + :param float y: ordonnée du centre + :param float r: rayon + :param float ouverture: abscisse du centre + :param float depart: ordonnée du centre + :param str couleur: couleur de trait (défaut 'black') + :param str remplissage: couleur de fond (défaut transparent) + :param float epaisseur: épaisseur de trait en pixels (défaut 1) + :param str tag: étiquette d'objet (défaut : pas d'étiquette) + :return: identificateur d'objet + """ + assert __canevas is not None + return __canevas.canvas.create_arc( + x - r, + y - r, + x + r, + y + r, + extent=ouverture, + start=depart, + style=tk.ARC, + outline=couleur, + fill=remplissage, + width=epaisseur, + tags=tag, + ) + + +@_fenetre_cree +def point( + x: float, y: float, + couleur: str = "black", epaisseur: float = 1, + tag: str = "" +) -> int: + """ + Trace un point aux coordonnées ``(x, y)`` en noir. + + :param float x: abscisse + :param float y: ordonnée + :param str couleur: couleur du point (défaut 'black') + :param float epaisseur: épaisseur de trait en pixels (défaut 1) + :param str tag: étiquette d'objet (défaut : pas d'étiquette) + :return: identificateur d'objet + """ + assert __canevas is not None + return cercle(x, y, epaisseur, + couleur=couleur, remplissage=couleur, tag=tag) + + +# Image + + +@_fenetre_cree +def image( + x: float, + y: float, + fichier: str, + largeur: Optional[int] = None, + hauteur: Optional[int] = None, + ancrage: Anchor = "center", + tag: str = "", +) -> int: + """ + Affiche l'image contenue dans ``fichier`` avec ``(x, y)`` comme centre. Les + valeurs possibles du point d'ancrage sont ``'center'``, ``'nw'``, + etc. Les arguments optionnels ``largeur`` et ``hauteur`` permettent de + spécifier des dimensions maximales pour l'image (sans changement de + proportions). + + :param largeur: largeur de l'image + :param hauteur: hauteur de l'image + :param float x: abscisse du point d'ancrage + :param float y: ordonnée du point d'ancrage + :param str fichier: nom du fichier contenant l'image + :param ancrage: position du point d'ancrage par rapport à l'image + :param str tag: étiquette d'objet (défaut : pas d'étiquette) + :return: identificateur d'objet + """ + assert __canevas is not None + if PIL_AVAILABLE: + tk_image = _load_pil_image(fichier, hauteur, largeur) + else: + tk_image = _load_tk_image(fichier, hauteur, largeur) + img_object = __canevas.canvas.create_image( + x, y, anchor=ancrage, image=tk_image, tags=tag + ) + return img_object + + +def _load_tk_image(fichier: str, + hauteur: Optional[int] = None, + largeur: Optional[int] = None) -> PhotoImage: + chemin = Path(fichier) + ph_image = PhotoImage(file=fichier) + largeur_o = ph_image.width() + hauteur_o = ph_image.height() + if largeur is None: + largeur = largeur_o + if hauteur is None: + hauteur = hauteur_o + zoom_l = max(1, largeur // largeur_o) + zoom_h = max(1, hauteur // hauteur_o) + red_l = max(1, largeur_o // largeur) + red_h = max(1, hauteur_o // hauteur) + largeur = largeur_o * zoom_l // red_l + hauteur = hauteur_o * zoom_h // red_h + if (chemin, largeur, hauteur) in __img: + return __img[(chemin, largeur, hauteur)] + ph_image = ph_image.zoom(zoom_l, zoom_h) + ph_image = ph_image.subsample(red_l, red_h) + __img[(chemin, largeur, hauteur)] = ph_image + return ph_image + + +def _load_pil_image(fichier: str, + hauteur: Optional[int] = None, + largeur: Optional[int] = None) -> PhotoImage: + chemin = Path(fichier) + img = Image.open(fichier) + if largeur is None: + largeur = img.width + if hauteur is None: + hauteur = img.height + if (chemin, largeur, hauteur) in __img: + return __img[(chemin, largeur, hauteur)] + img = img.resize((largeur, hauteur)) + ph_image = ImageTk.PhotoImage(img) + __img[(chemin, largeur, hauteur)] = ph_image # type:ignore + return ph_image # type:ignore + + +# Texte + + +@_fenetre_cree +def texte( + x: float, + y: float, + chaine: str, + couleur: str = "black", + ancrage: Anchor = "nw", + police: str = "Helvetica", + taille: int = 24, + tag: str = "", +) -> int: + """ + Affiche la chaîne ``chaine`` avec ``(x, y)`` comme point d'ancrage (par + défaut le coin supérieur gauche). + + :param float x: abscisse du point d'ancrage + :param float y: ordonnée du point d'ancrage + :param str chaine: texte à afficher + :param str couleur: couleur de trait (défaut 'black') + :param ancrage: position du point d'ancrage (défaut 'nw') + :param police: police de caractères (défaut : `Helvetica`) + :param taille: taille de police (défaut 24) + :param tag: étiquette d'objet (défaut : pas d'étiquette + :return: identificateur d'objet + """ + assert __canevas is not None + return __canevas.canvas.create_text( + x, y, + text=chaine, font=(police, taille), + tags=tag, fill=couleur, anchor=ancrage + ) + + +def taille_texte( + chaine: str, police: str = "Helvetica", taille: int = 24 +) -> Tuple[int, int]: + """ + Donne la largeur et la hauteur en pixel nécessaires pour afficher + ``chaine`` dans la police et la taille données. + + :param str chaine: chaîne à mesurer + :param police: police de caractères (défaut : `Helvetica`) + :param taille: taille de police (défaut 24) + :return: couple (w, h) constitué de la largeur et la hauteur de la chaîne + en pixels (int), dans la police et la taille données. + """ + font = Font(family=police, size=taille) + return font.measure(chaine), font.metrics("linespace") + + +############################################################################# +# Effacer +############################################################################# + + +@_fenetre_cree +def efface_tout() -> None: + """ + Efface la fenêtre. + """ + assert __canevas is not None + __canevas.canvas.delete("all") + + +@_fenetre_cree +def efface(objet_ou_tag: Union[int, str]) -> None: + """ + Efface ``objet`` de la fenêtre. + + :param: objet ou étiquette d'objet à supprimer + :type: ``int`` ou ``str`` + """ + assert __canevas is not None + __canevas.canvas.delete(objet_ou_tag) + + +############################################################################# +# Utilitaires +############################################################################# + + +def attente(temps: float) -> None: + start = time() + while time() - start < temps: + mise_a_jour() + + +@_fenetre_cree +def capture_ecran(file: str) -> None: + """ + Fait une capture d'écran sauvegardée dans ``file.png``. + """ + assert __canevas is not None + __canevas.canvas.postscript( # type: ignore + file=file + ".ps", + height=__canevas.height, + width=__canevas.width, + colormode="color", + ) + + subprocess.call( + "convert -density 150 -geometry 100% -background white -flatten" + " " + file + ".ps " + file + ".png", + shell=True, + ) + subprocess.call("rm " + file + ".ps", shell=True) + + +@_fenetre_cree +def touche_pressee(keysym: str) -> bool: + """ + Renvoie `True` si ``keysym`` est actuellement pressée. + :param keysym: symbole associé à la touche à tester. + :return: `True` si ``keysym`` est actuellement pressée, `False` sinon. + """ + assert __canevas is not None + return keysym in __canevas.pressed_keys + + +############################################################################# +# Gestions des évènements +############################################################################# + + +@_fenetre_cree +def donne_ev() -> Optional[FltkEvent]: + """ + Renvoie immédiatement l'événement en attente le plus ancien, + ou ``None`` si aucun événement n'est en attente. + """ + assert __canevas is not None + if not __canevas.ev_queue: + return None + return __canevas.ev_queue.popleft() + + +def attend_ev() -> FltkEvent: + """Attend qu'un événement ait lieu et renvoie le premier événement qui + se produit.""" + while True: + ev = donne_ev() + if ev is not None: + return ev + mise_a_jour() + + +def attend_clic_gauche() -> Tuple[int, int]: + """Attend qu'un clic gauche sur la fenêtre ait lieu et renvoie ses + coordonnées. **Attention**, cette fonction empêche la détection d'autres + événements ou la fermeture de la fenêtre.""" + while True: + ev = donne_ev() + if ev is not None and type_ev(ev) == "ClicGauche": + x, y = abscisse(ev), ordonnee(ev) + assert isinstance(x, int) and isinstance(y, int) + return x, y + mise_a_jour() + + +def attend_fermeture() -> None: + """Attend la fermeture de la fenêtre. Cette fonction renvoie None. + **Attention**, cette fonction empêche la détection d'autres événements.""" + while True: + ev = donne_ev() + if ev is not None and type_ev(ev) == "Quitte": + ferme_fenetre() + return + mise_a_jour() + + +def type_ev(ev: Optional[FltkEvent]) -> Optional[str]: + """ + Renvoie une chaîne donnant le type de ``ev``. Les types + possibles sont 'ClicDroit', 'ClicGauche', 'Touche' et 'Quitte'. + Renvoie ``None`` si ``evenement`` vaut ``None``. + """ + return ev if ev is None else ev[0] + + +def abscisse(ev: Optional[FltkEvent]) -> Optional[int]: + """ + Renvoie la coordonnée x associé à ``ev`` si elle existe, None sinon. + """ + x = attribut(ev, "x") + assert isinstance(x, int) or x is None + return x + + +def ordonnee(ev: Optional[FltkEvent]) -> Optional[int]: + """ + Renvoie la coordonnée y associé à ``ev`` si elle existe, None sinon. + """ + y = attribut(ev, "y") + assert isinstance(y, int) or y is None + return y + + +def touche(ev: Optional[FltkEvent]) -> str: + """ + Renvoie une chaîne correspondant à la touche associé à ``ev``, + si elle existe. + """ + keysym = attribut(ev, "keysym") + assert isinstance(keysym, str) + return keysym + + +def attribut(ev: Optional[FltkEvent], nom: str) -> Any: + if ev is None: + raise TypeEvenementNonValide( + f"Accès à l'attribut {nom} impossible sur un événement vide" + ) + tev, evtk = ev + if not hasattr(evtk, nom): + raise TypeEvenementNonValide( + f"Accès à l'attribut {nom} impossible " + f"sur un événement de type {tev}" + ) + attr = getattr(evtk, nom) + return attr if attr != "??" else None + + +@_fenetre_cree +def abscisse_souris() -> int: + assert __canevas is not None + return __canevas.canvas.winfo_pointerx() - __canevas.canvas.winfo_rootx() + + +@_fenetre_cree +def ordonnee_souris() -> int: + assert __canevas is not None + return __canevas.canvas.winfo_pointery() - __canevas.canvas.winfo_rooty() + + +@_fenetre_cree +def largeur_fenetre() -> int: + assert __canevas is not None + return __canevas.width + + +@_fenetre_cree +def hauteur_fenetre() -> int: + assert __canevas is not None + return __canevas.height diff --git a/maps/map1.txt b/maps/map1.txt new file mode 100644 index 0000000..c7c0e8c --- /dev/null +++ b/maps/map1.txt @@ -0,0 +1,10 @@ +╗╝╩╞╦╔ +╩╦╡╩╝╠ +╦╬╡╝╔╥ +╬═╥╩╚╗ +╨╦╬╔╣╗ +╬╨╩╨╞╔ +A 2 5 +D 4 2 1 +D 2 0 2 +D 4 0 3 diff --git a/maps/map2.txt b/maps/map2.txt new file mode 100644 index 0000000..5084f9c --- /dev/null +++ b/maps/map2.txt @@ -0,0 +1,10 @@ +╣╩╝╥╩╠ +╥╞╨╞╬╬ +═╚╠╡╝╥ +╨╦╝╡╬═ +║╗╝═╝╗ +╠╡╩╚╣╦ +A 4 1 +D 2 3 1 +D 3 5 2 +D 4 0 3 \ No newline at end of file diff --git a/maps/map3.txt b/maps/map3.txt new file mode 100644 index 0000000..b13be50 --- /dev/null +++ b/maps/map3.txt @@ -0,0 +1,10 @@ +╣╬╣╝╚║ +╝╬╩╨╚╗ +╠╩╗╔╦╗ +╬╥╬╡╚╔ +╥╞╗╩╦╞ +╦╩╚╔╦═ +A 4 0 +D 4 5 1 +D 2 5 2 +D 0 1 3 \ No newline at end of file diff --git a/maps/map4.txt b/maps/map4.txt new file mode 100644 index 0000000..eb7065c --- /dev/null +++ b/maps/map4.txt @@ -0,0 +1,10 @@ +╗╥║╠╚╝ +╨╥═╥╠╩ +╡╨╞╚═╔ +╠╣╝╝╩╗ +╩╣║╚╡╨ +╔╩╚╝╔╨ +A 5 2 +D 3 4 1 +D 5 1 2 +D 1 3 3 \ No newline at end of file diff --git a/maps/map_test.txt b/maps/map_test.txt new file mode 100644 index 0000000..0d612a9 --- /dev/null +++ b/maps/map_test.txt @@ -0,0 +1,10 @@ +╔╦╦╦╦╗ +╠╩╩╩╩╣ +╠═╗╔═╣ +╠═╣╠═╣ +╠╗╠╣╔╣ +╚╩╩╩╩╝ +A 0 2 +D 1 1 1 +D 2 0 2 +D 2 5 3 diff --git a/media/Dragon_s.png b/media/Dragon_s.png new file mode 100644 index 0000000..288727f Binary files /dev/null and b/media/Dragon_s.png differ diff --git a/media/Knight_s.png b/media/Knight_s.png new file mode 100644 index 0000000..676e5ee Binary files /dev/null and b/media/Knight_s.png differ diff --git a/media/treasure.png b/media/treasure.png new file mode 100644 index 0000000..4591ccf Binary files /dev/null and b/media/treasure.png differ diff --git a/tiles.txt b/tiles.txt new file mode 100644 index 0000000..b0ac67c --- /dev/null +++ b/tiles.txt @@ -0,0 +1,11 @@ +droits : ═ ║ +angles : ╔ ╗ ╚ ╝ +triples : ╠ ╣ ╦ ╩ +bouts : ╨ ╡ ╥ ╞ +quatre : ╬ + +exemple : +╔╦══╗ +╠╬╡╔╝ +║║╞╩╗ +╚╩══╝