Jelly , 309 байт в кодуванні Jelly
“Æ÷“¥s“ɲ“¡µ’;“ịƊ⁴çNṂ‘_\
OḌ;¢*5$%¥/µ“+⁷ż!¤ña¡jIȧƁfvḶg/Ọ=^ƝĠ0Ẇƭ³½N~=.Ɗ°ɗẇ⁵\ɦ*ɠPf⁾?ṾHḣ 2=⁹ƒ!©ƊĠṣƥ®Ƙ0Yƙ>!ȧtƊN0w,$ɠẎ46fẋ⁷(ṣẆm⁾ŻƓṫµsçwṣḂḲd0Ruṛ’ḃ21+\iµØW“&;:' ”;“¡3ȧ%⁾xƑ?{Ñṃ;Ċ70|#%ṭdṃḃ÷ƑĠẏþḢ÷ݳȦṖcẇọqƁe ʠ°oḲVḲ²ụċmvP[ỴẊẋ€kṢ ȯḂ;jɓỴẏeṾ⁴ḳḢ7Ẓ9ġƤṙb€xÇ4ɗ⁻>Ẉm!Ƈ)%Ḃẇ$ġ£7ȧ`ỵẈƘɗ¡Ṃ&|ƙƥ³ẏrṛbḋƙċ⁻ṁƲRṀẹṾ<ñ⁻Ṅ7j^ɓĊ’b58¤ị;0ị@
ḲÇ€t0”@;Ṫ
Спробуйте в Інтернеті!
Я вирішив, що прийшов час, коли я пішов на власний виклик. Використання Jelly (та його 8-бітової кодової сторінки) дає мені 12,5% переваги перед мовами, що відповідають лише ASCII, і Jelly є зручним для цього завдання через наявність вбудованих базових операторів перетворення з короткими назвами, але більшість заощаджень пояснюються кращим алгоритмом стиснення (ця програма в середньому менше одного байта на тип монстра).
Алгоритм та пояснення
Класифікація на основі слів
Я вирішив, що для того, щоб отримати хороший бал, потрібно скоріше скористатися структурою вхідних даних, ніж інші записи. Одне, що дуже помітно, - це те, що багато монстрів мають назви форми « прикметник вид "; a red dragon
і a blue dragon
- обидва типи дракона, і таким чином постають як D
. Деякі інші монстри мають назви форми " видова робота ", наприклад orc shaman
,; будучи типом орка, це виглядає як o
. Складні питання - нежить; a kobold zombie
- це і kobold, і зомбі, і останній стан має перевагу в іменуванні монстрів NetHack, тому ми б хотіли класифікувати це як Z
.
Як таке, я класифікував слова, які відображаються в іменах монстрів, таким чином: індикатор - це слово, яке настійно підказує відповідний клас монстра (наприклад, sphere
настійно говорить про те, що монстр перебуває в класі e
); неоднозначне слово це слово , яке робить набагато менше пропозиції ( lord
не сказати вам багато), а всі інші слова псевдослів , що ми не дбаємо про. Основна ідея полягає в тому, що ми переглядаємо слова в назві монстра від кінця назад до початку і вибираємо перший показник, який ми бачимо. Як таке, потрібно було забезпечити, щоб кожне ім’я монстра містило принаймні один показник, за яким повністю супроводжувались неоднозначні слова. Як виняток, слова, які відображаються в іменах монстрів, схожих на@
(найбільша група) всі класифікуються як неоднозначні. Перед індикатором може з’явитися все, що завгодно; наприклад, імена кольорів (такі як red
) завжди з’являються в імені раніше, ніж це свідчить індикатор, і тому вважаються несловами (оскільки вони ніколи не досліджуються під час визначення особи монстра).
Зрештою, ця програма сходить до хеш-таблиці, як це роблять інші програми. Однак таблиця не містить записів для всіх імен монстрів або для всіх слів, які відображаються в іменах монстрів; скоріше, він містить лише показники. Хеші неоднозначних слів не відображаються в таблиці, але повинні бути призначені порожнім слотам (спроба шукати неоднозначне слово завжди вийде порожнім). Для інших слів не має значення, відображається це слово в таблиці чи ні, чи стикається хеш чи ні, тому що ми ніколи не використовуємо значення пошуку неслова. (Таблиця є досить рідкою, тому більшість неслов'ян не відображається в таблиці, але декілька таких, як flesh
, наприклад , виявляються в таблиці як наслідок хеш-колізій.)
Ось кілька прикладів того, як працює ця частина програми:
woodchuck
- це одне слово довге (таким чином, показник), і пошук таблиці woodchuck
дає нам відповідь, яку ви бажаєте r
.
abbot
також є одним словом довго, але виглядає як @
. Як таке, abbot
вважається неоднозначним словом; пошук таблиці виходить порожнім, і ми повертаємо відповідь @
за замовчуванням.
vampire lord
складається з індикатора (vampire
відповідного V
) та неоднозначного слова ( lord
якого немає в таблиці). Це означає, що ми перевіряємо обидва слова (у зворотному порядку), а потім даємо правильну відповідь V
.
gelatinous cube
складається з неслова ( gelatinous
відповідногоH
внаслідок хеш-зіткнення) та індикатора ( cube
відповідного b
). Оскільки ми беремо лише останнє слово, знайдене в таблиці, воно повертається b
, як і очікувалося.
gnome mummy
складається з двох показників, gnome
відповідних G
іmummy
відповідних M
. Ми беремо останній показник і отримуємо M
, що ми хочемо.
Код для обробки словесної класифікації є останнім рядком програми Jelly. Ось як це працює:
ḲÇ€t0”@;Ṫ
Ḳ Split on spaces
Ç€ Call function 2 (table lookup) on each entry
t0 Remove trailing zeroes (function 2 returns 0 to mean "not found")
”@; Prepend an @ character
Ṫ Take the last result
Є два реальні випадки; якщо вхід складається повністю з неоднозначних слів, t0
видаляє весь результат пошуку таблиць і ми отримуємо an@
результат за замовчуванням; якщо на вході є індикатори, t0
видалить що-небудь праворуч від крайнього правого індикатора і Ṫ
дасть нам відповідний результат для цього показника.
Стиснення таблиці
Звичайно, розбиття введення на слова не вирішує проблему само собою; нам ще належить кодувати відповідність між показниками та відповідними класами монстрів (і відсутність відповідності у неоднозначних словах). Для цього я сконструював розріджену таблицю з 181 використаними записами (що відповідає 181 показникам; це велике поліпшення порівняно з 378 монстрами!) Та 966 загальних записів (що відповідає 966 вихідним значенням хеш-функції). Таблиця кодується в програмі за допомогою двох рядків: перша рядок визначає розміри "прогалин" у таблиці (які не містять записів); другий рядок визначає клас монстра, який відповідає кожному запису. Обидва вони представлені стисло за допомогою перетворення бази.
У програмі Jelly код для пошуку таблиці разом із самою програмою представлений у другому рядку, починаючи з першого µ
. Ось як працює ця частина програми:
“…’ḃ21+\iµØW“&;:' ”;“…’b58¤ị;0ị@
“…’ Base 250 representation of the gap sizes
ḃ21 Convert to bijective base 21
+\ Cumulative sum (converts gaps to indexes)
i Find the input in this list
µ Set as the new default for missing arguments
ØW Uppercase + lowercase alphabets (+ junk we ignore)
“&;:' ”; Prepend "&;:' "
“…’ Base 250 representation of the table entries
b58 Convert to base 58
¤ Parse the preceding two lines as a unit
i Use the table to index into the alphabets
;0 Append a zero
i@ Use {the value as of µ} to index into the table
Білективна база 21 подібна до бази 21, за винятком того, що 21 є юридичною цифрою, а 0 - ні. Це більш зручне для нас кодування, оскільки ми рахуємо два суміжні записи як пробіл у 1, так що ми можемо знайти дійсні індекси через сукупну суму. Якщо мова йде про частину таблиці, що містить значення, у нас є 58 унікальних значень, тому ми спочатку декодуємо в 58 послідовних цілих чисел, а потім знову декодуємо за допомогою таблиці пошуку, яка відображає їх у фактично використовуваних символах. (Більшість із них - це букви, тому ми починаємо цю вторинну таблицю пошуку із записів, що не належать до літер,&;:'
, а потім просто додаємо константу Jelly, яка починається з великих і малих алфавітів; вона також має деякі інші сміття, але нас це не хвилює про те, що.)
Значення вартового "індексу не знайдено" Jelly, якщо ви використовуєте його для індексації до списку, повертає останній елемент списку; таким чином, я додав нуль (цілий нуль, навіть незважаючи на те, що таблиця в основному складається з символів) до таблиці пошуку, щоб дати більш відповідний дозорний, щоб вказати пропущений запис.
Функція хешу
Інша частина програми - хеш-функція. Це починається досить просто, зOḌ
; це перетворює вхідний рядок у його ASCII-коди, а потім обчислює останній код, плюс 10 разів передостанній код, плюс 100 разів раніше, і так далі (це дуже коротке представлення в Jelly, оскільки воно частіше використовується як рядок → ціла функція перетворення). Однак, якби ми просто скоротили цей хеш безпосередньо за допомогою модульної операції, нам знадобиться досить велика таблиця. Тому замість цього я починаю з ланцюжка операцій зі зменшення таблиці. Вони працюють так: ми приймаємо п'яту потужність поточного хеш-значення; то ми зменшуємо значення модуля на постійну (яка константа залежить від того, яку операцію ми використовуємо). Цей ланцюг дає більше заощаджень (з точки зору зменшення отриманого розміру таблиці), ніж коштує (з точки зору необхідності кодування самого ланцюга операцій) двома способами: він може скласти таблицюнабагато менший (966, а не 3529 записів), і використання декількох етапів дає більше можливостей для введення вигідних зіткнень (цього не відбулося багато, але є одне таке зіткнення: Death
і Yeenoghu
хеш, і 806, тим самим дозволяючи нам видалити один запис із таблиці, як вони обидва йдуть&
). Модулі, які тут використовуються, є [3529, 2163, 1999, 1739, 1523, 1378, 1246, 1223, 1145, 966]. Між іншим, причина підвищення до п’ятої потужності полягає в тому, що якщо ви просто приймете значення безпосередньо, прогалини мають тенденцію залишатися однакового розміру, тоді як експоненціація переміщує прогалини навколо і може зробити можливим розподіл таблиці рівномірніше після ланцюг, а не застрявати в локальному мінімумі (більш рівномірно розподілені проміжки дозволяють чіткіше кодувати розміри зазору). Це має бути незвичайною силою, щоб запобігти тому, що x ² = (- x) ² вводить зіткнення, а 5 працювало краще, ніж 3.
Перший рядок програми кодує послідовність модулів, використовуючи дельта-кодування:
“…’;“…‘_\
“…’ Compressed integer list encoding, arbitrary sized integers
; Append
“…‘ Compressed integer list encoding, small integers (≤ 249)
_\ Take cumulative differences
Решта програми, початок другого рядка, реалізує хеш-функцію:
OḌ;¢*5$%¥/
O Take ASCII codepoints
Ḍ "Convert from decimal", generalized to values outside the range 0-9
;¢ Append the table of moduli from the previous line
/ Then reduce by:
*5$ raising to the power 5 (parsing this as a group)
%¥ and modulusing by the right argument (parsing this as a group, too).
Перевірка
Це сценарій Perl, який я використовував для перевірки правильності роботи програми:
use warnings;
use strict;
use utf8;
use IPC::Run qw/run/;
my %monsters = ("Aleax", "A", "Angel", "A", "Arch Priest", "@", "Archon", "A",
"Ashikaga Takauji", "@", "Asmodeus", "&", "Baalzebub", "&", "Chromatic Dragon",
"D", "Croesus", "@", "Cyclops", "H", "Dark One", "@", "Death", "&", "Demogorgon",
"&", "Dispater", "&", "Elvenking", "@", "Famine", "&", "Geryon", "&",
"Grand Master", "@", "Green-elf", "@", "Grey-elf", "@", "Hippocrates", "@",
"Ixoth", "D", "Juiblex", "&", "Keystone Kop", "K", "King Arthur", "@",
"Kop Kaptain", "K", "Kop Lieutenant", "K", "Kop Sergeant", "K", "Lord Carnarvon",
"@", "Lord Sato", "@", "Lord Surtur", "H", "Master Assassin", "@", "Master Kaen",
"@", "Master of Thieves", "@", "Medusa", "@", "Minion of Huhetotl", "&",
"Mordor orc", "o", "Nalzok", "&", "Nazgul", "W", "Neferet the Green", "@", "Norn",
"@", "Olog-hai", "T", "Oracle", "@", "Orcus", "&", "Orion", "@", "Pelias", "@",
"Pestilence", "&", "Scorpius", "s", "Shaman Karnov", "@", "Thoth Amon", "@",
"Twoflower", "@", "Uruk-hai", "o", "Vlad the Impaler", "V", "Wizard of Yendor",
"@", "Woodland-elf", "@", "Yeenoghu", "&", "abbot", "@", "acid blob", "b",
"acolyte", "@", "air elemental", "E", "aligned priest", "@", "ape", "Y",
"apprentice", "@", "arch-lich", "L", "archeologist", "@", "attendant", "@",
"baby black dragon", "D", "baby blue dragon", "D", "baby crocodile", ":",
"baby gray dragon", "D", "baby green dragon", "D", "baby long worm", "w",
"baby orange dragon", "D", "baby purple worm", "w", "baby red dragon", "D",
"baby silver dragon", "D", "baby white dragon", "D", "baby yellow dragon", "D",
"balrog", "&", "baluchitherium", "q", "barbarian", "@", "barbed devil", "&",
"barrow wight", "W", "bat", "B", "black dragon", "D", "black light", "y",
"black naga hatchling", "N", "black naga", "N", "black pudding", "P",
"black unicorn", "u", "blue dragon", "D", "blue jelly", "j", "bone devil", "&",
"brown mold", "F", "brown pudding", "P", "bugbear", "h", "captain", "@",
"carnivorous ape", "Y", "cave spider", "s", "caveman", "@", "cavewoman", "@",
"centipede", "s", "chameleon", ":", "chickatrice", "c", "chieftain", "@",
"clay golem", "'", "cobra", "S", "cockatrice", "c", "couatl", "A", "coyote", "d",
"crocodile", ":", "demilich", "L", "dingo", "d", "disenchanter", "R", "djinni",
"&", "dog", "d", "doppelganger", "@", "dust vortex", "v", "dwarf king", "h",
"dwarf lord", "h", "dwarf mummy", "M", "dwarf zombie", "Z", "dwarf", "h",
"earth elemental", "E", "electric eel", ";", "elf mummy", "M", "elf zombie", "Z",
"elf", "@", "elf-lord", "@", "energy vortex", "v", "erinys", "&", "ettin mummy",
"M", "ettin zombie", "Z", "ettin", "H", "fire ant", "a", "fire elemental", "E",
"fire giant", "H", "fire vortex", "v", "flaming sphere", "e", "flesh golem", "'",
"floating eye", "e", "fog cloud", "v", "forest centaur", "C", "fox", "d",
"freezing sphere", "e", "frost giant", "H", "gargoyle", "g", "garter snake", "S",
"gas spore", "e", "gecko", ":", "gelatinous cube", "b", "ghost", " ", "ghoul",
"Z", "giant ant", "a", "giant bat", "B", "giant beetle", "a", "giant eel", ";",
"giant mimic", "m", "giant mummy", "M", "giant rat", "r", "giant spider", "s",
"giant zombie", "Z", "giant", "H", "glass golem", "'", "glass piercer", "p",
"gnome king", "G", "gnome lord", "G", "gnome mummy", "M", "gnome zombie", "Z",
"gnome", "G", "gnomish wizard", "G", "goblin", "o", "gold golem", "'",
"golden naga hatchling", "N", "golden naga", "N", "gray dragon", "D", "gray ooze",
"P", "gray unicorn", "u", "green dragon", "D", "green mold", "F", "green slime",
"P", "gremlin", "g", "grid bug", "x", "guard", "@", "guardian naga hatchling",
"N", "guardian naga", "N", "guide", "@", "healer", "@", "hell hound pup", "d",
"hell hound", "d", "hezrou", "&", "high priest", "@", "hill giant", "H",
"hill orc", "o", "hobbit", "h", "hobgoblin", "o", "homunculus", "i",
"horned devil", "&", "horse", "u", "housecat", "f", "human mummy", "M",
"human zombie", "Z", "human", "@", "hunter", "@", "ice devil", "&", "ice troll",
"T", "ice vortex", "v", "iguana", ":", "imp", "i", "incubus", "&", "iron golem",
"'", "iron piercer", "p", "jabberwock", "J", "jackal", "d", "jaguar", "f",
"jellyfish", ";", "ki-rin", "A", "killer bee", "a", "kitten", "f", "knight", "@",
"kobold lord", "k", "kobold mummy", "M", "kobold shaman", "k", "kobold zombie",
"Z", "kobold", "k", "kraken", ";", "large cat", "f", "large dog", "d",
"large kobold", "k", "large mimic", "m", "leather golem", "'", "lemure", "i",
"leocrotta", "q", "leprechaun", "l", "lich", "L", "lichen", "F", "lieutenant",
"@", "little dog", "d", "lizard", ":", "long worm", "w", "lurker above", "t",
"lynx", "f", "mail daemon", "&", "manes", "i", "marilith", "&", "master lich",
"L", "master mind flayer", "h", "mastodon", "q", "mind flayer", "h", "minotaur",
"H", "monk", "@", "monkey", "Y", "mountain centaur", "C", "mountain nymph", "n",
"mumak", "q", "nalfeshnee", "&", "neanderthal", "@", "newt", ":", "ninja", "@",
"nurse", "@", "ochre jelly", "j", "ogre king", "O", "ogre lord", "O", "ogre", "O",
"orange dragon", "D", "orc mummy", "M", "orc shaman", "o", "orc zombie", "Z",
"orc", "o", "orc-captain", "o", "owlbear", "Y", "page", "@", "panther", "f",
"paper golem", "'", "piranha", ";", "pit fiend", "&", "pit viper", "S",
"plains centaur", "C", "pony", "u", "priest", "@", "priestess", "@", "prisoner",
"@", "purple worm", "w", "pyrolisk", "c", "python", "S", "quantum mechanic", "Q",
"quasit", "i", "queen bee", "a", "quivering blob", "b", "rabid rat", "r",
"ranger", "@", "raven", "B", "red dragon", "D", "red mold", "F",
"red naga hatchling", "N", "red naga", "N", "rock mole", "r", "rock piercer", "p",
"rock troll", "T", "rogue", "@", "rope golem", "'", "roshi", "@", "rothe", "q",
"rust monster", "R", "salamander", ":", "samurai", "@", "sandestin", "&",
"sasquatch", "Y", "scorpion", "s", "sergeant", "@", "sewer rat", "r", "shade", " ",
"shark", ";", "shocking sphere", "e", "shopkeeper", "@", "shrieker", "F",
"silver dragon", "D", "skeleton", "Z", "small mimic", "m", "snake", "S",
"soldier ant", "a", "soldier", "@", "spotted jelly", "j", "stalker", "E",
"steam vortex", "v", "stone giant", "H", "stone golem", "'", "storm giant", "H",
"straw golem", "'", "student", "@", "succubus", "&", "tengu", "i", "thug", "@",
"tiger", "f", "titan", "H", "titanothere", "q", "tourist", "@", "trapper", "t",
"troll", "T", "umber hulk", "U", "valkyrie", "@", "vampire bat", "B",
"vampire lord", "V", "vampire", "V", "violet fungus", "F", "vrock", "&", "warg",
"d", "warhorse", "u", "warrior", "@", "watch captain", "@", "watchman", "@",
"water demon", "&", "water elemental", "E", "water moccasin", "S", "water nymph",
"n", "water troll", "T", "werejackal", "d", "wererat", "r", "werewolf", "d",
"white dragon", "D", "white unicorn", "u", "winged gargoyle", "g",
"winter wolf cub", "d", "winter wolf", "d", "wizard", "@", "wolf", "d",
"wood golem", "'", "wood nymph", "n", "woodchuck", "r", "wraith", "W", "wumpus",
"q", "xan", "x", "xorn", "X", "yellow dragon", "D", "yellow light", "y",
"yellow mold", "F", "yeti", "Y", "zruty", "z");
for my $monster (sort keys %monsters) {
run ["./jelly", "fu", "monsters.j", $monster], \ "", \my $out;
print "$monster -> \"$out\" (",
($out ne $monsters{$monster} ? "in" : ""), "correct)\n";
}
mail daemon
> _ <