Haskell , 166 154 octets
(-12 octets grâce à Laikoni, (compréhension de zip et de liste au lieu de zipWith et lambda, meilleur moyen de générer la première ligne))
i#n|let k!p=p:(k+1)![m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))|(l,m,r)<-zip3(1:p)p$tail p++[1]];x=1<$[2..2^n]=mapM(putStrLn.map("M "!!))$take(2^n)$1!(x++0:x)
Essayez-le en ligne!
Explication:
La fonction i#ndessine un triangle ASCII de hauteur 2^naprès des iétapes d'itération.
Le codage utilisé en interne code les positions vides en tant que 1et les positions complètes en tant que 0. Par conséquent, la première ligne du triangle est codée comme [1,1,1..0..1,1,1]avec des 2^n-1uns des deux côtés du zéro. Pour construire cette liste, nous commençons par la liste x=1<$[2..2^n], c'est-à-dire la liste [2..2^n]avec tout ce qui est mappé 1. Ensuite, nous construisons la liste complète en tant quex++0:x
L'opérateur k!p(explication détaillée ci-dessous), à partir d'un index de ligne ket d'un correspondant, pgénère une liste infinie de lignes qui suivent p. Nous l'invoquons avec 1la ligne de départ décrite ci-dessus pour obtenir le triangle entier, puis prenons uniquement les premières 2^nlignes. Ensuite, nous imprimons simplement chaque ligne en remplaçant 1par espace et 0par M(en accédant à la liste "M "à l'emplacement 0ou 1).
Opérateur k!pest défini comme suit:
k!p=p:(k+1)![m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))|(l,m,r)<-zip3(1:p)p$tail p++[1]]
Premièrement, nous générons trois versions de p: 1:pqui est pavec un 1préfixe, plui-même et tail p++[1]qui est tout sauf le premier élément de p, avec un 1ajouté. Nous zippons ensuite ces trois listes, nous donnant effectivement tous les éléments de pleurs voisins gauche et droit, comme (l,m,r). Nous utilisons une liste de compréhension pour calculer ensuite la valeur correspondante dans la nouvelle ligne:
m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))
Pour comprendre cette expression, nous devons comprendre qu'il existe deux cas fondamentaux à prendre en compte: soit nous élargissons simplement la ligne précédente, soit nous nous trouvons à un point où commence un point vide dans le triangle. Dans le premier cas, nous avons un point rempli si l’un des points du voisinage est rempli. Cela peut être calculé comme suit m*l*r: si l'un de ces trois est zéro, alors la nouvelle valeur est zéro. L'autre cas est un peu plus compliqué. Ici, nous avons essentiellement besoin d'une détection de bord. Le tableau suivant donne les huit quartiers possibles avec la valeur résultante dans la nouvelle ligne:
000 001 010 011 100 101 110 111
1 1 1 0 1 1 0 1
Une formule simple pour obtenir ce tableau serait ce 1-m*r*(1-l)-m*l*(1-r)qui simplifie m*(2*l*r-l-r)+1. Nous devons maintenant choisir entre ces deux cas, c'est-à-dire où nous utilisons le numéro de ligne k. Si mod k (2^(n-i)) == 0, nous devons utiliser le second cas, sinon, nous utilisons le premier cas. Le terme 0^(mod k(2^n-i))est donc 0si nous devons utiliser le premier cas et 1si nous devons utiliser le second cas. En conséquence, nous pouvons utiliser
m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))
au total - si nous utilisons le premier cas, nous obtenons simplement m*l*r, alors que dans le second cas, un terme supplémentaire est ajouté, donnant le grand total de m*(2*l*r-l-r)+1.