Gerar código do esquema de pirâmide

32

Pyramid Scheme é uma linguagem que está sendo desenvolvida pela @ ConorO'Brien . No esquema de pirâmide, o código que você escreve se parece com isso:

      ^         ^
     / \       /3\
    /   \      ---
   /  +  \
  ^-------^
 /9\     /3\
/123\    ---
-----

Agora, esse código tem duas qualidades óbvias: é difícil de analisar e difícil de escrever. Conor resolveu o primeiro, no entanto, será seu trabalho resolver esse segundo problema.


O código acima é processado pelo interpretador PyramidScheme em uma matriz de cadeias aninhadas, assim:

[["+", ["9123", "3"]], "3"]

Sua tarefa é escrever um programa ou função que, dada uma matriz aninhada de cadeias, produza ou retorne o código PyramidScheme recriado. Você pode assumir que a matriz de entrada sempre será válida.

Uma pirâmide é um triângulo isósceles. O topo é ^, os lados inclinam-se diagonalmente para longe com /e \, e o fundo é -. Os dois cantos inferiores estão vazios ou contêm o início de outras pirâmides, que são argumentos. O meio é preenchido com o nome da pirâmide, ignorando as quebras de linha.

Veja como o analisador converte o código em um formato utilizável. Primeiro, ele procura uma pirâmide de nível superior. Se não houver argumentos, representa-o com uma única string e segue em frente. Caso contrário, ele representa é como uma matriz ["name",[arg1,arg2]]ou ["name",[arg1]]. Os argumentos são as pirâmides no canto inferior esquerdo e no canto inferior direito da pirâmide, que podem ser cadeias de caracteres ou mais matrizes descritas acima. Você pode perceber que isso se assemelha um pouco ao Lisp; nesse caso, você também pode ter notado o trocadilho horrível que é o nome do idioma. Depois que a pirâmide é totalmente representada, o analisador passa para a próxima.

Este é o , o código mais curto vence!

Casos de teste: essas não são as únicas saídas válidas, são exemplos de saídas válidas.

[["+", ["9123", "3"]], "3"]

      ^         ^
     / \       /3\
    /   \      ---
   /  +  \
  ^-------^
 /9\     /3\
/123\    ---
-----

[["out", [["chr", ["72"]], ["chr", ["101"]]]], ["out", [["chr", ["108"]]]], ["out", [["chr", ["108"]]]], ["out", [["chr", ["111"]]]]]

        ^      ^     ^     ^
       / \    / \   / \   / \
      /out\  /out\ /out\ /out\
     ^-----^ -----^----- -----^
    / \   / \    / \         / \
   /chr\ /chr\  /chr\       /chr\
  ^----- -----^ -----^     ^-----
 / \         / \    / \   / \
/72 \       /101\  /108\ /111\
-----       -----  ----- -----

[ ["+", [ ["asdfghjkl"], ["do", [ "1" ]] ]] ]

       ^
      / \
     / + \
    /     \
   ^-------^
  /a\     /d\
 /sdf\   /o  \
/ghjkl\ ^-----
-------/1\
       ---

Observe que no segundo caso de teste, a segunda e a terceira outpirâmide têm a ["chr", ["108"]]como parâmetro, que é recolhido em uma pilha de pirâmide compartilhada por duas de nível superior. Essa é uma otimização válida que seu código pode suportar, mas é completamente opcional; A pontuação não se baseia no tamanho da sua saída.

Para os curiosos, o primeiro caso é exibido 9126 3devido à impressão implícita de pirâmides de nível superior, o segundo é impresso Helloe o último é um erro de sintaxe, incluído apenas por ter uma estrutura elegante.


Você pode assumir que a entrada contém apenas ASCII imprimíveis, excluindo os espaços, ^, /, \, e -. A entrada sempre será válida e conterá pelo menos uma pirâmide. Não há limite para o tamanho da matriz ou das seqüências de entrada; no entanto, você pode escrever seu código como se o tipo inteiro padrão do seu idioma fosse uma precisão infinita e que seu computador tenha memória arbitrária. Se você usar a entrada como uma única sequência, poderá usar qualquer coisa razoável (vírgula, espaço etc., desde que esteja em ascii imprimível e não "ou []) para delimitar matrizes. Você não precisa incluir colchetes ao redor da coisa inteira e, em vez disso, usar várias matrizes separadas por seu delimitador.

Sua saída não precisa ser jogada no golfe, você pode inserir espaço extra ou tornar suas pirâmides maiores que o necessário. As pirâmides de nível superior devem estar na primeira linha. A saída deve ser uma sequência com novas linhas ou uma lista de sequências.

Qualquer um que faz incluir uma versão de seu código que otimamente golfs as pirâmides podem receber algum representante na forma de upvotes / bounties (mas provavelmente apenas upvotes).

Pavel
fonte
8
Sierpinski adoraria esse idioma.
mbomb007
4
Totalmente não postar este desafio, porque eu sou muito preguiçoso para o formato triângulos adequadamente ...
Pavel
A entrada @KodosJohnson pode ser uma matriz nativa.
Pavel
como você pode ter uma função com mais de dois argumentos?
Destructible Lemon
@DestructibleWatermelon A entrada nunca conterá uma matriz, de modo que será necessário passar dois argumentos para uma pirâmide, pois isso é impossível no esquema de pirâmide.
Pavel

Respostas:

26

Lisp comum - 2524 1890 bytes

(defun f(i)(let((s(loop as r in i collect(g r)))(n())(output""))(loop until n do(setf n T)(loop as r in s do(if(cdr r)(progn(setf output(c output(e r))(cdr r)(cdr(cdr r)))(setf n()))(setf output(c output(b(car r))))))(setf output(c output(format()"~%"))))output))(defun g(r)(if(stringp r)(d(m(length r))r)(if(<(length r)2)(d(m(length(car r)))(car r))(if(=(length(e r))1)(let((h(g(car(e r))))(p(d(m(length(car r)))(car r))))(let((o(+ 1(position #\^(e h))))(parent_length(car p)))(if(<(-(car h)o)parent_length)(l(cons(+ o parent_length)())(loop as n in(butlast(cdr p))collect(c(b o)n))(cons(c(subseq(e h)0 o)(car(last p)))())(loop as n in(cdr(cdr h))collect(c n(b (- parent_length(-(car h)o))))))(let((i(-(- o 1)parent_length)))(l(cons(car h)())(loop as n in(butlast(cdr p))collect(c(b o)n(b i)))(cons(c(subseq(nth 1 h)0 o)(car(last p))(b i))())(cddr h))))))(let((l-h(g(car(e r))))(r-h(g(e(e r)))))(let((ll(position #\^(e l-h)))(rl(position #\^(e r-h))))(let((lr(-(car l-h)ll 1))(rr(-(car r-h)rl 1)))(let((p(d(max(m(length(car r)))(ceiling(+ lr rl)2))(car r))))(let((m-pad(if(>(car p)(+ lr rl))(-(car p)lr rl)0)))(l(cons(+ ll 1(car p)1 rr)())(loop as n in(butlast(cdr p))collect(c(b(+ 1 ll))n(b(+ 1 rr))))(cons(c(subseq(e l-h)0(+ 1 ll))(car(last p))(subseq(e r-h)rl))())(loop as y in(append(cddr l-h)(make-list(length l-h):initial-element(b(car l-h))))as z in(append(cdr(cdr r-h))(make-list(length r-h):initial-element(b(car r-h))))collect(c y(b m-pad)z))))))))))))(defun d(r n)(cons(+(* 2 r)1)(l(cons(c(b r)"^"(b r))())(loop as i from 1 to r collect(c(b(- r i))"/"(subseq(c n(b(expt i 2)))(expt(- i 1)2)(expt i 2))"\\"(b(- r i))))(cons(make-string(+ 1(* 2 r)):initial-element #\-)()))))(defun m(l)(+ 1(floor(sqrt l))))(defun b(n)(make-string n :initial-element #\space))(defun c(&rest a)(apply 'concatenate 'string a))(defun l(&rest a)(apply 'concatenate 'list a))(defun e(tree)(nth 1 tree))

Obrigado ao @coredump por vários truques de golfe. Exemplo de saída da pergunta:

> (f '(("out" (("chr" ("72")) ("chr" ("101")))) ("out" (("chr" ("108")))) ("out" (("chr" ("108")))) ("out" (("chr" ("111"))))))
          ^               ^          ^          ^  
         /o\             /o\        /o\        /o\ 
        /ut \           /ut \      /ut \      /ut \
       /     \         ^-----     ^-----     ^-----
      /       \       /c\        /c\        /c\    
     ^---------^     /hr \      /hr \      /hr \   
    /c\       /c\   ^-----     ^-----     ^-----   
   /hr \     /hr \ /1\        /1\        /1\       
  ^-----    ^-----/08 \      /08 \      /11 \      
 /7\       /1\    -----      -----      -----      
/2  \     /01 \                                    
-----     -----                                    










> (f '( ("+" ( ("asdfghjkl") ("do" ( "1" )) )) ))
          ^        
         /+\       
        /   \      
       /     \     
      /       \    
     /         \   
    ^-----------^  
   /a\         /d\ 
  /sdf\       /o  \
 /ghjkl\     ^-----
/       \   /1\    
---------  /   \   
           -----   








> (f '(("+" ("9123" "3")) "3"))
       ^        ^  
      /+\      /3\ 
     /   \    /   \
    /     \   -----
   ^-------^       
  /9\     /3\      
 /123\   /   \     
/     \  -----     
-------            

Aqui está a versão original (principalmente) não-destruída:

(defun f (input)
    (let ((trees (loop for tree in input collect (g tree)))
          (done nil)
          (output ""))
        (loop while (not done)
            do  (setf done T) 
                (loop for tree in trees
                    do  (if (cdr tree)
                            (progn
                                (setf output (conStr output (car (cdr tree))))
                                (setf (cdr tree) (cdr (cdr tree)))
                                (setf done nil))
                            (setf output (conStr output (blank (car tree))))))
                (setf output (conStr output  (format nil "~%"))))
        output))

;creates a single tree
;output is a list, first element is the length of each line, the rest are the lines of text
(defun g (tree)
    (if (stringp tree)
        ;strings should be drawn as just the pyramid for the name
        (draw-body (min-rows (length tree)) tree)

        (if (< (length tree) 2)
            ;lists with no arguments should be drawn as just the pyramid for the name
            (draw-body (min-rows (length (car tree))) (car tree))
            (if (= (length (car (cdr tree))) 1)
                ;single child
                (let ((child (g (car (car (cdr tree))))) (parent (draw-body (min-rows (length (car tree))) (car tree))))
                    (let ((parent_offset (+ 1 (position #\^ (first-line child)))) (parent_length (car parent)))
                        (if (< (- (car child) parent_offset) parent_length)
                            (let ((child-fill (- parent_length (- (car child) parent_offset))))
                                (concatenate 'list 
                                    (cons (+ parent_offset parent_length) nil)
                                    (loop for line in (butlast (cdr parent))
                                        collect (conStr (blank parent_offset) line))
                                    (cons (conStr (subseq (nth 1 child) 0 parent_offset) (car (last parent))) nil)
                                    (loop for line in (cdr (cdr child))
                                        collect (conStr line (blank child-fill)))))
                            (let ((parent-fill (- (- parent_offset 1) parent_length)))
                                (concatenate 'list 
                                    (cons (car child) nil)
                                    (loop for line in (butlast (cdr parent))
                                        collect (conStr (blank parent_offset) line (blank parent-fill)))
                                    (cons (conStr (subseq (nth 1 child) 0 parent_offset) (car (last parent)) (blank parent-fill)) nil)
                                    (cdr (cdr child)))))))
                ;two children
                (let ((l-child (g (car (car (cdr tree))))) (r-child (g (car (cdr (car (cdr tree)))))))
                    (let ((lc-l-width (position #\^ (first-line l-child))) (rc-l-width (position #\^ (first-line r-child))))
                        (let ((lc-r-width (- (car l-child) lc-l-width 1)) (rc-r-width (- (car r-child) rc-l-width 1)))
                            (let ((parent (draw-body (max (min-rows (length (car tree))) (ceiling (+ lc-r-width rc-l-width) 2)) (car tree))))
                                (let ((m-pad (if (> (car parent) (+ lc-r-width rc-l-width))
                                            (- (car parent) lc-r-width rc-l-width)
                                            0)))
                                    (concatenate 'list
                                        (cons (+ lc-l-width 1 (car parent) 1 rc-r-width) nil)
                                        (loop for line in (butlast (cdr parent))
                                            collect (conStr (blank (+ 1 lc-l-width)) line (blank (+ 1 rc-r-width))))
                                        (cons (conStr (subseq (first-line l-child) 0 (+ 1 lc-l-width)) (car (last parent)) (subseq (first-line r-child) rc-l-width)) nil)
                                        (loop for left in (append (cdr (cdr l-child)) (make-list (length l-child) :initial-element (blank (car l-child))))
                                            for right in (append (cdr (cdr r-child)) (make-list (length r-child) :initial-element (blank (car r-child))))
                                            collect (conStr left (blank m-pad) right))))))))))))


;create a single pyramid
; output is a list, first element is the length of each line, the rest are the lines of text
(defun draw-body (rows name)
    (print rows)
    (print name)
    (cons (+ (* 2 rows) 1)
        (concatenate 'list (cons (conStr (blank rows) "^" (blank rows)) nil)
            (loop for i from 1 to rows
                collect (conStr (blank (- rows i)) "/" (subseq (conStr name (blank (expt i 2))) (expt (- i 1) 2) (expt i 2)) "\\" (blank (- rows i))))
            (cons (make-string (+ 1 (* 2 rows)) :initial-element #\-) nil))))

(defun min-rows (l)
    (+ 1 (floor (sqrt l))))

(defun blank (n)
    (make-string n :initial-element #\space))

(defun conStr (&rest args)
    (apply 'concatenate 'string args))

(defun first-line (tree)
    (car (cdr tree)))

Experimente Online!

Neil Lindquist
fonte
Você deve conseguir obter muitos bytes removendo espaços desnecessários.
Clismique
2
Bem-vindo ao PPCG e boa primeira resposta!
Kritixi Lithos
Algumas dicas para jogar golfe no CL: em loops, "para" também pode ser escrito "como"; você pode remover espaços antes e depois dos parênteses e aspas duplas; você pode substituir NIL por (); você também pode usar variáveis leitor, às vezes
coredump
... loop while (not x)é loop until x, (cdr (cdr x))é (cddr x), (setf a b c d)é mais curto do que (setf a b)seguido por (setf c d), etc. Mas isso já é uma resposta boa
coredump
2
Uma recompensa total de 350 reputação é significativa ... mas essa resposta merece. Uma resposta comum do Lisp para uma pergunta sobre a construção de perguntas para um dialeto Lisp ... Uau.
Wizzwizz4