Развертывание исходного кода Hexagony

52

Введение

Если вы не знакомы с гексагонией , это эзотерический язык, созданный Мартином Бюттнером. Дело в том, что этот язык принимает несколько форм для программы. Следующие программы эквивалентны:

abcdefg

а также

 a b
c d e
 f g

В общем, код свернут в обычный шестиугольник. Но учтите, что добавление новой команды в код приведет к созданию abcdefghследующей программы:

  a b c
 d e f g
h . . . .
 . . . .
  . . .

Как вы можете видеть, первым шагом является сворачивание кода в шестиугольник, и после этого шестиугольник заполняется no-ops ( .) до следующего центрированного шестиугольного числа .

Ваша задача проста: при задании строки (исходного кода) вывести полный исходный код шестиугольника.

Правила

  • Вы можете предоставить программу или функцию.
  • Допускается использование пробела, но только когда шестиугольник не теряет форму
  • Конечный пробел разрешен.
  • Обратите внимание, что пробелы в программе игнорируются . Так a b cравноabc
  • Используются только печатаемые символы ASCII ( 32 - 126), поэтому Spaceигнорируется только обычный символ.
  • Предположим, что длина строки больше 0.
  • Это , поэтому выигрывает представление с наименьшим количеством байтов!

Контрольные примеры

Input: ?({{&2'2':{):!/)'*/

Output:
  ? ( {
 { & 2 '
2 ' : { )
 : ! / )
  ' * /


Input: H;e;l;d;*;r;o;Wl;;o;*433;@.>;23<\4;*/

Output:
   H ; e ;
  l ; d ; *
 ; r ; o ; W
l ; ; o ; * 4
 3 3 ; @ . >
  ; 2 3 < \
   4 ; * /


Input: .?'.) .@@/'/ .!.>   +=(<.!)}    (  $>( <%

Output:
   . ? ' .
  ) . @ @ /
 ' / . ! . >
+ = ( < . ! )
 } ( $ > ( <
  % . . . .
   . . . .
Аднан
источник
6
Кроме того, я не уверен, хотите ли вы быть таким разборчивым, но при определении ширины кода обратные метки игнорируются, потому что они аннотируют следующий символ. Так abc`defgчто на самом деле станет pastebin.com/ZrdJmHiR
Мартин Эндер
2
@ MartinBüttner О, я этого не знал :). Для этой проблемы обратные галочки не будут проигнорированы.
Аднан
18
Я действительно хочу увидеть ответ в Hexagony на этот вопрос.
Арктур
2
@Adnan Возможно, лучшим ответом будет «Вы можете предположить, что входные данные не содержат отладочных флагов ( `символов)».
Riking
4
@Ampora Спроси, и ты получишь.
Мартин Эндер

Ответы:

13

Pyth, 57 54 50 49 48 46

V+UJfgh*6sUTlK-zd1_UtJ+*d-JNjd:.[K\.^TJZ=+Z+JN

Тестирование

Печать пробела в каждой строке.

Эта версия требует доказательства того, что 10 ^ n> = 3n (n - 1) + 1 для всех n> = 1 . Спасибо ANerdI и ErickWong за предоставление доказательств.

Следуя этим неравенствам: 10 ^ n> (1 + 3) ^ n = 1 + 3n + 9n (n - 1) + ...> 3n (n - 1) + 1, можно легко увидеть, что это верно для n> = 2 . Изучение случая n = 1 довольно тривиально, давая 10> 1 .

В качестве альтернативы, если взять производные этих уравнений дважды, это показывает, что 10 ^ n имеет большую вторую производную для всех n> = 1 , которую затем можно каскадно свести к первым производным и, наконец, к исходным уравнениям.

объяснение

              ##  Implicit: z=input(); Z=0
Jf...1        ##  Save to J the side length of the hexagon the code fills up
              ##  by finding the first number such that:
gh*6sUT       ##  the the T'th hexagonal number is greater than...
              ##  Computes 6 * T'th triangular number (by using sum 1..T-1) + 1
    lK-zd     ##  ...the length of the code without spaces (also save the string value to K)
V+UJ_UtJ      ##  For loop over N = [0, 1, ..., J-1, ..., 0]:
+*d-JN        ##  append J - N spaces to the front of the line
jd            ##  riffle the result of the next operation with spaces
:.[K\.yJ      ##  slice the string given by K padded to be the length of the Jth hexagon
              ##  number with noops
Z=+Z+JN       ##  from Z to Z + J + N, then set Z to be Z + J + N
FryAmTheEggman
источник
2
Сначала нужно доказать, что ln (10) * 10 ^ n> 6n-3 (производные) при n> = 1. Это легко, так как производные этих выражений являются ln (10) ^ 2 10 ^ n и 6. Так как 10 ^ n монотонно возрастает и 10 ^ 1> 6 * 1, 10 ^ n больше чем 6n-3 для всех п> = 1. Вы можете использовать ту же логику, чтобы завершить доказательство для 10 ^ n и 3n (n-1) +1.
Арктур
@Ampora Спасибо, я думал об использовании производных, но это казалось нечистым. Я не мог найти лучший способ, хотя, так высоко ценится!
FryAmTheEggman
Рад помочь. Calc может иногда становиться ужасно уродливым.
Арктур
в ссылке pyth.herokuapp.com/?code=etc выше я нахожу, что компилятор не запускается ...
RosLuP
1
@FryAmTheEggman Существует очень простой способ показать гораздо более сильную оценку 4 ^ n> 3n (n-1) + 1 для n> = 1, исчисление не требуется. Просто используйте тот факт, что (1 + 3) ^ n = 1 + 3n + 9n (n-1) / 2 + ... путем биномиального расширения. Первое и третье слагаемые непосредственно мажорируют 1 + 3n (n-1), поэтому неравенство является непосредственным, если существует третье слагаемое (то есть для n> = 2). Это оставляет только случай n = 1, который тривиален, так как RHS равен 1.
Эрик Вонг
90

Гексагония , 271 байт

Я представляю вам, первые 3% гексагонии самоинтерпретации ...

|./...\..._..>}{<$}=<;>'<..../;<_'\{*46\..8._~;/;{{;<..|M..'{.>{{=.<.).|.."~....._.>(=</.\=\'$/}{<}.\../>../..._>../_....@/{$|....>...</..~\.>,<$/'";{}({/>-'(<\=&\><${~-"~<$)<....'.>=&'*){=&')&}\'\'2"'23}}_}&<_3.>.'*)'-<>{=/{\*={(&)'){\$<....={\>}}}\&32'-<=._.)}=)+'_+'&<

Попробуйте онлайн! Вы также можете запустить его на себя, но это займет около 5-10 секунд.

В принципе, это может вписаться в длину стороны 9 (для оценки 217 или меньше), потому что для этого используются только 201 команда, а для версии без гольфа, которую я написал первым (для стороны 30), требовалось всего 178 команд. Тем не менее, я уверен, что на то, чтобы все привести в порядок, понадобится целая вечность, поэтому я не уверен, действительно ли я попытаюсь это сделать.

Также должно быть возможно сыграть это немного в размере 10, избегая использования последних одного или двух рядов, так что завершающие no-ops могут быть опущены, но это потребовало бы существенной перезаписи, как одного из первых путей присоединения использует нижний левый угол.

объяснение

Давайте начнем с развертывания кода и аннотирования путей потока управления:

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

Это все еще довольно грязно, так что вот та же диаграмма для «неопрятного» кода, который я написал первым (на самом деле, это длина стороны 20, и первоначально я написал код на стороне длины 30, но это было настолько редко, что не улучшает читаемость, поэтому я немного его сжал, чтобы сделать размер более разумным):

введите описание изображения здесь
Нажмите для увеличения версии.

Цвета точно такие же, за исключением нескольких очень мелких деталей, команды неуправляемого потока также точно такие же. Итак, я объясню, как это работает, на основе версии без гольфа, и если вы действительно хотите знать, как работает гольф, вы можете проверить, какие части соответствуют каким в большем шестиугольнике. (Единственный улов заключается в том, что код для игры в гольф начинается с зеркала, так что фактический код начинается в правом углу и идет влево.)

Основной алгоритм почти идентичен моему ответу CJam . Есть два отличия:

  • Вместо того, чтобы решать уравнение центрированных гексагональных чисел, я просто вычисляю последовательные центрированные гексагональные числа до тех пор, пока одно из них не станет равным или больше, чем длина ввода. Это потому, что у гексагонии нет простого способа вычисления квадратного корня.
  • Вместо того, чтобы сразу заполнять ввод no-ops, я проверяю позже, исчерпал ли я уже введенные команды во вводе, и .вместо этого печатаю a .

Это означает, что основная идея сводится к:

  • Чтение и сохранение входной строки при расчете ее длины.
  • Найдите наименьшую длину стороны N(и соответствующее центрированное шестиугольное число hex(N)), которая может содержать весь ввод.
  • Вычислить диаметр 2N-1.
  • Для каждой строки вычислите отступ и количество ячеек (которые суммируются 2N-1). Напечатайте отступ, напечатайте ячейки (используя, .если ввод уже исчерпан), напечатайте перевод строки.

Обратите внимание на то, что есть только no-ops, поэтому фактический код начинается в левом углу (тот $, который перепрыгивает через >, поэтому мы действительно начинаем с ,темно-серого пути).

Вот начальная сетка памяти:

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

Таким образом, указатель памяти начинается с обозначенного ребром ввода , указывая на север. ,читает байт из STDIN или a, -1если мы ударили EOF в этот край. Следовательно, <сразу после является условием того, прочитали ли мы все входные данные. Давайте пока будем оставаться в цикле ввода. Следующий код, который мы выполняем

{&32'-

Это записывает 32 в пространство , помеченное ребром , а затем вычитает его из входного значения в ребро, помеченное как дифференциал . Обратите внимание, что это никогда не может быть отрицательным, потому что мы гарантируем, что ввод содержит только печатный ASCII. Это будет ноль, когда ввод был пробел. (Как указывает Тимви, это все равно будет работать, если на входе могут содержаться переводы строк или табуляции, но оно также удалит все другие непечатаемые символы с кодами символов менее 32). В этом случае <указатель инструкций (IP) отклоняется слева и светло-серый путь взят. Этот путь просто сбрасывает позицию MP с помощью {=и затем читает следующий символ - таким образом, пробелы пропускаются. В противном случае, если символ не был пробелом, мы выполняем

=}}})&'+'+)=}

Это сначала перемещается вокруг шестиугольника через край длины до его противоположного края дифференциала , с =}}}. Затем он копирует значение из напротив длины кромки в длину края, а также увеличивает его с )&'+'+). Через секунду мы увидим, почему это имеет смысл. Наконец, мы перемещаем новое ребро с помощью =}:

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

(Конкретные значения ребер взяты из последнего контрольного примера, приведенного в тесте.) В этот момент цикл повторяется, но со всем смещением на один шестиугольник на северо-восток. Итак, после прочтения другого символа, мы получаем это:

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

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

Когда мы закончим с циклом ввода, память будет выглядеть так (где я уже обозначил несколько новых ребер для следующей части):

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

Это %последний символ, который мы прочитали, 29это количество непробельных символов, которые мы прочитали. Теперь мы хотим найти длину стороны шестиугольника. Во-первых, есть некоторый линейный код инициализации в темно-зеленом / сером пути:

=&''3{

Здесь, =&копирует длину (в нашем примере 29) в ребро, помеченное как длина . Затем ''3перемещается к ребру с меткой 3 и устанавливает его значение 3(которое нам просто нужно в качестве константы в вычислении). Наконец {движется к краю с надписью N (N-1) .

Теперь мы входим в синюю петлю. Этот цикл увеличивается N(сохраняется в ячейке с меткой N ), а затем вычисляет его центрированное шестиугольное число и вычитает его из входной длины. Линейный код, который делает это:

{)')&({=*'*)'-

Здесь, {)перемещается и приращения N . ')&(перемещается к краю с меткой N-1 , копирует Nтуда и уменьшает его. {=*вычисляет их произведение в N (N-1) . '*)умножает это на константу 3и увеличивает результат на ребро с меткой hex (N) . Как и ожидалось, это шестое центрированное шестиугольное число. Наконец '-вычисляет разницу между этим и длиной ввода. Если результат положительный, длина стороны еще не достаточно велика, и цикл повторяется (где }}MP возвращается к краю, обозначенному N (N-1) ).

Как только длина стороны будет достаточно большой, разница будет нулевой или отрицательной, и мы получим это:

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

Во-первых, теперь есть действительно длинный линейный зеленый путь, который выполняет некоторую необходимую инициализацию для цикла вывода:

{=&}}}32'"2'=&'*){=&')&}}

В {=&начинается с копирования результата в дифф края в длину края, потому что мы потом нужно что - то неположительны там. }}}32записывает 32 в край помеченного пространства . '"2записывает константу 2 в немаркированный край над diff . '=&копирует N-1во второй край с той же меткой. '*)умножает его на 2 и увеличивает его так, чтобы мы получили правильное значение в ребре с меткой 2N-1 в верхней части. Это диаметр шестиугольника. {=&')&копирует диаметр в другой край, помеченный 2N-1 . Наконец }}возвращается к краю с надписью 2N-1 в верхней части.

Давайте перемаркируем края:

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

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

Мы сейчас на розовом пути, который вычисляет отступ . ('-уменьшает значение итератора строк и вычитает его из N-1 (в ребро с отступом ). Короткая сине-серая ветвь в коде просто вычисляет модуль результата ( ~отрицает значение, если оно отрицательное или ноль, и ничего не происходит, если оно положительное). Остальная часть розового пути - это то, "-~{что вычитает отступ от диаметра до края ячеек, а затем перемещается обратно к краю отступа .

Грязно-желтый путь теперь печатает отступ. Содержимое цикла действительно просто

'";{}(

Где '"перемещается к краю пробела , ;печатает его, {}возвращается к отступу и (уменьшает его.

Когда мы закончим с этим (второй) темно-серый путь ищет следующий символ для печати. В =}перемещается в положение (что означает, на клетки края, указывающие на юг). Тогда у нас есть очень узкая петля, {}которая просто перемещается вниз по двум краям в направлении юго-запада, пока мы не достигнем конца сохраненной строки:

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

Заметьте, что я пометил один край там EOF? , Как только мы обработаем этот символ, мы сделаем это ребро отрицательным, чтобы {}цикл завершился здесь вместо следующей итерации:

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

В коде мы находимся в конце темно-серого пути, где 'возвращаемся на один шаг назад к вводимому символу. Если ситуация является одной из двух последних диаграмм (то есть, есть еще один символ из ввода, который мы еще не распечатали), то мы выбираем зеленый путь (нижний, для людей, которые плохо разбираются в зеленом и синий). Это довольно просто: ;печатает сам символ. 'перемещается к соответствующему краю пробела, который по-прежнему содержит 32 ранее и ;печатает этот пробел. Тогда {~делает наш EOF? отрицательный для следующей итерации, 'перемещает на шаг назад, чтобы мы могли вернуться к северо-западному концу строки с помощью еще одного сложного }{цикла. Который заканчивается на длинуячейка (неположительная ниже гекса (N) . Наконец, }возвращается к краю ячейки .

Если мы уже исчерпали ввод, тогда цикл, который ищет EOF? на самом деле закончится здесь:

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

В этом случае 'перемещаемся в ячейку длины , и вместо этого мы берем голубой (верхний) путь, который печатает неоперативный код. Код в этой ветке линейный:

{*46;{{;{{=

{*46;Пишет 46 в край не меченый не-оп и печатает его (т.е. периода). Затем {{;перемещается к краю пространства и печатает это. В {{=двигается обратно к клеткам краям для следующей итерации.

В этот момент пути объединяются и (уменьшают границу ячеек . Если итератор еще не равен нулю, мы возьмем светло-серый путь, который просто меняет направление MP =и затем ищет следующий символ для печати.

В противном случае мы достигли конца текущей строки, и вместо этого IP выберет фиолетовый путь. Вот как выглядит сетка памяти в этой точке:

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

Фиолетовый путь содержит это:

=M8;~'"=

=Снова меняет направление МП. M8устанавливает для его значений значение 778(потому что код символа Mis 77и цифры будут добавлены к текущему значению). Это происходит 10 (mod 256), поэтому, когда мы печатаем его ;, мы получаем перевод строки. Затем ~снова делает край отрицательным, '"возвращается к краю линий и снова =меняет MP.

Теперь, если край линий равен нулю, мы закончили. IP выберет (очень короткий) красный путь, где @завершает программу. В противном случае мы продолжаем идти по фиолетовому пути, который возвращается в розовый, чтобы напечатать еще одну строку.


Блок-схемы управления, созданные с помощью HexagonyColorer Тимви . Диаграммы памяти, созданные с помощью визуального отладчика в его Esoteric IDE .

Мартин Эндер
источник
19
Я часто говорю об этом в ответах на гексагонию: просто, воу.
Конор О'Брайен
5
Хм ... но ... Ват ... ум = взорван
Аднан
Я надеялся, что кто-то сделает это и ... Вау. У меня нет слов. Это потрясающе.
Арктур
19
Шаг второй - напиши остальные 97%. :)
ASCIIThenANSI
Шаг третий - как ответ с наименьшим количеством байтов.
Том М
19

CJam, 56 52 50 48 байтов

Моей первой мыслью было: «Эй, у меня уже есть код для этого!» Но тогда я не мог потрудиться собрать необходимые фрагменты из кода Ruby, особенно потому, что они не очень подходили для игры в гольф. Поэтому я попробовал что-то еще в CJam ...

lS-{_,4*(3/mq:D1%}{'.+}wD{D(2/-z_S*D@-@/(S*N@s}/

Проверьте это здесь.

объяснение

Сначала немного математики о центрированных шестиугольных числах. Если обычный шестиугольник имеет длину стороны N, то он будет содержать 3N(N-1)+1ячейки, которые должны равняться длине исходного кода k. Мы можем решить это, Nпотому что это простое квадратное уравнение:

N = 1/2 ± √(1/4 + (k-1)/3)

Мы можем игнорировать отрицательный корень, потому что это дает отрицательный N. Чтобы это могло иметь решение, нам нужно, чтобы квадратный корень был полуцелым. Или, другими словами, √(1 + 4(k-1)/3) = √((4k-1)/3)должно быть целым числом (к счастью, это целое число является диаметром D = 2N-1шестиугольника, который нам все равно понадобится). Таким образом, мы можем многократно добавлять один, .пока это условие не будет выполнено.

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

lS-     e# Read input and remove spaces.
{       e# While the first block yields something truthy, evaluate the second...
  _,    e#   Duplicate the code and get its length k.
  4*(   e#   Compute 4k-1.
  3/    e#   Divide by 3.
  mq    e#   Take the square root.
  :D    e#   Store this in D, just in case we're done, because when we are, this happens
        e#   to be the diameter of the hexagon.
  1%    e#   Take modulo 1. This is 0 for integers, and non-zero for non-integers.
}{      e# ...
  '.+   e#   Append a no-op to the source code.
}w
D{      e# For every i from 0 to D-1...
  D(2/  e#   Compute (D-1)/2 = N, the side length.
  -z    e#   Subtract that from the current i and get its modulus. That's the size of the
        e#   indentation on this line.
  _S*   e#   Duplicate and get a string with that many spaces.
  D@-   e#   Subtract the other copy from D to get the number of characters of code
        e#   in the current line.
  @/    e#   Pull up the source code and split into chunks of this size.
  (S*   e#   Pull off the first chunk and riffle it with spaces.
  N     e#   Push a linefeed character.
  @s    e#   Pull up the remaining chunks and join them back into a single string.
}/

Оказывается, нам вообще не нужно использовать двойную арифметику (кроме квадратного корня). Из-за умножения на 4 нет столкновений при делении на 3, и искомый kбудет первым, чтобы получить целочисленный квадратный корень.

Мартин Эндер
источник
8

Perl, 203 200 198

включает в себя + 1 для -p

s/\s//g;{($l=y///c)>($h=1+3*++$n*($n-1))&&redo}$s=$_.'.'x($h-$l);for($a=$n;$a<($d=2*$n-1);$a++){$s=~s/.{$a}/$&\n/,$s=reverse($s)for 0..1}$_=join$/,map{(' 'x abs($n-$i++-1)).$_}$s=~/\S+/g;s/\S/ $&/g

беги как: echo abc | perl -p file.pl

Очень наивный подход:

#!/usr/bin/perl -p

s/\s//g;                            # ignore spaces and EOL etc.
{                                   # find the smallest hex number:
    ($l=y///c)                      # calc string length
    > ($h=1+3*++$n*($n-1))          # 
    && redo                         # (should use 'and', but..)
}

$s = $_                             # save $_ as it is used in the nested for
   . '.' x ($h-$l);                 # append dots to fill hexagon

for ( $a = $n; $a < ($d=2*$n-1); $a++ )
{
        $s=~s/.{$a}/$&\n/,          # split lines
        $s=reverse($s)              # mirror
    for 0..1                        # twice
}

$_ = join$/,                        # join using newline
map {                               # iterate the lines
    (' 'x abs($n-$i++-1)) .$_       # prepend padding
} $s=~/\S+/g;                       # match lines

s/\S/ $&/g                          # prepend spaces to characters
                                    # -p takes care of printing $_

  • обновление 200 сохраняет байтовую переменную перемещения и еще 2, пропуская final ;; сам код до 200 байт сейчас!
  • обновить 198 сохранить 2 байта, используя $s=~/\S+/gвместоsplit/\n/,$s
Кинни
источник
7

JavaScript (ES6), 162 172

Анонимная функция

Размер шестиугольника найден, решая уравнение из Википедии

3*n*(n-1)-1 = l

Формула решения в основном

n = ceil(3+sqrt(12*l-3))/6)

С некоторой алгеброй и некоторым приближением (спасибо и @ user18655 тоже) оно становится

n = trunc(sqrt(l/3-1/12)+1.4999....)
s=>eval("s=s.match(/\\S/g);m=n=Math.sqrt(s.length/3-1/12)+1.49999|0;p=o=``;for(i=n+n;--i;i>n?++m:--m)for(o+=`\n`+` `.repeat(n+n-m),j=m;j--;o+=` `)o+=s[p++]||`.`")

Более читаемый

s=>{
  s=s.match(/\S/g);
  m=n=Math.sqrt(s.length/3-1/12)+1.49999;
  p=o='';
  for(i=n+n; --i; i>n?++m:--m)
    for(o += '\n'+' '.repeat(n+n-m), j=m; j--; o += ' ')
      o+=s[p++]||'.';
  return o
}

Тестовый фрагмент (лучше полная страница - время выполнения ~ 1 минута)

f=s=>eval("s=s.match(/\\S/g);m=n=Math.sqrt(s.length/3-1/12)+1.49999|0;p=o=``;for(i=n+n;--i;i>n?++m:--m)for(o+=`\n`+` `.repeat(n+n-m),j=m;j--;o+=` `)o+=s[p++]||`.`")

t=0;
r='0';
(T=_=>t++<816?(O.innerHTML=f(r=t%10+r),setTimeout(T,20)):0)()
pre { font-size: 66% }
<pre id=O></pre>

edc65
источник
1
Вы можете использовать n=...+1-1e-9|0вместо того, n=Math.ceil(...)чтобы сохранить 2 байта. Вы также можете перейти на ES7 и использовать **0.5вместо, Math.sqrtно это на ваше усмотрение . Я обычно просто держу свои ответы ES6, потому что они работают в моем браузере, ха-ха!
user81655 16.12.15
@ user81655 хороший совет, спасибо
edc65
5

Pyth, 52 51 байт

Jfgh**3TtTl=H-zd1=+H*\.*lHTV+UJt_UJAcH]+JN+*-JNdjdG

Попробуйте онлайн. Тестирование.

Каждая строка имеет один дополнительный начальный пробел, как разрешено OP.

объяснение

 f              1          |   find first number n for which
             -zd           |           remove spaces from input
           =H              |         put result in H
          l                |       length of input without spaces
  g                        |     is less than or equal to
   h**3TtT                 |       nth centered hexagonal number
J                          | put result (hexagon side length) in J
                           |
      *lHT                 |      ten times length of input without spaces
   *\.                     |   that amount of dots
=+H                        | append to H
                           |
  UJ                       |    numbers 0 up to side length - 1
 +  t_UJ                   |   add numbers side length - 2 down to 0
V                          | loop over result
            +JN            |       current loop number + side length
         cH]               |     split to two parts at that position
        A                  |   put parts to G and H
                 -JN       |       side length - current loop number - 1
                *   d      |     that many spaces
                     jdG   |     join code on the line (G) by spaces
               +           |   concatenate parts and print
PurkkaKoodari
источник
5

Сетчатка , 161 байт

Спасибо FryAmTheEggman за сохранение 2 байта.

Этот ответ не конкурирует. После этого испытания Retina видела несколько обновлений, и я почти уверен, что использую некоторые новые функции (хотя я не проверял).

Число байтов предполагает кодировку ISO 8859-1. Первая строка содержит один пробел. Обратите внимание, что большинство из них на ·самом деле являются центральными точками (0xB7).

 

^
$._$*·¶
^·¶
¶
((^·|\2·)*)·\1{5}·+
$2·
^·*
$.&$* ·$&$&$.&$* 
M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$
+m`^( *·+)· *¶(?=\1)
$& 
·
 ·
O$`(·)|\S
$1
·
.
G-2`

Попробуйте онлайн!

Что ж...

объяснение

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

 

Хотя это и не выглядит много, на первых этапах удаляются пробелы из входных данных.

^
$._$*·¶

Мы начинаем с добавления дополнительной строки, содержащей Mцентральные точки, где Mдлина ввода (после удаления пробелов).

^·¶
¶

Если ввод был одним символом, мы снова удаляем эту центральную точку. Это прискорбный особый случай, который не рассматривается на следующем этапе.

((^·|\2·)*)·\1{5}·+
$2·

Это вычисляет требуемую длину стороны Nминус 1. Вот как это работает: центрированные шестиугольные числа имеют форму 3*N*(N-1) + 1. Поскольку треугольные числа равны N*(N-1)/2, это означает, что шестиугольные числа в шесть раз больше треугольного числа плюс 1. Это удобно, потому что сопоставление треугольных чисел (которые на самом деле справедливы 1 + 2 + 3 + ... + N) в регулярном выражении довольно легко с помощью прямых ссылок. Соответствует (^·|\2·)*наибольшему треугольному числу, которое может. В качестве приятного бонуса, $2тогда будет проведен индекс этого треугольного числа. Чтобы умножить его на 6, мы собираем его в группу 1и сопоставляем еще 5 раз. Мы убедитесь , что есть по крайней мере еще два ·с ·и·+, Таким образом, индекс найденного треугольного числа не увеличивается, пока не будет на один символ больше, чем центрированное шестиугольное число.

В конце концов, это совпадение дает нам на два меньше длины стороны требуемого шестиугольника в группе $2, поэтому мы записываем это вместе с еще одной центральной точкой, чтобы получить N-1.

^·*
$.&$* ·$&$&$.&$* 

Это превращает нашу цепочку N-1центральных точек в N-1пробелы, 2N-1центральные точки и другие N-1пробелы. Обратите внимание, что это максимальный отступ, за которым следует диаметр шестиугольника, а затем снова отступ.

M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$

Это неприятно долго, но в основном это просто дает нам все совпадающие совпадения, которые являются либо а) 2N-1длиной символов и в первой строке, либо б) второй строкой. Это расширяет результат предыдущего этапа в полный, но странно изогнутый шестиугольник. Например, для ввода 12345678мы получили бы:

  ···
 ····
·····
···· 
···  
12345678

Вот почему нам нужно было добавлять пробелы и на предыдущем этапе.

+m`^( *·+)· *¶(?=\1)
$& 

Это исправляет отступ строк после центра путем многократного отступа любой строки, которая короче предыдущей (игнорируя конечные пробелы), поэтому мы получаем это:

  ···
 ····
·····
 ···· 
  ···  
12345678

Теперь мы просто вставляем несколько пробелов с

·
 ·

Что дает нам:

   · · ·
  · · · ·
 · · · · ·
  · · · · 
   · · ·  
12345678

Фу, это сделано.

O$`(·)|\S
$1

Время, чтобы заполнить строку ввода в центральных точках. Это делается с помощью этапа сортировки. Мы сопоставляем все центральные точки и каждый символ в последней строке и сортируем их по результату данной замены. Эта замена является пустой для символов в последней строке и ·для центральных точек, поэтому происходит то, что центральные точки просто сортируются до конца (поскольку сортировка стабильна). Это перемещает вводимые символы на место:

   1 2 3
  4 5 6 7
 8 · · · ·
  · · · · 
   · · ·  
········

Осталось две вещи:

·
.

Это превращает центральные точки в регулярные периоды.

G-2`

И это отбрасывает последнюю строку.

Мартин Эндер
источник
1

JavaScript (ES6), 144 байта

(s,n=1,l=0,p=0,m=s.match(/\S/g))=>m[n]?f(s,n+6*++l,l):[...Array(l+l+1)].map((_,i,a)=>a.map((_,j)=>j<l-i|j<i-l?``:m[p++]||`.`).join` `).join`\n`

Где \nпредставляет буквальный символ новой строки. Использует технику для создания шестиугольника, которую я ранее использовал в нескольких других ответах с . Для ES7 получение квадратных корней работает немного короче, чем рекурсивный подход:

(s,p=0,m=s.match(/\S/g),l=(~-m.length/3)**.5+.5|0)=>[...Array(l+l+1)].map((_,i,a)=>a.map((_,j)=>j<l-i|j<i-l?``:m[p++]||`.`).join` `).join`\n`
Нил
источник
1

Python 3 , 144 байта

c=input().replace(' ','')
n=x=1
while x<len(c):x+=n*6;n+=1
c=c.ljust(x,'.')
while c:print(' '*(x-n)+' '.join(c[:n]));c=c[n:];n-=(len(c)<x/2)*2-1

Попробуйте онлайн!

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

Джо Кинг
источник