Сделай немного снега!

18

Ваша задача: сгенерировать снежинку Коха на n-ю глубину. Вам не нужно делать полную снежинку Коха, только одну сторону стартового треугольника. Википедия о хлопьях Коха: https://en.wikipedia.org/wiki/Koch_snowflake .

Правила:

  • Программа должна генерировать одну сторону снежинки Коха на n-ю глубину.
  • Выход должен быть ASCII.
  • Вы можете создать целую снежинку; это не обязательно.
  • Применяются стандартные правила ввода / вывода, лазейки и прочее.
  • Пробел не имеет значения, пока все символы находятся в правильном месте относительно друг друга.
  • Самый короткий код выигрывает!

Тестовые случаи:

п = 0:

__

п = 1:

__/\__

п = 2:

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

п = 3:

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

Я надеюсь в этом есть смысл. Обратите внимание, что в каждом тестовом случае фрактал можно разделить на три равные по длине части. Также обратите внимание, что ширина каждой снежинки в три раза больше ширины снежинки предыдущего поколения.

Товарищ Спаркл Пони
источник
FYI, было решено , что это не боян из этого .
Товарищ SparklePony
Я не думаю, что вы должным образом определили, каково правильное представление ASCII n-й кривой Коха.
orlp
Я не уверен, что пропорции имеют смысл. Тип non-dupe используется __/\__с двумя подчеркиваниями, что делает каждую итерацию в 3 раза больше предыдущей. Использование только одного подчеркивания приводит к противоречиям, которые становятся неловкими при n = 3. Например, наружные части имеют ширину 12 , а в средней части имеет только ширину 10, как следствие , /_и _\ которые являются слишком тесно. И даже до этого у вас есть _расширение в два раза шире /и \ .
Орджан Йохансен
Я думаю /_и _\ единственная действительно фатальная часть - подчеркивания должны идти, потому что они должны быть в том же положении, что /и \ . Как только это будет сделано, вещи могут увеличиться в 3 раза с n = 1 и далее (но n = 0 не подходит.)
Орджан Йохансен
Увы, нет, средняя часть по-прежнему имеет ширину, не совпадающую с внешними частями, о чем свидетельствует n = 3 с шириной 52, а не 54 = 2 * 3 ^ 3. Попробуйте один из них . Я включил перевернутые версии с частями, которые отображаются только с n = 4 или n = 5 - они отличаются от верхних тем, где подчеркивания сброшены.
Орджан Йохансен

Ответы:

10

Haskell , 308 300 299 байт

Редактирование:

  • -4 байт: Изменение zipWith(+)к zipWith(-)и регулировочные кодировок и зачеты избавились от каждого знака отрицания.
  • -1 байт: дальнейшая настройка кодировки позволила #отбросить несколько имен переменных , используя r=reverseвместо прямого сопоставления с образцом.
  • -2 байта: использование оператора вместо алфавита для zipWith(-).
  • -1 байт: определение o=[0,0]сокращения списка констант.
  • -1 байт: объединение двух ветвей ?.
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]

Попробуйте онлайн! (К сожалению, все, что больше n = 3, становится ужасно обёрнутым и нечитаемым, но вы можете скопировать его в другую программу, чтобы увидеть.)

вариации

Как это устроено

  • kявляется главной функцией, она принимает Int nи возвращает String.
  • iterate(>>=(:[1,4,1]))[6]генерирует бесконечный список, содержащий для каждого n повороты между последовательными линиями в этой итерации кривой, стиль графики черепахи, как числа, номинально между 0и 5. Каждая итерация - просто предыдущая с 1,4,1чередованием ходов . Единственная причина, с которой начинаются списки, 6а не 0состоит в том, чтобы заставить gcdхитрость fработать, избегая f 0.
  • scanl1(+)преобразует повороты в «абсолютные» направления, по модулю 6. 0Средство вправо, тогда каждое большее число на 60 градусов против часовой стрелки от предыдущего. (Ну, было бы 60 градусов, если бы это был правильный рисунок, а не ASCII.)
  • f преобразует абсолютное направление в список пар (кодировка символов, смещение), который кодирует, какие символы добавить к кривой (для горизонтальных направлений он генерирует две пары, в противном случае - одну), и как изменяется относительная позиция.
  • В #операторе перебирает предыдущий список (характер, кодирование смещения) пар, генерируя фактический (координату, символ) пар.
  • Принципы кодирования:
    • Символ от _/\номинально представляет линию, проведенную от начального угла через прямоугольную ячейку до другого конечного угла.
    • Координаты ячеек имеют форму [y,x]сверху вниз, слева направо, так что они сортируются в том порядке, в котором мы хотим их напечатать. Столбцы основаны на 1. Списки используются вместо кортежей для более короткой векторной арифметики с (&)=zipWith(-).
    • Угол обозначается теми же координатами, [y,x]что и ячейка слева вверху. Это гарантирует, что все смещения от угла к соседним ячейкам неотрицательны, избегая отрицательных констант.
    • Тем не менее, угловые координаты обнуляются, чтобы все операции над векторами были вычитаниями, а не сложениями, что позволяет избежать всех других явных знаков.
    • Список кодирования смещения - это [y1,x1,x2,y2]где [y1,x1]смещение координат от начального угла к ячейке символа и [y2,x2]смещение от конечного угла к ячейке символа. Это означает:
      • Списки кодирования для направлений 3.. 5являются только противоположностью списков для 0.. 2, позволяя создавать их с помощью [id,r]<*>.
      • Вся необходимая векторная арифметика может быть выполнена с использованием (&)=zipWith(-)либо списка кодирования, либо его обратного.
  • После сортировки списка пар (координата, символ) они передаются ?, и Stringиз них создается окончательный результат.
    • In x?l@(([_,w],c):r) x- это x-координата предыдущего символа, показанного в этой строке, или 0если в начале строки; lэто весь текущий список, wэто x-координата следующего добавляемого cсимвола, это символ и rоставшийся список.
    • На этом этапе y-координаты больше не нужны. Поскольку каждая строка содержит символы, а первый символ каждой строки находится слева от конца предыдущей, начало новых строк определяется путем проверки, уменьшилась ли x-координата.
    • Подчеркивание имеет большее значение ASCII, чем \и /, поэтому оно сортируется последним, если перекрывается другим символом в той же позиции. Таким образом, избыточное подчеркивание обнаруживается путем проверки повторения x-координаты.
Орджан Йохансен
источник
Ницца! Я приму это, если сегодня не будет больше активности по этому вопросу.
Товарищ SparklePony