Haskell , 166 154 byte
(-12 byte grazie a Laikoni, (comprensione di zip e list invece di zipWith e lambda, modo migliore di generare la prima riga))
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)
Provalo online!
Spiegazione:
La funzione i#ndisegna un triangolo ASCII di altezza 2^ndopo i ipassaggi dell'iterazione.
La codifica utilizzata codifica internamente le posizioni vuote come 1e le posizioni complete come 0. Pertanto, la prima riga del triangolo è codificata come [1,1,1..0..1,1,1]con 2^n-1quelle su entrambi i lati dello zero. Per creare questo elenco, iniziamo con l'elenco x=1<$[2..2^n], ovvero l'elenco [2..2^n]con tutto ciò a cui è mappato 1. Quindi, creiamo l'elenco completo comex++0:x
L'operatore k!p(spiegazione dettagliata di seguito), dato un indice di riga ke un corrispondente pgenera un elenco infinito di righe che seguono p. Lo invochiamo con 1e la linea di partenza sopra descritta per ottenere l'intero triangolo, quindi prendiamo solo le prime 2^nlinee. Quindi, stampiamo semplicemente ogni riga, sostituendola 1con spazio e 0con M(accedendo all'elenco "M "nella posizione 0o 1).
L'operatore k!pè definito come segue:
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]]
Innanzitutto, generiamo tre versioni di p: 1:pche è pcon un 1anteposto, pse stesso e tail p++[1]che è tutto tranne il primo elemento di p, con un 1allegato. Quindi comprimiamo queste tre liste, dandoci effettivamente tutti gli elementi pcon i loro vicini sinistro e destro, come (l,m,r). Usiamo una comprensione dell'elenco per poi calcolare il valore corrispondente nella nuova riga:
m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))
Per comprendere questa espressione, dobbiamo renderci conto che ci sono due casi di base da considerare: o semplicemente espandiamo la linea precedente o siamo in un punto in cui inizia un punto vuoto nel triangolo. Nel primo caso, abbiamo uno spazio pieno se uno degli spazi del quartiere è pieno. Questo può essere calcolato come m*l*r; se uno di questi tre è zero, il nuovo valore è zero. L'altro caso è un po 'più complicato. Qui, abbiamo sostanzialmente bisogno del rilevamento dei bordi. La tabella seguente mostra gli otto possibili quartieri con il valore risultante nella nuova riga:
000 001 010 011 100 101 110 111
1 1 1 0 1 1 0 1
Una formula semplice per produrre questa tabella sarebbe quella 1-m*r*(1-l)-m*l*(1-r)che semplifica m*(2*l*r-l-r)+1. Ora dobbiamo scegliere tra questi due casi, che è dove utilizziamo il numero di riga k. Se mod k (2^(n-i)) == 0, dobbiamo usare il secondo caso, altrimenti usiamo il primo caso. Il termine 0^(mod k(2^n-i))quindi è 0se dobbiamo usare il primo caso e 1se dobbiamo usare il secondo caso. Di conseguenza, possiamo usare
m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))
in totale - se utilizziamo il primo caso, otteniamo semplicemente m*l*r, mentre nel secondo caso viene aggiunto un termine aggiuntivo, che dà il totale complessivo di m*(2*l*r-l-r)+1.