Jelly , 309 byte dalam pengkodean 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”@;Ṫ
Cobalah online!
Saya memutuskan sudah saatnya saya menghadapi tantangan saya sendiri. Penggunaan Jelly (dan codepage 8-bit-nya) memberi saya keuntungan 12,5% dibandingkan bahasa ASCII saja, dan Jelly nyaman untuk tantangan ini karena memiliki operator konversi basis bawaan dengan nama pendek, tetapi sebagian besar penghematan disebabkan oleh algoritma kompresi yang lebih baik (rata-rata program ini kurang dari satu byte per jenis monster).
Algoritma dan penjelasan
Klasifikasi berbasis kata
Saya memutuskan bahwa untuk mendapatkan skor yang baik, perlu memanfaatkan struktur input lebih banyak daripada entri lainnya. Satu hal yang sangat mencolok adalah bahwa banyak monster memiliki nama bentuk " spesies kata sifat "; a dan a keduanya jenis naga, dan dengan demikian muncul sebagai . Beberapa monster lain memiliki nama bentuk " pekerjaan spesies ", seperti ; menjadi jenis orc, ini muncul sebagai . Masalah rumit adalah mayat hidup; a adalah kobold dan zombie, dan negara bagian terakhir diutamakan dalam penamaan monster NetHack, jadi kami ingin mengklasifikasikan ini sebagai .red dragon
blue dragon
D
orc shaman
o
kobold zombie
Z
Karena itu, saya mengklasifikasikan kata-kata yang muncul dalam nama monster sebagai berikut: indikator adalah kata yang sangat menyarankan kelas monster yang sesuai (misalnya sphere
sangat menyarankan bahwa monster itu ada di kelas e
); sebuah kata ambigu adalah kata yang membuat jauh lebih sedikit dari saran ( lord
tidak cerita banyak), dan semua kata lain adalah nonwords bahwa kita tidak peduli tentang. Ide dasarnya adalah bahwa kita melihat kata-kata dalam nama monster dari ujung mundur ke awal, dan memilih indikator pertama yang kita lihat. Dengan demikian, perlu untuk memastikan bahwa setiap nama monster mengandung setidaknya satu indikator, yang diikuti sepenuhnya oleh kata-kata yang ambigu. Sebagai pengecualian, kata-kata yang muncul dalam nama-nama monster yang terlihat seperti@
(kelompok terbesar) semuanya diklasifikasikan sebagai ambigu. Apa pun dapat muncul di depan indikator; misalnya, nama warna (seperti red
) selalu muncul lebih awal dalam nama daripada indikator, dan dengan demikian dianggap bukan kata (karena mereka tidak pernah diperiksa saat menentukan identitas monster).
Pada akhirnya, program ini datang ke tabel hash, seperti program lainnya. Namun, tabel tidak mengandung entri untuk semua nama monster, atau untuk semua kata yang muncul dalam nama monster; melainkan hanya berisi indikator. Hash dari kata-kata yang ambigu tidak muncul dalam tabel, tetapi harus ditugaskan ke slot kosong (berusaha mencari kata yang ambigu akan selalu muncul kosong). Untuk yang bukan kata, tidak masalah apakah kata tersebut muncul di tabel atau tidak, atau apakah hash bertabrakan atau tidak, karena kita tidak pernah menggunakan nilai mencari kata yang bukan. (Tabel ini cukup jarang, sehingga sebagian besar non-kata tidak muncul dalam tabel, tetapi beberapa, seperti flesh
, ditemukan dalam tabel sebagai konsekuensi dari tabrakan hash.)
Berikut ini beberapa contoh cara kerja bagian dari program ini:
woodchuck
adalah satu kata panjang (dengan demikian indikator), dan pencarian tabel woodchuck
memberi kita jawaban yang diinginkan r
.
abbot
juga panjang satu kata, tetapi terlihat seperti @
. Dengan demikian, abbot
dianggap sebagai kata yang ambigu; pencarian tabel muncul kosong, dan kami mengembalikan jawaban @
secara default.
vampire lord
terdiri dari indikator ( vampire
sesuai dengan V
) dan kata yang mendua ( lord
, yang tidak ada dalam tabel). Ini berarti bahwa kami memeriksa kedua kata (dalam urutan terbalik), lalu memberikan jawaban yang benar V
.
gelatinous cube
terdiri dari kata non (( gelatinous
sesuai dengan H
akibat tabrakan hash) dan indikator ( cube
, sesuai dengan b
). Karena kami hanya mengambil kata terakhir yang ditemukan dalam tabel, ini akan kembali b
, seperti yang diharapkan.
gnome mummy
terdiri dari dua indikator, gnome
sesuai dengan G
dan mummy
sesuai dengan M
. Kami mengambil indikator terakhir, dan dapatkan M
, itulah yang kami inginkan.
Kode untuk menangani klasifikasi berbasis kata adalah baris terakhir dari program Jelly. Begini cara kerjanya:
ḲÇ€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
Ada dua kasus nyata; jika input seluruhnya terdiri dari kata-kata yang ambigu, t0
menghapus seluruh output dari tabel lookup dan kami mendapatkan @
hasil secara default; jika ada indikator dalam input, t0
akan menghapus apa pun di sebelah kanan indikator paling kanan, dan Ṫ
akan memberi kami hasil yang sesuai untuk indikator itu.
Kompresi tabel
Tentu saja, memecah input menjadi kata-kata tidak menyelesaikan masalah dengan sendirinya; kita masih harus menyandikan korespondensi antara indikator dan kelas monster yang sesuai (dan kurangnya korespondensi dari kata-kata yang ambigu). Untuk melakukan ini, saya membuat tabel sederhana dengan 181 entri yang digunakan (sesuai dengan 181 indikator; ini merupakan peningkatan besar dari 378 monster!), Dan 966 entri total (sesuai dengan nilai output 966 fungsi hash). Tabel dikodekan dalam programnya melalui penggunaan dua string: string pertama menentukan ukuran "celah" dalam tabel (yang tidak mengandung entri); dan string kedua menentukan kelas monster yang sesuai dengan setiap entri. Keduanya disajikan secara ringkas melalui konversi basis.
Dalam program Jelly, kode untuk pencarian tabel, bersama dengan program itu sendiri, diwakili di baris kedua, dari yang pertama µ
dan seterusnya. Begini cara kerja bagian dari program ini:
“…’ḃ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
Base bijective 21 seperti base 21, kecuali bahwa 21 adalah digit hukum dan 0 tidak. Ini adalah pengkodean yang lebih nyaman bagi kami karena kami menghitung dua entri yang berdekatan memiliki celah 1, sehingga kami dapat menemukan indeks yang valid melalui jumlah kumulatif. Ketika sampai pada bagian tabel yang menyimpan nilai-nilai, kita memiliki 58 nilai unik, jadi pertama-tama kita mendekode menjadi 58 bilangan bulat berturut-turut, dan kemudian mendekode lagi menggunakan tabel pencarian yang memetakan ini ke dalam karakter aktual yang digunakan. (Sebagian besar adalah huruf, jadi kami memulai tabel pencarian sekunder ini dengan entri non-huruf &;:'
,, dan kemudian hanya menambahkan konstanta Jelly yang dimulai dengan huruf besar dan kecil; ia juga memiliki beberapa sampah lain tetapi kami tidak peduli tentang itu.)
Nilai sentinel "index not found" Jelly, jika Anda menggunakannya untuk mengindeks ke dalam daftar, mengembalikan elemen terakhir dari daftar; jadi, saya menambahkan nol (bilangan bulat nol, meskipun tabel sebagian besar terdiri dari karakter) ke tabel pencarian untuk memberikan sentinel yang lebih tepat untuk menunjukkan entri yang hilang.
Fungsi hash
Bagian yang tersisa dari program adalah fungsi hash. Ini dimulai cukup sederhana, denganOḌ
; ini mengubah string input menjadi kode ASCII, dan kemudian menghitung kode terakhir, ditambah 10 kali kode kedua dari belakang, ditambah 100 kali kode sebelumnya, dan seterusnya (ini memiliki representasi yang sangat singkat di Jelly karena lebih umum digunakan sebagai string → fungsi konversi integer). Namun, jika kita hanya mengurangi hash ini secara langsung melalui operasi modulus, kita akan membutuhkan tabel yang agak besar. Jadi sebagai gantinya, saya memulai dengan rantai operasi untuk mengurangi tabel. Mereka masing-masing bekerja seperti ini: kita mengambil kekuatan kelima dari nilai hash saat ini; lalu kita mengurangi nilai modulo menjadi konstanta (konstanta mana yang bergantung pada operasi mana yang kita gunakan). Rantai ini memberikan lebih banyak penghematan (dalam hal mengurangi ukuran tabel yang dihasilkan) daripada biayanya (dalam hal perlu menyandikan rantai operasi itu sendiri), dengan dua cara: ia dapat membuat tabeljauh lebih kecil (966 daripada 3529 entri), dan penggunaan beberapa tahap memberikan lebih banyak kesempatan untuk memperkenalkan tabrakan yang bermanfaat (ini tidak banyak terjadi, tetapi ada satu tabrakan seperti itu: keduanya Death
dan Yeenoghu
hash hingga 806, sehingga memungkinkan kami untuk menghapus satu entri dari tabel, karena keduanya pergi ke&
). Moduli yang digunakan di sini adalah [3529, 2163, 1999, 1739, 1523, 1378, 1246, 1223, 1145, 966]. Kebetulan, alasan untuk menaikkan ke kekuatan kelima adalah bahwa jika Anda hanya mengambil nilai secara langsung, kesenjangan cenderung tetap dengan ukuran yang sama, sedangkan eksponensial memindahkan celah di sekitar dan dapat memungkinkan meja untuk didistribusikan lebih merata setelah rantai daripada terjebak dalam minimum lokal (kesenjangan yang lebih merata memungkinkan pengodean ukuran celah yang lebih singkat). Ini harus menjadi kekuatan aneh untuk mencegah fakta bahwa x ² = (- x ) ² memperkenalkan tabrakan, dan 5 bekerja lebih baik daripada 3.
Baris pertama program mengkodekan urutan moduli menggunakan delta encoding:
“…’;“…‘_\
“…’ Compressed integer list encoding, arbitrary sized integers
; Append
“…‘ Compressed integer list encoding, small integers (≤ 249)
_\ Take cumulative differences
Sisa program, awal baris kedua, mengimplementasikan fungsi 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).
Verifikasi
Ini adalah skrip Perl yang saya gunakan untuk memverifikasi bahwa program ini bekerja dengan benar:
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
> _ <