ASCII Art of the Day # 2 - Flow Snakes

32

Przepływu węża, znany również jako krzywa Gosper'a jest fraktalny krzywa rośnie wykładniczo z każdym rozmiaru zlecenia / iteracji procesie prostym. Poniżej znajdują się szczegóły dotyczące budowy i kilka przykładów dla różnych zamówień:

Zamów 1 Flow Snake :

____
\__ \
__/

Zamówienie 2 Flow Snake :

      ____
 ____ \__ \
 \__ \__/ / __
 __/ ____ \ \ \
/ __ \__ \ \/
\ \ \__/ / __
 \/ ____ \/ /
    \__ \__/
    __/

Zamów 3 Flow Snake :

                 ____
            ____ \__ \
            \__ \__/ / __
            __/ ____ \ \ \    ____
           / __ \__ \ \/ / __ \__ \
      ____ \ \ \__/ / __ \/ / __/ / __
 ____ \__ \ \/ ____ \/ / __/ / __ \ \ \
 \__ \__/ / __ \__ \__/ / __ \ \ \ \/
 __/ ____ \ \ \__/ ____ \ \ \ \/ / __
/ __ \__ \ \/ ____ \__ \ \/ / __ \/ /
\ \ \__/ / __ \__ \__/ / __ \ \ \__/
 \/ ____ \/ / __/ ____ \ \ \ \/ ____
    \__ \__/ / __ \__ \ \/ / __ \__ \
    __/ ____ \ \ \__/ / __ \/ / __/ / __
   / __ \__ \ \/ ____ \/ / __/ / __ \/ /
   \/ / __/ / __ \__ \__/ / __ \/ / __/
   __/ / __ \ \ \__/ ____ \ \ \__/ / __
  / __ \ \ \ \/ ____ \__ \ \/ ____ \/ /
  \ \ \ \/ / __ \__ \__/ / __ \__ \__/
   \/ / __ \/ / __/ ____ \ \ \__/
      \ \ \__/ / __ \__ \ \/
       \/      \ \ \__/ / __
                \/ ____ \/ /
                   \__ \__/
                   __/

Budowa

Rozważ kolejność zbudowania 1 Flow Snake'a ze ścieżką zawierającą 7 krawędzi i 8 wierzchołków (oznaczonych poniżej. Powiększone dla możliwości):

4____5____6
 \         \
 3\____2   7\
       /
0____1/

Teraz dla każdego następnego zamówienia po prostu zastępujesz krawędzie obróconą wersją tego oryginalnego wzoru zamówienia 1. Aby wymienić krawędzie, użyj 3 następujących zasad:

1 W przypadku poziomej krawędzi zamień ją na oryginalny kształt w następujący sposób:

________
\       \
 \____   \
     /
____/

2 W przypadku /krawędzi ( 12w powyższej konstrukcji) zamień ją na następującą obróconą wersję:

 /
/   ____
\  /   /
 \/   /
     /
____/

3 W przypadku \krawędzi ( 34i 67powyżej) zamień ją na następującą obróconą wersję:

 /
/   ____ 
\   \   \
 \   \   \
  \  /
   \/

Na przykład będzie wyglądać kolejność 2 z wierzchołkami z oznaczonej kolejności 1

            ________
            \       \
  ________   \____   \6
  \       \      /   /
   \____   \5___/   /   ____
       /            \   \   \
  4___/   ________   \   \   \7
 /        \       \   \  /
/   ____   \____   \2  \/
\   \   \      /   /
 \   \   \3___/   /   ____
  \  /            \  /   /
   \/   ________   \/   /
        \       \      /
         \____   \1___/
             /
        0___/

Teraz dla dowolnego wyższego rzędu wystarczy rozbić bieżący poziom na krawędzie o długości 1 /, 1 \lub 2 _i powtórzyć proces. Należy pamiętać, że nawet po wymianie wspólne wierzchołki między dowolnymi dwoma kolejnymi krawędziami są nadal zbieżne.

Wyzwanie

  • Musisz napisać funkcję pełnego programu, który odbiera jedną liczbę całkowitą Nza pomocą argumentu STDIN / ARGV / function lub najbliższego odpowiednika i wypisuje kolejność NFlow Snake na STDOUT.
  • Wejściowa liczba całkowita jest zawsze większa niż 0.
  • Nie powinno być żadnych spacji wiodących, które nie są częścią wzoru.
  • Nie powinno być ani końcowych spacji, ani wystarczającej spacji końcowych do wypełnienia wzoru, aby całkowicie wypełnić minimalny prostokąt ograniczający.
  • Końcowy znak nowej linii jest opcjonalny.

Zabawne fakty

  • Flow Snakes to gra słów o Snow Flakes, która przypomina ten wzór dla rzędu 2 i wyższych
  • Flow and Snakes faktycznie odgrywają rolę we wzorze, ponieważ wzór składa się z jednej przepływającej przez nie ścieżki.
  • Jeśli zauważysz uważnie, wzorzec rzędu 2 (a także wyższy) składa się z rotacji wzorca rzędu 1 obróconego na wspólnym wierzchołku bieżącej i poprzedniej krawędzi.
  • Istnieje wariant węży przepływowych spoza ASCII, który można znaleźć tutaj i w kilku innych miejscach.

To jest więc wygrywa najkrótszy kod w bajtach!


Tabela liderów

Pierwszy post z serii generuje tabelę wyników.

Aby mieć pewność, że Twoje odpowiedzi się pojawią, zacznij każdą odpowiedź od nagłówka, używając następującego szablonu Markdown:

# Language Name, N bytes

gdzie Njest rozmiar twojego zgłoszenia. Jeśli poprawić swój wynik, to może zachować stare porachunki w nagłówku, uderzając je przez. Na przykład:

# Ruby, <s>104</s> <s>101</s> 96 bytes
Optymalizator
źródło
Jeśli dobrze rozumiem, kształty 1,2,3 są powiększone 2x, więc dolny rząd w 2 powinien składać się z 4 podkreślników, a nie 3.
edc65
@ edc65 Kształty w przykładach są idealnie dobrane. Jeśli mówisz o części konstrukcyjnej, tak, to jest powiększone i są 3 podkreślenia, dzięki czemu numer krawędzi zajmuje czwarte miejsce
Optymalizator
Ale nie ma numerów krawędzi w kształcie 2 (w części konstrukcyjnej tak). Dno kształtu 2 powinno być równe dnie kształtu 1.
edc65
@ edc65 O, tam! Naprawiony!
Optymalizator
3
Przeczytałem ten tytuł jako „Płatki śniegu” i nawet nie zauważyłem prawdziwego tytułu, dopóki nie zwróciłeś uwagi na różnicę.
mbomb007

Odpowiedzi:

4

CJam, 144 bajty

0_]0a{{_[0X3Y0_5]f+W@#%~}%}ri*{_[YXW0WW]3/If=WI6%2>#f*.+}fI]2ew{$_0=\1f=~-
"__1a /L \2,"S/=(@\+\~.+}%_2f<_:.e>\:.e<:M.-:)~S*a*\{M.-~3$3$=\tt}/zN*

Dodano nową linię, aby uniknąć przewijania. Wypróbuj online

Program działa w kilku krokach:

  1. Początkowy fraktal (rząd 1) jest kodowany jako ciąg 7 kątów (koncepcyjnie, wielokrotności 60 °) reprezentujących kierunek ruchu
  2. Fraktal „nakłada się” na segment poziomy (fraktal 0 rzędu) N razy, aby wygenerować wszystkie „ruchy” w fraktale N rzędu
  3. Począwszy od [0 0], ruchy są tłumaczone na sekwencję punktów o współrzędnych [xy]
  4. Każdy segment (para punktów) jest konwertowany na 1 lub 2 tryplety [xyc], reprezentujące znak c o współrzędnych x, y
  5. Określa się prostokąt ograniczający, współrzędne są dopasowywane i generowana jest macierz odstępów
  6. Dla każdej trypletu znak c jest umieszczany w pozycji x, y w macierzy, a ostateczna macierz jest dostosowywana do wyjścia
aditsu
źródło
Ta odpowiedź jest wystarczająco długa, aby skorzystać z kodowania bajtów: goo.gl/D1tMoc
Dennis
@Dennis Nie jestem pewien, czy skończyłem grać w golfa ... i dlaczego umieściłeś go w bloku?
aditsu
Nie jestem do końca pewien ... Twoja odpowiedź jest imponująca. Spędziłem cały dzień, próbując to naprawić.
Dennis
@Dennis Thanks; btw, czy uważasz, że ogólnie można używać znaków niedrukowalnych / kontrolnych? Zwykle staram się ich unikać
aditsu
Jeśli mogę ich uniknąć bez zwiększania liczby bajtów, robię to. Ale krótszy jest krótszy. : P W przypadkach takich jak ten, w których kompresuję sam kod, a nie jakiś ciąg znaków lub tablicę, zwykle uwzględniam obie wersje w odpowiedzi.
Dennis
16

Python 2, 428 411 388 bajtów

Ten był dość trudny. Wzory nie zachowują swoich proporcji po każdym kroku, co oznacza, że ​​bardzo trudno jest proceduralnie utworzyć obraz z jego poprzednika. To, co robi ten kod, choć jest dość nieczytelne po intensywnym golfie matematyki, w rzeczywistości wyznacza granicę od początku do końca za pomocą funkcji rekurencyjnie zdefiniowanej D.

Rozmiar był również problemem i skończyłem dopiero na środku 5*3**nkwadratowego boku i później kadrowałem rzeczy, chociaż jeśli wymyślę lepszy sposób obliczenia rozmiaru, mógłbym go zmienić.

n=input();s=5*3**n
r=[s*[" "]for i in[0]*s]
def D(n,x,y,t=0):
 if n<1:
    x-=t%2<1;y+=t%3>1;r[y][x]='_/\\'[t/2]
    if t<2:r[y][x+2*t-1]='_'
    return[-1,2,0,1,0,1][t]+x,y-(2<t<5)
 for c in[int(i)^t%2for i in"424050035512124224003"[t/2::3]][::(t^1)-t]:x,y=D(n-1,x,y,c)
 return x,y
D(n,s/2,s/2)
S=[''.join(c).rstrip()for c in r]
for l in[c[min(c.find('\\')%s for c in S):]for c in S if c]:print l
KSab
źródło
Wow, to jest niesamowite. Chcesz spróbować w AAoD # 1?
Optymalizator
r=[s*[" "]for i in range(s)]-> r=[[" "]*s]*s]
ogolą
1
@sirpercival niestety, że nie będzie działać z powodu , jak *powtarza zmienny przedmiotów .
grc
och, racja, zapomniałem
sirpercival
Możesz zapisać niektóre bajty, wstawiając l, przełączając się print'\n'.join()na drukowanie wewnątrz pętli for, używając return[...][t]+x,i usuwając nawiasy z (t%2). Możesz także użyć, min(c.find('\\')%s for c in S)jeśli zmienisz nazwę listy, Saby nie zastępowała początkowej wartości s.
grc
12

JavaScript ( ES6 ), 356 362 370

To trudne ...

Każdy kształt jest przechowywany jako ścieżka. Istnieje 6 podstawowych bloków konstrukcyjnych (3 + 3 do tyłu)

  • 0przekątna w górę od lewej do prawej dolnej ( 4do tyłu)
  • 1ukośna dolna lewa do góry prawa ( 5do tyłu)
  • 2pozioma od lewej do prawej ( 6do tyłu)

Dla każdego z nich istnieje krok zastępujący, który należy zastosować przy zwiększaniu kolejności:

  • 0-> 0645001(wstecz 4-> 5441024)
  • 1-> 2116501(wstecz 5-> 5412556)
  • 2-> 2160224(wstecz 6-> 0664256)

wartości wstępnie wypełnione w htablicy, nawet jeśli elementy 4..6 można uzyskać z 0..2 za pomocą

;[...h[n]].reverse().map(x=>x^4).join('')

Aby uzyskać kształt dla podanej kolejności, ścieżka jest wbudowana w zmienną p, stosując wielokrotnie podstawienia. Następnie główna pętla iteruje zmienną p i rysuje kształt w tablicy g [], gdzie każdy element jest rzędem.
Począwszy od pozycji (0,0), każdy indeks może stać się ujemny (indeks y przy wysokich rzędach). Unikam ujemnych indeksów y przesuwających całą tablicę g, ilekroć znajduję ujemną wartość y. Nie obchodzi mnie, czy indeks x staje się ujemny, ponieważ w JS dozwolone są indeksy ujemne, tylko trochę trudniejsze w zarządzaniu.
W ostatnim kroku skanuję główną tablicę za pomocą .map, ale dla każdego wiersza muszę użyć jawnej pętli for (;;), używając bzmiennej, która zawiera najmniej osiągnięty indeks x (który będzie <0).
wconsole.log w wersji znajduje się przydatna nowa linia wiodąca, którą można łatwo zamienić na nową linię kończącą 2 wiersze, tak jak w wersji fragmentu.

f=o=>{
  g=[],x=y=b=0,
  h='064500192116501921602249954410249541255690664256'.split(9);
  for(p=h[2];--o;)p=p.replace(/./g,c=>h[c]);
  for(t of p)
    z='\\/_'[s=t&3],
    d=s-(s<1),
    t>3&&(x-=d,y+=s<2),
    y<0&&(y++,g=[,...g]),r=g[y]=g[y]||[],
    s?s>1?r[x]=r[x+1]=z:r[x]=z:r[x-1]=z,
    t<3&&(x+=d,y-=s<2),
    x<b?b=x:0;
  g.map(r=>
  {
    o+='\n';
    for(x=b;x<r.length;)o+=r[x++]||' '
  },o='');
  console.log(o)
}

Przydatny fragment do przetestowania (w przeglądarce Firefox):

f=o=>{
  g=[],x=y=b=0,
  h='064500192116501921602249954410249541255690664256'.split(9);
  for(p=h[2];--o;)p=p.replace(/./g,c=>h[c]);
  for(t of p)
    z='\\/_'[s=t&3],
    d=s-(s<1),
    t>3&&(x-=d,y+=s<2),
    y<0&&(y++,g=[,...g]),r=g[y]=g[y]||[],
    s?s>1?r[x]=r[x+1]=z:r[x]=z:r[x-1]=z,
    t<3&&(x+=d,y-=s<2),
    x<b?b=x:0;
  g.map(r=>
  {
    for(x=b;x<r.length;)o+=r[x++]||' ';
    o+='\n'
  },o='');
  return o
}

// TEST

fs=9;
O.style.fontSize=fs+'px'

function zoom(d) { 
  d += fs;
  if (d > 1 && d < 40)
    fs=d, O.style.fontSize=d+'px'
}
#O {
  font-size: 9px;
  line-height: 1em;
}
<input id=I value=3><button onclick='O.innerHTML=f(I.value)'>-></button>
<button onclick="zoom(2)">Zoom +</button><button onclick="zoom(-2)">Zoom -</button>
<br>
<pre id=O></pre>

edc65
źródło
6

Haskell, 265 bajtów

(?)=div
(%)=mod
t[a,b]=[3*a+b,2*b-a]
_#[0,0]=0
0#_=3
n#p=[352,6497,2466,-1]!!((n-1)#t[(s+3)?7|s<-p])?(4^p!!0%7)%4
0&_=0
n&p=(n-1)&t p+maximum(abs<$>sum p:p)
n!b=n&[1,-b]
f n=putStr$unlines[["__ \\/   "!!(2*n#t[a?2,-b]+a%2)|a<-[b-n!2+1..b+n!2+0^n?3]]|b<-[-n!0..n!0]]

(Uwaga: na GHC przed 7.10, będzie trzeba dodać import Control.Applicativelub wymienić abs<$>z map abs$).

Uruchom online na Ideone.com

f n :: Int -> IO ()rysuje nflownake poziomu . Rysunek jest obliczany w kolejności bitmap, a nie wzdłuż krzywej, co pozwala algorytmowi działać w przestrzeni O (n) (to znaczy logarytmicznie w rozmiarze rysunku). Prawie połowa moich bajtów jest przeznaczona na obliczanie, który prostokąt narysować!

Anders Kaseorg
źródło
Zalogowałem się i teraz działa! Miły!
Optymalizator
Okazuje się, że wcześniej nie działał na Ideone.com, ponieważ zakładałem 64-bitową int. Naprawiono teraz (poświęcenie 2 bajtów).
Anders Kaseorg
Jest w porządku, ponieważ logowanie wymagało tylko mojego adresu e-mail do potwierdzenia.
Optymalizator
5

Perl, 334 316 309

$_=2;eval's/./(map{($_,"\1"x7^reverse)}2003140,2034225,4351440)[$&]/ge;'x($s=<>);
s/2|3/$&$&/g;$x=$y=3**$s-1;s!.!'$r{'.qw($y--,$x++ ++$y,--$x $y,$x++ $y,--$x
$y--,--$x ++$y,$x++)[$&]."}=$&+1"!eeg;y!1-6!//__\\!,s/^$x//,s/ *$/
/,print for
grep{/^ */;$x&=$&;$'}map{/^/;$x=join'',map$r{$',$_}||$",@f}@f=0..3**$s*2

Parametr pobrany na standardowym wejściu. Przetestuj mnie .

nutki
źródło
5

Haskell, 469 419 390 385 365 bajtów

funkcja f :: Int-> IO () przyjmuje na wejściu liczbę całkowitą i wypisuje węża przepływu

e 0=[0,0];e 5=[5,5];e x=[x]
f n=putStr.t$e=<<g n[0]
k=map$(53-).fromEnum
g 0=id
g n=g(n-1).(=<<)(k.(words"5402553 5440124 1334253 2031224 1345110 2003510"!!))
x=s$k"444666555666"
y=s$k"564645554545"
r l=[minimum l..maximum l]
s _[]=[];s w(x:y)=w!!(x+6):map(+w!!x)(s w y)
t w=unlines[["_/\\\\/_ "!!(last$6:[z|(c,d,z)<-zip3(x w)(y w)w,c==i&&d==j])|i<-r.x$w]|j<-r.y$w]
Damien
źródło
Daje to 2x powiększone liczby. Myślę, że pytanie dotyczy mniejszych liczb u góry i użyłem tylko powiększonych 2 × liczb, aby wyjaśnić, jak zbudowana jest taśma przepływowa.
Anders Kaseorg
Masz rację. Poprawiłem
Damien
Można użyć $w definicji ki wymienić (!!)az (a!!)którego można się pozbyć z niektórych nawiasach. Poza tym wydaje się, że znasz wiele sztuczek. Nice
dumny haskeller
4

C, 479 474 468 827 bajtów

Chyba nie ma pobicia od Perla i Haskella, ale ponieważ nie ma tu jeszcze zgłoszenia C:

#define C char
C *q="053400121154012150223433102343124450553245";X,Y,K,L,M,N,i,c,x,y,o;F(C*p,
int l,C d){if(d){l*=7;C s[l];for(i=0;i<l;i++)s[i]=q[(p[i/7]%8)*7+i%7];return F
(s,l,d-1);}x=0;y=0;o=32;while(l--){c=*p++%8;for(i=!(c%3)+1;i--;) {K=x<K?x:K;L=
y<L?y:L;M=x>M?x:M;N=y>N?y:N;y+=c&&c<3;x-=c%5>1;if(x==X&y==Y)o="_\\/"[c%3];y-=c
>3;x+=c%5<2;}}return X<M?o:10;}main(l){F(q,7,l);for(Y=L;Y<N;Y++)for(X=K;X<=M;X
++)putchar(F(q,7,l));}

Aby zaoszczędzić miejsce na wywołaniu atoi (), dla poziomu wykorzystywana jest liczba argumentów przekazywanych do programu.

Program działa w trybie O (n ^ 3) lub gorszym; najpierw ścieżka jest obliczana raz, aby znaleźć współrzędne min / maks, a następnie dla każdej pary (x, y) jest obliczana raz, aby znaleźć znak w tej konkretnej lokalizacji. Strasznie wolno, ale oszczędza na administrowaniu pamięcią.

Przykład uruchom na http://codepad.org/ZGc648Xi

Zevv
źródło
Użyj X,Y,K,L,M,N,i,j,c;zamiast int X,Y,K,L,M,N,i,j,c;i main(l)zamiastvoid main(int l)
Spikatrix
Tak, dziękuję, już się ogoliłem i jeszcze trochę, przygotuję nową wersję.
Zevv
Wyjście w najnowszej wersji wydaje się być przycięte i nieco odcięte na końcach.
Optymalizator
Przesłałem niewłaściwy obiekt blob, ten powinien być w porządku.
Zevv
4

Python 2, 523 502 475 473 467 450 437 bajtów

l=[0]
for _ in l*input():l=sum([map(int,'004545112323312312531204045045050445212331'[t::6])for t in l],[])
p=[]
x=y=q=w=Q=W=0
for t in l:T=t|4==5;c=t in{2,4};C=t<3;q=min(q,x);Q=max(Q,x+C);w=min(w,y);W=max(W,y);a=C*2-1;a*=2-(t%3!=0);b=(1-T&c,-1)[T&1-c];x+=(a,0)[C];y+=(0,b)[c];p+=[(x,y)];x+=(0,a)[C];y+=(b,0)[c]
s=[[' ']*(Q-q)for _ in[0]*(W-w+1)]
for t,(x,y)in zip(l,p):x-=q;s[y-w][x:x+1+(t%3<1)]='_/\_'[t%3::3]
for S in s:print''.join(S)

Pffft, kosztowało mnie jakieś 3 godziny, ale było fajnie!

Chodzi o podzielenie zadania na kilka etapów:

  1. Oblicz wszystkie krawędzie (zakodowane jako 0-5) w kolejności pojawiania się (tak od początku węża do końca)
  2. Oblicz pozycję dla każdej krawędzi (i zapisz wartości minimalne i maksymalne dla xiy)
  3. Zbuduj ciągi, z których się składa (i użyj wartości min, aby zrównoważyć, aby nie uzyskać ujemnych wskaźników)
  4. Wydrukuj struny

Oto kod w postaci bez golfa:

# The input
n = int(input())

# The idea:
# Use a series of types (_, /, \, %), and positions (x, y)
# Forwards:   0: __  1: /  2: \
# Backwards:  3: __  4: /  5: \

# The parts
pieces = [
    "0135002",
    "0113451",
    "4221502",
    "5332043",
    "4210443",
    "5324551"
]
# The final types list
types = [0]
for _ in range(n):
    old = types
    types = []
    for t in old:
        types.extend(map(int,pieces[t]))

# Calculate the list of positions (and store the mins and max')
pos = []
top = False
x = 0
y = 0
minX = 0
minY = 0
maxX = 0
maxY = 0
for t in types:
    # Calculate dx
    dx = 1 if t < 3 else -1
    if t%3==0:
        dx *= 2         # If it's an underscore, double the horizontal size
    # Calculate dy
    top = t in {1, 5}
    dy = 0
    if top and t in {0, 3, 1, 5}:
        dy = -1
    if not top and t in {2, 4}:
        dy = 1
    # If backwards, add dx before adding the position to the list
    if t>2:
        x += dx
    # If top to bottom, add dy before adding the position to the list
    if t in {2,4}:
        y += dy
    # Add the current position to the list
    pos += [(x, y)]
    # In the normal cases (going forward and up) modify the x and y after changing the position
    if t<3:
        x += dx
    if t not in {2, 4}:
        y += dy
    # Store the max and min vars
    minX = min(minX, x)
    maxX = max(maxX, x + (t<3)) # For forward chars, add one to the length (we never end with __'s)
    minY = min(minY, y)
    maxY = max(maxY, y)

# Create the string (a grid of charachters)
s = [[' '] * (maxX - minX) for _ in range(maxY - minY + 1)]
for k, (x, y) in enumerate(pos):
    x -= minX
    y -= minY
    t = types[k]
    char = '/'
    if t % 3 == 0:
        char = '__'
    if t % 3 == 2:
        char = '\\'
    s[y][x : x + len(char)] = char

# Print the string
for printString in s:
    print("".join(printString))

Edycja: Zmieniłem język na Python 2, aby był zgodny z moją odpowiedzią dla # 3 (i oszczędza również 6 dodatkowych bajtów)

Matty
źródło
Dobra robota; jedna prosta poprawka, którą możesz wprowadzić, zmieniłaby l.extend(x)się l+=x. Prawdopodobnie możesz też użyć codegolf.stackexchange.com/questions/54/… zamiast tego, .split()którego używasz (zrobiłem coś podobnego w mojej odpowiedzi)
KSab
@KSab Dzięki, czuję się teraz naprawdę głupio za używanieextend
Matty
0

Pari / GP, 395

Pętla nad pozycjami znaków x, y i obliczanie, jaki znak ma zostać wydrukowany. Umiarkowane próby zminimalizowania, zdobyte spacją i rozebrane komentarze.

k=3;
{
  S = quadgen(-12);  \\ sqrt(-3)
  w = (1 + S)/2;     \\ sixth root of unity
  b = 2 + w;         \\ base

  \\ base b low digit position under 2*Re+4*Im mod 7 index
  P = [0, w^2, 1, w, w^4, w^3, w^5];
  \\ rotation state table
  T = 7*[0,0,1,0,0,1,2, 1,2,1,0,1,1,2, 2,2,2,0,0,1,2];
  C = ["_","_",  " ","\\",  "/"," "];

  \\ extents
  X = 2*sum(i=0,k-1, vecmax(real(b^i*P)));
  Y = 2*sum(i=0,k-1, vecmax(imag(b^i*P)));

  for(y = -Y, Y,
     for(x = -X+!!k, X+(k<3),  \\ adjusted when endpoint is X limit
        z = (x- (o = (x+y)%2) - y*S)/2;
        v = vector(k,i,
                   z = (z - P[ d = (2*real(z) + 4*imag(z)) % 7 + 1 ])/b;
                   d);
        print1( C[if(z,3,
                     r = 0;
                     forstep(i=#v,1, -1, r = T[r+v[i]];);
                     r%5 + o + 1)]) );  \\ r=0,7,14 mod 5 is 0,2,4
     print())
}

Każdy znak jest pierwszą lub drugą komórką sześciokątną. Lokalizacja komórki to liczba zespolona z podzielona na podstawę b = 2 + w z cyframi 0, 1, w ^ 2, ..., w ^ 5, gdzie w = e ^ (2pi / 6) szósty pierwiastek jedności. Cyfry te są przechowywane jako rozróżnienie od 1 do 7, a następnie przenoszone od góry do dołu przez tabelę stanu dla obrotu netto. Jest to w stylu kodu Flownake autorstwa Eda Shoutena (xytoi), ale tylko dla rotacji netto, bez przekształcania cyfr w indeks „N” na ścieżce. Zakresy odnoszą się do początku 0 w środku kształtu. Dopóki limit nie jest punktem końcowym, są one środkiem sześciokąta o długości 2 znaków i potrzebny jest tylko 1 z tych znaków. Ale gdy początkiem i / lub końcem węża jest X limit, potrzebne są 2 znaki, czyli k = 0 początek i koniec <3. Pari ma wbudowane „quady”, takie jak sqrt (-3), ale to samo można zrobić z częściami rzeczywistymi i urojonymi osobno.

Kevin Ryde
źródło
1
To nie do końca spełnia zasady dotyczące początkowych i końcowych białych znaków.
Anders Kaseorg
Dzięki, poprawiłem. Twój haskell pokonał mnie godzinę do pętli x, y to zrobił. Powinieneś napisać przed czekaniem, aby zobaczyć, czy przyjdzie kolejna inspiracja :-).
Kevin Ryde
Teraz koniec węża jest odcięty dla k = 0, 1, 2. (Matematyka jest irytująca w ten sposób - ja też musiałem sobie z tym poradzić.)
Anders Kaseorg
Ach, kochanie, gdy punktem końcowym jest maksimum x. Hmm
Kevin Ryde