Zrób trochę śniegu!

18

Twoje zadanie: wygeneruj płatek śniegu Kocha na n-tej głębokości. Nie musisz tworzyć pełnego płatka śniegu Kocha, tylko jedną stronę trójkąta początkowego. Wikipedia na temat płatków Kocha: https://en.wikipedia.org/wiki/Koch_snowflake .

Zasady:

  • Program musi wygenerować jedną stronę płatka śniegu Kocha na n-tej głębokości.
  • Wyjściem musi być ASCII.
  • Państwo może generować cały płatek śniegu; nie jest to wymagane.
  • Obowiązują standardowe zasady dotyczące wejścia / wyjścia oraz luki i inne rzeczy.
  • Biała spacja nie ma znaczenia, o ile wszystkie znaki znajdują się we właściwym miejscu względem siebie.
  • Najkrótszy kod wygrywa!

Przypadki testowe:

n = 0:

__

n = 1:

__/\__

n = 2:

      __/\__
      \    /
__/\__/    \__/\__

n = 3:

                        __/\__
                        \    /
                  __/\__/    \__/\__
                  \                /
                  /_              _\
                    \            /
      __/\__      __/            \__      __/\__
      \    /      \                /      \    /
__/\__/    \__/\__/                \__/\__/    \__/\__

Mam nadzieję, że to ma sens. Zauważ, że w każdym przypadku testowym fraktal można podzielić na trzy części o równej długości. Zauważ również, że szerokość każdego płatka śniegu jest trzy razy większa niż szerokość poprzedniej generacji płatka śniegu.

Towarzyszu SparklePony
źródło
FYI, uznano, że nie jest to dupe od tego .
Towarzysz SparklePony
Nie sądzę, żebyś właściwie zdefiniował, jaka jest właściwa reprezentacja ASCII n-tej krzywej Kocha.
orlp
Nie jestem pewien, czy proporcje mają sens. W trybie bez duplikatu zastosowano __/\__dwa podkreślenia, dzięki którym każda iteracja była 3 razy większa niż poprzednia. Używanie tylko jednego podkreślenia wydaje się dawać sprzeczności, które zaczynają być naprawdę niezręczne w n = 3. Np. Zewnętrzne części mają szerokość 12, podczas gdy środkowa część ma tylko szerokość 10, w wyniku czego /_i _\ które są zbyt ciasne. A nawet wcześniej _rozszerzasz do dwukrotnej szerokości /i \ .
Ørjan Johansen
Myślę, że /_i _\ to jedyny naprawdę krytyczny część - podkreślenia trzeba iść, ponieważ muszą być w tej samej pozycji jak /i \ . Gdy to się stanie, rzeczy mogą wzrosnąć 3 razy od n = 1 (ale n = 0 nie pasuje.)
Ørjan Johansen
Niestety, nie, środkowa część nadal ma szerokość niepasującą do zewnętrznych części, o czym świadczy n = 3 o szerokości 52 zamiast 54 = 2 * 3 ^ 3. Wypróbuj jedną z nich . Dołączyłem wersje do góry nogami z częściami wyświetlanymi tylko od n = 4 lub n = 5 - różnią się one od tych w górę, w których upuszczane są podkreślenia.
Ørjan Johansen

Odpowiedzi:

10

Haskell , 308 300 299 bajtów

Edycje:

  • -4 bajtów: Zmieniające zipWith(+)się zipWith(-)i regulacja kodowania i korekcje pozbył każdego znaku negacji.
  • -1 bajt: Dalsze dopracowanie kodowania pozwoliło na usunięcie kilku nazw zmiennych #przy użyciu r=reversezamiast bezpośredniego dopasowania wzorca.
  • -2 bajty: Użycie operatora zamiast alfanum dla zipWith(-).
  • -1 bajt: Definiowanie w o=[0,0]celu skrócenia stałych listy.
  • -1 bajt: Scalenie dwóch gałęzi ?.
import Data.List
k n=0?sort(o#(f=<<scanl1(+)(iterate(>>=(:[1,4,1]))[6]!!n)))
x?l@(([_,w],c):r)|x>w='\n':0?l|0<1=([2..w-x]>>" ")++[c|w>x]++w?r
_?_=""
w#((c,l):m)=(l&w,c):r l&(l&w)#m
_#_=[]
f x=zip"_/\\_/\\"([id,r]<*>[0:1:o,[0,1,0,1],o++[1,1]])!!mod x 6<$[1,3..gcd 3x]
(&)=zipWith(-)
r=reverse
o=[0,0]

Wypróbuj online! (Niestety, wszystko większe niż n = 3 zostaje okropnie opakowane i nieczytelne, ale możesz skopiować to do innego programu, aby to zobaczyć.)

Wariacje

Jak to działa

  • kjest główną funkcją, pobiera Int ni zwraca a String.
  • iterate(>>=(:[1,4,1]))[6]generuje nieskończoną listę zawierającą, dla każdego n, zwoje między kolejnymi liniami w tej iteracji krzywej, styl grafiki żółwia, jako liczby nominalnie między 0a 5. Każda iteracja jest tylko poprzednia z 1,4,1przeplotem zwojów . Jedynym powodem, dla którego podlisty zaczynają, 6zamiast 0robić gcdsztuczkę, fjest unikanie f 0.
  • scanl1(+)zamienia zwoje w kierunki „absolutne”, aż do modułu 6. A 0oznacza w prawo, a następnie każda wyższa liczba wynosi 60 stopni w kierunku przeciwnym do ruchu wskazówek zegara w stosunku do poprzedniego. (Cóż, byłoby to 60 stopni, gdyby był to właściwy rysunek, a nie ASCII.)
  • f konwertuje kierunek bezwzględny na listę par (znak, kodowanie offsetowe), która koduje znaki, które należy dodać do krzywej (dla kierunków poziomych generuje dwie pary, w przeciwnym razie jedną), oraz sposób zmiany położenia względnego.
  • Że #iteracje przez operatora poprzedniej listy (znak offsetowy, kodowanie), generowanie par rzeczywistych (współrzędnych par znaków).
  • Zasady kodowania:
    • Znak z _/\nominalnie reprezentuje linię od narożnika początkowego przez prostokątną komórkę do innego narożnika końcowego.
    • Współrzędne komórki mają postać [y,x], od góry do dołu, od lewej do prawej, dzięki czemu sortują się w kolejności, w jakiej chcemy je wydrukować. Kolumny są oparte na 1. Listy są używane zamiast krotek dla krótszej arytmetyki wektorowej z (&)=zipWith(-).
    • Narożnik jest oznaczony tymi samymi współrzędnymi, [y,x]co komórka w lewym górnym rogu. Zapewnia to, że wszystkie przesunięcia od rogu do sąsiednich komórek są nieujemne, unikając stałych ujemnych.
    • Jednak współrzędne narożnika są przekazywane dookoła zanegowane, aby wszystkie operacje wektorowe mogły być odejmowane zamiast dodawania, co pozwala uniknąć wszystkich innych wyraźnych znaków.
    • Lista kodowania przesunięć to miejsce, w [y1,x1,x2,y2]którym [y1,x1]znajduje się przesunięcie współrzędnych od narożnika początkowego do komórki znakowej i [y2,x2]jest przesunięciem od narożnika końcowego do komórki znakowej. To znaczy:
      • Listy kodowania dla kierunków 3.. 5są tylko odwrotnością list dla 02, pozwalając na ich generowanie [id,r]<*>.
      • Wszystkie niezbędne arytmetyki wektorowej można dokonać za pomocą (&)=zipWith(-)listy kodowania lub jej odwrotności.
  • Po posortowaniu listy par (współrzędnych, znaków) są one przekazywane do ?, co generuje Stringz nich finał .
    • In x?l@(([_,w],c):r) xjest współrzędną x poprzedniego znaku pokazanego w tym wierszu lub 0jeśli jest na początku wiersza; ljest całą bieżącą listą, wjest współrzędną x następnego znaku do dodania, cjest znakiem i rjest pozostałą listą.
    • Na tym etapie współrzędne Y nie są już potrzebne. Ponieważ każda linia zawiera znaki, a pierwszy znak każdej linii znajduje się daleko na lewo od końca poprzedniej, początek nowych linii jest wykrywany przez sprawdzenie, czy współrzędna x zmniejszyła się.
    • Podkreślenie ma większą wartość ASCII niż \i /, więc jest sortowane jako ostatnie, jeśli nakłada się z innym znakiem na tej samej pozycji. W ten sposób wykrywany jest nadmiarowy znak podkreślenia, sprawdzając, czy współrzędna X została powtórzona.
Ørjan Johansen
źródło
Ładny! Zaakceptuję to, jeśli dzisiaj nie będzie już więcej aktywności w tym pytaniu.
Towarzysz SparklePony,