Jelly , 309 bytes en la codificación de 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”@;Ṫ
Pruébalo en línea!
Decidí que era hora de intentar mi propio desafío. El uso de Jelly (y su página de códigos de 8 bits) me da una ventaja del 12.5% sobre los idiomas exclusivos de ASCII, y Jelly es conveniente para este desafío debido a que tiene operadores de conversión de base incorporados con nombres cortos, pero la mayoría de los ahorros se deben a un mejor algoritmo de compresión (este programa promedia menos de un byte por tipo de monstruo).
Algoritmo y explicación
Clasificación basada en palabras
Decidí que para obtener una buena puntuación, era necesario aprovechar más la estructura de la entrada que otras entradas. Una cosa que es muy notable es que muchos monstruos tienen nombres de la forma " especie adjetiva "; a y a son ambos tipos de dragón, y por lo tanto aparecen como . Algunos otros monstruos tienen nombres de la forma " trabajo de especies ", como el ; siendo un tipo de orco, esto aparece como . Los asuntos complicados son los no muertos; a es tanto un kobold como un zombie, y el último estado tiene prioridad en la denominación de monstruos de NetHack, por lo que nos gustaría clasificar esto como .red dragon
blue dragon
D
orc shaman
o
kobold zombie
Z
Como tal, clasifiqué las palabras que aparecen en los nombres de los monstruos de la siguiente manera: un indicador es una palabra que sugiere fuertemente la clase de monstruo apropiada (por ejemplo sphere
, sugiere fuertemente que el monstruo está en clase e
); una palabra ambigua es una palabra que hace una sugerencia mucho menor ( lord
no le dice mucho), y todas las demás palabras son palabras que no nos importan. La idea básica es que miremos las palabras en el nombre del monstruo desde el final hacia atrás hasta el comienzo, y seleccionamos el primer indicador que vemos. Como tal, era necesario asegurarse de que cada nombre de monstruo contuviera al menos un indicador, seguido completamente por palabras ambiguas. Como excepción, las palabras que aparecen en los nombres de monstruos que parecen un@
(el grupo más grande) están clasificados como ambiguos. Cualquier cosa puede aparecer antes de un indicador; por ejemplo, los nombres de color (como red
) siempre aparecen antes en un nombre que un indicador y, por lo tanto, se consideran no palabras (ya que nunca se examinan al determinar la identidad de un monstruo).
Al final, este programa se reduce a una tabla hash, como lo hacen los otros programas. Sin embargo, la tabla no contiene entradas para todos los nombres de monstruos, o para todas las palabras que aparecen en los nombres de monstruos; más bien, contiene solo los indicadores. Los hash de palabras ambiguas no aparecen en la tabla, pero deben asignarse a espacios vacíos (intentar buscar una palabra ambigua siempre aparecerá vacía). Para las no palabras, no importa si la palabra aparece en la tabla o no, o si el hash choca o no, porque nunca usamos el valor de buscar una no palabra. (La tabla es bastante escasa, por lo que la mayoría de las no palabras no aparecen en la tabla, pero algunas, como por ejemplo flesh
, se encuentran en la tabla como consecuencia de colisiones hash).
Aquí hay algunos ejemplos de cómo funciona esta parte del programa:
woodchuck
es una sola palabra larga (por lo tanto, un indicador), y la búsqueda en la tabla woodchuck
nos da la respuesta deseada r
.
abbot
También es una sola palabra larga, pero parece un @
. Como tal, abbot
se considera una palabra ambigua; la búsqueda en la tabla aparece vacía y devolvemos una respuesta de @
forma predeterminada.
vampire lord
consiste en un indicador ( vampire
correspondiente a V
) y una palabra ambigua ( lord
que no está en la tabla). Esto significa que verificamos ambas palabras (en orden inverso), luego damos la respuesta correcta de V
.
gelatinous cube
consiste en una no palabra ( gelatinous
correspondiente a H
debido a una colisión hash) y un indicador ( cube
correspondiente a b
). Como solo tomamos la última palabra que se encuentra en la tabla, esto regresa b
, como se esperaba.
gnome mummy
consta de dos indicadores, gnome
correspondientes G
y mummy
correspondientes a M
. Tomamos el último indicador y obtenemos M
, que es lo que queremos.
El código para manejar la clasificación basada en palabras es la última línea del programa Jelly. Así es como funciona:
ḲÇ€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
Hay dos casos reales; si la entrada consiste completamente en palabras ambiguas, t0
elimina la salida completa de las búsquedas de la tabla y obtenemos un @
resultado por defecto; Si hay indicadores en la entrada, t0
borrará cualquier cosa a la derecha del indicador más a la derecha, y Ṫ
nos dará el resultado correspondiente para ese indicador.
Tabla de compresión
Por supuesto, dividir la entrada en palabras no resuelve el problema por sí solo; Todavía tenemos que codificar la correspondencia entre los indicadores y las clases de monstruos correspondientes (y la falta de correspondencia de palabras ambiguas). Para hacer esto, construí una tabla dispersa con 181 entradas utilizadas (correspondientes a los 181 indicadores; ¡esto es una gran mejora sobre los 378 monstruos!), Y 966 entradas totales (correspondientes a los 966 valores de salida de la función hash). La tabla se codifica en el programa mediante el uso de dos cadenas: la primera cadena especifica los tamaños de los "espacios" en la tabla (que no contienen entradas); y la segunda cadena especifica la clase de monstruo que corresponde a cada entrada. Ambos están representados de manera concisa a través de la conversión de base.
En el programa Jelly, el código para la búsqueda de la tabla, junto con el programa en sí, se representa en la segunda línea, desde la primera en µ
adelante. Así es como funciona esta parte del programa:
“…’ḃ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
La base biyectiva 21 es como la base 21, excepto que 21 es un dígito legal y 0 no lo es. Esta es una codificación más conveniente para nosotros porque contamos dos entradas adyacentes como que tienen un espacio de 1, para que podamos encontrar los índices válidos a través de la suma acumulativa. Cuando se trata de la parte de la tabla que contiene los valores, tenemos 58 valores únicos, por lo que primero decodificamos en 58 enteros consecutivos y luego decodificamos nuevamente usando una tabla de búsqueda que los asigna a los caracteres reales que se están utilizando. (La mayoría de estas son letras, por lo que comenzamos esta tabla de búsqueda secundaria con las entradas que no son letras &;:'
, y luego agregamos una constante Jelly que comienza con los alfabetos en mayúsculas y minúsculas; también tiene otra basura pero no nos importa sobre eso.)
El valor centinela "índice no encontrado" de Jelly, si lo usa para indexar en una lista, devuelve el último elemento de la lista; por lo tanto, agregué un cero (un cero entero, aunque la tabla está hecha principalmente de caracteres) a la tabla de búsqueda para dar un centinela más apropiado para indicar una entrada faltante.
Función hash
La parte restante del programa es la función hash. Esto comienza simplemente, conOḌ
; esto convierte la cadena de entrada en sus códigos ASCII y luego calcula el último código, más 10 veces el penúltimo código, más 100 veces el código anterior, y así sucesivamente (esto tiene una representación muy corta en Jelly porque se usa más comúnmente como un cadena → función de conversión de enteros). Sin embargo, si simplemente redujimos este hash directamente a través de una operación de módulo, terminaríamos necesitando una tabla bastante grande. Entonces, en cambio, empiezo con una cadena de operaciones para reducir la tabla. Cada uno funciona así: tomamos la quinta potencia del valor hash actual; luego reducimos el valor del módulo a una constante (cuya constante depende de la operación que estemos usando). Esta cadena ofrece más ahorros (en términos de reducción del tamaño de la tabla resultante) de lo que cuesta (en términos de la necesidad de codificar la cadena de operaciones en sí), de dos maneras: puede hacer la tablamucho más pequeño (966 en lugar de 3529 entradas), y el uso de múltiples etapas brinda más oportunidades para introducir colisiones beneficiosas (esto no sucedió mucho, pero existe una colisión de este tipo: ambas Death
y Yeenoghu
hash hasta 806, lo que nos permite eliminar una. entrada de la mesa, ya que ambos van a&
) Los módulos que se usan aquí son [3529, 2163, 1999, 1739, 1523, 1378, 1246, 1223, 1145, 966]. Por cierto, la razón para elevar a la quinta potencia es que si solo toma el valor directamente, los huecos tienden a permanecer del mismo tamaño, mientras que la exponenciación mueve los huecos y puede hacer que la mesa se distribuya de manera más uniforme después del encadenar en lugar de atascarse en un mínimo local (los huecos distribuidos de manera más uniforme permiten una codificación terser de los tamaños de huecos). Esto tiene que ser un poder extraño para evitar el hecho de que x ² = (- x ) ² introduciendo colisiones, y 5 funcionó mejor que 3.
La primera línea del programa codifica la secuencia de módulos usando la codificación delta:
“…’;“…‘_\
“…’ Compressed integer list encoding, arbitrary sized integers
; Append
“…‘ Compressed integer list encoding, small integers (≤ 249)
_\ Take cumulative differences
El resto del programa, el inicio de la segunda línea, implementa la función hash:
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).
Verificación
Este es el script de Perl que utilicé para verificar que el programa funciona correctamente:
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
> _ <