Crop ASCII Art Challenge

13

L'art ASCII est amusant. Les éditeurs de texte modernes sont très bons pour manipuler le texte. Les langages de programmation modernes sont-ils à la hauteur?

Une tâche courante dans la manipulation d'œuvres d'art ASCII consiste à rogner le texte dans un rectangle entre deux caractères. C'est la tâche que vous devez mettre en œuvre dans ce défi.

Détails

Votre programme prendra 3 entrées:

  • le premier est le caractère 'start' du bloc - marquant le coin supérieur gauche
  • le second est le caractère «fin» du bloc - marquant le coin inférieur droit
  • le troisième est une forme de texte multiligne, soit une chaîne, soit une liste de chaînes, soit un nom de fichier, ou autre

Le résultat sera un texte multiligne (encore une fois, dans l'un des formats ci-dessus) rogné dans le rectangle entre les entrées données. Notez que les deux premières entrées peuvent ne pas être uniques.

Cas de bord

Les boîtes doivent toujours avoir un volume d'au moins 2. Ainsi:

()     (
       )

sont des boîtes mais celles-ci:

)(     )      (
       (     )

ne le sont pas (avec start = (et end =) ).

L'entrée ne contiendra qu'une seule case. Ainsi, les caractères de début et de fin ne doivent apparaître qu'une seule fois, sauf s'ils sont identiques, auquel cas ils doivent apparaître exactement deux fois.

De plus, chaque ligne de l'entrée doit être au moins aussi longue que la distance entre le début d'une ligne et le bord droit de la zone de l'entrée.

Votre programme n'a pas besoin de gérer des entrées invalides; ils peuvent entraîner un comportement indéfini.

Règles

Des règles de code-golf typiques s'appliquent. Le code le plus court gagne.

Exemples

Journée ensoleillée: start: ( end: ) input:

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

Production:

(but this text
is in a box  )

Notez également le dépouillement de l'espace horizontal. Les cultures d'art ASCII sont 2D.

Jour de pluie: start: ( end: ) input:

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

Production:

(
)

Même début / fin: start: / end: / input:

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

Production:

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

Entrée invalide: start: ( end: ) input:

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

Entrée non valide 2: start: ( end: ) input:

(The lines must already be square 
so this line that is too short
relative to this end, is illegal)
LambdaBeta
la source
Qu'en est-il d'une boîte valide avec une ligne extérieure plus courte que la boîte?
seadoggie01
1
clarifié, également entrée invalide
LambdaBeta
quel devrait être le résultat en cas de saisie invalide? ou sont-ils mentionnés pour ne pas être pris en charge?
Uriel
1
Le résultat ressemble beaucoup à un comportement indéfini en C, ne vous inquiétez pas, tout se passe.
LambdaBeta
C'est un petit défi désagréable: beau travail!
seadoggie01

Réponses:

15

Vim, 16 , 12 octets / touches

#<C-v>Nj*yggVGp

Essayez-le en ligne! dans l'interpréteur V

Les éditeurs de texte modernes sont très bons pour manipuler le texte. Les langages de programmation modernes sont-ils à la hauteur?

Je parie que les anciens éditeurs de texte sont encore meilleurs! :RÉ

Même si ce n'est pas nécessairement le cas, cette réponse fonctionne avec les deux entrées "invalides" données,

 rectangular (
) which is ill

et

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

Explication:

#               " 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
la source
1
Soit dit en passant, c'est exactement le cas d'utilisation que je faisais pour m'inviter à écrire ce défi. J'ai eu ma macro q /\/<cr><c-v>nygv$o0dpou quelque chose comme ça depuis trop longtemps :)
LambdaBeta
2
Ouais, le rectangulaire est le plus malade !
AdmBorkBork
6

Gelée , 13 octets

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

Un lien dyadique acceptant une liste de caractères de début et de fin à gauche et une liste de lignes (sous forme de listes de caractères) à droite qui donne une liste de lignes (sous forme de listes de caractères).

Essayez-le en ligne! (programme complet - si les entrées sont en Python valide, elles nécessiteront des guillemets de chaîne Python.)

Comment?

=€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)

Par exemple, avec left = ['a', 'b']et right (comme une liste de listes de caractères - les lignes):

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

=€renvoie une liste de deux listes de listes (la première effectue 'a'=, la seconde 'b'=):

00000000         00000000
00100000         00000000
00000000    ,    00000000
00000000         00000100
00000000         00000000

la sommation donne une seule liste de listes (sommation par élément):

00000000
00100000
00000000
00000100
00000000

ŒṪnous donne ensuite les indices multidimensionnels (1-indexés) des non-zéros, [[2,3],[4,6]]c'est-à-dire [[top,left],[bottom,right]].

r/effectue ensuite [2,3]r[4,6]qui, puisque rvectorise, est comme [2r4, 3r6], évaluant à [[2,3,4],[3,4,5,6]]- ie [rows,columns].

,þ/exécute alors [2,3,4],þ[3,4,5,6]þest une instruction de poduct externe et ,est paire. Cela donne toutes les [row,column]valeurs par colonne, dans ce cas:

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

Nous voulons ceux-ci par ligne, donc Zest utilisé pour transposer ceci en:

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

Enfin œịindexe de nouveau dans les lignes d'entrée:

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

Il convient de noter que lorsque les deux caractères de délimitation sont identiques, ils =€identifient les deux fois mais SŒṪfinissent par faire la bonne chose, car 2c'est vrai, par exemple avec ['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
la source
... J'ai lu l'explication, mais je ne la comprends toujours pas. o_o Pourriez-vous ajouter un exemple concret, peut-être?
DLosc
Incitation: j'accepterai votre réponse si elle est entièrement expliquée. :)
LambdaBeta
1
@DLosc - terminé, j'espère que cela aide.
Jonathan Allan
@LambdaBeta - la réponse V est plus courte.
Jonathan Allan
... trompe même la réponse de Vim.
Jonathan Allan
5

APL (Dyalog) , 38 30 octets

4 octets enregistrés grâce à @EriktheOutgolfer

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

Essayez-le en ligne!

Uriel
la source
trop compliqué. vous pouvez accepter une matrice au lieu d'un vecteur de vecteurs, trouver les deux positions avec ⍸matrix∊separators, et prendre / déposer avec elles
ngn
(⍸a=⎕)↓(1+⍸a=⎕)↑a←⎕avec⎕io←0
ngn
@ngn OP dit que le nombre de caractères diffère entre les lignes, j'ai donc supposé que l'entrée devait être vectorielle avant le traitement. Malgré tout, j'ai besoin des parties de sélection (d'abord et pivoter) au cas où le délimiteur s'affiche plusieurs fois (voir le troisième cas de test), mais je suppose que la goutte coupe quelques octets, alors merci! Je mettrai à jour une fois que j'arriverai sur PC
Uriel
oups ... J'ai oublié le troisième cas, désolé. Alors peut-être: ⊃{⌽⊖⍵↓⍨⊃⍸⍺=⍵}/⎕⎕⎕(sic, avec 3 quads de fin) qui est encore plus court. Ou ... ⎕⎕(↑⎕)si une matrice prémélangée n'est pas autorisée.
ngn
3

Gelée , 14 octets

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

Essayez-le en ligne!

Erik le Outgolfer
la source
J'ai essayé d'exécuter votre code sur certains des autres exemples, mais je n'ai eu qu'un plantage. Est-ce un bug ou est-ce que je fais juste quelque chose de mal?
Ilmari Karonen
@IlmariKaronen Vous n'avez pas correctement cité le deuxième argument (vous ne l'avez pas mentionné dans le billet); enveloppez-le entre guillemets simples ou doubles. Comme vous l'avez invoqué, le deuxième argument est un tuple ( ()) vide (Python ), non '()'. S'il est analysable, il doit être cité, cependant, mon //n'a pas besoin d'être cité (opérateur de division entier sans opérandes? Hm ...).
Erik the Outgolfer
@IlmariKaronen Je "pense" que ()Jelly vient juste de l'interpréter comme une sorte de personnage spécial. La plupart des paires de personnages que j'essaie de travailler. J'adorerais entendre ce que les gens plus familiers avec Jelly pensent. EDIT: ninja-ed par erik the outgolfer
LambdaBeta
OK, ouais, ça marche.
Ilmari Karonen
3

Python 2 , 130 octets

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]

Essayez-le en ligne!

TFeld
la source
2

Toile , 37 octets

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

Essayez-le ici!

36 octets pour obtenir les coordonnées des caractères (et les convertir en x, y, w, h car c'est ce qu'il faut) et 1 octet pour obtenir la sous-section. Il doit y avoir une meilleure approche

dzaima
la source
2

JavaScript (ES6), 98 octets

Prend l'entrée comme deux entiers et un tableau de chaînes. Renvoie un tableau de chaînes.

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

Essayez-le en ligne!

Commenté

( 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
la source
filter et map ?! Est-ce que la construction d'une nouvelle baie reduceou d'une solution récursive ne fonctionnerait pas plus rapidement? Sur mon téléphone, dans le pub ou j'essayerais moi-même.
Shaggy
@Shaggy Il y a probablement un chemin plus court en effet, mais je pense que cette méthode est vouée à utiliser 2 passes: la 2ème boucle ne peut pas démarrer avant la fin de la 1ère et les deux Xet Ysont connues avec certitude.
Arnauld du
2

Java 10, 204 octets

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

Modifie le tableau d'entrée au lieu d'en renvoyer un nouveau pour économiser des octets. Cela signifie cependant que les lignes supprimées deviennent à la ""place. Si cela n'est pas autorisé, je le changerai.

Essayez-le en ligne.

Explication:

(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

Par exemple:

Avec les entrées start = "(", end = ")"etlines =

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

la première boucle le recadrera en haut et à gauche, en le changeant comme suit:

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

la deuxième boucle le recadrera en bas et à droite, en le changeant comme suit:

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

Rétine 0.8.2 , 110 octets

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

Essayez-le en ligne!Explication:

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

Supprimez les lignes d'entrée précédant la première ligne de la boîte.

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

Si le caractère de début se trouve dans la colonne de gauche de l'entrée, supprimez-le.

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

Si le caractère de début n'a pas encore été supprimé, déplacez toutes les colonnes d'entrée vers la gauche et répétez depuis le début.

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

Supprimez le caractère de fin et tout ce qui se trouve dans l'entrée après le caractère de fin.

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

Tronquez toutes les lignes à la longueur de la ligne suivante. Cela fonctionne en comptant tous les caractères sauf un sur chaque ligne, puis en essayant de faire correspondre jusqu'à autant de caractères sur la ligne suivante; si cela réussit, la deuxième ligne était plus courte, donc le dernier caractère est supprimé et la boucle se répète.

Neil
la source
0

C (gcc) , 237 octets

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

Essayez-le en ligne!

Je suis sûr à 99% que cela peut être raccourci en utilisant une sorte de fonction d'aide pour trouver l'index horizontal et le pointeur sur un caractère, car il est répété deux fois. Hélas, je n'ai pas trouvé de moyen assez court pour le faire, je pourrai réessayer plus tard si je trouve le temps.

La description

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
la source
1
Encore mieux: 219 octets
Zacharý
0

Stax , 15 octets

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

Exécuter et déboguer

Il prend le jeu de caractères délimiteurs de boîte (1 ou 2) sur la première ligne d'entrée. Le reste des lignes est le corps d'entrée.

Déballé, non golfé et commenté, il ressemble à ceci.

            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

Exécutez celui-ci

récursif
la source