Нарисуйте случайный гекса-глиф

23

введите описание изображения здесь

Изображение выше называется гекса-глифом. Гекса-глифы - это несколько классных шаблонов, которые я придумал во время рисования на уроках DiffEq. Вот как вы это делаете:

  1. Рассмотрим следующий набор точек в форме правильной гексаграммы. Внутренний шестиугольник - это то, что будет содержать окончательный глиф, а внешние 6 точек образуют звезду, и именно там мы начнем рисовать наши линии.

введите описание изображения здесь

  1. Из шести внешних точек случайным образом выберите пару. Для эффективности между двумя выбранными точками должна быть хотя бы одна другая точка (в противном случае это не повлияет на конечный показатель). Затем из каждой из двух точек наведите луч на другой. Этот луч заблокирован предыдущими строками.

введите описание изображения здесь

  1. Повторяйте этот процесс, пока все 9 ребер не будут сформированы, как показано на следующих нескольких изображениях.

введите описание изображения здесь

  1. Вот пример блокировки лучей. Концы сегмента луча все еще видны, но средняя часть закрыта первыми двумя нарисованными нами сегментами.

введите описание изображения здесь

  1. Эти два луча также «заблокированы», но это не вызывает видимой разницы, потому что они заблокированы той же самой другой линией.

введите описание изображения здесь

  1. Перемотка вперед, пока все 9 линий не нарисованы. Если вы хотите более подробное объяснение этих пропущенных шагов, я могу изложить.

введите описание изображения здесь

  1. Наконец, удалите точки звезды. Чтобы сделать его более красивым, толстые точки также удаляются.

введите описание изображения здесь

Соревнование

Ваша задача - вывести визуальное представление случайного гекса-глифа. Это код-гольф, побеждает меньше байтов.

  1. Все возможные гекса-глифы должны появляться с некоторой положительной вероятностью. Различные гекса-глифы создаются путем изменения порядка прорисовки 9 ребер.

  2. Кроме того, все изображения, выводимые вашей программой, должны быть действительными гекса-символами. Определенные шаблоны (например, полный контур внутреннего шестиугольника) не могут отображаться в виде гекса-глифа, и поэтому ваша программа не должна их выводить.

  3. На выходе должно быть графическое изображение (напечатанное на экране или в файле).

  4. Шестиугольник должен быть правильным, но может появляться в любой ориентации.

  5. Отражения / повороты не считаются уникальными. (Это может облегчить выполнение требования 1).

PhiNotPi
источник
8
I made up while doodling during my DiffEq class, Как происходят все великие открытия ...: P
Rɪᴋᴇʀ
Каковы минимальные требования к изображению? В какой степени искусство ASCII должно быть узнаваемым, если каждое ребро представлено и расплывчато в правильном месте?
Джон Дворак
@JanDvorak Я удалил художественную опцию ASCII задачи (например, в течение 2 минут после публикации), потому что программы, которые производят ASCII-art и графические результаты, не легко сопоставимы.
PhiNotPi
тогда как насчет пиксельной графики? Заголовок PPM не слишком тяжелый, и после этого единственное отличие заключается в использовании '01'чередования пробелов вместо ' *'.
Джон Дворак
@JanDvorak Вывод будет правильно отформатированный файл изображения, верно? Тогда я не вижу в этом ничего плохого.
PhiNotPi

Ответы:

18

Mathematica, 273 268 264 242 байта

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]],{_,__}]]

Оказывает как верхний индекс Tв Mathematica и постфиксный оператор транспонирования.

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

Обратите внимание, что это полноценная программа, и если вы хотите запустить код несколько раз в течение одного сеанса REPL, вам придется использовать префикс Clear[b].

Вот результаты 20 пробежек:

введите описание изображения здесь

объяснение

Это решение вообще не использует внешние звездные точки. Вместо этого он работает непосредственно с точками, которые являются частью гексаглифа, и линиями, которые покрывают три из них одновременно.

Давайте обозначим точки:

введите описание изображения здесь

1начинается с немного странного угла, но это происходит из-за (также несколько странного) поведения по умолчанию CirclePoints. Стартовый шестигранник оттуда оказался самым дешевым.

Теперь мы хотим найти соответствующие линии через три из этих точек, которые соответствуют соединенным точкам внешней звезды. Те, что расположены вокруг шестиугольника - это, конечно, всего 3 смежные точки (по модулю 12), начиная с нечетного числа. Те, что в центре состоят из четного числа n, 13и n+6.

Представления этих линий (в виде списков из трех точек генерируются следующим кодом):

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

PartitionГенерирует линию вокруг шестиугольника и Arrayлинии через центр. Чтобы обработать оба луча, мы сопоставляем эту функцию со списком линий:

{#,Reverse@#}&

Теперь мы перемешиваем их, RandomSampleчтобы обработать их в случайном порядке. Join @@сглаживает список пар, так что у нас есть список лучей.

Короткий перерыв: чтобы отслеживать, какие точки уже заблокированы, мы используем функцию поиска b, которая инициализируется Trueдля всех значений с помощью b@_=k=1>0;. При обработке луча мы сохраняем все точки до первой точки, которая имеет b[n] == False( включая эту):

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

Я чувствую, что сейчас это самая играбельная часть игры ... использование двух временных переменных для игры в Mastermind кажется действительно дорогим. В любом случае, результат этого дает нам точки на линии, которую мы можем нарисовать. Теперь эта функция отображается на каждой из этих точек:

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

Первая часть генерирует список всех 13 точек, используя результаты чередования двух вызовов CirclePoints(с разными радиусами для центров краев и углов шестиугольника). Обратите внимание, b@#=!kчто теперь устанавливает значение таблицы поиска для текущей точки, чтобы Falseникакой дальнейший луч не мог пройти через нее. Наконец, значение используется в качестве индекса в списке координат для получения правильной 2D-точки.

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

Это отбрасывает все одноэлементные списки, потому что они будут отображаться как отдельные (и видимые) точки. Наконец, мы отображаем результат:

Graphics[Line/@...]
Мартин Эндер
источник
b@_=1>0=b=1>0&
CalculatorFeline
@CatsAreFluffy Я не думаю, что это работает, потому что я должен быть в состоянии перезаписать отдельные значения позже.
Мартин Эндер
Хорошее использование CirclePoints.
DavidC
Я оценил эту ссылку на Youtube.
DanTheMan
8

Обувь (Рубин) Rev C 184 байта

12 байтов, сохраненных за счет передачи ответственности за проверку того, следует ли выводить конкретную половину линии из основной программы в метод рисования. Основная программа все же должна проверить, полностью ли заблокирована вся строка.

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)}}

Обувь (Рубин) 205 ... Rev B 196 байт

Обувь - это инструмент для построения графического интерфейса на основе рубина и т. Д. Я впервые его использую. mothereff.in/byte-counter считает мое представление 196 байтами, но по какой-то причине Shoes считает его 202.

Кроме того, Ruby позволяет вам делать что-то вроде, t[a=i.ord]но, как ни странно, с ботинками это работает не так, как ожидалось.

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)}}

объяснение

Я не рассматриваю части линии вне шестиугольника. Я рисую только ту часть, которая должна быть нарисована. Важно то, пересекают ли линии пересечения (если мы рисуем только те части, которые должны быть нарисованы, это означает, что они начинаются / заканчиваются на пересечениях.)

Основное правило состоит в том, что, если обе конечные точки линии были посещены, линия блокируется и не должна быть нарисована. Поскольку линии нарисованы в двух половинах, мы также должны проверить, была ли достигнута средняя точка, чтобы увидеть, следует ли рисовать каждую половину или нет.

Я отслеживаю, какие точки были посещены в массиве t[]. В конечном итоге в нем содержится запись для каждой физической координаты в сетке ниже. Не существует отдельного 13-элементного логического массива. К концу t[]может быть 87 элементов, хотя только до 13 будут содержать полезные данные.

Внутри координаты конечных точек линий задаются одним числом z, где z% 6 - координата y, а z / 6 - координата x. В этой системе шестигранник сплющен. Когда линии построены, масштаб x умножается на 8, а масштаб y умножается на 14, что является очень близким рациональным приближением к правильному отношению: 14/8 = 1,75 против sqrt (3) = 1,732.

Внутренняя система координат показана ниже с несколькими примерами выходных данных.

введите описание изображения здесь

Ungolfed

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
    )
  }
}

Больше примеров выходов

Это было сделано с более старой версией программы. Разница лишь в том, что расположение гексаглифа в окне теперь немного отличается.

введите описание изображения здесь

Уровень реки St
источник
mothereff.in/byte-counter counts my submission as 196 bytes, but for some reason Shoes counts it as 202.Я не на 100% знаю, правда ли это, но я думаю, что причина, по которой Shoes считал ваш код как 202 байта вместо 196, заключается в том, что ваши символы новой строки на самом деле представляют собой последовательность из двух символов «\ r \ n». Это делает каждый перевод строки учитывается дважды. Вот ответ переполнения стека относительно \ r и \ n.
К Чжан
Хе-хе, я не могу понять имя 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 байта

Я адаптировал идею Level River St, чтобы проверить, будет ли линия заблокирована, проверяя, посещали ли обе конечные точки ранее. Это экономит 27 байтов. Предложения по игре в гольф приветствуются.

Редактировать: Исправление ошибок и игра в гольф g(p,q)на 3 байта. Гольф Lна один байт.

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

Сами гекса-глифы довольно малы, поскольку в качестве основы мы используем 12-пиксельный шестиугольник (по соображениям игры в гольф). Вот несколько примеров гекса-глифов (извинения за плохое обрезание):

Пример гекса-глифа Пример гекса-глифа Пример гекса-глифа Пример гекса-глифа Пример гекса-глифа Пример гекса-глифа

Sherlock9
источник
Может сэкономить несколько байтов:R=range;G=goto
Тим Час