Введение
Если вы не знакомы с гексагонией , это эзотерический язык, созданный Мартином Бюттнером. Дело в том, что этот язык принимает несколько форм для программы. Следующие программы эквивалентны:
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:
. ? ' .
) . @ @ /
' / . ! . >
+ = ( < . ! )
} ( $ > ( <
% . . . .
. . . .
abc`defg
что на самом деле станет pastebin.com/ZrdJmHiR`
символов)».Ответы:
Pyth,
575450494846Тестирование
Печать пробела в каждой строке.
Эта версия требует доказательства того, что 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 , которую затем можно каскадно свести к первым производным и, наконец, к исходным уравнениям.
объяснение
источник
Гексагония , 271 байт
Я представляю вам, первые 3% гексагонии самоинтерпретации ...
Попробуйте онлайн! Вы также можете запустить его на себя, но это займет около 5-10 секунд.
В принципе, это может вписаться в длину стороны 9 (для оценки 217 или меньше), потому что для этого используются только 201 команда, а для версии без гольфа, которую я написал первым (для стороны 30), требовалось всего 178 команд. Тем не менее, я уверен, что на то, чтобы все привести в порядок, понадобится целая вечность, поэтому я не уверен, действительно ли я попытаюсь это сделать.
Также должно быть возможно сыграть это немного в размере 10, избегая использования последних одного или двух рядов, так что завершающие no-ops могут быть опущены, но это потребовало бы существенной перезаписи, как одного из первых путей присоединения использует нижний левый угол.
объяснение
Давайте начнем с развертывания кода и аннотирования путей потока управления:
Это все еще довольно грязно, так что вот та же диаграмма для «неопрятного» кода, который я написал первым (на самом деле, это длина стороны 20, и первоначально я написал код на стороне длины 30, но это было настолько редко, что не улучшает читаемость, поэтому я немного его сжал, чтобы сделать размер более разумным):
Нажмите для увеличения версии.
Цвета точно такие же, за исключением нескольких очень мелких деталей, команды неуправляемого потока также точно такие же. Итак, я объясню, как это работает, на основе версии без гольфа, и если вы действительно хотите знать, как работает гольф, вы можете проверить, какие части соответствуют каким в большем шестиугольнике. (Единственный улов заключается в том, что код для игры в гольф начинается с зеркала, так что фактический код начинается в правом углу и идет влево.)
Основной алгоритм почти идентичен моему ответу CJam . Есть два отличия:
.
вместо этого печатаю a .Это означает, что основная идея сводится к:
N
(и соответствующее центрированное шестиугольное числоhex(N)
), которая может содержать весь ввод.2N-1
.2N-1
). Напечатайте отступ, напечатайте ячейки (используя,.
если ввод уже исчерпан), напечатайте перевод строки.Обратите внимание на то, что есть только no-ops, поэтому фактический код начинается в левом углу (тот
$
, который перепрыгивает через>
, поэтому мы действительно начинаем с,
темно-серого пути).Вот начальная сетка памяти:
Таким образом, указатель памяти начинается с обозначенного ребром ввода , указывая на север.
,
читает байт из STDIN или a,-1
если мы ударили EOF в этот край. Следовательно,<
сразу после является условием того, прочитали ли мы все входные данные. Давайте пока будем оставаться в цикле ввода. Следующий код, который мы выполняемЭто записывает 32 в пространство , помеченное ребром , а затем вычитает его из входного значения в ребро, помеченное как дифференциал . Обратите внимание, что это никогда не может быть отрицательным, потому что мы гарантируем, что ввод содержит только печатный ASCII. Это будет ноль, когда ввод был пробел. (Как указывает Тимви, это все равно будет работать, если на входе могут содержаться переводы строк или табуляции, но оно также удалит все другие непечатаемые символы с кодами символов менее 32). В этом случае
<
указатель инструкций (IP) отклоняется слева и светло-серый путь взят. Этот путь просто сбрасывает позицию MP с помощью{=
и затем читает следующий символ - таким образом, пробелы пропускаются. В противном случае, если символ не был пробелом, мы выполняемЭто сначала перемещается вокруг шестиугольника через край длины до его противоположного края дифференциала , с
=}}}
. Затем он копирует значение из напротив длины кромки в длину края, а также увеличивает его с)&'+'+)
. Через секунду мы увидим, почему это имеет смысл. Наконец, мы перемещаем новое ребро с помощью=}
:(Конкретные значения ребер взяты из последнего контрольного примера, приведенного в тесте.) В этот момент цикл повторяется, но со всем смещением на один шестиугольник на северо-восток. Итак, после прочтения другого символа, мы получаем это:
Теперь вы можете видеть, что мы постепенно записываем ввод (минус пробелы) вдоль северо-восточной диагонали, с символами на каждом другом ребре, а длина до этого символа сохраняется параллельно длине, обозначенной как ребро .
Когда мы закончим с циклом ввода, память будет выглядеть так (где я уже обозначил несколько новых ребер для следующей части):
Это
%
последний символ, который мы прочитали,29
это количество непробельных символов, которые мы прочитали. Теперь мы хотим найти длину стороны шестиугольника. Во-первых, есть некоторый линейный код инициализации в темно-зеленом / сером пути:Здесь,
=&
копирует длину (в нашем примере 29) в ребро, помеченное как длина . Затем''3
перемещается к ребру с меткой 3 и устанавливает его значение3
(которое нам просто нужно в качестве константы в вычислении). Наконец{
движется к краю с надписью N (N-1) .Теперь мы входим в синюю петлю. Этот цикл увеличивается
N
(сохраняется в ячейке с меткой N ), а затем вычисляет его центрированное шестиугольное число и вычитает его из входной длины. Линейный код, который делает это:Здесь,
{)
перемещается и приращения N .')&(
перемещается к краю с меткой N-1 , копируетN
туда и уменьшает его.{=*
вычисляет их произведение в N (N-1) .'*)
умножает это на константу3
и увеличивает результат на ребро с меткой hex (N) . Как и ожидалось, это шестое центрированное шестиугольное число. Наконец'-
вычисляет разницу между этим и длиной ввода. Если результат положительный, длина стороны еще не достаточно велика, и цикл повторяется (где}}
MP возвращается к краю, обозначенному N (N-1) ).Как только длина стороны будет достаточно большой, разница будет нулевой или отрицательной, и мы получим это:
Во-первых, теперь есть действительно длинный линейный зеленый путь, который выполняет некоторую необходимую инициализацию для цикла вывода:
В
{=&
начинается с копирования результата в дифф края в длину края, потому что мы потом нужно что - то неположительны там.}}}32
записывает 32 в край помеченного пространства .'"2
записывает константу 2 в немаркированный край над diff .'=&
копируетN-1
во второй край с той же меткой.'*)
умножает его на 2 и увеличивает его так, чтобы мы получили правильное значение в ребре с меткой 2N-1 в верхней части. Это диаметр шестиугольника.{=&')&
копирует диаметр в другой край, помеченный 2N-1 . Наконец}}
возвращается к краю с надписью 2N-1 в верхней части.Давайте перемаркируем края:
Ребро, на котором мы сейчас находимся (который все еще содержит диаметр шестиугольника), будет использоваться для итерации по линиям вывода. Ребро с меткой отступ вычислит, сколько пробелов необходимо в текущей строке. Ячейки, помеченные ребром, будут использоваться для перебора количества ячеек в текущей строке.
Мы сейчас на розовом пути, который вычисляет отступ .
('-
уменьшает значение итератора строк и вычитает его из N-1 (в ребро с отступом ). Короткая сине-серая ветвь в коде просто вычисляет модуль результата (~
отрицает значение, если оно отрицательное или ноль, и ничего не происходит, если оно положительное). Остальная часть розового пути - это то,"-~{
что вычитает отступ от диаметра до края ячеек, а затем перемещается обратно к краю отступа .Грязно-желтый путь теперь печатает отступ. Содержимое цикла действительно просто
Где
'"
перемещается к краю пробела ,;
печатает его,{}
возвращается к отступу и(
уменьшает его.Когда мы закончим с этим (второй) темно-серый путь ищет следующий символ для печати. В
=}
перемещается в положение (что означает, на клетки края, указывающие на юг). Тогда у нас есть очень узкая петля,{}
которая просто перемещается вниз по двум краям в направлении юго-запада, пока мы не достигнем конца сохраненной строки:Заметьте, что я пометил один край там EOF? , Как только мы обработаем этот символ, мы сделаем это ребро отрицательным, чтобы
{}
цикл завершился здесь вместо следующей итерации:В коде мы находимся в конце темно-серого пути, где
'
возвращаемся на один шаг назад к вводимому символу. Если ситуация является одной из двух последних диаграмм (то есть, есть еще один символ из ввода, который мы еще не распечатали), то мы выбираем зеленый путь (нижний, для людей, которые плохо разбираются в зеленом и синий). Это довольно просто:;
печатает сам символ.'
перемещается к соответствующему краю пробела, который по-прежнему содержит 32 ранее и;
печатает этот пробел. Тогда{~
делает наш EOF? отрицательный для следующей итерации,'
перемещает на шаг назад, чтобы мы могли вернуться к северо-западному концу строки с помощью еще одного сложного}{
цикла. Который заканчивается на длинуячейка (неположительная ниже гекса (N) . Наконец,}
возвращается к краю ячейки .Если мы уже исчерпали ввод, тогда цикл, который ищет EOF? на самом деле закончится здесь:
В этом случае
'
перемещаемся в ячейку длины , и вместо этого мы берем голубой (верхний) путь, который печатает неоперативный код. Код в этой ветке линейный:{*46;
Пишет 46 в край не меченый не-оп и печатает его (т.е. периода). Затем{{;
перемещается к краю пространства и печатает это. В{{=
двигается обратно к клеткам краям для следующей итерации.В этот момент пути объединяются и
(
уменьшают границу ячеек . Если итератор еще не равен нулю, мы возьмем светло-серый путь, который просто меняет направление MP=
и затем ищет следующий символ для печати.В противном случае мы достигли конца текущей строки, и вместо этого IP выберет фиолетовый путь. Вот как выглядит сетка памяти в этой точке:
Фиолетовый путь содержит это:
=
Снова меняет направление МП.M8
устанавливает для его значений значение778
(потому что код символаM
is77
и цифры будут добавлены к текущему значению). Это происходит10 (mod 256)
, поэтому, когда мы печатаем его;
, мы получаем перевод строки. Затем~
снова делает край отрицательным,'"
возвращается к краю линий и снова=
меняет MP.Теперь, если край линий равен нулю, мы закончили. IP выберет (очень короткий) красный путь, где
@
завершает программу. В противном случае мы продолжаем идти по фиолетовому пути, который возвращается в розовый, чтобы напечатать еще одну строку.Блок-схемы управления, созданные с помощью HexagonyColorer Тимви . Диаграммы памяти, созданные с помощью визуального отладчика в его Esoteric IDE .
источник
CJam,
56525048 байтовМоей первой мыслью было: «Эй, у меня уже есть код для этого!» Но тогда я не мог потрудиться собрать необходимые фрагменты из кода Ruby, особенно потому, что они не очень подходили для игры в гольф. Поэтому я попробовал что-то еще в CJam ...
Проверьте это здесь.
объяснение
Сначала немного математики о центрированных шестиугольных числах. Если обычный шестиугольник имеет длину стороны
N
, то он будет содержать3N(N-1)+1
ячейки, которые должны равняться длине исходного кодаk
. Мы можем решить это,N
потому что это простое квадратное уравнение:Мы можем игнорировать отрицательный корень, потому что это дает отрицательный N. Чтобы это могло иметь решение, нам нужно, чтобы квадратный корень был полуцелым. Или, другими словами,
√(1 + 4(k-1)/3) = √((4k-1)/3)
должно быть целым числом (к счастью, это целое число является диаметромD = 2N-1
шестиугольника, который нам все равно понадобится). Таким образом, мы можем многократно добавлять один,.
пока это условие не будет выполнено.Остальное - простая петля, которая выкладывает шестиугольник. Полезное наблюдение для этой части состоит в том, что пробелы в отступе плюс непробелы в коде в каждой строке складываются в диаметр.
Оказывается, нам вообще не нужно использовать двойную арифметику (кроме квадратного корня). Из-за умножения на 4 нет столкновений при делении на 3, и искомый
k
будет первым, чтобы получить целочисленный квадратный корень.источник
Perl,
203200198включает в себя + 1 для
-p
беги как:
echo abc | perl -p file.pl
Очень наивный подход:
;
; сам код до 200 байт сейчас!$s=~/\S+/g
вместоsplit/\n/,$s
источник
JavaScript (ES6), 162
172Анонимная функция
Размер шестиугольника найден, решая уравнение из Википедии
Формула решения в основном
С некоторой алгеброй и некоторым приближением (спасибо и @ user18655 тоже) оно становится
Более читаемый
Тестовый фрагмент (лучше полная страница - время выполнения ~ 1 минута)
источник
n=...+1-1e-9|0
вместо того,n=Math.ceil(...)
чтобы сохранить 2 байта. Вы также можете перейти на ES7 и использовать**0.5
вместо,Math.sqrt
но это на ваше усмотрение . Я обычно просто держу свои ответы ES6, потому что они работают в моем браузере, ха-ха!Pyth,
5251 байтПопробуйте онлайн. Тестирование.
Каждая строка имеет один дополнительный начальный пробел, как разрешено OP.
объяснение
источник
Сетчатка , 161 байт
Спасибо FryAmTheEggman за сохранение 2 байта.
Этот ответ не конкурирует. После этого испытания Retina видела несколько обновлений, и я почти уверен, что использую некоторые новые функции (хотя я не проверял).
Число байтов предполагает кодировку ISO 8859-1. Первая строка содержит один пробел. Обратите внимание, что большинство из них на
·
самом деле являются центральными точками (0xB7).Попробуйте онлайн!
Что ж...
объяснение
Кажется, проще всего сначала создать макет, используя только один символ (
·
в данном случае), а затем заполнить полученный макет входными символами. Основными причинами этого являются то, что использование одного символа позволяет мне использовать обратные ссылки и повторение символов, где непосредственное размещение ввода потребовало бы дорогостоящих групп балансировки.Хотя это и не выглядит много, на первых этапах удаляются пробелы из входных данных.
Мы начинаем с добавления дополнительной строки, содержащей
M
центральные точки, гдеM
длина ввода (после удаления пробелов).Если ввод был одним символом, мы снова удаляем эту центральную точку. Это прискорбный особый случай, который не рассматривается на следующем этапе.
Это вычисляет требуемую длину стороны
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
пробелы. Обратите внимание, что это максимальный отступ, за которым следует диаметр шестиугольника, а затем снова отступ.Это неприятно долго, но в основном это просто дает нам все совпадающие совпадения, которые являются либо а)
2N-1
длиной символов и в первой строке, либо б) второй строкой. Это расширяет результат предыдущего этапа в полный, но странно изогнутый шестиугольник. Например, для ввода12345678
мы получили бы:Вот почему нам нужно было добавлять пробелы и на предыдущем этапе.
Это исправляет отступ строк после центра путем многократного отступа любой строки, которая короче предыдущей (игнорируя конечные пробелы), поэтому мы получаем это:
Теперь мы просто вставляем несколько пробелов с
Что дает нам:
Фу, это сделано.
Время, чтобы заполнить строку ввода в центральных точках. Это делается с помощью этапа сортировки. Мы сопоставляем все центральные точки и каждый символ в последней строке и сортируем их по результату данной замены. Эта замена является пустой для символов в последней строке и
·
для центральных точек, поэтому происходит то, что центральные точки просто сортируются до конца (поскольку сортировка стабильна). Это перемещает вводимые символы на место:Осталось две вещи:
Это превращает центральные точки в регулярные периоды.
И это отбрасывает последнюю строку.
источник
JavaScript (ES6), 144 байта
Где
\n
представляет буквальный символ новой строки. Использует технику для создания шестиугольника, которую я ранее использовал в нескольких других ответах с гексагональной сеткой . Для ES7 получение квадратных корней работает немного короче, чем рекурсивный подход:источник
Python 3 , 144 байта
Попробуйте онлайн!
При этом используется довольно различное количество начальных пробелов для шестиугольников разных размеров, но общая форма сохраняется.
источник