Crop ASCII Art Challenge

13

El arte ASCII es divertido. Los editores de texto modernos son muy buenos manipulando texto. ¿Los lenguajes de programación modernos están a la altura?

Una tarea común en la manipulación de arte ASCII es recortar texto en un rectángulo entre dos caracteres. Esta es la tarea que debe implementar en este desafío.

Detalles

Su programa tomará 3 entradas:

  • el primero es el carácter de "inicio" del bloque, que marca la esquina superior izquierda
  • el segundo es el carácter 'final' del bloque, que marca la esquina inferior derecha
  • el tercero es alguna forma de texto de varias líneas, ya sea una cadena, o una lista de cadenas, o nombre de archivo, o lo que sea

El resultado será texto multilínea (nuevamente, en cualquiera de los formatos anteriores) recortado en el rectángulo entre las entradas dadas. Tenga en cuenta que las dos primeras entradas pueden no ser únicas.

Casos de borde

Las cajas siempre deben tener un volumen de al menos 2. Por lo tanto, estas:

()     (
       )

son cajas pero estas:

)(     )      (
       (     )

no lo son (con inicio = (y fin = )).

La entrada solo contendrá una caja. Por lo tanto, los caracteres iniciales y finales solo deben aparecer una vez, a menos que sean el mismo carácter, en cuyo caso deben aparecer exactamente dos veces.

Además, cada línea en la entrada debe ser al menos tan larga como la distancia desde el inicio de una línea hasta el borde derecho del cuadro en la entrada.

Su programa no necesita manejar entradas inválidas; pueden dar lugar a un comportamiento indefinido.

Reglas

Se aplican las reglas típicas de código de golf. El código más corto gana.

Ejemplos

Día soleado: start: ( end: ) input:

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

Salida:

(but this text
is in a box  )

Tenga en cuenta la eliminación del espacio horizontal también. Los cultivos de arte ASCII son 2d.

Día lluvioso: start: ( end: ) input:

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

Salida:

(
)

Mismo inicio / final: start: / end: / input:

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

Salida:

/----------------------------\
| 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
fuente
¿Qué pasa con un cuadro válido con una línea externa que es más corta que el cuadro?
seadoggie01
1
aclarado, también entrada no válida
LambdaBeta
¿Cuál debería ser el resultado en caso de una entrada no válida? o se mencionan para que no tengan que ser atendidos?
Uriel
1
El resultado es muy parecido al comportamiento indefinido en C, no te preocupes por eso, todo vale.
LambdaBeta
Este es un pequeño desafío desagradable: ¡buen trabajo!
seadoggie01

Respuestas:

15

Vim, 16 , 12 bytes / pulsaciones de teclas

#<C-v>Nj*yggVGp

Pruébalo en línea! en el intérprete V

Los editores de texto modernos son muy buenos manipulando texto. ¿Los lenguajes de programación modernos están a la altura?

¡Apuesto a que los viejos editores de texto son aún mejores! :RE

Aunque no necesariamente tiene que hacerlo, esta respuesta funciona con las dos entradas "inválidas" dadas, generando

 rectangular (
) which is ill

y

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

Explicación:

#               " 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
fuente
1
Por cierto, este es exactamente el caso de uso que estaba haciendo para incitarme a escribir este desafío. Tuve mi macro q como /\/<cr><c-v>nygv$o0dpo algo así durante demasiado tiempo :)
LambdaBeta
2
Sí, rectangular es el más illest !
AdmBorkBork
6

Jalea , 13 bytes

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

Un enlace diádico que acepta una lista de caracteres iniciales y finales a la izquierda y una lista de líneas (como listas de caracteres) a la derecha que produce una lista de líneas (como listas de caracteres).

Pruébalo en línea! (programa completo: si las entradas son Python válidas, requerirán el uso de comillas).

¿Cómo?

=€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 ejemplo, con left = ['a', 'b']y right (como una lista de listas de caracteres: las líneas):

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

=€produce una lista de dos listas de listas (la primera realiza 'a'=, la segunda 'b'=):

00000000         00000000
00100000         00000000
00000000    ,    00000000
00000000         00000100
00000000         00000000

sumando esto produce una sola lista de listas (sumando elementos sabios):

00000000
00100000
00000000
00000100
00000000

ŒṪluego nos da los índices multidimensionales (1 indexados) de los no ceros, [[2,3],[4,6]]es decir [[top,left],[bottom,right]].

r/luego realiza lo [2,3]r[4,6]que, como rvectoriza, es como [2r4, 3r6]evaluar a [[2,3,4],[3,4,5,6]], es decir [rows,columns].

,þ/luego realiza [2,3,4],þ[3,4,5,6]donde þes una instrucción de podio externo y ,es par. Esto produce todos los [row,column]valores por columna, en este 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 estos por fila, por lo que Zse utiliza para transponer esto a:

[[[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 los œịíndices vuelven a las líneas de entrada:

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

Vale la pena señalar que cuando ambos caracteres delimitadores son iguales, los =€identifica dos veces pero SŒṪtermina haciendo lo correcto, ya que 2es verdad, por ejemplo, con ['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
fuente
... leí la explicación, pero todavía no la entiendo. o_o ¿Podría agregar un ejemplo trabajado, tal vez?
DLosc
Incentivización: aceptaré su respuesta si se explica completamente. :)
LambdaBeta
1
@DLosc - hecho, espero que ayude.
Jonathan Allan
@LambdaBeta: la respuesta V es más corta.
Jonathan Allan
... err la respuesta de Vim incluso.
Jonathan Allan
5

APL (Dyalog) , 38 30 bytes

4 bytes guardados gracias a @EriktheOutgolfer

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

Pruébalo en línea!

Uriel
fuente
demasiado complicado. usted podría aceptar una matriz en lugar de vectores de vectores, encontrar las dos posiciones con ⍸matrix∊separators, y hacer de tener / gota con ellos
NGN
(⍸a=⎕)↓(1+⍸a=⎕)↑a←⎕con⎕io←0
ngn
@ngn OP dijo que el número de caracteres difiere entre líneas, por lo que supuse que la entrada debería ser vectorial antes del procesamiento. Aun así, necesito las partes de selección (primero y rotar) en caso de que el delimitador se muestre varias veces (ver el tercer caso de prueba), pero supongo que la caída reduce algunos bytes, ¡así que gracias! Actualizaré una vez que llegue a la PC
Uriel
Uy ... Me olvidé del tercer caso, lo siento. Entonces tal vez: ⊃{⌽⊖⍵↓⍨⊃⍸⍺=⍵}/⎕⎕⎕(sic, con 3 quads finales) que es aún más corto. O ... ⎕⎕(↑⎕)si no se permite una matriz premezclada.
ngn
3

Jalea , 14 bytes

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

Pruébalo en línea!

Erik el Outgolfer
fuente
Intenté ejecutar su código en algunos de los otros ejemplos, pero solo tuve un bloqueo. ¿Es eso un error o solo estoy haciendo algo mal?
Ilmari Karonen
@IlmariKaronen No ha citado el segundo argumento correctamente (no lo mencionó en la publicación); envuélvelo entre comillas simples o dobles. De la forma en que lo ha invocado, el segundo argumento es una tupla ( ()) vacía (Python ), no '()'. Si es parseable, necesita ser citado, sin embargo, my //no necesita ser citado (operador de división entera sin operandos? Hm ...).
Erik the Outgolfer
@IlmariKaronen "Creo" que ()Jelly lo está interpretando como una especie de personaje especial. La mayoría de los pares de personajes que intento funcionan. Me encantaría saber qué piensan las personas más familiarizadas con Jelly. EDITAR: ninja-ed por erik the outgolfer
LambdaBeta
OK, sí, eso funciona.
Ilmari Karonen
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]

Pruébalo en línea!

TFeld
fuente
2

Lienzo , 37 bytes.

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

Pruébalo aquí!

36 bytes para obtener las coordenadas de los caracteres (y convertirlos a x, y, w, h porque eso es lo que se necesita) y 1 byte para obtener la subsección. Debe haber un mejor enfoque

dzaima
fuente
2

JavaScript (ES6), 98 bytes

Toma la entrada como dos enteros y una serie de cadenas. Devuelve una matriz de cadenas.

(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))

Pruébalo en línea!

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
fuente
filter y map ?! ¿Construir una nueva matriz reduceo una solución recursiva no funcionaría más corto? En mi teléfono, en el pub o lo probaría yo mismo.
Shaggy
@Shaggy Probablemente haya un camino más corto, pero creo que este método está condenado a usar 2 pases: el segundo bucle no puede comenzar antes de que termine el primero y ambos, Xy Yson seguros.
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 la matriz de entrada en lugar de devolver una nueva para guardar bytes. Sin embargo, esto significa que las líneas eliminadas se convierten en su ""lugar. Si esto no está permitido, lo cambiaré.

Pruébalo en línea.

Explicación:

(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 ejemplo:

Con las entradas start = "(", end = ")"ylines =

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

el primer bucle lo recortará en la parte superior e izquierda, cambiándolo a esto:

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

el segundo ciclo lo recortará en la parte inferior y derecha, cambiándolo a esto:

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

Retina 0.8.2 , 110 bytes

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

Pruébalo en línea! Explicación:

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

Elimine las líneas de entrada que preceden a la primera línea del cuadro.

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

Si el carácter inicial está en la columna izquierda de la entrada, elimínelo.

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

Si el carácter inicial aún no se ha eliminado, cambie todas las columnas de entrada a la izquierda por una y repita desde el principio.

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

Elimine el carácter final y todo en la entrada después del carácter final.

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

Truncar todas las líneas a la longitud de la siguiente línea. Esto funciona contando todos los caracteres menos uno en cada línea, y luego tratando de hacer coincidir tantos caracteres en la línea siguiente; Si esto tiene éxito, la segunda línea es más corta, por lo que se elimina el último carácter y se repite el ciclo.

Neil
fuente
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;}}

Pruébalo en línea!

Estoy 99% seguro de que esto se puede acortar usando algún tipo de función auxiliar para encontrar el índice horizontal y el puntero a un personaje, ya que se repite dos veces. Por desgracia, no pude encontrar una manera lo suficientemente corta para hacerlo, puedo intentarlo más tarde si encuentro el tiempo.

Descripción

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
fuente
1
Aún mejor: 219 bytes
Zacharý
0

Stax , 15 bytes

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

Ejecutar y depurarlo

Toma el conjunto de caracteres delimitadores de caja (1 o 2) en la primera línea de entrada. El resto de las líneas son el cuerpo de entrada.

Desempaquetado, sin golf y comentado, se ve así.

            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

Ejecute este

recursivo
fuente