Jelly , 309 Bytes in Jellys Codierung
“Æ÷“¥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”@;Ṫ
Probieren Sie es online!
Ich entschied, dass es an der Zeit war, meine eigene Herausforderung anzunehmen. Die Verwendung von Jelly (und seiner 8-Bit-Codepage) bietet mir einen Vorteil von 12,5% gegenüber den Nur-ASCII-Sprachen, und Jelly ist für diese Herausforderung geeignet, da es integrierte Operatoren für die Basiskonvertierung mit kurzen Namen hat, aber die meisten Einsparungen erzielt sind auf einen besseren Komprimierungsalgorithmus zurückzuführen (dieses Programm berechnet im Durchschnitt weniger als ein Byte pro Monstertyp).
Algorithmus und Erklärung
Wortbasierte Klassifikation
Ich entschied, dass es für eine gute Punktzahl notwendig war, die Struktur der Eingabe besser auszunutzen als bei anderen Einträgen. Eine Sache , die sehr auffällig ist , dass viele Monster haben Namen der Form „ Adjektiv Spezies “; a red dragon
und a blue dragon
sind beide Drachentypen und erscheinen daher als D
. Einige andere Monster haben Namen der Form " Spezies Job ", wie die orc shaman
; Als eine Art Ork erscheint dies als o
. Komplizierende Dinge sind die Untoten; a kobold zombie
ist sowohl ein Kobold als auch ein Zombie, und der letztere Status hat Vorrang bei der Benennung von NetHack-Monstern. Daher möchten wir dies als klassifizieren Z
.
Als solches habe ich die Wörter, die in Monsternamen vorkommen, wie folgt klassifiziert: Ein Indikator ist ein Wort, das stark auf die entsprechende Monsterklasse sphere
hinweist (z. B. deutet stark darauf hin, dass das Monster in der Klasse ist e
); ein mehrdeutiges wort ist ein wort, das viel weniger suggeriert ( lord
nicht viel sagt), und alle anderen wörter sind nicht wörter , die uns egal sind. Die Grundidee ist, dass wir die Wörter im Monsternamen vom Ende bis zum Anfang von hinten betrachten und den ersten Indikator auswählen, den wir sehen. Daher musste sichergestellt werden, dass jeder Monstername mindestens einen Indikator enthielt, auf den ausschließlich mehrdeutige Wörter folgten. Ausnahmsweise Wörter, die in den Namen von Monstern vorkommen, die aussehen wie@
(die größte Gruppe) werden alle als mehrdeutig eingestuft. Vor einem Indikator kann alles stehen. Beispielsweise erscheinen Farbnamen (wie red
) in einem Namen immer früher als ein Indikator und werden daher als Nichtwörter betrachtet (da sie bei der Bestimmung der Identität eines Monsters niemals untersucht werden).
Am Ende läuft dieses Programm wie die anderen Programme auf eine Hash-Tabelle hinaus. Die Tabelle enthält jedoch nicht Einträge für alle Monsternamen oder für alle Wörter, die in Monsternamen vorkommen. vielmehr enthält es nur die Indikatoren. Die Hashes mehrdeutiger Wörter erscheinen nicht in der Tabelle, sondern müssen leeren Slots zugewiesen werden (der Versuch, ein mehrdeutiges Wort nachzuschlagen, bleibt immer leer). Bei Nichtwörtern spielt es keine Rolle, ob das Wort in der Tabelle erscheint oder nicht, oder ob der Hash kollidiert oder nicht, da wir niemals den Wert verwenden, ein Nichtwort nachzuschlagen. (Die Tabelle ist ziemlich spärlich, daher werden die meisten Nichtwörter nicht in der Tabelle angezeigt, aber einige, wie z. B. flesh
, werden als Folge von Hash-Kollisionen in der Tabelle gefunden.)
Hier einige Beispiele, wie dieser Teil des Programms funktioniert:
woodchuck
ist ein einzelnes Wort lang (also ein Indikator) und die Tabellensuche woodchuck
gibt uns die beabsichtigte Antwort r
.
abbot
ist auch ein einziges Wort lang, sieht aber aus wie ein @
. Als solches abbot
wird ein mehrdeutiges Wort betrachtet; Die Tabellensuche ist leer und wir geben @
standardmäßig eine Antwort von zurück.
vampire lord
besteht aus einem Indikator ( vampire
entspricht V
) und einem mehrdeutigen Wort ( lord
das nicht in der Tabelle enthalten ist). Das heißt, wir überprüfen beide Wörter (in umgekehrter Reihenfolge) und geben dann die richtige Antwort von V
.
gelatinous cube
besteht aus einem Nichtwort ( gelatinous
entspricht H
einer Hash-Kollision) und einem Indikator ( cube
entspricht b
). Da wir nur das letzte Wort nehmen, das in der Tabelle gefunden wurde, wird dies b
wie erwartet zurückgegeben.
gnome mummy
besteht aus zwei Indikatoren, gnome
entsprechend G
und mummy
entsprechend M
. Wir nehmen den letzten Indikator und erhalten M
, was wir wollen.
Der Code zum Behandeln der wortbasierten Klassifizierung ist die letzte Zeile des Jelly-Programms. So funktioniert das:
ḲÇ€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
Es gibt zwei reale Fälle; Wenn die Eingabe vollständig aus mehrdeutigen Wörtern besteht, wird t0
die gesamte Ausgabe der Tabellensuche gelöscht, und es @
wird standardmäßig ein Ergebnis erhalten. Wenn Indikatoren in der Eingabe vorhanden sind, t0
werden alle Elemente rechts vom Indikator ganz rechts gelöscht und Ṫ
das entsprechende Ergebnis für diesen Indikator ausgegeben.
Tabellenkomprimierung
Natürlich löst das Aufteilen der Eingabe in Wörter das Problem nicht von selbst; Wir müssen immer noch die Korrespondenz zwischen Indikatoren und den entsprechenden Monsterklassen codieren (und die fehlende Korrespondenz von mehrdeutigen Wörtern). Dazu habe ich eine spärliche Tabelle mit 181 Einträgen (entsprechend den 181 Indikatoren; dies ist eine große Verbesserung gegenüber den 378 Monstern!) Und 966 Gesamteinträgen (entsprechend den 966 Ausgabewerten der Hash-Funktion) erstellt. Die Tabelle wird im Programm mit zwei Zeichenketten codiert: Die erste Zeichenkette gibt die Größe der "Lücken" in der Tabelle an (die keine Einträge enthalten); und die zweite Zeichenfolge gibt die Monsterklasse an, die jedem Eintrag entspricht. Beides wird übersichtlich über die Basisumsetzung dargestellt.
Im Jelly-Programm wird der Code für die Tabellensuche zusammen mit dem Programm selbst von Anfang µ
an in der zweiten Zeile dargestellt . So funktioniert dieser Teil des Programms:
“…’ḃ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
Die bijektive Basis 21 ist wie die Basis 21, mit der Ausnahme, dass 21 eine gültige Ziffer ist und 0 nicht. Dies ist für uns eine bequemere Codierung, da wir zwei benachbarte Einträge mit einer Lücke von 1 zählen, sodass wir die gültigen Indizes über die kumulative Summe finden können. Wenn es um den Teil der Tabelle geht, der die Werte enthält, haben wir 58 eindeutige Werte, also dekodieren wir zuerst in 58 aufeinanderfolgende ganze Zahlen und dekodieren dann erneut mit einer Nachschlagetabelle, die diese den tatsächlich verwendeten Zeichen zuordnet. (Die meisten davon sind Buchstaben, also beginnen wir diese sekundäre Nachschlagetabelle mit den Nicht-Buchstaben-Einträgen &;:'
und hängen dann einfach eine Jelly-Konstante an, die mit den Groß- und Kleinbuchstaben beginnt. Sie enthält auch einen anderen Müll, der uns aber egal ist über das.)
Wenn Sie den Sentinel-Wert "index not found" von Jelly zum Indizieren in eine Liste verwenden, wird das letzte Element der Liste zurückgegeben. Daher habe ich der Nachschlagetabelle eine Null angehängt (eine ganzzahlige Null, auch wenn die Tabelle hauptsächlich aus Zeichen besteht), um ein passenderes Sentinel anzugeben, das auf einen fehlenden Eintrag hinweist.
Hash-Funktion
Der verbleibende Teil des Programms ist die Hash-Funktion. Das fängt einfach genug an, mitOḌ
; Dies konvertiert die Eingabezeichenfolge in ihre ASCII - Codes und berechnet dann den letzten Code plus den 10 - fachen vorletzten Code plus den 100 - fachen Code zuvor und so weiter String → Integer-Konvertierungsfunktion). Wenn wir diesen Hash jedoch einfach direkt über eine Modul-Operation reduzieren würden, bräuchten wir eine ziemlich große Tabelle. Stattdessen beginne ich mit einer Operationskette, um die Tabelle zu verkleinern. Sie arbeiten jeweils folgendermaßen: Wir nehmen die fünfte Potenz des aktuellen Hashwerts; dann reduzieren wir den Wert modulo um eine Konstante (welche Konstante von der von uns verwendeten Operation abhängt). Diese Kette bietet auf zwei Arten mehr Einsparungen (in Bezug auf die Reduzierung der resultierenden Tabellengröße) als Kosten (in Bezug auf die Notwendigkeit, die Operationskette selbst zu codieren): Sie kann die Tabelle erstellenviel kleiner (966 statt 3529 Einträge), und die Verwendung mehrerer Stufen bietet mehr Gelegenheit, nützliche Kollisionen einzuführen (dies ist nicht viel passiert, aber es gibt eine solche Kollision: sowohl Death
als auch Yeenoghu
Hash auf 806, wodurch wir eine entfernen können Eintrag vom Tisch, als sie beide gehen&
). Die hier verwendeten Module sind [3529, 2163, 1999, 1739, 1523, 1378, 1246, 1223, 1145, 966]. Im Übrigen liegt der Grund für die Erhöhung auf die fünfte Potenz darin, dass die Lücken in der Regel gleich groß bleiben, wenn Sie den Wert direkt eingeben, während die Exponentiation die Lücken verschiebt und eine gleichmäßigere Verteilung des Tisches nach dem bewirkt Kette statt in einem lokalen Minimum stecken zu bleiben (gleichmäßig verteilte Lücken ermöglichen eine engere Kodierung der Lückengrößen). Dies muss eine ungerade Potenz sein, um zu verhindern, dass x² = (- x )² Kollisionen einführt und 5 besser als 3 funktioniert.
Die erste Zeile des Programms codiert die Folge von Modulen mit Delta-Codierung:
“…’;“…‘_\
“…’ Compressed integer list encoding, arbitrary sized integers
; Append
“…‘ Compressed integer list encoding, small integers (≤ 249)
_\ Take cumulative differences
Der Rest des Programms, der Beginn der zweiten Zeile, implementiert die Hash-Funktion:
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).
Nachprüfung
Dies ist das Perl-Skript, mit dem ich überprüft habe, ob das Programm ordnungsgemäß funktioniert:
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
> _ <