Faites de la neige!

18

Votre tâche: générer un flocon de neige Koch à la nième profondeur. Vous n'avez pas besoin de faire un flocon de neige Koch complet, juste un côté du triangle de départ. Wikipédia sur les flocons de Koch: https://en.wikipedia.org/wiki/Koch_snowflake .

Règles:

  • Le programme doit générer un côté du flocon de neige de Koch à la nième profondeur.
  • La sortie doit être ASCII.
  • Vous pouvez générer le flocon de neige entier; ce n'est pas obligatoire.
  • Des règles standard pour les entrées / sorties et les échappatoires et autres s'appliquent.
  • L'espace n'a pas d'importance, tant que tous les personnages sont au bon endroit les uns par rapport aux autres.
  • Le code le plus court gagne!

Cas de test:

n = 0:

__

n = 1:

__/\__

n = 2:

      __/\__
      \    /
__/\__/    \__/\__

n = 3:

                        __/\__
                        \    /
                  __/\__/    \__/\__
                  \                /
                  /_              _\
                    \            /
      __/\__      __/            \__      __/\__
      \    /      \                /      \    /
__/\__/    \__/\__/                \__/\__/    \__/\__

J'espère que cela a du sens. Notez que dans chaque cas de test, la fractale peut être divisée en trois parties de longueur égale. Notez également que la largeur de chaque flocon de neige est trois fois la largeur de la génération précédente du flocon de neige.

Camarade SparklePony
la source
Pour info, il a été convenu que ce n'est pas une dupe de cela .
Camarade SparklePony
Je ne pense pas que vous ayez correctement défini la représentation ASCII de la nième courbe de Koch.
orlp
Je ne suis pas sûr que les proportions aient un sens. Le non-dupe utilisé __/\__avec deux soulignements, ce qui rend chaque itération 3 fois plus grande que la précédente. L'utilisation d'un seul soulignement semble donner des contradictions qui commencent à devenir vraiment gênantes dans n = 3. Par exemple, les parties extérieures ont une largeur 12 tandis que la partie médiane n'a qu'une largeur 10, en raison de la /_et _\ qui sont trop à l'étroit. Et même avant cela, vous avez _deux fois la largeur de /et \ .
Ørjan Johansen
Je pense que le /_et _\ sont la seule partie vraiment fatale - les soulignements doivent disparaître, car ils doivent être dans la même position que le /et \ . Une fois cela fait, les choses peuvent s'étendre de 3 fois à partir de n = 1 (mais n = 0 ne correspond pas.)
Ørjan Johansen
Hélas, non, la partie médiane a toujours une largeur ne correspondant pas aux parties extérieures, comme en témoigne n = 3 ayant une largeur 52 plutôt que 54 = 2 * 3 ^ 3. Essayez l' un de ceux - ci . J'ai inclus des versions à l'envers avec des parties n'apparaissant que de n = 4 ou n = 5 - elles diffèrent de celles vers le haut dans lesquelles les traits de soulignement sont déposés.
Ørjan Johansen

Réponses:

10

Haskell , 308 300 299 octets

Modifications:

  • -4 octets: Modification zipWith(+)de zipWith(-)et réglage encodages et des compensations se sont débarrassés de tout signe de négation.
  • -1 octet: un ajustement supplémentaire de l'encodage a permis de supprimer plusieurs noms de variable en #utilisant r=reverseau lieu d'une correspondance directe de modèle.
  • -2 octets: utilisation d'un opérateur au lieu d'un alphanum pour zipWith(-).
  • -1 octet: Définition o=[0,0]pour raccourcir les constantes de liste.
  • -1 octet: fusion de deux branches de ?.
import Data.List
k n=0?sort(o#(f=<<scanl1(+)(iterate(>>=(:[1,4,1]))[6]!!n)))
x?l@(([_,w],c):r)|x>w='\n':0?l|0<1=([2..w-x]>>" ")++[c|w>x]++w?r
_?_=""
w#((c,l):m)=(l&w,c):r l&(l&w)#m
_#_=[]
f x=zip"_/\\_/\\"([id,r]<*>[0:1:o,[0,1,0,1],o++[1,1]])!!mod x 6<$[1,3..gcd 3x]
(&)=zipWith(-)
r=reverse
o=[0,0]

Essayez-le en ligne! (Malheureusement, tout ce qui est supérieur à n = 3 est horriblement enveloppé et illisible, mais vous pouvez le copier dans un autre programme pour le voir.)

Variations

Comment ça fonctionne

  • kest la fonction principale, elle prend un Int net retourne un String.
  • iterate(>>=(:[1,4,1]))[6]génère une liste infinie contenant, pour chaque n, les tours entre les lignes consécutives dans cette itération de courbe, style graphique tortue, sous forme de nombres entre 0et 5. Chaque itération est juste la précédente avec les spires 1,4,1entrelacées. La seule raison pour laquelle les sous-listes commencent au 6lieu de 0est de faire l' gcdaffaire en févitant f 0.
  • scanl1(+)convertit les virages en directions "absolues", jusqu'à modulo 6. A 0signifie vers la droite, puis chaque nombre supérieur est de 60 degrés dans le sens antihoraire par rapport au précédent. (Eh bien, ce serait 60 degrés s'il s'agissait d'un dessin approprié plutôt que ASCII.)
  • f convertit une direction absolue en une liste de paires (caractères, codage de décalage) qui code les caractères à ajouter à la courbe (pour les directions horizontales, elle génère deux paires, sinon une), et comment la position relative change.
  • L' #opérateur parcourt la liste précédente de paires (caractères, codage offset), générant des paires réelles (coordonnées, caractères).
  • Principes d'encodage:
    • Un caractère de _/\représente nominalement une ligne tracée d'un coin de départ à travers une cellule rectangulaire à un coin de fin différent.
    • Les coordonnées des cellules sont de la forme [y,x], de haut en bas, de gauche à droite, afin qu'elles soient triées dans l'ordre où nous voulons les imprimer. Les colonnes sont basées sur 1. Les listes sont utilisées à la place des tuples pour une arithmétique vectorielle plus courte avec (&)=zipWith(-).
    • Un coin est indiqué avec les mêmes coordonnées [y,x]que la cellule en haut à gauche. Cela garantit que tous les décalages d'un coin vers les cellules voisines sont non négatifs, en évitant les constantes négatives.
    • Cependant, les coordonnées des coins sont transmises négativement pour permettre à toutes les opérations vectorielles d'être des soustractions au lieu d'additions, ce qui évite tous les autres signes explicites.
    • Une liste de codage de décalage est [y1,x1,x2,y2][y1,x1]est le décalage de coordonnées du coin de départ à la cellule de caractère et [y2,x2]est le décalage du coin de fin à la cellule de caractère. Ça signifie:
      • Les listes d'encodage pour les directions 3.. ne 5sont que l'inverse des listes pour 0.. 2, ce qui permet de les générer avec [id,r]<*>.
      • Toute l'arithmétique vectorielle nécessaire peut être effectuée en utilisant (&)=zipWith(-)une liste de codage ou son inverse.
  • Après avoir trié la liste des paires (coordonnées, caractères), elles sont transmises à ?, ce qui génère la finale à Stringpartir d'elles.
    • In x?l@(([_,w],c):r) xest la coordonnée x du caractère précédent affiché sur cette ligne, ou 0si au début d'une ligne; lest la liste actuelle entière, west la coordonnée x du caractère suivant à ajouter, cest le caractère et rest la liste restante.
    • À ce stade, les coordonnées y ne sont plus nécessaires. Étant donné que chaque ligne contient des caractères et que le premier caractère de chaque ligne se trouve bien à gauche de la fin de la précédente, le début de nouvelles lignes est détecté en vérifiant si la coordonnée x a diminué.
    • Le soulignement a une valeur ASCII plus grande que \et /, il est donc trié en dernier s'il chevauche un autre caractère à la même position. Ainsi, un trait de soulignement redondant est détecté en vérifiant qu'une coordonnée x a été répétée.
Ørjan Johansen
la source
Agréable! Je l'accepterai s'il n'y a plus d'activité sur cette question aujourd'hui.
Camarade SparklePony