Crop ASCII Art Challenge

13

Sztuka ASCII jest fajna. Nowoczesne edytory tekstu są bardzo dobre w manipulowaniu tekstem. Czy nowoczesne języki programowania są w stanie sprostać zadaniu?

Jednym z typowych zadań w manipulacji grafiką ASCII jest przycinanie tekstu do prostokąta między dwoma znakami. To zadanie musisz wykonać w tym wyzwaniu.

Detale

Twój program pobierze 3 dane wejściowe:

  • pierwszy to znak „początku” bloku - oznaczenie lewego górnego rogu
  • drugi to „koniec” znaku bloku - oznaczenie prawego dolnego rogu
  • trzeci to jakaś forma tekstu wielowierszowego, albo ciąg, albo lista ciągów, nazwa pliku lub cokolwiek innego

Wynikiem będzie tekst wielowierszowy (ponownie w dowolnym z powyższych formatów) przycięty do prostokąta między podanymi danymi wejściowymi. Zauważ, że pierwsze dwa dane wejściowe mogą nie być unikalne.

Skrzynie na brzeg

Pudełka muszą zawsze mieć objętość co najmniej 2. Zatem te:

()     (
       )

pudełka, ale te:

)(     )      (
       (     )

nie są (z początkiem = (i końcem = )).

Dane wejściowe będą zawierać tylko jedno pole. Dlatego początkowy i końcowy znak musi wystąpić tylko raz, chyba że jest to ten sam znak, w którym to przypadku musi wystąpić dokładnie dwa razy.

Dodatkowo każda linia na wejściu musi być co najmniej tak długa, jak odległość od początku linii do prawej krawędzi pola na wejściu.

Twój program nie musi obsługiwać nieprawidłowych danych wejściowych; mogą powodować niezdefiniowane zachowanie.

Zasady

Obowiązują typowe zasady gry w golfa. Najkrótszy kod wygrywa.

Przykłady

Słoneczny dzień: start: ( end: ) input:

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

Wynik:

(but this text
is in a box  )

Zwróć też uwagę na usuwanie poziomej przestrzeni. Uprawy artystyczne ASCII to 2d.

Deszczowy dzień: start: ( end: ) input:

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

Wynik:

(
)

Ten sam początek / koniec: start: / end: / input:

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

Wynik:

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

Niepoprawne dane wejściowe: start: ( end: ) input:

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

Nieprawidłowe dane wejściowe 2: start: ( end: ) input:

(The lines must already be square 
so this line that is too short
relative to this end, is illegal)
LambdaBeta
źródło
Co z prawidłowym polem z linią na zewnątrz, która jest krótsza niż pole?
seadoggie01
1
wyjaśnione, również nieprawidłowe dane wejściowe
LambdaBeta,
jaki powinien być wynik w przypadku nieprawidłowego wprowadzenia? czy są wymienione, aby nie trzeba było się nimi zajmować?
Uriel
1
Wynik jest bardzo podobny do niezdefiniowanego zachowania w C, nie martw się tym, wszystko pójdzie.
LambdaBeta
To paskudne małe wyzwanie: niezła robota!
seadoggie01

Odpowiedzi:

15

Vim, 16 , 12 bajtów / naciśnięć klawiszy

#<C-v>Nj*yggVGp

Wypróbuj online! w tłumaczu V.

Nowoczesne edytory tekstu są bardzo dobre w manipulowaniu tekstem. Czy nowoczesne języki programowania są w stanie sprostać zadaniu?

Założę się, że stare edytory tekstu są jeszcze lepsze! :RE

Chociaż nie musi to koniecznie, ta odpowiedź działa z obiema danymi wejściami „niepoprawnymi”

 rectangular (
) which is ill

i

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

Wyjaśnienie:

#               " 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
źródło
1
Nawiasem mówiąc, jest to dokładnie przypadek, który zrobiłem, aby zachęcić mnie do napisania tego wyzwania. Miałem q makro jako /\/<cr><c-v>nygv$o0dplub coś w tym stylu zbyt długo :)
LambdaBeta,
2
Tak, prostokątny jest najmniejszy !
AdmBorkBork,
6

Galaretka , 13 bajtów

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

Dyadyczny link akceptujący listę znaków początkowych i końcowych po lewej stronie oraz listę linii (jako listy znaków) po prawej stronie, która daje listę linii (jako listy znaków).

Wypróbuj online! (pełny program - jeśli dane wejściowe są poprawne w Pythonie, będą wymagały cytowania napisów w Pythonie).

W jaki sposób?

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

Na przykład z lewym = ['a', 'b']i prawym (jako lista list znaków - wiersze):

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

=€daje listę dwóch list list (pierwsza wykonuje 'a'=, druga 'b'=):

00000000         00000000
00100000         00000000
00000000    ,    00000000
00000000         00000100
00000000         00000000

sumowanie to daje pojedynczą listę list (sumowanie elementarne):

00000000
00100000
00000000
00000100
00000000

ŒṪdaje nam (indeksowane 1) wielowymiarowe wskaźniki niezerowe, [[2,3],[4,6]]tj [[top,left],[bottom,right]].

r/następnie wykonuje to, [2,3]r[4,6]co, ponieważ rwektoryzuje, jest podobne [2r4, 3r6], oceniając na [[2,3,4],[3,4,5,6]]- tj [rows,columns].

,þ/następnie wykonuje, [2,3,4],þ[3,4,5,6]gdzie þjest instrukcja zewnętrznego poduct i ,jest parą. Daje to wszystkie [row,column]wartości według kolumn, w tym przypadku:

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

Chcemy je po wierszu, więc Zsłuży do transponowania tego do:

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

Na koniec œịindeksuje z powrotem do linii wejściowych:

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

Warto zauważyć, że gdy oba ograniczające znaki są takie same, =€identyfikuje je dwa razy, ale SŒṪostatecznie robi właściwą rzecz, ponieważ 2jest to zgodne z prawdą, np . ['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
źródło
... czytam wyjaśnienie, ale wciąż go nie rozumiem. o_o Czy mógłbyś dodać działający przykład?
DLosc
Zachęcanie: Przyjmę twoją odpowiedź, jeśli zostanie w pełni wyjaśniona. :)
LambdaBeta,
1
@DLosc - gotowe, mam nadzieję, że to pomoże.
Jonathan Allan,
@LambdaBeta - odpowiedź V jest krótsza.
Jonathan Allan,
... nawet błędnie odpowiedział Vimowi.
Jonathan Allan
5

APL (Dyalog) , 38 30 bajtów

4 bajty zapisane dzięki @EriktheOutgolfer

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

Wypróbuj online!

Uriel
źródło
zbyt skomplikowane. możesz zaakceptować macierz zamiast wektora wektorów, znaleźć dwie pozycje ⍸matrix∊separatorsi zabrać / upuścić z nimi
ngn
(⍸a=⎕)↓(1+⍸a=⎕)↑a←⎕z⎕io←0
ngn
@ngn OP powiedział, że liczba znaków różni się między wierszami, więc założyłem, że dane wejściowe powinny być wektorowe przed przetwarzaniem. Mimo to potrzebuję części wyboru (najpierw i obracaj), jeśli separator pokazuje się kilka razy (patrz trzeci przypadek testowy), ale myślę, że kropla przecina kilka bajtów, więc dzięki! Będę aktualizować, kiedy dojdę do komputera
Uriel
Ups ... Przepraszam, zapomniałem o trzeciej sprawie. Więc może: ⊃{⌽⊖⍵↓⍨⊃⍸⍺=⍵}/⎕⎕⎕(sic, z 3 końcowymi quadami), co jest jeszcze krótsze. Lub ... ⎕⎕(↑⎕)jeśli wstępnie zmieszana matryca nie jest dozwolona.
ngn
3

Galaretka , 14 bajtów

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

Wypróbuj online!

Erik the Outgolfer
źródło
Próbowałem uruchomić Twój kod na kilku innych przykładach, ale dostałem tylko awarię. Czy to błąd, czy robię coś złego?
Ilmari Karonen,
@IlmariKaronen Nie zacytowałeś poprawnie drugiego argumentu (nie wspomniałeś o tym w poście); zawiń w pojedyncze lub podwójne cudzysłowy. Sposób w jaki ją wywołałeś, drugi argument to pusta krotka (Python) ( ()), a nie '()'. Jeśli jest parsowalny, musi być cytowany, jednak mój //nie musi być cytowany (operator dzielenia liczb całkowitych bez operandów? Hm ...).
Erik the Outgolfer,
@IlmariKaronen „Myślę”, że ()to właśnie Jelly interpretuje jako coś specjalnego. Większość par postaci próbuję pracować. Bardzo chciałbym usłyszeć, co myślą ludzie bardziej zaznajomieni z Jelly. EDYCJA: ninja-ed by erik the outgolfer
LambdaBeta
OK, tak, to działa.
Ilmari Karonen,
3

Python 2 , 130 bajtów

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]

Wypróbuj online!

TFeld
źródło
2

Płótno , 37 bajtów

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

Wypróbuj tutaj!

36 bajtów na uzyskanie współrzędnych znaków (i przekonwertowanie ich na x, y, w, h, ponieważ to wystarczy) i 1 bajt na uzyskanie podsekcji. Musi być lepsze podejście

dzaima
źródło
2

JavaScript (ES6), 98 bajtów

Pobiera dane wejściowe jako dwie liczby całkowite i tablicę ciągów. Zwraca tablicę ciągów.

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

Wypróbuj online!

Skomentował

( 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
źródło
filter i map ?! Czy zbudowanie nowej macierzy reducelub rozwiązanie rekurencyjne nie zadziałałoby krócej? Na telefonie, w pubie lub sam bym spróbował.
Shaggy
@Shaggy Tam chyba rzeczywiście krótsza droga, ale myślę, że ta metoda jest skazane użyć 2 karnety: 2nd pętli nie może rozpocząć się przed dniem 1 jeden wypowiada i jak Xi Yznane są na pewno.
Arnauld,
2

Java 10, 204 bajty

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

Zmienia tablicę wejściową zamiast zwracać nową, aby zapisać bajty. Oznacza to ""jednak, że zamiast tego stają się usunięte linie . Jeśli nie jest to dozwolone, zmienię to.

Wypróbuj online.

Wyjaśnienie:

(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

Na przykład:

Z wejściami start = "(", end = ")"alines =

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

pierwsza pętla spowoduje przycięcie go u góry i po lewej stronie, zmieniając go w ten sposób:

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

druga pętla spowoduje przycięcie go u dołu i po prawej stronie, zmieniając go w ten sposób:

["",
 "(but this text",
 "is in a box  )",
 ""]
Kevin Cruijssen
źródło
1

Retina 0.8.2 , 110 bajtów

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

Wypróbuj online! Wyjaśnienie:

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

Usuń linie wprowadzania poprzedzające pierwszy wiersz pola.

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

Jeśli znak początkowy znajduje się w lewej kolumnie danych wejściowych, usuń go.

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

Jeśli znak początkowy nie został jeszcze usunięty, przesuń wszystkie kolumny wejściowe pozostawione o jedną i powtórz od początku.

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

Usuń znak końcowy i wszystkie dane wejściowe po znaku końcowym.

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

Obetnij wszystkie linie do długości następnej linii. Działa to poprzez zliczenie wszystkich znaków oprócz jednego w każdym wierszu, a następnie próby dopasowania do tylu znaków w następnym wierszu; jeśli to się powiedzie, to druga linia była krótsza, więc ostatni znak jest usuwany i pętla się powtarza.

Neil
źródło
0

C (gcc) , 237 bajtów

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

Wypróbuj online!

Jestem w 99% pewien, że można to skrócić za pomocą funkcji pomocniczej, aby znaleźć poziomy wskaźnik i wskaźnik do znaku, ponieważ jest on powtarzany dwukrotnie. Niestety nie mogłem znaleźć wystarczająco krótkiego sposobu, aby to zrobić, mogę spróbować ponownie później, jeśli znajdę czas.

Opis

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
źródło
1
Jeszcze lepiej: 219 bajtów
Zacharý
0

Stax , 15 bajtów

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

Uruchom i debuguj

Pobiera zestaw znaków ogranicznika pola (1 lub 2) w pierwszym wierszu wprowadzania. Reszta wierszy to treść wejściowa.

Rozpakowane, niepolowane i skomentowane, wygląda to tak.

            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

Uruchom ten

rekurencyjny
źródło