Solution de bluesheet pour Mazopotamia

misc algo

17 novembre 2023

Mazopotamia

200 points

Partie Algorithme (théorie)

On commence par regarder le labyrinthe d’exemple pour élaborer une stratégie. Ma stratégie est (dans un premier temps) la suivante : On fait un graphe, chaque sommet est une porte. Deux sommets A et B sont reliés (unidirectionnellement, de A vers B) si et seulement si en sortant de A on peut aller à B. On effectue l’algorithme de Dijkstra sur ce graphe pour obtenir le chemin.

J’ai ensuite précisé cette stratégie au fur et à mesure que je rencontrais des problèmes.

  • Problème : Avec ma stratégie actuelle, je ne prend pas en compte le fait qu’en entrant d’un côté d’une porte, je dois sortir par l’autre. Solution : on sépare chaque porte en 4 sommets :

    • entrée 1 vers l’intérieur (quand on arrive à la porte par l’entrée 1) (relié unidirectionnellement à entrée 2 vers l’extérieur)
    • entrée 1 vers l’extérieur (quand on repart de la porte par l’entrée 1) (relié unidirectionnellement à ce qui est atteignable en partant de l’entrée 1 de la porte)
    • entrée 2 vers l’intérieur (quand on arrive à la porte par l’entrée 2) (relié unidirectionnellement à entrée 1 vers l’extérieur)
    • entrée 2 vers l’extérieur (quand on repart de la porte par l’entrée 2) (relié unidirectionnellement à ce qui est atteignable en partant de l’entrée 2 de la porte)
  • Problème : Comment je sais si je peux aller à la porte B entrée 1 en venant de A entrée 2 (par exemple) ? Solution : On regarde déjà si la couleur de B suit la couleur de A dans le cycle des couleurs. Si c’est bon, on va effectuer Dijkstra sur un graphe où chaque case libre est un sommet (en sachant que toute autre porte que B est comptée comme un mur) Si l’entrée de la porte est atteignable, alors on obtient un chemin reliant la porte A entrée 2 à la porte B entrée 1 (qu’on garde précieusement en mémoire). Sinon, on sait qu’on ne doit pas relier ces deux portes.

L’algorithme semble fin prêt : On utilise plein de fois ( O(n²) où n = nombre de portes) le djikstra “micro” pour déterminer les liens entre les portes (qui est atteignable depuis qui ? par quel chemin ?), puis le djikstra “macro” pour déterminer par quelles portes on doit passer. On recolle ensuite les morceaux : le djikstra “macro” nous dit qu’on doit emprunter le chemin : A1 -> B2 -> C2 -> D1 (par exemple), et les djikstra micro nous on dit que pour faire A1 -> B2 il faut faire NWWSE (par exemple). La complexité me semble correcte bien qu’un peu lourde ( O(n² * l² * log(l)) où n = nb de portes, l = longueur d’un côté du labyrinthe), mais on verra bien. On a toutes les pièces du puzzle, et la partie théorique est finie !

Partie Implémentation

J’aime le Python orienté objet. Je suis donc parti là dessus. Le __init__ de la classe Maze transforme la longue chaîne base64 en image, puis lit l’ordre des couleurs et parse le labyrinthe dans un tableau à 2 dimensions. Il détecte ensuite le départ et l’arrivée, et effectue tous les djikstra “micro” (cf partie algorithme). La fonction solve effectue le dijkstra “macro”, et donne donc le résultat final. Génial, on a une classe qui permet de résoudre les problèmes posés en 2 lignes ! Pour la fonction djikstra, j’ai repris l’implémentation trouvée ici. Maintenant, il ne reste plus qu’à gérer la partie réseau. Un gros big up à la classe Netcat trouvable ici qui permet de simplifier grandement l’écriture des communications réseau.

Conclusion

On a assemblé toutes les pièces, on règle la partie réseau et on fait tourner ! Je suis agréablement surpris par la rapidité de mon algorithme (qui ne me semble pas être optimal bien que correct), et je découvre qu’il y a 31 labyrinthes proposés. Et le flag nous est donné à la fin du 31ème labyrinthe :)