Завдяки FryAmTheEggman за необхідне натхнення для рішення XOR.
0000 !@
0001 ?.|@!
0010 #?#!)@
0011 ?!@
0100 +?|@!?
0101 ??!@
0110 ?<@!!<_\~(
0111 ?<<@!
1000 )\!#?@{
1001 (~?/@#!
1010 ??|@!)
1011 \#??!1@
1100 ?(~!@
1101 ?.|@!)
1110 ?$@#)!<
1111 1!@
Усі програми використовують 0
для false та 1
for 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
). Коли #
зустрічається a, приймається поточне значення за модулем 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
: Б
? ?
! @ .
. .
Woohoo, ще один простий: читати A
, читати B
, друкувати B
, припиняти. Але насправді є альтернативи цьому. Оскільки A
це лише один символ, ми можемо також прочитати його ,
:
,?!@
Також є можливість використання одинарного ?
дзеркала та використання дзеркала для його перегляду двічі:
?|@! ?>@!
0110
: Xor
? < @
! ! < _
\ ~ ( . .
. . . .
. . .
Як я вже говорив вище, це був єдиний хвірт, який не впишеться в бічну довжину 2, тому це власноручне рішення FryAmTheEggman і я, і є хороший шанс, що це не оптимально. Можна виділити два випадки. Якщо A = 0
потік управління досить простий (тому що в такому випадку нам потрібно тільки друкувати B
):
Починаємо на червоній стежці. ?
читає A
, <
- це гілка, яка відхиляє лівий нуль. ІР накручується донизу, потім _
- інше дзеркало, і коли 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-байтове рішення:
\>?@!
Він працює по суті так само, але фактичні шляхи виконання зовсім інші, і він використовує кут для розгалуження замість а <
.
1000
: Ні
) \
! # ?
@ {
Це може бути моя улюблена програма, знайдена в цьому пошуку. Найкрутіше, що ця реалізація nor
насправді працює до 5 входів. Мені доведеться трохи розібратися в деталях моделі пам'яті, щоб пояснити цю. Отже, як швидке оновлення, модель пам’яті Hexagony - це окрема шестикутна сітка, де кожен край містить ціле значення (спочатку всі нульові). Існує вказівник пам’яті (MP), який вказує край і напрямок уздовж цього краю (такий, що перед поточним краєм і за ним є два сусідні краї із значущими лівими та правими сусідами). Ось діаграма ребер, які ми будемо використовувати, з MP, що починається, як показано червоним кольором:
Спершу розглянемо випадок, коли обидва входи 0
:
Ми починаємо з сірого шляху, який просто збільшує край А до 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
: Не Б
? ?
| @ !
) .
Цей досить простий. Шлях виконання - це горизонтальна гілка, про яку ми вже знаємо 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
? (
~ ! @
. .
Просто простий лінійний: читати 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
: Нанд
? $
@ # )
! <
Останній складний. Якщо ви все ще читаєте, ви майже зробили це. :) Давайте розглянемо 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 .