Представляю вашій увазі перші 3% самоперекладача шестикутників ...
|./...\..._..>}{<$}=<;>'<..../;<_'\{*46\..8._~;/;{{;<..|M..'{.>{{=.<.).|.."~....._.>(=</.\=\'$/}{<}.\../>../..._>../_....@/{$|....>...</..~\.>,<$/'";{}({/>-'(<\=&\><${~-"~<$)<....'.>=&'*){=&')&}\'\'2"'23}}_}&<_3.>.'*)'-<>{=/{\*={(&)'){\$<....={\>}}}\&32'-<=._.)}=)+'_+'&<
Спробуйте в Інтернеті! Ви також можете запустити його на себе, але це займе близько 5-10 секунд.
В принципі, це може вписатись у бічну довжину 9 (для оцінки 217 або менше), оскільки для цього використовується лише 201 команда, а для неперевершеної версії, яку я написав спочатку (на стороні 30), потрібно було лише 178 команд. Однак я майже впевнений, що для того, щоб все було правильно, знадобиться назавжди, тому я не впевнений, чи дійсно я спробую це зробити.
Слід також мати можливість пограти в гольф трохи розміром 10, уникаючи використання останніх одного чи двох рядів, таким чином, щоб пропуски не можна було опустити, але це вимагало б істотного переписування, як одного з перших шляхів приєднується використовує нижній лівий кут
Пояснення
Почнемо з розгортання коду та анотації контурів потоку управління:
Це все ще безладно, тому ось така ж схема для коду, що не використовується для "без вологів", який я написав спочатку (насправді це довжина бічної сторони 20, і спочатку я написав код на 30-бічній стороні, але це було настільки рідко, що він би не взагалі не покращую читабельність, тому я її трохи ущільнив, щоб зробити розмір трохи розумнішим):
Клацніть для збільшення версії.
Кольори абсолютно однакові, окрім кількох дуже незначних деталей, команди, які не контролюють потік, також точно однакові. Тож я поясню, як це працює за версією, що не використовується для гольфу, і якщо ви дійсно хочете дізнатися, як працює гольф, ви можете перевірити, які деталі там відповідають, а які у великому шестикутнику. (Єдина уловка полягає в тому, що гольф-код починається з дзеркала, так що фактичний код починається в правому куті, ідучи ліворуч.)
Основний алгоритм майже ідентичний моїй відповіді CJam . Є дві відмінності:
- Замість того, щоб розв'язувати рівняння по центру з шестикутним числом, я просто обчислював послідовні центрировані шестикутні числа, поки одне не дорівнює або не перевищує довжину вводу. Це тому, що в шестикутника немає простого способу обчислити квадратний корінь.
- Замість того, щоб одразу вкладати вхід без операцій, я перевіряю пізніше, чи я вже вичерпав команди на вході, і надрукував
.
а, якщо є.
Це означає, що основна ідея зводиться до:
- Прочитайте та зберігайте вхідний рядок, обчислюючи його довжину.
- Знайдіть найменшу бічну довжину
N
(і відповідне по центру шестикутне число hex(N)
), яка може вмістити весь вхід.
- Обчисліть діаметр
2N-1
.
- Для кожного рядка обчисліть відступ та кількість комірок (які підсумовують
2N-1
). Роздрукуйте відступ, надрукуйте клітинки (використовуючи, .
якщо вхід уже вичерпано), надрукуйте подачу рядків.
Зауважте, що немає лише опісів, тому власне код починається в лівому куті (той $
, який перестрибує через >
, тому ми дійсно починаємо з ,
темно-сірого шляху).
Ось початкова сітка пам'яті:
Таким чином, вказівник пам'яті починається на крайовому вході , вказуючи на північ. ,
читає байт зі STDIN або a, -1
якщо ми потрапили EOF в цей край. Отже, <
відразу після цього є умовою того, чи ми прочитали всі матеріали. Залишимося зараз у циклі введення. Наступний код, який ми виконуємо, - це
{&32'-
Це записує 32 в простір , позначений ребром , а потім віднімає його від вхідного значення в краю, позначеному diff . Зауважте, що це ніколи не може бути негативним, оскільки ми гарантуємо, що вхід містить лише друкований ASCII. Це буде нуль, коли на вході був пробіл. (Як зазначає Тімві, це все одно спрацювало, якби вхід міг містити канали ліній або вкладки, але він також викреслив би всі інші недруковані символи з кодами символів менше 32.) У цьому випадку <
відхиляється вказівник інструкції (IP) зліва і йде світло-сірий шлях. Цей шлях просто скидає положення MP з, {=
а потім читає наступний символ - таким чином, пропуски пропускаються. В іншому випадку, якщо персонаж не був пробілом, ми виконуємо
=}}})&'+'+)=}
Це спочатку рухається навколо шестикутника через край довжини до його протилежного краю різниці , з =}}}
. Потім він копіює значення з навпроти довжини кромки в довжину краю, а також збільшує його з )&'+'+)
. Ми побачимо через секунду, чому це має сенс. Нарешті, пересуваємо новий край за допомогою =}
:
(Конкретні значення ребра припадають на останній тестовий випадок, поданий у виклику.) На цьому етапі цикл повторюється, але зі всім зміщенням одного шестикутника на північний схід. Отже, прочитавши іншого персонажа, ми отримуємо це:
Тепер ви можете бачити, що ми поступово пишемо введення (мінус пробіли) по діагоналі північного сходу, з символами на кожному іншому краї, а довжина до цього символу зберігається паралельно довжині ребра .
Коли ми закінчимося з циклом введення, пам'ять буде виглядати приблизно так (де я вже позначив кілька нових ребер для наступної частини):
%
Є останнім символом ми читаємо, то 29
є число символів без пробілів ми читаємо. Тепер ми хочемо знайти бічну довжину шестикутника. По-перше, є деякий лінійний код ініціалізації у темно-зеленому / сірому шляху:
=&''3{
Тут =&
скопіюємо довжину (29 у нашому прикладі) у крайку, позначену довжиною . Потім ''3
переходить до краю, позначеного 3, і встановлює його значення 3
(яке нам просто потрібно як константа в обчисленні). Нарешті {
рухається до краю, позначеного N (N-1) .
Тепер вводимо синю петлю. Цей приріст циклу N
(зберігається у комірці з позначкою N ) потім обчислює його центроване шестикутне число і віднімає його від вхідної довжини. Лінійний код, який робить це:
{)')&({=*'*)'-
Тут, {)
переміщається і збільшує N . ')&(
переходить до краю, позначеного N-1 , копіює N
його та зменшує його. {=*
обчислює їх добуток у N (N-1) . '*)
множиться на те, що на постійні 3
та збільшення кроків виходить шестигранник, позначений краєм (N) . Як і очікувалося, це N-центр по шестикутній кількості. Нарешті '-
обчислює різницю між цією та довжиною введення. Якщо результат позитивний, довжина сторони ще недостатньо велика, і петля повторюється (де }}
пересуньте МП назад до краю, позначеного N (N-1) ).
Як тільки довжина сторони буде достатньо великою, різниця буде нульовою або негативною, і ми отримаємо наступне:
По-перше, зараз існує дійсно довгий лінійний зелений шлях, який робить певну необхідну ініціалізацію для вихідного циклу:
{=&}}}32'"2'=&'*){=&')&}}
В {=&
починається з копіюванням результату в диффе краю в довжині край, тому що пізніше потрібно що - то непозитивним там. }}}32
записує 32 в пробіл, позначений краєм . '"2
записує константу 2 в неозначений край над розд . '=&
копії N-1
на другий край з тією ж міткою. '*)
помножуємо його на 2 і збільшуємо його, так що ми отримуємо правильне значення в краю, позначеному вгорі 2N-1 . Це діаметр шестикутника. {=&')&
копіює діаметр в інший край, позначений 2N-1 . Нарешті, }}
повертається назад до краю з позначкою 2N-1 вгорі.
Розберемо ребра:
Край, на якому ми перебуваємо в даний момент (який досі має діаметр шестикутника), буде використаний для перебору ліній виводу. Ребро з міткою відступ буде обчислити , скільки простору необхідно на поточному рядку. Клітини, позначені краєм, будуть використовуватися для ітерації над кількістю комірок у поточному рядку.
Зараз ми на рожевій стежці, яка обчислює відступ . ('-
зменшує ітератор рядків і віднімає його від N-1 (до краю відступу ). Коротка синя / сіра гілка в коді просто обчислює модуль результату ( ~
заперечує значення, якщо воно негативне або нульове, і нічого не відбувається, якщо воно є позитивним). Решта рожевого контуру - "-~{
це віднімання відступу від діаметра до краю комірок, а потім переміщення назад до краю відступу .
Зараз брудно-жовтий шлях друкує відступ. Вміст циклу справді просто
'";{}(
Там, де '"
рухається до простору , ;
друкується, {}
повертається до відступу і (
зменшує його.
Коли ми закінчимо з цим, (другий) темно-сірий шлях шукає наступного символу для друку. В =}
переміщається в положення (що означає, на клітини краю, що вказують на південь). Тоді у нас дуже щільна петля, {}
яка просто рухається вниз по двох краях в напрямку Південний Захід, поки ми не потрапимо в кінець збереженої рядки:
Зауважте, що я відновив один край там EOF? . Після обробки цього символу ми зробимо цей край негативним, так що {}
цикл закінчиться тут замість наступної ітерації:
У коді ми знаходимося в кінці темно-сірого шляху, де '
рухаємось на один крок назад на вхідний символ. Якщо ситуація є однією з двох останніх діаграм (тобто є ще символ із введення, який ми ще не надрукували), то ми беремо зелений шлях (нижній, для людей, які не вподобають зелений та синій). Це досить просто: ;
друкує самого персонажа. '
переміщується до відповідного крайового простору, який все ще містить 32 з попереднього, і ;
друкує цей простір. Тоді {~
робить наш EOF? мінус для наступної ітерації, '
відсуває крок назад, щоб ми могли повернутися до північно-західного кінця рядка ще однією щільною }{
петлею. Який закінчується по довжиніклітинку ( непозитивну, розташовану нижче шістнадцяткової (N) . Нарешті, }
повертається назад до краю комірок) .
Якщо ми вже вичерпали вхід, то цикл, який шукає EOF? фактично припиняється тут:
У цьому випадку '
рухається до комірки довжини , і ми замість цього беремо світло-блакитний (верхній) шлях, який друкує неоперативний варіант. Код у цій галузі лінійний:
{*46;{{;{{=
Запис {*46;
46 у край, позначений no-op, і друкує його (тобто період). Потім {{;
рухається до крайового простору і друкує це. В {{=
рухається назад до клітинам краю для наступної ітерації.
У цей момент шляхи з'єднуються назад і (
зменшують краю комірок . Якщо ітератор ще не дорівнює нулю, ми підемо світло-сірим шляхом, який просто переверне напрямок MP =
і далі піде на друк наступного символу.
В іншому випадку ми дійшли до кінця поточного рядка, і IP замість цього скористається фіолетовим шляхом. Ось як виглядає сітка пам'яті в цей момент:
Фіолетовий шлях містить це:
=M8;~'"=
=
Знову змінює напрямок МП. M8
встановлює набори своїм значенням 778
(оскільки код символу M
є 77
і цифри додаватимуть себе до поточного значення). Це трапляється 10 (mod 256)
так, коли ми друкуємо його ;
, ми отримуємо лінійку передач. Потім ~
знов робить край негативним, '"
повертається до краю рядків і =
ще раз повертає MP.
Тепер, якщо край рядків дорівнює нулю, ми закінчили. IP буде проходити (дуже короткий) червоний шлях, де @
програма припиняється. В іншому випадку ми продовжуємо пурпуровий шлях, який замикається назад на рожевий, щоб надрукувати ще один рядок.
Діаграми керування потоком, створені за допомогою шестигранника Colour Timwi . Діаграми пам'яті, створені за допомогою візуального відладчика в його Езотеричному IDE .
abc`defg
би насправді стати pastebin.com/ZrdJmHiR