Anti-aliasing ASCII art

33

tło

Sztuka ASCII to praktyka tworzenia obrazów przy użyciu tekstu ASCII do kształtowania kształtów.

Aliasing to efekt utworzony przez duże „piksele” grafiki ASCII, które mają wielkość znaków. Obraz staje się blokujący i trudny do zobaczenia. Wygładzanie usuwa ten efekt, tworząc gradient i zmiękczając twarde krawędzie sztuki ASCII.

Wyzwanie

Twoim wyzwaniem jest napisanie możliwie najkrótszego programu, który weźmie kawałek ASCII i wyświetli wersję, która została wygładzona.

Jaki rodzaj antyaliasingu?

Cała sztuka ASCII będzie się składać z dwóch rodzajów symboli: spacji i spacji. Dla każdego znaku niebiałego twój program musi ustalić, czy znajduje się w pozycji, w której należy go wygładzić. Jeśli tak, musisz zastąpić go prawidłowym znakiem. Jeśli tak nie jest, postać pozostaje ta sama.

Skąd wiesz, czy postać wymaga wygładzenia? Odpowiedź zależy od postaci, które znajdują się bezpośrednio powyżej, poniżej, lewej i prawej strony znaku ( nie przekątnych ). Oto wykres, kiedy wymagane jest wygładzanie, gdzie ?i xmoże oznaczać dowolny znak inny niż biały.

 x? ->  d?
 ?      ? 


?x  -> ?b 
 ?      ? 

 ?      ? 
?x  -> ?F 


 ?      ? 
 x? ->  Y?



 x  ->  ;   Note: This character has been changed from _ to ;
 ?      ? 

 ?      ? 
 x  ->  V 



?x  -> ?> 



 x? ->  <?



 x  ->  @ 

Dane wejściowe (i przykładowa grafika ASCII sprzed antyaliasingu)

Najpierw będą dwa wiersze wprowadzania (do STDIN), liczba H, po której następuje liczba W. Następnie pojawią się H wiersze o dokładnie W znakach (z wyjątkiem nowej linii). Te kolejne wiersze będą sztuką ASCII, którą należy wygładzić. Oto przykładowe dane wejściowe (nie piękne, ale testowe):

7
9
  888888 
 888888  
999 98  7
 666666  
  666666 
   6666  
    6    

Wyjście (i przykładowa sztuka antyaliasingu)

Twój program powinien wypisać do STDOUT grafikę ASCII (o tych samych wymiarach), która została wygładzona. Oto dane wyjściowe dla powyższego wejścia. Zwróć uwagę, jak znaki obramowania są traktowane jako graniczące białe znaki.

  d8888> 
 d8888F  
<99 98  @
 Y6666b  
  Y6666> 
   Y66F  
    V    

To może nie wyglądać tak dobrze (ze względu na odstępy między wierszami w bloku kodu), lepiej wygląda z większą grafiką ASCII, a jakość zależy od dokładnej użytej czcionki.

Inny przykład

Wkład

12
18
   xx  xxx  xxx   
  xxxx  xxx  xxx  
 xxxxxx  xxx  xxx 
xxx  xxx  xxx  xxx
xxxx xxx  xxx  xxx
 xxxxxx  xxx  xxx 
  xxxx  xxx  xxx  
x  xx  xxx  xxx  x
xx    xxx  xxx  xx
xxx  xxx  xxx  xxx
xxxx  xxx  xxx  xx
xxxxx  xxx  xxx  x

Wydajność

   db  <xb  <xb   
  dxxb  Yxb  Yxb  
 dxxxxb  Yxb  Yxb 
dxx  xxb  xxb  xxb
Yxxb xxF  xxF  xxF
 YxxxxF  dxF  dxF 
  YxxF  dxF  dxF  
;  YF  dxF  dxF  ;
xb    dxF  dxF  dx
xxb  <xF  <xF  <xx
xxxb  Yxb  Yxb  Yx
Yxxx>  Yx>  Yx>  V

Reguły, ograniczenia i uwagi

Twój program powinien być napisany tylko drukowalnymi znakami ASCII, abyśmy mogli tworzyć z nich dzieła sztuki. Poza tym obowiązują standardowe zasady gry w golfa kodowego.

PhiNotPi
źródło
Ponieważ nie ma jeszcze odpowiedzi, zmieniłem jedną postać na wykresie wygładzania. _stał się, ;ponieważ działa lepiej.
PhiNotPi
To może być moje ulubione pytanie dotyczące golfa wszechczasów. Pracuję nad 4 różnymi niesamowitymi rozwiązaniami.
captncraig
Chociaż jestem zdezorientowany. Mówisz, że przekątne się nie liczą, ale wszystkie twoje diagramy pokazują znaki zapytania wypełniające przekątne. Z tego, co widzę w przykładach, może być bezpiecznie patrzeć tylko na boki, ale się mylę? Czy przekątne mają kiedykolwiek znaczenie?
captncraig
Nie, przekątne nigdy nie mają znaczenia. Prawdopodobnie stanie się bardziej jasne, jeśli usunę przekątne z tabeli.
PhiNotPi
Myślę, że w twoim przykładzie może być literówka; Uważam, że prawa kolumna powinna mieć Y na wewnętrznej krawędzi. Z przyjemnością wymyśliłem odpowiedź na to pytanie, dobre pytanie: D
Ed James

Odpowiedzi:

8

Rubinowy, 180 168 znaków

gets
w=1+gets.to_i
f=*(readlines*"").chars
f.zip(f[1..-1]+s=[" "],s+f,s*w+f,f[w..-1]+s*w){|a,*b|$><<"@;V#{a}>bF#{a}<dY#{a*5}"[a>" "?(b.map{|y|y>" "?1:0}*"").to_i(2):3]}

Kolejna implementacja Rubiego, która przyjmuje podejście zip. Drugi przykład możesz zobaczyć online .

Edycja: użycie readlineszapisuje 12 znaków.

Howard
źródło
6

Rubinowy 275 265 263 261 258 254 244 243 214 212 207

H=0...gets.to_i
W=0...gets.to_i
G=readlines
z=->x,y{(H===y&&W===x&&' '!=G[y][x])?1:0}
H.map{|j|W.map{|i|l=G[j][i]
G[j][i]="@V;#{l}>Fb#{l}<Yd#{l*5}"[z[i+1,j]*8+z[i-1,j]*4+z[i,j+1]*2+z[i,j-1]]if' '!=l}}
puts G

Próbka 1: http://ideone.com/PfNMA

Próbka 2: http://ideone.com/sWijD

Cristian Lupascu
źródło
1) Nawiasy wokół definicji zakresu nie są konieczne. 2) 0..h-1można zapisać jako 0...h. 3) G=[];h.times{G<<gets}można zapisać jako G=readlines, podobnie jak w kodzie C #. 4) Po kroku 3. zmienna h staje się bezużyteczna, wartości h i w są używane tylko raz, więc h=gets.to_i;w=gets.to_i;H=(0..h-1);W=(0..w-1)można je zapisać jako H=0...gets.to_i;W=0...gets.to_i. 5) W tym przypadku andmożna zapisać jako &&, który nie potrzebuje przestrzeni wokół. 6) Masz dodatkowy; i policzyłeś nowy wiersz na końcu pliku, które nie są konieczne. Oznacza to 214 znaków: ideone.com/CiW0l
manatwork
Wow, dzięki! Wiedziałem, że trzeba wprowadzić ulepszenia, ale nigdy nie sądziłem, że jest ich tak wiele. Próbowałem, H=0..gets.to_ikiedy napisałem kod, ale wydaje się, że nie działał (oczywiście musiał być z innych powodów).
Cristian Lupascu
1
Nadal dwa punkty, w których możesz zmniejszyć o co najmniej 7 znaków: 1) Możesz użyć mapzamiast each2) z=->...zamiast def z...end.
Howard
@ Howard Dzięki, zastosowałem mapzamiast eachzmiany. Jednak w przypadku składni lambda myślę, że wymagałoby to użycia zformy z.call(args)zamiast formy z(args), co zwiększyłoby liczbę znaków. Daj mi znać, jeśli coś mi umknie.
Cristian Lupascu
@ Howard Nevermind, właśnie dowiedziałem się, czego mi brakuje. Zaktualizuję, aby użyć []użycia lambda .
Cristian Lupascu
4

JavaScript, 410 znaków:

function(t){m={"10110":"b","11100":"d","01101":"Y","00111":"F","10100":";","00101":"V","00110":">","01100":"<","00100":"@"},d="join",l="length",t=t.split('\n').splice(2),t=t.map(function(x)x.split('')),f=function(i,j)t[i]?(t[i][j]||' ')==' '?0:1:0;for(o=t[l];o--;){for(p=t[o][l];p--;){y=[f(o+1,p),f(o,p+1),f(o,p),f(o,p-1),f(o-1,p)],t[o][p]=m[y[d]('')]||t[o][p]}}t=t.map(function(x)x[d](''))[d]('\n');return t;}

bez golfa:

function(t){
    m={
        "10110":"b",
        "11100":"d",
        "01101":"Y",
        "00111":"F",
        "10100":";",
        "00101":"V",
        "00110":">",
        "01100":"<",
        "00100":"@"
    },
    d="join",
    l="length",
    t=t.split('\n').splice(2),
    t=t.map(function(x) x.split('')),
    f=function(i,j) t[i]?(t[i][j]||' ')==' '?0:1:0;

    for(o=t[l];o--;){
        for(p=t[o][l];p--;){
            y=[f(o+1,p),f(o,p+1),f(o,p),f(o,p-1),f(o-1,p)],

            t[o][p]=m[y[d]('')]||t[o][p]
        }
    }
    t=t.map(function(x)x[d](''))[d]('\n');
    return t;
}

Oryginalny, 440 znaków:

function (t){m={"10110":"b","11100":"d","01101":"Y","00111":"F","10100":";","00101":"V","00110":">","01100":"<","00100":"@"},s="split",d="join",l="length",t=t[s]('\n').splice(2),t=t.map(function(x) x[s]('')),f=function(i,j)i<0||i>=t[l]?0:(j<0||j>=t[i][l]?0:t[i][j]==' '?0:1);for(o=t[l];o--;){for(p=t[o][l];p--;){y=[f(o+1,p),f(o,p+1),f(o,p),f(o,p-1),f(o-1,p)],h=m[y[d]('')];if(h){t[o][p]=h}}}t=t.map(function(x) x[d](''))[d]('\n');return t;}

NB Zakładam, że pierwsze dwa wiersze wejściowe są w rzeczywistości nieistotne, a rozmiar kolejnych wierszy jest prawidłowy. Liczę też, że może uda mi się odciąć jeszcze kilka znaków, kiedy będę miał szansę!

Ed James
źródło
1
Wymień deklarację m do m={22:"b",28:"d",13:"Y",7:"F",20:";",5:"V",6:">",12:"<",4:"@"}indeksu następnie przekonwertować M z parseInt(): m[parseInt(y[d](''),2)]. Zmniejsza to rozmiar do 373 znaków.
manatwork
3

Python, 259 znaków

H=input()
W=input()+1
I=' '.join(raw_input()for i in' '*H)
for i in range(H):print''.join(map(lambda(s,a,b,c,d):(s*5+'dY<'+s+'bF>'+s+';V@'+' '*16)[16*(s==' ')+8*(a==' ')+4*(b==' ')+2*(c==' ')+(d==' ')],zip(I,I[1:]+' ',' '+I,I[W:]+' '*W,' '*W+I))[i*W:i*W+W-1])

Program odczytuje dane wejściowe w jednym ciągu I(ze spacjami oddzielającymi linie), zamyka listę 5 krotek zawierających znak i cztery otaczające go znaki, a następnie wyszukuje wynikowy znak za pomocą indeksowania łańcucha.

Keith Randall
źródło
3

PHP - 359 330 282 268 257 znaków

<?php
$i=fgets(STDIN)+0;$w=fgets(STDIN)+1;$s='';$m='@<;d>0b0VY00F000';
for(;$i--;)$s.=fgets(STDIN);
for(;++$i<strlen($s);){
$b=trim($s[$i])?0:15;
foreach(array($i+1,$i+$w,$i-1,$i-$w)as$k=>$x)
$b|=pow(2,$k)*(isset($s[$x])&&trim($s[$x]));
echo $m[$b]?$m[$b]:$s[$i];}
Zardzewiały Fausak
źródło
@PhiNotPi Nie działało, ponieważ moje lokalnie zapisane pliki testowe miały EOL w stylu Windows \r\n. Zaktualizowałem swój kod do pracy z EOL w stylu uniksowym \n.
Rusty Fausak,
Ok, wydaje się, że teraz działa.
PhiNotPi
2

Python, 246 241

H=input();W=1+input()
S=' '
o=W*S
F=o+'\n'.join((raw_input()+o)[:W-1]for k in range(H))+o
print ''.join((16*x+'@;<d>b'+2*x+'V'+x+'Y'+x+'F'+3*x)[
16*(x>S)|8*(a>S)|4*(l>S)|2*(r>S)|(b>S)]for
x,a,l,r,b in zip(F[W:-W],F,F[W-1:],F[W+1:],F[2*W:]))

WC i test na próbce 2, różnej od wydajności rozwiązania Ruby u góry:

t:~$ wc trans.py && python trans.py < lala2 > o && diff -q o ruby_out2_sample
  2 11 241 trans.py
t:~$
edwkar
źródło
1

C # 591 563

string A(string t){var s=new StringReader(t);var h=int.Parse(s.ReadLine());var w=int.Parse(s.ReadLine());var lines=s.ReadToEnd().Split(new[]{"\r\n"},StringSplitOptions.None).Select(x=>x.ToCharArray()).ToArray();for(var i=0;i<h;i++)for(var j=0;j<w;j++){var c=lines[i][j];if(c==' ')continue;var n=(i>0?(lines[i-1][j]!=' '?1:0):0)+(i<h-1?(lines[i+1][j]!=' '?2:0):0)+(j>0?(lines[i][j-1]!=' '?4:0):0)+(j<w-1?(lines[i][j+1]!=' '?8:0):0);lines[i][j]=new[]{'@','V',';',c,'>','F','b',c,'<','Y','d',c,c,c,c,c}[n];}return string.Join("\r\n",lines.Select(l=>new string(l)));}
Cristian Lupascu
źródło