Спасибо FryAmTheEggman за необходимое вдохновение для решения XOR.
0000 !@
0001 ?.|@!
0010 #?#!)@
0011 ?!@
0100 +?|@!?
0101 ??!@
0110 ?<@!!<_\~(
0111 ?<<@!
1000 )\!#?@{
1001 (~?/@#!
1010 ??|@!)
1011 \#??!1@
1100 ?(~!@
1101 ?.|@!)
1110 ?$@#)!<
1111 1!@
Все программы используют 0
для false и 1
для true.
Попробуйте онлайн! Это не набор тестов, вам придется копировать в разные программы и вводить данные самостоятельно.
Вышеупомянутое решение находится в пределах 2 байтов оптимальности (я полагаю, если мы не ослабим интерпретацию правдивости / ложности). Я пусть перебор баллотироваться около двух дней по всем программам , которые вписываются в боковой длину 2, то есть до 7 байт (не совсем все программы - я сделал несколько предположений о то , что каждых действительных потребностях программы и что нет действительная программа могла бы иметь). Поиском были найдены решения для 15 из 16 возможных ворот - и зачастую гораздо больше, чем один. Вы можете найти список всех альтернативных решений в этой папке, где я также сгруппировал их по эквивалентному поведению. Те, что я показываю выше, я выбрал, потому что это либо самое простое, либо самое интересное решение, и я добавлю их объяснения завтра.
Что касается 16-го шлюза: XOR - единственный вентиль, который, очевидно, не может быть реализован в 7 байтах. К сожалению, поиск по более крупным программам невозможен с кодом, который у меня есть в настоящее время. Так что XOR нужно было писать от руки. Самая короткая, которую я нашел до сих пор, - это вышеуказанная 10-байтовая программа, основанная на неудачной (но очень близкой) попытке FryAmTheEggman. Возможно, что существует 8-байтовое или 9-байтовое решение, но, кроме этого, все решения действительно должны быть оптимальными.
Пояснения
Предупреждение: стена текста. На случай, если кому-то интересно, как на самом деле работают эти сильно сжатые программы Hexagony, я привел объяснения каждой из них ниже. Я попытался выбрать простейшее решение для каждого шлюза в тех случаях, когда существует более одной оптимальной программы, чтобы объяснения были достаточно краткими. Тем не менее, некоторые из них все еще поражают воображение, поэтому я подумал, что они заслуживают немного большей проработки.
0000
: Ложь
Я не думаю, что нам понадобится диаграмма для этого:
! @
. . .
. .
Поскольку вся сетка памяти инициализируется нулями, !
просто печатает ноль и @
завершает программу.
Это также единственное 2-байтовое решение.
0001
: А также
? .
| @ !
. .
Это в основном реализует короткое замыкание . Серая диаграмма ниже показывает начало программы, где читается первый ввод, ?
а указатель инструкций (IP) оборачивается в левый угол, где его |
отражает зеркало. Теперь угол действует как условный, так что есть два разных пути выполнения в зависимости от значения первого ввода. Красная диаграмма показывает поток управления для A = 0
и зеленая диаграмма для A = 1
:
Как вы можете видеть, когда A
есть 0
, тогда мы просто распечатываем его и завершаем (помните, что все .
они не используются). Но когда A
есть 1
, то IP снова пересекает первый ряд, читая B
и печатая его.
Всего существует шестнадцать 5-байтовых решений для этих ворот. Четырнадцать из них, по сути, такие же, как и выше, либо используют >
вместо, |
либо заменяют .
команду, которая фактически не выполняет никаких действий, либо ставят ?
во вторую позицию:
?.|@! .?|@! ?=|@! =?|@! ?_|@! _?|@! ?0|@!
?.>@! .?>@! ?=>@! =?>@! ?_>@! _?>@! ?0>@!
И тогда есть два других решения (которые эквивалентны друг другу). Они также реализуют ту же логику короткого замыкания, но пути выполнения немного сумасшедшие (и оставлены в качестве упражнения для читателя):
?<!@|
?<!@<
0010
: А а не Б
# ?
# ! )
@ .
Это также реализует форму короткого замыкания, но из-за использования #
потока управления намного сложнее. #
является условным IP-коммутатором. Hexagony фактически поставляется с шестью IP - адресов , помеченных 0
к 5
, которые начинаются в шести углах сетки, указывая вдоль их по часовой стрелке края (и программа всегда начинается с IP 0
). Когда #
встречается, текущее значение берется по модулю 6
, и поток управления продолжается с соответствующим IP. Я не уверен, какой приступ безумия заставил меня добавить эту функцию, но она, безусловно, допускает некоторые удивительные программы (вроде этой)
Мы будем различать три случая. Когда A = 0
, программа довольно проста, потому что значение всегда 0
когда #
встречается так, что IP-переключение не происходит:
#
ничего не делает, ?
читает A
(то есть тоже ничего не делает), #
все еще ничего не делает, !
печатает 0
, )
увеличивает его (это важно, иначе IP не будет переходить на третью строку), @
завершает программу. Достаточно просто. Теперь давайте рассмотрим случай (A, B) = (1, 0)
:
Красный путь все еще соответствует IP 0
, и я добавил зеленый путь для IP 1
. Мы видим, что после ?
чтения A
(на 1
этот раз) #
переключается на IP, который начинается в верхнем правом углу. Это означает, что ?
можете читать B
( 0
). Теперь )
увеличивает это значение до 1
такого, что #
в левом верхнем углу ничего не происходит, и мы остаемся с IP 1
. В !
печатает 1
и IP обтекает левую диагональ. #
по-прежнему ничего не делает и @
завершает программу.
Наконец, действительно странный случай, когда оба входа 1
:
На этот раз второй вход также 1
и )
увеличивает его до 2
. Это означает, что #
в левом верхнем углу другой IP-переключатель переключается на IP 2
, обозначенный синим цветом. На этом пути мы сначала увеличиваем его до 3
(хотя это не имеет значения), а затем передаем ?
третий раз. Так как мы теперь нажали EOF (т.е. ввод исчерпан), ?
возвращает 0
, !
печатает это и @
завершает программу.
Примечательно, что это единственное 6-байтовое решение для этих ворот.
0011
: A
? !
@ . .
. .
Это достаточно просто, и нам не понадобится диаграмма: ?
читает A
, !
печатает, @
завершает работу.
Это единственное 3-байтовое решение для этих ворот. (В принципе, это также было бы возможно сделать ,;@
, но поиск не включал ;
, потому что я не думаю, что он когда-либо сможет сэкономить байты !
для этой задачи.)
0100
: B, а не A
+ ?
| @ !
? .
Этот намного проще, чем его "брат" 0010
. Поток управления фактически такой же, как мы видели выше для 0001
(А). Если A = 0
, то IP пересекает нижнюю строку, считывая B
и распечатывая ее перед завершением. Если A = 1
затем IP снова пересекает первую строку, также считывая B
, но +
добавляет два неиспользуемых края памяти, все, что он делает, - сбрасывает текущее значение 0
, чтобы !
всегда печатать 0
.
Существует довольно много 6-байтовых альтернатив этому (всего 42). Во-первых, существует масса решений, эквивалентных вышеперечисленным. Мы снова можем свободно выбирать между |
и >
и +
можем быть заменены любой другой командой, которая дает нам пустое ребро:
"?|@!? &?|@!? '?|@!? *?|@!? +?|@!? -?|@!? ^?|@!? {?|@!? }?|@!?
"?>@!? &?>@!? '?>@!? *?>@!? +?>@!? -?>@!? ^?>@!? {?>@!? }?>@!?
Кроме того, мы также можем использовать ]
вместо ?
. ]
переходит к следующему IP (т.е. выбирает IP 1
), так что вместо этого эта ветвь использует ?
в правом верхнем углу. Это дает еще 18 решений:
"?|@!] &?|@!] '?|@!] *?|@!] +?|@!] -?|@!] ^?|@!] {?|@!] }?|@!]
"?>@!] &?>@!] '?>@!] *?>@!] +?>@!] -?>@!] ^?>@!] {?>@!] }?>@!]
И затем есть шесть других решений, которые все работают по-разному с различными уровнями сумасшествия:
/[<@!? ?(#!@] ?(#>@! ?/@#/! [<<@!? [@$\!?
0101
: B
? ?
! @ .
. .
Woohoo, еще один простой: читать A
, читать B
, печатать B
, прекратить. Хотя на самом деле есть альтернативы этому. Поскольку A
это только один символ, мы также можем прочитать его ,
:
,?!@
И есть также возможность использовать одно ?
и использовать зеркало, чтобы пройти через него дважды:
?|@! ?>@!
0110
: Xor
? < @
! ! < _
\ ~ ( . .
. . . .
. . .
Как я уже говорил выше, это были единственные ворота, которые не вписывались бы в длину стороны 2, так что это рукописное решение от FryAmTheEggman и меня, и есть большая вероятность, что оно не оптимально. Есть два случая, чтобы различать. Если A = 0
поток управления довольно прост (потому что в этом случае нам нужно только напечатать B
):
Мы начинаем по красной дорожке. ?
читает A
, <
это ветвь, которая отклоняет ноль слева. IP переносится вниз, затем _
- другое зеркало, а когда IP попадает в угол, он переносится в верхний левый угол и продолжается по синему пути. ?
читает B
, !
печатает это. Теперь (
уменьшает это. Это важно, потому что это гарантирует, что значение не положительное (оно 0
или -1
сейчас). Это заставляет IP переноситься в правый угол, где @
завершается программа.
Когда A = 1
все становится немного сложнее. В этом случае мы хотим напечатать not B
, что само по себе не так уж сложно, но путь исполнения немного триповый.
На этот раз <
IP отклоняет право, а затем следующий <
просто действует как зеркало. Таким образом, IP пересекает тот же путь в обратном направлении, читая B
при ?
повторном обнаружении IP переходит в правый угол и продолжается по зеленой дорожке. Она следующие встречи , (~
которые являются «декремент, умножить на -1», которые свопы 0
и 1
поэтому рассчитывает not B
. \
это просто зеркало и !
печатает желаемый результат. Затем ?
пытается вернуть другое число, но возвращает ноль. IP теперь продолжается в левом нижнем углу на синем пути. (
уменьшает, <
отражает,(
снова уменьшается, так что текущее значение является отрицательным, когда IP достигает угла. Он перемещается по нижней правой диагонали, а затем, наконец, нажимает, @
чтобы завершить программу.
0111
: Или же
? <
< @ !
. .
Более короткое замыкание.
A = 0
Случай (красный контур) немного запутанным здесь. IP отклоняется влево, переносится в нижний левый угол, сразу же отражается <
и возвращается ?
к чтению B
. Затем он заворачивает к rigt углу, печатает B
с !
и заканчивается.
A = 1
Случай (зеленый путь) немного проще. <
Филиал отклоняет IP право, поэтому мы просто напечатать !
, завернуть обратно в левом верхнем углу, и заканчиваются в @
.
Есть только одно 5-байтовое решение:
\>?@!
Он работает по сути одинаково, но фактические пути выполнения весьма различны и используют угол для ветвления вместо a <
.
1000
: Ни
) \
! # ?
@ {
Это может быть моя любимая программа, найденная в этом поиске. Самое классное, что эта реализация на nor
самом деле работает до 5 входов. Мне придется немного углубиться в детали модели памяти, чтобы объяснить это. Таким образом, для быстрого освежения модель памяти Hexagony представляет собой отдельную шестиугольную сетку, где каждое ребро содержит целочисленное значение (изначально все ноль). Есть указатель памяти (MP), который указывает ребро и направление вдоль этого ребра (так, что есть два соседних ребра перед и за текущим ребром, с значимыми левыми и правыми соседями). Вот диаграмма ребер, которые мы будем использовать, начиная с MP, как показано красным:
Давайте сначала рассмотрим случай, когда оба входа 0
:
Мы начинаем с серого пути, который просто увеличивает ребро A до 1
так, чтобы #
переключиться на IP, 1
который является синим путем, начиная с правого верхнего угла. \
ничего не делает там и ?
читает вход. Мы переносим в верхний левый угол, где )
увеличивается этот вход. Теперь, пока ввод равен нулю, это приведет к 1
, так что #
ничего не делает. Затем {
перемещает MP влево, то есть на первой итерации от к B . Так как у этого края все еще есть свой начальный ноль, IP возвращается в верхний правый угол и на новый край памяти. Так что этот цикл будет продолжаться до тех пор, пока он читает нули, перемещая MP вокруг шестиугольника от B?
от C до D и так далее. Неважно, ?
возвращает ли ноль, потому что это был вход или потому что это был EOF.
После шести итераций через эту петлю, {
возвращается к . На этот раз ребро уже содержит значение с самой первой итерации, поэтому IP оборачивается в левый угол и вместо этого продолжает зеленый путь. просто печатает это и завершает программу.1
!
1
@
А что если какой-либо из входов есть 1
?
Затем ?
читает это 1
в какой-то момент и )
увеличивает его до 2
. Это означает, #
что теперь снова переключатся IP-адреса, и мы продолжим в правом углу по красной дорожке. ?
читает другой ввод (если он есть), который на самом деле не имеет значения, и {
перемещается на один край дальше. Это должен быть неиспользуемый фронт, следовательно, он работает до 5 входов. IP переносится в верхний правый угол, где он сразу отражается, и переносится в левый угол. !
печатает 0
на неиспользованном крае и #
переключается обратно на IP 0
. Этот IP-адрес все еще находился на #
юго-западе (серый путь), поэтому он сразу же нажимает на @
и завершает программу.
Всего существует семь 7-байтовых решений для этих ворот. 5 из них работают так же, как это, и просто используют другие команды для перемещения к неиспользуемому краю (и могут ходить по другому шестиугольнику или в другом направлении):
)\!#?@" )\!#?@' )\!#?@^ )\!#?@{ )\!#?@}
И есть еще один класс решений, который работает только с двумя входами, но пути выполнения которых на самом деле еще более беспорядочные:
?]!|<)@ ?]!|<1@
1001
: Равенство
( ~
? / @
# !
Это также делает очень умным использование условного выбора IP. Нам нужно еще раз различать A = 0
и A = 1
. В первом случае мы хотим напечатать not B
, во втором мы хотим напечатать B
. Ибо A = 0
мы также различаем два случая B
. Давайте начнем с A = B = 0
:
Мы начинаем на сером пути. (~
может быть проигнорировано, IP переносится в левый угол (все еще на сером пути) и читает A
с ?
. (
уменьшает это, так что мы получаем -1
и IP обернуть в левый нижний угол. Теперь, как я уже говорил ранее, #
принимает значение по модулю, 6
прежде чем выбрать IP, так что значение -1
фактически получает IP 5
, который начинается в левом углу на красном пути. ?
читает B
, (
уменьшает это так, чтобы мы оставались на IP, 5
когда мы #
снова нажмем. ~
отрицает -1
так, что IP переносится в правый нижний угол, печатает 1
и завершается.
Теперь , если B
это 1
вместо того, чтобы , текущее значение будет , 0
когда мы попали #
во второй раз, поэтому мы переходим обратно в IP 0
( в настоящее время на зеленом пути). Это ударяет ?
в третий раз, уступает 0
, !
печатает это и @
заканчивается.
Наконец, случай, когда A = 1
. На этот раз текущее значение уже равно нулю, когда мы нажимаем #
в первый раз, так что это никогда не переключается на IP 5
в первую очередь. Мы просто продолжаем немедленно на зеленой тропе. ?
теперь не просто дает ноль, а возвращает B
вместо этого. !
печатает его и @
завершает снова.
Всего есть три 7-байтовых решения для этих ворот. Два других работают очень по-разному (даже друг от друга) и используют еще более странно #
. В частности, они читают одно или несколько значений с помощью ,
(чтение кода символа вместо целого числа), а затем используют это значение по модулю 6, чтобы выбрать IP. Это довольно орешки.
),)#?@!
?~#,~!@
1010
: Не B
? ?
| @ !
) .
Это довольно просто. Путь выполнения - это горизонтальная ветвь, о которой мы уже знали and
ранее. ??
читает, A
а потом сразу B
. После отражения в |
и разветвления, для B = 0
мы выполним нижнюю ветвь, где )
увеличивает значение, на 1
которое затем печатается !
. В верхней ветви (если B = 1
) ?
просто сбрасывается край, на 0
который затем также печатается !
.
Для этих ворот есть восемь 6-байтовых программ. Четыре из них в значительной степени одинаковы, используя либо >
вместо, |
либо 1
вместо )
(или оба):
??>@!) ??>@!1 ??|@!) ??|@!1
Два используют один, ?
который используется дважды из-за зеркала. Отрицание тогда происходит, как мы сделали xor
с или (~
или ~)
.
?>!)~@ ?>!~(@
И, наконец, два решения используют условный IP-коммутатор, потому что зачем использовать простой способ, если извилистый также работает:
??#)!@ ??#1!@
1011
: B подразумевает A
\ #
? ? !
1 @
Это использует довольно сложную IP-коммутацию. На A = 1
этот раз я начну со случая, потому что это проще:
Мы начинаем с серого пути, который читает A
с, ?
а затем нажимает на #
. Так A
как 1
это переключается на IP 1
(зеленый путь). !
Сразу печатает того, IP - обертывания в левом верхнем углу, читает B
(излишне) и завершает работу .
Когда A = 0
все становится немного интереснее. Сначала давайте рассмотрим A = B = 0
:
На этот раз, #
ничего не делает, и мы остаемся на IP 0
(красный путь с этого момента). ?
читает B
и 1
превращает его в 1
. После переноса в верхний левый угол мы #
снова нажимаем, и в конце концов оказываемся на зеленом пути, и печатаем, 1
как и прежде, перед завершением.
Наконец, вот (A, B) = (0, 1)
ложный случай:
Обратите внимание, что я удалил начальный серый путь для ясности, но программа начинается так же, и мы в конечном итоге на красный путь, как и раньше. Так что на этот раз второй ?
возвращается 1
. Теперь мы сталкиваемся с 1
. На этом этапе важно понять, что на самом деле делают цифры в гексагонии (пока мы использовали их только в нулях): когда встречается цифра, текущее значение умножается на 10, а затем цифра добавляется. Это обычно используется для записи десятичных чисел дословно в исходный код, но это означает, что B = 1
на самом деле отображается в значение 11
. Поэтому, когда мы нажимаем #
, это принимается по модулю, 6
чтобы дать, 5
и, следовательно, мы переключаемся на IP 5
(а не 1
как раньше) и продолжаем идти по синему пути. Удары?
третий раз возвращает ноль, поэтому !
печатает, что, а после еще двух ?
, IP оборачивается внизу справа, где программа завершается.
Для этого есть четыре 7-байтовых решения, и все они работают по-разному:
#)/!?@$ <!?_@#1 \#??!1@ |/)#?@!
1100
: Не А
? (
~ ! @
. .
Просто простой линейный один: чтение A
с ?
, свести на нет с (~
, печатать !
, оканчиваются @
.
Есть одно альтернативное решение, и ~)
вместо этого оно отрицает :
?~)!@
1101
: A подразумевает B
? .
| @ !
) .
Это намного проще, чем противоположное значение, о котором мы только что говорили. Это снова одна из тех горизонтальных программ ветвления, например, для and
. Если A
есть 0
, он просто увеличивается до 1
нижней ветви и печатается. В противном случае верхняя ветвь выполняется снова, где ?
читает B
и затем !
печатает это вместо.
Там в тонну альтернатив здесь (66 решений в целом), в основном за счет свободного выбора эффективных нет-OPS. Для начала мы можем варьировать вышеупомянутое решение теми же способами, что and
и мы, и мы также можем выбирать между )
и 1
:
?.|@!) .?|@!) ?=|@!) =?|@!) ?_|@!) _?|@!) ?0|@!)
?.|@!1 .?|@!1 ?=|@!1 =?|@!1 ?_|@!1 _?|@!1 ?0|@!1
?.>@!) .?>@!) ?=>@!) =?>@!) ?_>@!) _?>@!) ?0>@!)
?.>@!1 .?>@!1 ?=>@!1 =?>@!1 ?_>@!1 _?>@!1 ?0>@!1
И тогда есть другой вариант , используя условный выбор IP, где первая команда может быть выбрана почти произвольно, и есть также выбор между )
и 1
для некоторых из этих вариантов:
"?#1!@ &?#1!@ '?#1!@ )?#1!@ *?#1!@ +?#1!@ -?#1!@ .?#1!@
0?#1!@ 1?#1!@ 2?#1!@ 3?#1!@ 4?#1!@ 5?#1!@ 6?#1!@ 7?#1!@
8?#1!@ 9?#1!@ =?#1!@ ^?#1!@ _?#1!@ {?#1!@ }?#1!@
"?#)!@ &?#)!@ '?#)!@ *?#)!@ +?#)!@ -?#)!@
0?#)!@ 2?#)!@ 4?#)!@ 6?#)!@
8?#)!@ ^?#)!@ _?#)!@ {?#)!@ }?#)!@
1110
: Nand
? $
@ # )
! <
Последний сложный. Если вы все еще читаете, вы почти сделали это. :) Давайте посмотрим A = 0
сначала:
?
читает, A
а затем мы ударили $
. Это команда перехода (например, Befunge #
), которая пропускает следующую инструкцию, чтобы мы не заканчивались на @
. Вместо этого IP продолжает в #
. Однако , так как A
есть 0
, это ничего не делать. )
увеличивает его 1
так, чтобы IP продолжался по нижнему пути, где 1
печатается. В <
отклоняет IP - вправо , где он заворачивает к левому углу , и программа завершается.
Далее, когда на входе (A, B) = (1, 0)
мы получаем эту ситуацию:
Это по сути то же самое , как и раньше , за исключением , что на #
мы переходим к IP 1
(зеленый путь), но так как B
это 0
мы вернуться к IP , 0
когда мы попали #
во второй раз (теперь синий путь), где он печатает , 1
как и раньше.
Наконец, A = B = 1
дело:
На этот раз, когда мы #
во второй раз, текущее значение все еще 1
так, чтобы мы не изменили IP снова. <
Отражает это и в третий раз мы попали , ?
мы получаем ноль. Следовательно, IP оборачивается внизу слева, где !
печатается ноль, и программа заканчивается.
Для этого существует девять 7-байтовых решений. Первая альтернатива просто использует 1
вместо )
:
?$@#1!<
Тогда есть два решения, которые помогут вам оценить количество IP-коммутаций:
)?#_[!@ 1?#_[!@
Это на самом деле поразило меня: интересная часть заключается в том, что IP-переключение можно использовать в качестве условного отсрочки. Языковые правила переключения IP таковы, что текущий IP делает еще один шаг, прежде чем произойдет переключение. Если этот шаг проходит через угол, то текущее значение решает, на какой ветви будет продолжаться IP, если мы когда-нибудь переключимся на него. Именно это происходит, когда ввод A = B = 1
. Хотя все это согласуется с тем, как я проектировал язык, я никогда не знал об этом значении спецификации, поэтому приятно, когда мой язык учит меня новым трюкам: D.
Тогда есть третье решение, степень IP-переключения которого еще хуже (хотя он не использует этот отложенный условный эффект):
>?1]#!@
А потом еще один:
?$@#)!<
И затем есть эти четыре эквивалентных решения, которые используют некоторую безусловную IP-коммутацию и вместо этого реализуют всю логику через ответвления и углы:
]<?<@!) ]<?<@!1 ]|?<@!) ]|?<@!1
1111
: Правда
1 !
@ . .
. .
Вы заработали себе что-то простое для конца: установите преимущество 1
, напечатайте !
, завершите с @
. :)
Конечно, есть одна альтернатива:
)!@
Как обычно, все блок-схемы управления созданы с помощью HexagonyColorer Тимви и диаграммы памяти с его EsotericIDE .