Jelly , 309 octets dans l'encodage 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”@;Ṫ
Essayez-le en ligne!
J'ai décidé qu'il était temps que j'essaye de relever mon propre défi. L'utilisation de Jelly (et de sa page de codes 8 bits) me donne un avantage de 12,5% sur les langues exclusivement ASCII. Jelly est pratique pour ce défi en raison de ses opérateurs de conversion de base intégrés avec des noms abrégés, mais la plupart des économies sont dus à un meilleur algorithme de compression (ce programme a en moyenne moins d'un octet par type de monstre).
Algorithme et explication
Classification basée sur les mots
J'ai décidé que pour obtenir un bon score, il était nécessaire de tirer davantage parti de la structure de l'entrée que d'autres entrées. Une chose qui est très remarquable est que beaucoup de monstres ont des noms de la forme " espèces adjectives "; a et a sont les deux types de dragon, et apparaissent donc comme . Certains autres monstres ont des noms de la forme " travail d' espèce ", tels que le ; étant un type d'orc, cela apparaît comme . Les choses qui compliquent sont les morts-vivants; a est à la fois un kobold et un zombie, et le dernier état est prioritaire dans la dénomination de monstre NetHack, nous voudrions donc classer cela comme .red dragon
blue dragon
D
orc shaman
o
kobold zombie
Z
En tant que tel, j'ai classé les mots qui apparaissent dans les noms de monstres de la manière suivante: un indicateur est un mot qui suggère fortement la classe de monstre appropriée (par exemple, il sphere
suggère fortement que le monstre est en classe e
); un mot ambigu est un mot qui fait beaucoup moins de suggestion ( lord
ne vous en dit pas beaucoup), et tous les autres mots sont des non - mots qui ne nous intéressent pas. L'idée de base est que nous examinons les mots dans le nom du monstre de la fin au début, puis choisissons le premier indicateur que nous voyons. En tant que tel, il était nécessaire de veiller à ce que chaque nom de monstre contienne au moins un indicateur, suivi entièrement par des mots ambigus. Exceptionnellement, les mots qui apparaissent dans les noms de monstres et qui ressemblent à des@
(le groupe le plus important) sont tous classés comme ambigus. Tout peut apparaître devant un indicateur; Par exemple, les noms de couleurs (tels que red
) apparaissent toujours plus tôt dans un nom que l'indicateur et sont donc considérés comme des non-mots (car ils ne sont jamais examinés lors de la détermination de l'identité d'un monstre).
En fin de compte, ce programme se résume à une table de hachage, comme le font les autres programmes. Cependant, le tableau ne contient pas d'entrées pour tous les noms de monstres, ni pour tous les mots apparaissant dans les noms de monstres; au contraire, il ne contient que les indicateurs. Les hachages de mots ambigus n'apparaissent pas dans le tableau, mais doivent être attribués à des emplacements vides (toute recherche d'un mot ambigu sera toujours vide). Pour les non-mots, peu importe que le mot apparaisse dans la table ou non, que le hachage soit en collision ou non, car nous n'utilisons jamais la valeur de la recherche d'un non-mot. (Le tableau est relativement rare, la plupart des non-mots n'apparaissent pas dans le tableau, mais quelques-uns, tels que flesh
, figurent dans le tableau à la suite de collisions de hachage.)
Voici quelques exemples du fonctionnement de cette partie du programme:
woodchuck
est un seul mot long (donc un indicateur), et la recherche de table sur woodchuck
nous donne la réponse voulue r
.
abbot
est aussi un seul mot long, mais ressemble à un @
. En tant que tel, abbot
est considéré comme un mot ambigu; la recherche dans la table est vide et nous renvoyons une réponse @
par défaut.
vampire lord
consiste en un indicateur ( vampire
correspondant à V
) et un mot ambigu ( lord
, qui ne figure pas dans le tableau). Cela signifie que nous vérifions les deux mots (dans l'ordre inverse), puis donnons la réponse correcte de V
.
gelatinous cube
se compose d'un non-mot ( gelatinous
correspondant à H
une collision de hachage) et d'un indicateur ( cube
correspondant à b
). Comme nous ne prenons que le dernier mot trouvé dans la table, cela retourne b
, comme prévu.
gnome mummy
se compose de deux indicateurs, gnome
correspondant à G
et mummy
correspondant à M
. Nous prenons le dernier indicateur et obtenons M
ce qui est ce que nous voulons.
Le code de traitement de la classification basée sur les mots est la dernière ligne du programme Jelly. Voici comment cela fonctionne:
ḲÇ€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
Il y a deux cas réels; si l'entrée consiste entièrement en mots ambigus, t0
supprime la sortie complète des recherches dans la table et nous obtenons un @
résultat par défaut; s'il y a des indicateurs dans l'entrée, t0
supprimera tout ce qui se trouve à droite de l'indicateur le plus à droite et Ṫ
nous donnera le résultat correspondant pour cet indicateur.
Compression de la table
Bien sûr, casser l'entrée en mots ne résout pas le problème en lui-même; nous devons encore encoder la correspondance entre les indicateurs et les classes de monstres correspondantes (et le manque de correspondance de mots ambigus). Pour ce faire, j'ai construit un tableau fragmenté avec 181 entrées utilisées (correspondant aux 181 indicateurs; ceci représente une grande amélioration par rapport aux 378 monstres!) Et 966 entrées au total (correspondant aux 966 valeurs de sortie de la fonction de hachage). La table est encodée dans le programme via l’utilisation de deux chaînes: la première chaîne spécifie les tailles des "espaces" de la table (qui ne contiennent aucune entrée); et la deuxième chaîne spécifie la classe de monstres qui correspond à chaque entrée. Celles-ci sont toutes deux représentées de manière concise via une conversion de base.
Dans le programme Jelly, le code pour la recherche de table, ainsi que le programme lui-même, est représenté dans la deuxième ligne, à partir de la première µ
. Voici comment cette partie du programme fonctionne:
“…’ḃ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 bijective 21 est semblable à la base 21, sauf que 21 est un chiffre légal et que 0 ne l’est pas. C’est un codage plus pratique pour nous, car nous comptons deux entrées adjacentes comme ayant un espace de 1, de sorte que nous puissions trouver les index valides via une somme cumulative. Pour ce qui est de la partie de la table qui contient les valeurs, nous avons 58 valeurs uniques. Nous décodons d’abord en 58 entiers consécutifs, puis décodons à nouveau à l’aide d’une table de correspondance qui les met en correspondance avec les caractères réellement utilisés. (La plupart de ces lettres sont des lettres. Nous commençons donc cette table de recherche secondaire par les entrées autres que des lettres, &;:'
puis nous ajoutons simplement une constante Jelly qui commence par les alphabets majuscules et minuscules; à propos de ça.)
La valeur sentinelle de la gelée "index introuvable", si vous l'utilisez pour indexer une liste, renvoie le dernier élément de la liste; ainsi, j'ai ajouté un zéro (un entier zéro, même si la table est principalement composée de caractères) à la table de recherche pour donner une sentinelle plus appropriée pour indiquer une entrée manquante.
Fonction de hachage
La partie restante du programme est la fonction de hachage. Cela commence simplement assez, avecOḌ
; ceci convertit la chaîne d'entrée en ses codes ASCII, puis calcule le dernier code, plus 10 fois l'avant-dernier code, plus 100 fois le code précédent, etc. (la représentation est très courte dans Jelly car elle est plus couramment utilisée chaîne de caractères → fonction de conversion entière). Cependant, si nous réduisions simplement ce hachage directement via une opération de module, nous aurions besoin d'une table assez grande. Alors au lieu de cela, je commence avec une chaîne d'opérations pour réduire la table. Ils fonctionnent chacun comme ceci: nous prenons le cinquième pouvoir de la valeur de hachage actuelle; nous réduisons ensuite la valeur modulo d'une constante (laquelle dépend de l'opération que nous utilisons). Cette chaîne génère plus d'économies (en termes de réduction de la taille de la table résultante) qu'elle n'en coûte (en termes de nécessité de coder la chaîne d'opérations elle-même), de deux manières: elle peut créer la tablebeaucoup plus petit (966 entrées que 3529), et l'utilisation de plusieurs étapes donne plus de possibilités d'introduire des collisions bénéfiques (cela ne s'est pas produit beaucoup, mais il y en a une: les deux Death
et le Yeenoghu
hash jusqu'à 806, ce qui nous permet d'en supprimer une entrée de la table, car ils vont tous les deux à&
) Les modules utilisés ici sont [3529, 2163, 1999, 1739, 1523, 1378, 1246, 1223, 1145, 966]. Incidemment, la raison pour passer à la cinquième puissance est que si vous prenez simplement la valeur directement, les espaces ont tendance à rester de la même taille, alors que l’exponentiation déplace les espaces et peut permettre à la table d’être distribuée plus uniformément après la suppression. chaîne plutôt que de rester coincé dans un minimum local (des écarts plus équitablement répartis permettent un codage plus précis de la taille des écarts). Ce doit être une puissance impaire afin de prévenir le fait que x ² = (- x ) ² introduisant des collisions, et 5 mieux fonctionné que trois.
La première ligne du programme code la séquence de modules en utilisant le codage delta:
“…’;“…‘_\
“…’ Compressed integer list encoding, arbitrary sized integers
; Append
“…‘ Compressed integer list encoding, small integers (≤ 249)
_\ Take cumulative differences
Le reste du programme, le début de la deuxième ligne, implémente la fonction de hachage:
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).
Vérification
C'est le script Perl que j'ai utilisé pour vérifier que le programme fonctionne correctement:
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
> _ <