Crop ASCII Art Challenge

13

A arte ASCII é divertida. Os editores de texto modernos são muito bons em manipular o texto. As linguagens de programação modernas estão à altura da tarefa?

Uma tarefa comum na manipulação de arte ASCII é cortar o texto em um retângulo entre dois caracteres. Esta é a tarefa que você deve implementar neste desafio.

Detalhes

Seu programa terá três entradas:

  • o primeiro é o caractere 'start' do bloco - marcando o canto superior esquerdo
  • o segundo é o caractere 'final' do bloco - marcando o canto inferior direito
  • a terceira é uma forma de texto com várias linhas, uma string ou lista de strings, ou nome do arquivo ou qualquer outra coisa

O resultado será um texto com múltiplas linhas (novamente, em qualquer um dos formatos acima) recortado no retângulo entre as entradas fornecidas. Observe que as duas primeiras entradas podem não ser exclusivas.

Casos de borda

As caixas devem sempre ter volume pelo menos 2. Assim, estas:

()     (
       )

são caixas, mas estas:

)(     )      (
       (     )

não são (com início = (e fim = )).

A entrada conterá apenas uma caixa. Portanto, os caracteres inicial e final devem ocorrer apenas uma vez, a menos que sejam o mesmo caractere; nesse caso, devem ocorrer exatamente duas vezes.

Além disso, cada linha na entrada deve ter pelo menos a distância entre o início de uma linha e a borda direita da caixa na entrada.

Seu programa não precisa manipular entradas inválidas; eles podem resultar em comportamento indefinido.

Regras

Aplicam-se regras típicas de código-golfe. O menor código vence.

Exemplos

Dia ensolarado: start: ( end: ) input:

This is some text
. (but this text
  is in a box  ).
So only it is important.

Resultado:

(but this text
is in a box  )

Observe a remoção do espaço horizontal também. As culturas artísticas ASCII são 2d.

Dia chuvoso: start: ( end: ) input:

This is some text (
But is that even  )
really a box?

Resultado:

(
)

Mesmo começo / fim: start: / end: / input:

Oh, I get how this could be useful
 /----------------------------\
 | All this text is in a box! |
 \----------------------------/

Resultado:

/----------------------------\
| All this text is in a box! |
\----------------------------/

Entrada inválida: start: ( end: ) input:

Boxes are rectangular ( so this has
0 volume ) which is illegal.

Entrada inválida 2: start: ( end: ) input:

(The lines must already be square 
so this line that is too short
relative to this end, is illegal)
LambdaBeta
fonte
Que tal uma caixa válida com uma linha externa menor que a caixa?
seadoggie01
1
esclareceu, também entrada inválida
LambdaBeta
qual deve ser o resultado no caso de uma entrada inválida? ou são mencionados para que não precisem ser resolvidos?
Uriel
1
O resultado é muito parecido com o comportamento indefinido em C, não se preocupe com isso, vale tudo.
LambdaBeta 13/09
Este é um pequeno desafio desagradável: bom trabalho!
precisa saber é o seguinte

Respostas:

15

Vim, 16 , 12 bytes / pressionamentos de tecla

#<C-v>Nj*yggVGp

Experimente online! no intérprete V

Os editores de texto modernos são muito bons em manipular o texto. As linguagens de programação modernas estão à altura da tarefa?

Aposto que editores de texto antigos são ainda melhores! : D

Mesmo que não seja necessariamente necessário, esta resposta funciona com as duas entradas "inválidas" fornecidas, gerando

 rectangular (
) which is ill

e

(The lines must already be square
so this line that is too short
relative to this end, is illegal)

Explicação:

#               " Move backward to the previous occurrence of the word (or in this case, character) under the cursor
 <C-v>          " Start a visual block selection
      N         " Go to the next occurrence of the last searched term (guaranteed to be line 1)
       j        " Move down a line
        *       " Move forward to the next occurrence of the character under the cursor
         y      " Yank (copy) the whole visually selected block
          gg    " Go to line 1
            VG  " Select every line
              p " And paste what we last copied over it, deleting the whole buffer and replacing it with the block
James
fonte
1
Aliás, esse é exatamente o caso de uso que eu estava fazendo para solicitar que eu escrevesse esse desafio. Eu tive a minha q macro como /\/<cr><c-v>nygv$o0dpou algo assim por muito tempo :)
LambdaBeta
2
Sim, retangular é o mais doente !
AdmBorkBork 13/09
6

Gelatina , 13 bytes

=€SŒṪr/,þ/Zœị

Um link diádico que aceita uma lista de caracteres iniciais e finais à esquerda e uma lista de linhas (como listas de caracteres) à direita, que produz uma lista de linhas (como listas de caracteres).

Experimente online! (programa completo - se as entradas forem válidas para Python, elas exigirão citações em string de Python.)

Quão?

=€SŒṪr/,þ/Zœị - Link: [start, stop], lines
 €            - for each (of [start, stop]):
=             -   equals? (vectorises across the lines)
  S           - sum (vectorises)
   ŒṪ         - multi-dimensional truthy (i.e. non-zero) indices
      /       - reduce by:
     r        -   inclusive range (vectorises)
         /    - reduce by:
        þ     -    outer product with:
       ,      -       pair
          Z   - transpose
           œị - multi-dimensional index-into (the lines)

Como exemplo, com esquerda = ['a', 'b']e direita (como uma lista de listas de caracteres - as linhas):

--------
--a+++--
--++++--
--+++b--
--------

=€produz uma lista de duas listas de listas (a primeira executa 'a'=, a segunda 'b'=):

00000000         00000000
00100000         00000000
00000000    ,    00000000
00000000         00000100
00000000         00000000

somar isso gera uma única lista de listas (somar elemento a elemento):

00000000
00100000
00000000
00000100
00000000

ŒṪentão nos fornece os índices multidimensionais (indexados 1) dos não-zeros, [[2,3],[4,6]]- ie [[top,left],[bottom,right]].

r/então executa o [2,3]r[4,6]que, desde rvetoriza, é como [2r4, 3r6], avaliando para [[2,3,4],[3,4,5,6]]- ie [rows,columns].

,þ/em seguida, executa [2,3,4],þ[3,4,5,6]where þé uma instrução de poduto externo e ,é par. Isso gera todos os [row,column]valores por coluna, neste caso:

[[[2,3],[3,3],[4,3]],
 [[2,4],[3,4],[4,4]],
 [[2,5],[3,5],[4,5]],
 [[2,6],[3,6],[4,6]]]

Queremos isso por linha, então Zé usado para transpor isso para:

[[[2,3],[2,4],[2,5],[2,6]],
 [[3,3],[3,4],[3,5],[3,6]],
 [[4,3],[4,4],[4,5],[4,6]]]

Finalmente, os œịíndices retornam para as linhas de entrada:

a+++
++++
+++b

Vale ressaltar que, quando os dois caracteres delimitadores são os mesmos, =€identifica-os duas vezes, mas SŒṪacaba fazendo a coisa certa, pois 2é verdade, por exemplo, com ['a','a']:

--------         00000000   00000000        00000000
--a+++--         00100000   00100000        00200000
--++++--  =€ ->  00000000 , 00000000  S ->  00000000  ŒṪ ->  [[2,3],[4,6]]
--+++a--         00000100   00000100        00000020
--------         00000000   00000000        00000000
Jonathan Allan
fonte
... Eu li a explicação, mas ainda não a entendo. o_o Você poderia adicionar um exemplo trabalhado, talvez?
DLosc
Incentivização: aceitarei sua resposta se for totalmente explicada. :)
LambdaBeta
1
@DLosc - pronto, espero que ajude.
Jonathan Allan
@LambdaBeta - a resposta V é mais curta.
Jonathan Allan
... errar a resposta Vim mesmo.
Jonathan Allan
5

APL (Dyalog) , 38 30 bytes

4 bytes salvos graças a @EriktheOutgolfer

(1-⍨w-⍨⊃⍸⎕=s)↑(w←∊⊃⌽⍸⎕=s)↑s←↑⎕

Experimente online!

Uriel
fonte
muito complicado. você poderia aceitar uma matriz em vez de vetor de vetores, encontrar as duas posições com ⍸matrix∊separators, e fazer take / queda com eles
NGN
(⍸a=⎕)↓(1+⍸a=⎕)↑a←⎕with⎕io←0
ngn 14/09/18
@ngn OP disse que o número de caracteres difere entre as linhas, então eu assumi que a entrada deveria ser vetorial antes do processamento. Mesmo assim, preciso das partes de seleção (primeiro e girar), caso o delimitador seja exibido algumas vezes (consulte o terceiro caso de teste), mas acho que a queda corta alguns bytes, então obrigado! Eu vou atualizar quando eu chegar para PC
Uriel
oops ... esqueci o terceiro caso, desculpe. Então talvez: ⊃{⌽⊖⍵↓⍨⊃⍸⍺=⍵}/⎕⎕⎕(sic, com 3 quads à direita), que é ainda mais curto. Ou ... ⎕⎕(↑⎕)se uma matriz pré-misturada não for permitida.
ngn 14/09/18
3

Gelatina , 14 bytes

œẹⱮẎQr/Ṛṭþ/œị⁸

Experimente online!

Erik, o Outgolfer
fonte
Tentei executar o seu código em alguns dos outros exemplos, mas só tive uma falha. Isso é um bug, ou estou apenas fazendo algo errado?
Ilmari Karonen 13/09/18
@IlmariKaronen Você não citou o segundo argumento corretamente (não mencionou isso no post); envolva-o entre aspas simples ou duplas. Da maneira que você o invocou, o segundo argumento é uma tupla ( ()) vazia (Python ), não '()'. Se for analisável, ele precisa ser citado, no entanto, o meu //não precisa ser citado (operador de divisão inteira sem operandos? Hm ...).
Erik the Outgolfer
@IlmariKaronen Eu acho que isso ()está sendo interpretado por Jelly como algum tipo de personagem especial. A maioria dos pares de caracteres que eu tento trabalhar. Eu adoraria ouvir o que as pessoas mais familiarizadas com Jelly pensam. EDIT: ninja-ed por erik the outgolfer
LambdaBeta 13/09
OK, sim, isso funciona.
Ilmari Karonen 13/09/18
3

Python 2 , 130 bytes

def f(s,e,t):
 a=[(i,l.find(c))for i,l in enumerate(t)for c in s+e if c in l];r,c,R,C=a[0]+a[-1]
 for l in t[r:R+1]:print l[c:C+1]

Experimente online!

TFeld
fonte
2

Tela , 37 bytes

{³⁴⁰;x≡‽┐
X⁸)J╵⁶;┤ω┤⁵X⁶⁸⁰K├;┐┤└∔┘┘∔;@

Experimente aqui!

36 bytes para obter as coordenadas dos caracteres (e convertê-los em x, y, w, h porque é isso que leva) e 1 byte para obter a subseção. Deve haver uma abordagem melhor

dzaima
fonte
2

JavaScript (ES6), 98 bytes

Recebe a entrada como dois números inteiros e uma matriz de seqüências de caracteres. Retorna uma matriz de strings.

(x,y,a,X=Y=0)=>a.filter(s=>!Y&&(Y=-~s.indexOf(y,X?X-1:X=-~s.indexOf(x)),X)).map(s=>s.slice(X-1,Y))

Experimente online!

Comentado

( x,                          // x = start character
  y,                          // y = end character
  a,                          // a[] = array of strings
  X =                         // X = position of x, plus 1
  Y = 0                       // Y = position of y, plus 1
) =>                          //
  a.filter(s =>               // for each string s in a[]:
    !Y &&                     //   reject this string if Y is non-zero
    (                         //   otherwise, use the 2nd condition:
      Y = -~s.indexOf(        //     update Y:
        y,                    //       by looking for y in s
        X ?                   //       if X is non-zero:
          X - 1               //         start the search at X - 1
        :                     //       else:
          X = -~s.indexOf(x)  //         update X and start the search at X
      ),                      //     end of Y update
      X                       //     keep this string if X is non-zero
    )                         //   end of 2nd condition
  )                           // end of filter()
  .map(s =>                   // for each remaining string s:
    s.slice(X - 1, Y)         //   remove left and right characters outside the box
  )                           // end of map()
Arnauld
fonte
filter e map ?! Criar uma nova matriz reduceou uma solução recursiva não funcionaria mais curto? No meu telefone, no pub ou eu mesmo daria uma chance.
Shaggy
@ Shaggy Provavelmente existe uma maneira mais curta, mas acho que esse método está fadado a usar 2 passes: o segundo loop não pode ser iniciado antes que o primeiro termine e ambos Xe Ysejam conhecidos com certeza.
Arnauld
2

Java 10, 204 bytes

(s,e,a)->{int b=-1,i=0;for(;i<a.length;i++)a[i]=(b=b<0?a[i].indexOf(s):b)<0|a[i].length()<b?"":a[i].substring(b);for(b=-1;i-->0;)a[i]=(b=b<0?a[i].indexOf(e):b)<0|a[i].length()<b?"":a[i].substring(0,b+1);}

Modifica a matriz de entrada em vez de retornar uma nova para salvar bytes. Isso significa que as linhas removidas se tornam "", em vez disso. Se isso não for permitido, vou alterá-lo.

Experimente online.

Explicação:

(s,e,a)->{                 // Method with 2 Strings & String-array parameters and no return
  int b=-1,                //  Boundaries-integer, starting at -1
  i=0;for(;i<a.length;i++) //  Loop `i` in the range [0, amountOfLines)
    a[i]=                  //   Change the `i`th line in the array to:
      (b=b<0?              //    If `b` is -1:
          a[i].indexOf(s)  //     Set `b` to the index of `s` in the current line
                           //     (which is still -1 if it's not found)
         :                 //    Else (starting index already found)
          b                //     Leave `b` unchanged
      )<0                  //    Then, if `b` is -1,
         |a[i].length()<b? //    or the current line-length is too short:
       ""                  //     Remove the current line
      :                    //    Else:
       a[i].substring(b);  //     Shorten the line by removing every character before `b`
  for(b=-1;                //  Reset `b` to -1
      i-->0;)              //  Loop `i` in the range (amountOfLines, 0]
    a[i]=                  //  Change the `i`th line in the array to:
       (b=b<0?a[i].indexOf(e):b)<0|a[i].length()<b?"":
                           //   Similar as above (with end `e` instead of start `s`),
         a[i].substring(0,b+1);}
                           //   except we remove every character after `b` this time

Por exemplo:

Com as entradas start = "(", end = ")"elines =

["This is some text",
 ". (but this text",
 "  is in a box  ).",
 "So only it is important."]

o primeiro loop irá cortá-lo na parte superior e esquerda, alterando-o para este:

["",
 "(but this text",
 "is in a box  ).",
 " only it is important."]

o segundo loop irá cortá-lo na parte inferior e direita, alterando-o para este:

["",
 "(but this text",
 "is in a box  )",
 ""]
Kevin Cruijssen
fonte
1

Retina 0.8.2 , 110 bytes

^((.)¶.)(.*¶)+(.*\2)
$1¶$4
^(.)(¶.¶\1)
$2
}s`(?<=^.¶.+)¶.
¶
s`^¶(.)¶(.*\1).*
$2
+m`^((.)+).¶((?<-2>.)+)$
$1¶$3

Experimente online! Explicação:

^((.)¶.)(.*¶)+(.*\2)
$1¶$4

Exclua as linhas de entrada anteriores à primeira linha da caixa.

^(.)(¶.¶\1)
$2

Se o caractere inicial estiver na coluna esquerda da entrada, exclua-o.

}s`(?<=^.¶.+)¶.
¶

Se o caractere inicial ainda não tiver sido excluído, altere todas as colunas de entrada deixadas por uma e repita desde o início.

s`^¶(.)¶(.*\1).*
$2

Exclua o caractere final e tudo na entrada após o caractere final.

+m`^((.)+).¶((?<-2>.)+)$
$1¶$3

Trunque todas as linhas para o comprimento da próxima linha. Isso funciona contando todos os caracteres, exceto um, em cada linha e, em seguida, tentando corresponder a tantos caracteres na linha seguinte; se for bem-sucedido, a segunda linha será mais curta; o último caractere será excluído e o loop será repetido.

Neil
fonte
0

C (gcc) , 237 bytes

f(c,r,o,p)char*p,*c;{char*_=strchr(p,r),*a,b;*_=0;a=strrchr(p,10);a=(a?a:p);*_=r;r=_-a;p=a;_=strrchr(p,o);*_=0;a=strrchr(p,10);a=(a?a:p);*_=o;o=_-a+1;_[1]=0;for(_=p;_;_=strchr(_+1,10)){b=_[o];_[o]=0;strcat(c,_+r);strcat(c,"\n");_[o]=b;}}

Experimente online!

Tenho 99% de certeza de que isso pode ser reduzido usando algum tipo de função auxiliar para encontrar o índice horizontal e o ponteiro para um caractere, pois é repetido duas vezes. Infelizmente, não consegui encontrar uma maneira curta o suficiente para fazê-lo; posso tentar novamente mais tarde, se encontrar tempo.

Descrição

f(c,r,o,p)char*p,*c;{
    char*_=strchr(p,r),*a,b;         // find opening char (and declare vars)
    *_=0;a=strrchr(p,10);            // find \n before it
    a=(a?a:p);                       // deal with single line inputs
    *_=r;r=_-a;                      // save left margin width in r
    p=a;                             // crop everything before opening line

    _=strchr(p,o);                   // find closing char
    *_=0;a=strrchr(p,10);            // find \n before it
    a=(a?a:p);                       // deal with single line inputs
    *_=o;o=_-a+1;                    // save width in o
    _[1]=0;                          // crop everything after closing char
    for(_=p;_;_=strchr(_+1,10)){       // for each line
        b=_[o];_[o]=0;
        strcat(c,_+r);
        strcat(c,"\n");
        _[o]=b;
    }
}
LambdaBeta
fonte
1
Ainda melhor: 219 bytes
Zacharý 16/11
0

Stax , 15 bytes

╛↨½╝v∞░W╧)╗Ö≈☼k

Execute e depure

Leva o conjunto de caracteres delimitadores de caixa (1 ou 2) na primeira linha de entrada. O restante das linhas é o corpo da entrada.

Descompactado, não jogado e comentado, é assim.

            first line of input is the delimiter characters
dL          discard the first line of input and listify the rest into an array
{           begin block for iteration
  Mr        rotate matrix 90 degrees
  {         begin block for while loop
    ch      copy first row of block
    y|&C    if it insersects with the first line of input, break iteration
    D       drop the first line
  W         do-while loop until break
}4*         execute block 4 times
m           display result lines

Execute este

recursivo
fonte