Narysuj losowy glif heksowy

23

wprowadź opis zdjęcia tutaj

Powyższy obraz nazywa się glifem heksa. Heksa-glify to kilka fajnych wzorów, które wymyśliłem podczas gry w DiffEq. Oto, jak to zrobić:

  1. Rozważ następujący zestaw punktów w kształcie zwykłego heksagramu. Wewnętrzny sześciokąt będzie zawierał ostatni glif, podczas gdy zewnętrzne 6 punktów tworzy gwiazdę i tam zaczynamy rysować nasze linie.

wprowadź opis zdjęcia tutaj

  1. Z zewnętrznych sześciu punktów losowo wybierz parę. Aby uzyskać wydajność, między dwoma wybranymi punktami powinien znajdować się co najmniej jeden inny punkt (w przeciwnym razie nie miałoby to wpływu na ostateczną liczbę). Następnie z każdego z dwóch punktów skieruj promień w kierunku drugiego. Promień ten jest blokowany przez poprzednie linie.

wprowadź opis zdjęcia tutaj

  1. Powtarzaj ten proces, aż utworzy się wszystkie 9 krawędzi, jak pokazano na kilku następnych obrazach.

wprowadź opis zdjęcia tutaj

  1. Oto przykład blokowania promieni. Końce segmentu promienia są nadal widoczne, ale środkowa część jest zasłonięta przez dwa pierwsze narysowane segmenty.

wprowadź opis zdjęcia tutaj

  1. Te dwa promienie są również „blokowane”, ale nie powoduje to widocznej różnicy, ponieważ są blokowane przez tę samą drugą linię.

wprowadź opis zdjęcia tutaj

  1. Szybkie przewijanie do momentu narysowania wszystkich 9 linii. Jeśli chcesz uzyskać bardziej szczegółowe wyjaśnienie pominiętych kroków, mogę wyjaśnić.

wprowadź opis zdjęcia tutaj

  1. Na koniec usuń punkty gwiazdy. Grube kropki są również usuwane, aby wyglądać ładniej.

wprowadź opis zdjęcia tutaj

Wyzwanie

Wyzwanie polega na wygenerowaniu wizualnej reprezentacji losowego glifu heksa. To jest golf golfowy, wygrywa najmniej bajtów.

  1. Wszystkie możliwe glify heksowe powinny pojawić się z pewnym prawdopodobieństwem dodatnim. Różne hex-glify są generowane przez zmianę kolejności rysowania 9 krawędzi.

  2. Ponadto wszystkie obrazy wyprowadzane przez Twój program muszą być poprawnymi glifami hexa. Pewne wzory (jak kompletny zarys wewnętrznego sześciokąta) nie mogą pojawić się jako glif heksowy, więc program nie może ich wyprowadzać.

  3. Wyjście powinno być obrazem graficznym (wydrukowanym do ekranu lub pliku).

  4. Sześciokąt musi być regularny, ale może pojawiać się w dowolnej orientacji.

  5. Odbicia / obroty nie są uważane za wyjątkowe. (Może to ułatwić spełnienie wymagania 1).

PhiNotPi
źródło
8
I made up while doodling during my DiffEq class. Sposób, w jaki dzieją się wszystkie wielkie odkrycia ...: P
Rɪᴋᴇʀ
Jakie są minimalne wymagania dotyczące obrazu? W jakim stopniu sztuka ASCII musi być rozpoznawalna, dopóki każda krawędź jest reprezentowana i umieszczona w niejasnym miejscu?
John Dvorak,
@JanDvorak Usunąłem opcję graficzną ASCII wyzwania (jak w ciągu 2 minut od opublikowania), ponieważ programy produkujące grafiki ASCII i grafiki nie są łatwo porównywalne.
PhiNotPi
co wtedy z pikselami? Nagłówek PPM nie jest zbyt ciężki, a potem jedyną różnicą jest użycie '01'z przeplotem zamiast ' *'.
John Dvorak,
@JanDvorak Wyjście to byłby odpowiednio sformatowany plik obrazu, prawda? Więc nie widzę w tym nic złego.
PhiNotPi

Odpowiedzi:

18

Mathematica, 273 268 264 242 bajtów

c=CirclePoints;b@_=k=1>0;Graphics[Line/@Cases[Append[Join@@({c@6,{3^.5/2,-Pi/6}~c~6}),{0,0}][[b@#=!k;#]]&/@TakeWhile[#,t=k;(r=t;t=b@#;r)&]&/@Join@@RandomSample[{#,Reverse@#}&/@Partition[Range@12,3,2,1]~Join~Array[{2#,13,2#+6}&,3]],{_,__}]]

Czyni jako odnośnikiem Tw Mathematicą i operator przyrostek transpozycji.

Uporządkowanie błędów w tym zajęło mi wieczność ... pod koniec zhakowałem kilka rzeczy razem, aby działało, więc jest to zdecydowanie nieoptymalne. Zastanawiam się także, czy ogólnie lepiej byłoby lepiej zaimplementować specyfikację dosłownie poprzez linie w zewnętrznym sześciokącie i pozwolić funkcjom geometrycznym Mathematica obsługiwać przecięcia.

Pamiętaj, że jest to pełny program i jeśli chcesz uruchomić kod wiele razy w ciągu jednej sesji REPL, musisz go poprzedzić Clear[b].

Oto wyniki 20 przebiegów:

wprowadź opis zdjęcia tutaj

Wyjaśnienie

To rozwiązanie w ogóle nie wykorzystuje zewnętrznych punktów gwiazdowych. Zamiast tego działa bezpośrednio z punktami, które są częścią heksaglypha i liniami pokrywającymi trzy z nich jednocześnie.

Oznaczmy punkty:

wprowadź opis zdjęcia tutaj

1zaczyna się w nieco dziwnym zakręcie, ale wynika to z (również nieco dziwnego) domyślnego zachowania CirclePoints. Stamtąd uruchomienie sześciokąta okazało się najtańsze.

Teraz chcemy znaleźć odpowiednie linie przez trzy z tych punktów, które odpowiadają połączonym punktom gwiazdy zewnętrznej. Te wokół sześciokąta to oczywiście tylko 3 sąsiednie punkty (moduł 12), zaczynając od liczby nieparzystej. Te całym centrum składa się z numeru nawet n, 13i n+6.

Reprezentacje tych linii (w formie list trzech punktów są generowane przez następujący kod):

Partition[Range@12,3,2,1]~Join~Array[{2#,13,2#+6}&,3]

PartitionGeneruje linie wokół sześciokąta i Arraylinii przez centrum. Aby przetworzyć obie wiązki, mapujemy tę funkcję na listę linii:

{#,Reverse@#}&

Teraz tasujemy je, RandomSampleaby przetwarzać je w losowej kolejności. Join @@spłaszcza listę par, dzięki czemu mamy listę belek.

Krótka przerwa: aby śledzić, które punkty są już zablokowane, używamy funkcji wyszukiwania b, która jest inicjowana Truedla wszystkich wartości przez b@_=k=1>0;. Podczas przetwarzania belki zachowujemy wszystkie punkty do pierwszego punktu, który ma b[n] == False(w tym ten):

TakeWhile[#,t=k;(r=t;t=b@#;r)&]&

Wydaje mi się, że jest to obecnie najbardziej golfowa część ... użycie dwóch zmiennych tymczasowych do gry Mastermind wydaje się naprawdę drogie. W każdym razie wynik tego daje nam punkty w linii, którą możemy narysować. Teraz ta funkcja jest mapowana na każdy z tych punktów:

Append[Join@@({c@6,{3^.5/2,-Pi/6}~c~6}),{0,0}][[b@#=!k;#]]&

Pierwsza część generuje listę wszystkich 13 punktów na podstawie przeplatanych wyników dwóch wywołań do CirclePoints(z różnymi promieniami dla środków krawędzi i narożników sześciokąta). Zwróć uwagę na to, b@#=!kco teraz ustawia wartość tabeli odnośników dla bieżącego punktu, aby Falseżadna dalsza wiązka nie mogła przez niego przejść. Na koniec wartość jest używana jako indeks do listy współrzędnych w celu uzyskania prawidłowego punktu 2D.

Cases[...,{_,__}]

Spowoduje to odrzucenie wszystkich list jednoelementowych, ponieważ będą renderowane jako pojedyncze (i widoczne) punkty. Na koniec renderujemy wynik:

Graphics[Line/@...]
Martin Ender
źródło
b@_=1>0=b=1>0&
CalculatorFeline
@CatsAreFluffy Nie sądzę, że to działa, ponieważ muszę później móc zastąpić poszczególne wartości.
Martin Ender,
Dobre wykorzystanie CirclePoints.
DavidC
Doceniam ten link na Youtube.
DanTheMan
8

Buty (Ruby) Rev C 184 bajty

12 bajtów zaoszczędzonych poprzez przeniesienie odpowiedzialności za sprawdzenie, czy konkretna półwiersz powinna zostać narysowana z programu głównego do metody rysowania. Główny program nadal musi jednak sprawdzić, czy cała linia jest całkowicie zablokowana.

Shoes.app{t=[]
d=->p,q{t[p]&&t[q]||line(p/6*8,p%6*14,q/6*8,q%6*14)}
%w{1I IW WM M5 5' '1 =A P. R,}.shuffle.map{|i|b=i.sum/2
c=b*2-a=i.ord
t[a]&&t[c]||(d[a,b]
d[b,c]
t[a]=t[b]=t[c]=1)}}

Buty (Ruby) 205 ... Rev B 196 bajtów

Buty to oparte na rubinie narzędzie do tworzenia GUI itp. Po raz pierwszy go używam. mothereff.in/byte-counter liczy moje przesłanie jako 196 bajtów, ale z jakiegoś powodu Shoes liczy je jako 202.

Ponadto Ruby pozwala robić rzeczy takie, t[a=i.ord]ale dziwnie, wydaje się, że nie działa zgodnie z oczekiwaniami z Shoes.

Shoes.app{t=[]
d=->p,q{line(p/6*8,p%6*14,q/6*8,q%6*14)}
%w{1I IW WM M5 5' '1 =A P. R,}.shuffle.map{|i|b=i.sum/2
c=b*2-a=i.ord
t[a]&&t[c]||(t[a]&&t[b]||d[a,b]
t[b]&&t[c]||d[b,c]
t[a]=t[b]=t[c]=1)}}

Wyjaśnienie

Nie uważam części linii poza sześciokątem. Rysuję tylko tę część, którą trzeba narysować. Ważne jest, czy linie przecinają przecięcia (jeśli narysujemy tylko te części, które należy narysować, oznacza to, że zaczynają się / kończą na przecięciach).

Podstawowa zasada jest taka, że ​​jeśli odwiedzono oba punkty końcowe linii, linia jest zablokowana i nie powinna być rysowana. Ponieważ linie są rysowane w dwóch połowach, musimy również sprawdzić, czy punkt środkowy został odwiedzony, aby zobaczyć, czy każda połowa powinna zostać narysowana, czy nie.

Śledzę, które punkty zostały odwiedzone w tablicy t[]. Ostatecznie zawiera wpis dla każdej fizycznej współrzędnej na siatce poniżej. Nie ma oddzielnej 13-elementowej tablicy logicznej. Do końca t[]może zawierać 87 elementów, ale tylko 13 może zawierać przydatne dane.

Wewnętrznie współrzędne punktów końcowych linii są podawane przez pojedynczą liczbę z, gdzie z% 6 to współrzędna y, a z / 6 to współrzędna x. W tym systemie sześciokąt jest spłaszczony. Kiedy linie są wykreślane, skala x jest mnożona przez 8, a skala y jest mnożona przez 14, co jest bardzo bliskim racjonalnym przybliżeniem do prawidłowego stosunku: 14/8 = 1,75 vs sqrt (3) = 1,732.

Wewnętrzny układ współrzędnych pokazano poniżej z kilkoma przykładowymi danymi wyjściowymi.

wprowadź opis zdjęcia tutaj

Bez golfa

Shoes.app{
  t=[]                                          #Empty array for status tracking
  d=->p,q{line(p/6*8,p%6*14,q/6*8,q%6*14)}      #Drawing method. Convert p and q into x,y pairs, scale and draw line.
  %w{1I IW WM M5 5' '1 =A P. R,}.shuffle.map{|i|#take an array of the coordinates of the endpoints of each line, shuffle, then for each line
    b=i.sum/2                                   #b = midpoint of line, convert ASCII sum to number (average of the two coordinates)
    a=i.ord                                     #a = first endpoint of line, convert ASCII to number (no need to write i[0].ord)
    c=b*2-a                                     #c = second endpoint of line (calculating is shorter than writing i[1].ord)
    t[a]&&t[c]||(                               #if both endpoints have already been visited, line is completely blocked, do nothing. ELSE
      t[a]&&t[b]||d[a,b]                        #if first endpoint and midpoint have not both been visited, draw first half of line
      t[b]&&t[c]||d[b,c]                        #if second endpoint and midpoint have not both been visited, draw second half of line
      t[a]=t[b]=t[c]=1                          #mark all three points of the line as visited
    )
  }
}

Więcej próbek wyjściowych

Zostały one wykonane ze starszą wersją programu. Jedyna różnica polega na tym, że położenie sześciokąta w oknie jest teraz nieco inne.

wprowadź opis zdjęcia tutaj

Level River St
źródło
mothereff.in/byte-counter counts my submission as 196 bytes, but for some reason Shoes counts it as 202.Nie wiem w 100%, czy to prawda, ale myślę, że powodem, dla którego Shoes policzył twój kod jako 202 bajty zamiast 196, jest to, że twoje znaki nowej linii są w rzeczywistości sekwencją dwóch znaków „\ r \ n”. To sprawia, że ​​każda nowa linia jest liczona dwukrotnie. Oto odpowiedź na pytanie o przepełnienie stosu dotyczące \ ri \ n.
K Zhang
Hehe, nie mogę się pozbyć nazwy Ruby with Shoes XD
Beta Decay
3

Python, 604 591 574 561 538 531 536 534 528 493 483 452 431 420 419 415 388 385 384 bajtów

Zaadaptowałem pomysł Level River St, aby sprawdzić, czy linia zostanie zablokowana, sprawdzając, czy oba punkty końcowe linii były już wcześniej odwiedzane. Oszczędza to 27 bajtów. Sugestie dotyczące gry w golfa mile widziane.

Edycja: Naprawianie błędów i gra g(p,q)w golfa przez 3 bajty. Grał Lw golfa na jeden bajt.

from turtle import*
from random import*
R=range
G=goto
*L,=R(9)
shuffle(L)
a=[0]*13
ht()
T=12
c=[(j.imag,j.real)for j in(1j**(i/3)*T*.75**(i%2/2)for i in R(T))]+[(0,0)]
def g(p,q):pu();G(c[p]);a[p]*a[q]or pd();G(c[q])
for m in L:
 p=2*m;x,y,z=R(p,p+3)
 if m<6:
  if a[x]*a[z%T]<1:g(x,y);g(y,z%T);a[x]=a[y]=a[z%T]=1
 else:
  if a[p-11]*a[p-5]<1:g(p-11,T);g(p-5,T);a[p-11]=a[p-5]=a[T]=1

Ungolfing:

from turtle import*
from random import*

def draw_line(points, p_1, p_2):
    penup()
    goto(points[p_1])
    if not (a[p] and a[q]):
        pendown()
    goto(points[p_2])

def draw_glyph():
    ht()
    nine_lines = list(range(9))
    shuffle(nine_lines)
    size = 12
    center = [0,0]

    points = []
    for i in range(12):      # put in a point of a dodecagon
                             # if i is even, keep as hexagon point
                             # else, convert to hexagon midpoint
        d = 1j**(i/3) * 12   # dodecagon point
        if i%2:
            d *= .75**.5     # divide by sqrt(3/4) to get midpoint
        points += (d.imag, d.real)
    points.append(center)

    a = [0]*13
    for m in nine_lines:
        p = 2*m
        if m<6:
            x, y, z = p, p+1, p+2
            if not (a[x] and a[z%12]):
                draw_line(points, x, y)
                draw_line(points, y, z%12)
                a[x] = a[y] = a[z%12] = 1
        else:
            if not (a[p-11] and a[p-5]):
                draw_line(p-11, 12)
                draw_line(p-5, 12)
                a[p-11] = a[p-5] = a[12] = 1

Same heksa-glify są dość małe, ponieważ używamy 12-pikselowego sześciokąta jako podstawy (ze względów golfowych). Oto kilka przykładów heksa-glifów (przeprosiny za słabe kadrowanie):

Przykład glifu sześciokątnego Przykład glifu sześciokątnego Przykład glifu sześciokątnego Przykład glifu sześciokątnego Przykład glifu sześciokątnego Przykład glifu sześciokątnego

Sherlock9
źródło
Można zaoszczędzić kilka bajtów:R=range;G=goto
Tim Čas,