Giúp tôi nhận ra quái vật của tôi


35

Lý lịch

Trò chơi máy tính NetHack có từ năm 1987, trước khi việc sử dụng đồ họa trong trò chơi máy tính được thiết lập rộng rãi. Có rất nhiều quái vật trong trò chơi, và có khả năng rất cần phải xuất hiện trên màn hình cùng một lúc, vì vậy quái vật được vẽ theo một cách rất tối thiểu: một con quái vật được vẽ đơn giản như một nhân vật ASCII trên màn hình.

Ngoài việc có rất nhiều quái vật, còn có rất nhiều loại quái vật. Nó có thể quan trọng để biết đó là cái nào; bạn sẽ phải phản ứng khác nhau khi nhìn thấy một con mèo con và nhìn thấy một con rồng. Như vậy, hầu hết ASCII được sử dụng để đại diện cho quái vật; ví dụ, một con mèo con fvà một con rồng đỏ D. Điều này có nghĩa là có thể rất hữu ích để biết một con quái vật nhất định sẽ trông như thế nào, vì nó sẽ giúp bạn nhận ra nó nếu bạn gặp nó sau này trong trò chơi. (Lưu ý rằng có nhiều loại quái vật hơn các nhân vật ASCII, vì vậy một số trong số chúng chia sẻ; cả rồng đỏ và rồng xanh đều có D.)

Bài tập

Chương trình của bạn phải lấy tên của quái vật NetHack làm đầu vào và tạo ra ký tự ASCII đại diện cho nó trong trò chơi làm đầu ra. Chương trình được phép giả định rằng thực tế đầu vào là tên của quái vật NetHack; nó có thể nếu nó muốn sụp đổ, tạo ra kết quả vô nghĩa, vv nếu đầu vào không hợp lệ.

Đoạn mã sau đây là một đối tượng JSON cung cấp ánh xạ đầy đủ các đầu vào có thể vào các đầu ra tương ứng của chúng:

Về cơ bản, tác vụ ở đây là "được cung cấp một khóa trong từ điển được đại diện bởi đối tượng JSON ở trên, trả về giá trị tương ứng".

Lưu ý rằng thách thức này, theo một cách nào đó, là một ngược ; thay vì chuyển từ đầu vào nhỏ / trống sang đầu ra lớn, bạn sẽ chuyển từ đầu vào lớn sang đầu ra nhỏ. (Do đó, có rất nhiều thông tin dư thừa trong đầu vào, mà bạn có thể bỏ qua hoặc sử dụng theo ý muốn). Nó cũng khá giống với regex golf, ngoại trừ việc a) mọi ngôn ngữ đều được phép (không chỉ regex) và b) có nhiều hơn hai kết quả đầu ra có thể. (Chúng tôi đã có một vài nhiệm vụ như thế này trước đây, chẳng hạn như những hai , nhưng nhiệm vụ này là hơi khác vì hành vi đầu vào / đầu ra chỉ có mô hình mạnh hơn).

Làm rõ

Bạn có thể sử dụng bất kỳ định dạng đầu vào và đầu ra hợp lý nào (ví dụ: bạn có thể tạo đầu ra dưới dạng một ký tự hoặc dưới dạng mã ASCII của nó hoặc dưới dạng một chuỗi dài một ký tự). Bạn có thể gửi một chức năng thay vì một chương trình đầy đủ, nếu bạn thích.

Điều này đã được đề cập trong các lỗ hổng tiêu chuẩn, nhưng chỉ để nhắc lại: bạn không thể lưu trữ sự tương ứng giữa đầu vào và đầu ra bất cứ nơi nào khác ngoài mã nguồn của chương trình. Thách thức này về cơ bản là đại diện cho hành vi đầu vào / đầu ra trong không gian nhỏ nhất có thể, do đó bạn không được thực hiện những việc như tải xuống danh sách từ Internet, lưu trữ thư từ trong một tệp bên ngoài, khởi động NetHack ở chế độ gỡ lỗi và sinh ra quái vật trong câu hỏi để xem nó trông như thế nào, v.v. (Bên cạnh đó, tôi không muốn phải chiến đấu với quái vật để kiểm tra bài nộp của bạn.)

Điều kiện chiến thắng

Đây là , vì vậy mục thắng sẽ là mục ngắn nhất, được tính bằng byte. Chúc may mắn!


6
mail daemon> _ <
briantist

1
Gợi ý: có lẽ bạn cũng có thể sắp xếp danh sách các quái vật theo thứ tự theo biểu tượng ASCII mà chúng đại diện
Kritixi Lithos

2
Thở dài - đó là một trò chơi hay, đó là những ngày ...
GreenAsJade

2
@GreenAsJade vẫn là một game hay như vậy! Trên thực tế, một phiên bản mới đã được phát hành vài tháng trước sau một vài năm không hoạt động
nmjcman101

1
Một BROWNING BROWN hoang dã đã xuất hiện !!
Bạch tuộc ma thuật Urn

Câu trả lời:


11

Jelly , 309 byte trong mã hóa của 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”@;Ṫ

Hãy thử trực tuyến!

Tôi quyết định đã đến lúc tôi phải tự mình thực hiện thử thách. Việc sử dụng Jelly (và mã hóa 8 bit của nó) mang lại cho tôi lợi thế 12,5% so với các ngôn ngữ chỉ có ASCII và Jelly thuận tiện cho thử thách này do có các toán tử chuyển đổi cơ sở tích hợp với tên ngắn, nhưng phần lớn tiết kiệm là do thuật toán nén tốt hơn (chương trình này tính trung bình ít hơn một byte cho mỗi loại quái vật).

Thuật toán và giải thích

Phân loại dựa trên từ

Tôi quyết định rằng để có được điểm cao, cần phải tận dụng lợi thế của cấu trúc đầu vào hơn các mục khác. Một điều rất đáng chú ý là nhiều quái vật có tên thuộc dạng " loài tính từ "; a và a đều là hai loại rồng, và do đó xuất hiện dưới dạng . Một số quái vật khác có tên của dạng " công việc loài ", chẳng hạn như ; là một loại orc, điều này xuất hiện như . Các vấn đề phức tạp là xác sống; a vừa là kobold vừa là zombie, và trạng thái sau được ưu tiên trong việc đặt tên quái vật NetHack, do đó chúng tôi muốn phân loại nó thành .red dragonblue dragonD orc shamanokobold zombieZ

Như vậy, tôi đã phân loại các từ xuất hiện trong tên quái vật như sau: chỉ báo là một từ gợi ý mạnh mẽ đến lớp quái vật thích hợp (ví dụ spheremạnh mẽ gợi ý rằng quái vật ở trong lớp e); một từ mơ hồ là một từ mà làm cho ít của một gợi ý rất nhiều ( lordkhông cho bạn biết nhiều), và tất cả các từ khác là nonwords mà chúng ta không quan tâm. Ý tưởng cơ bản là chúng ta nhìn vào các từ trong tên quái vật từ cuối trở về đầu và chọn chỉ số đầu tiên mà chúng ta thấy. Như vậy, cần phải đảm bảo rằng mỗi tên quái vật chứa ít nhất một chỉ số, được theo dõi hoàn toàn bằng những từ mơ hồ. Như một ngoại lệ, những từ xuất hiện trong tên của quái vật trông giống như một@(nhóm lớn nhất) đều được phân loại là mơ hồ. Bất cứ điều gì có thể xuất hiện trước một chỉ số; ví dụ, tên màu (chẳng hạn như red) luôn xuất hiện sớm hơn trong tên so với chỉ báo và do đó được coi là từ khóa (vì chúng không bao giờ được kiểm tra trong khi xác định danh tính của quái vật).

Cuối cùng, chương trình này đi xuống một bảng băm, giống như các chương trình khác làm. Tuy nhiên, bảng không chứa các mục nhập cho tất cả các tên quái vật hoặc cho tất cả các từ xuất hiện trong tên quái vật; đúng hơn, nó chỉ chứa các chỉ số. Các giá trị băm của các từ mơ hồ không xuất hiện trong bảng, nhưng phải được gán cho các vị trí trống (cố gắng tìm kiếm một từ mơ hồ sẽ luôn xuất hiện trống rỗng). Đối với từ khóa, không quan trọng là từ đó có xuất hiện trong bảng hay không, hoặc liệu hàm băm có va chạm hay không, bởi vì chúng ta không bao giờ sử dụng giá trị của việc tìm kiếm một từ không. (Bảng khá thưa thớt, vì vậy hầu hết các từ khóa không xuất hiện trong bảng, nhưng một số ít, chẳng hạn như flesh, được tìm thấy trong bảng do hậu quả của va chạm băm.)

Dưới đây là một số ví dụ về cách phần này của chương trình hoạt động:

  • woodchucklà một từ dài (do đó là một chỉ báo) và việc tra cứu bảng trên woodchuckcho chúng ta câu trả lời dự định r.
  • abbotcũng là một từ dài, nhưng trông giống như một @. Như vậy, abbotđược coi là một từ mơ hồ; việc tra cứu bảng xuất hiện trống rỗng và chúng tôi trả lời câu trả lời @theo mặc định.
  • vampire lordbao gồm một chỉ báo ( vampiretương ứng với V) và một từ mơ hồ ( lord, không có trong bảng). Điều này có nghĩa là chúng tôi kiểm tra cả hai từ (theo thứ tự ngược lại), sau đó đưa ra câu trả lời đúng V.
  • gelatinous cubebao gồm một từ không ( gelatinoustương ứng với Hdo va chạm băm) và chỉ báo ( cube, tương ứng với b). Vì chúng ta chỉ lấy từ cuối cùng được tìm thấy trong bảng, điều này trả về b, như mong đợi.
  • gnome mummybao gồm hai chỉ số, gnometương ứng Gmummytương ứng với M. Chúng tôi lấy chỉ số cuối cùng, và nhận được M, đó là những gì chúng tôi muốn.

Mã để xử lý phân loại dựa trên từ là dòng cuối cùng của chương trình Jelly. Đây là cách nó hoạt động:

ḲÇ€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

Có hai trường hợp thực tế; nếu đầu vào chỉ bao gồm các từ mơ hồ, hãy t0xóa toàn bộ đầu ra của tra cứu bảng và chúng ta sẽ nhận được @kết quả theo mặc định; nếu có các chỉ báo trong đầu vào, t0sẽ xóa bất cứ thứ gì ở bên phải của chỉ báo ngoài cùng bên phải và sẽ cho chúng ta kết quả tương ứng cho chỉ báo đó.

Nén bảng

Tất nhiên, việc ngắt đầu vào thành các từ không tự giải quyết được vấn đề; chúng ta vẫn phải mã hóa sự tương ứng giữa các chỉ số và các lớp quái vật tương ứng (và thiếu sự tương ứng từ các từ mơ hồ). Để làm điều này, tôi đã xây dựng một bảng thưa thớt với 181 mục được sử dụng (tương ứng với 181 chỉ số; đây là một cải tiến lớn so với 378 quái vật!) Và tổng số 966 mục (tương ứng với 966 giá trị đầu ra của hàm băm). Bảng được mã hóa trong chương trình anh ta thông qua việc sử dụng hai chuỗi: chuỗi đầu tiên chỉ định kích thước của các "khoảng trống" trong bảng (không chứa mục nào); và chuỗi thứ hai chỉ định lớp quái vật tương ứng với mỗi mục. Cả hai đều được thể hiện một cách súc tích thông qua chuyển đổi cơ sở.

Trong chương trình Jelly, mã cho tra cứu bảng, cùng với chính chương trình, được thể hiện trong dòng thứ hai, từ dòng đầu tiên µtrở đi. Đây là cách phần này của chương trình hoạt động:

“…’ḃ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

Cơ sở sinh học 21 giống như cơ sở 21, ngoại trừ 21 là số hợp pháp và 0 không. Đây là một mã hóa thuận tiện hơn cho chúng tôi vì chúng tôi tính hai mục liền kề là có khoảng cách là 1, để chúng tôi có thể tìm thấy các chỉ mục hợp lệ thông qua tổng tích lũy. Khi nói đến phần của bảng chứa các giá trị, chúng ta có 58 giá trị duy nhất, vì vậy trước tiên chúng ta giải mã thành 58 số nguyên liên tiếp và sau đó giải mã lại bằng bảng tra cứu ánh xạ các giá trị này vào các ký tự thực tế đang được sử dụng. (Hầu hết đây là các chữ cái, vì vậy chúng tôi bắt đầu bảng tra cứu thứ cấp này với các mục không phải là chữ cái &;:' , và sau đó chỉ thêm một hằng số Jelly bắt đầu bằng các chữ cái viết hoa và chữ thường; nó cũng có một số rác khác nhưng chúng tôi không quan tâm về điều đó.)

Giá trị sentinel "không tìm thấy" của Jelly, nếu bạn sử dụng nó để lập chỉ mục vào danh sách, sẽ trả về phần tử cuối cùng của danh sách; do đó, tôi đã thêm một số không (một số nguyên bằng 0, mặc dù bảng chủ yếu được tạo thành các ký tự) vào bảng tra cứu để đưa ra một thông báo thích hợp hơn để chỉ ra một mục bị thiếu.

Hàm băm

Phần còn lại của chương trình là hàm băm. Điều này bắt đầu đơn giản là đủ, vớiOḌ; điều này chuyển đổi chuỗi đầu vào thành mã ASCII của nó và sau đó tính toán mã cuối cùng, cộng với 10 lần mã áp chót, cộng với 100 lần mã trước đó, v.v. (điều này có một đại diện rất ngắn trong Jelly vì nó được sử dụng phổ biến hơn như một chuỗi → hàm chuyển đổi số nguyên). Tuy nhiên, nếu chúng ta đơn giản giảm băm này trực tiếp thông qua thao tác mô đun, cuối cùng chúng ta sẽ cần một bảng khá lớn. Vì vậy, thay vào đó, tôi bắt đầu với một chuỗi các hoạt động để giảm bảng. Mỗi cái đều hoạt động như thế này: chúng ta lấy sức mạnh thứ năm của giá trị băm hiện tại; sau đó chúng ta giảm modulo giá trị một hằng số (hằng số phụ thuộc vào thao tác chúng ta đang sử dụng). Chuỗi này mang lại nhiều tiền tiết kiệm hơn (về việc giảm kích thước bảng kết quả) so với chi phí (về việc cần phải mã hóa chính chuỗi hoạt động), theo hai cách: nó có thể tạo bảngnhỏ hơn nhiều (966 thay vì 3529 mục) và việc sử dụng nhiều giai đoạn mang lại nhiều cơ hội hơn để giới thiệu các va chạm có lợi (điều này không xảy ra nhiều, nhưng có một xung đột như vậy: cả hai DeathYeenoghubăm đến 806, do đó cho phép chúng tôi loại bỏ một nhập từ bảng, khi cả hai đi đến&). Các mô-đun được sử dụng ở đây là [3529, 2163, 1999, 1739, 1523, 1378, 1246, 1223, 1145, 966]. Ngẫu nhiên, lý do để tăng sức mạnh thứ năm là vì nếu bạn chỉ lấy giá trị trực tiếp, các khoảng trống có xu hướng giữ nguyên kích thước, trong khi lũy thừa di chuyển các khoảng trống xung quanh và có thể khiến bảng có thể được phân phối đều hơn sau chuỗi thay vì bị mắc kẹt trong một mức tối thiểu cục bộ (các khoảng trống phân bố đồng đều hơn cho phép mã hóa các kích thước khoảng cách hơn). Đây phải là một sức mạnh kỳ lạ để ngăn chặn thực tế rằng x ² = (- x ) ² giới thiệu va chạm và 5 hoạt động tốt hơn 3.

Dòng đầu tiên của chương trình mã hóa chuỗi các mô đun bằng mã hóa delta:

“…’;“…‘_\
“…’       Compressed integer list encoding, arbitrary sized integers
   ;      Append
    “…‘   Compressed integer list encoding, small integers (≤ 249)
       _\ Take cumulative differences

Phần còn lại của chương trình, bắt đầu của dòng thứ hai, thực hiện hàm băm:

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).

xác minh

Đây là tập lệnh Perl tôi đã sử dụng để xác minh rằng chương trình hoạt động chính xác:

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";
}

10

JavaScript (ES6), 915 ... 902 890 byte

w=>[..."aZM?o@;LWu&P?D@zF@W: @aT&@nCEfvQ&R&Tb'b@&p@:Srn @ahlrdpdT'TRv:HUYG@&fSfYdG&SGHL@Mh@G@gs';@CS@km@OsirA@q@njOZS@O@';HYqHE&DJavq&&aYaBmZMf;bv@EqHg@Z@;dm@M@?@rs@d@@oDAosDT@d@ZeBVrq@jFooD@VV&&BvMEDKiuiPC@&@DYrD&eD@D@@:AwccKZiF:DKLXAwdL@w&@@u'Hc@@q&;D:::WjdN@N@xD&eFh@gh@&Md?&Ye@@&h@hNN'Z&qtKEd@@HtH&@'@&@xd&dZsv@oo@FDyd@@&&@&@HS'Hw?DF@@@MPfDfi'AH&@@pkZkuMyZhFNN'P?d@u@NN&B@uo'fdi@?ke&"].find((_,i)=>!(s-=`GD4~#_@'R<1*~7C7RbZ6F'"Sa&!*1),#''3'.+B6(K$.l%9&!#0@51""~/+!gaW!/.(5'-@0'';!%C.&""!-.$16.2>(#&g!!O,#8A50O!)*(9b|Z4@7V).;*A*HWO(g1$/*-4&SL1I#K$#"3"#=e/'V~4'B(*,.3),$@D3)*76-"\\&kL7(-4#=7!!#+(B/B!-%!"_+!")+)0$1:E84!L191&)(255)!3O<,90NN6&;Q2'"bO=*h7.%1![<o!%M'G5/R.0$-J*%\\~6T?>)16""L&!X94T4"3$!2'^070Y2a"##)#"&n&(+1*&!-M""73R5%'y0~$-6<".MV?+1*ED>!B6b!)%&)8.+$&X0~Q'E%8&#%S/H.1<#>~!sU`.charCodeAt(i)-32),w=w.replace(/\W/g,1),s=parseInt((w+=w+w)[0]+w[2]+w[3]+w[6]+[...w].pop(),36)%8713)

Định dạng

Dưới đây là một phiên bản được định dạng của mã với dữ liệu tải trọng bị cắt ngắn.

w => [..."aZM(…)"].find(
  (_, i) =>
    !(s -= `GD4(…)`.charCodeAt(i) - 32),
    w = w.replace(/\W/g, 1),
    s = parseInt((w += w + w)[0] + w[2] + w[3] + w[6] + [...w].pop(), 36) % 8713
)

Làm thế nào nó hoạt động

Bước 1

Trước tiên chúng tôi giảm tên quái vật bằng cách:

  1. Thay thế các ký tự không chữ và số (dấu cách và dấu gạch ngang) bằng 1's.
  2. Lặp lại chuỗi này 3 lần để đảm bảo rằng chúng ta có đủ ký tự để làm việc cho bước tiếp theo.
  3. Chỉ giữ các ký tự thứ 1, 3, 4, 7 và cuối cùng.

Ví dụ:

1.34..7..L
Demogorgon -> Dmorn
^ ^^  ^  ^

             1.34..7.L
orc mummy -> orc1mummy -> oc1my
             ^ ^^  ^ ^

        1.34..7....L
xorn -> xornxornxorn -> xrnrn
        ^ ^^  ^    ^

Điều này dẫn đến một vài va chạm. Ví dụ, "Master Assassin""Master Kaen"cả hai đều giảm xuống "Mst1n". May mắn thay, tất cả các tên quái vật va chạm đều có chung một biểu tượng ( @trong trường hợp này).

Bước 2

Sau đó, chúng tôi giải thích chuỗi 5 ký tự này như một đại lượng 36 cơ sở để chuyển đổi nó thành số thập phân (thao tác này không phân biệt chữ hoa chữ thường) và chúng tôi áp dụng một modulo 8713, được chọn theo kinh nghiệm để tạo ra một danh sách các khóa không va chạm.

Ví dụ:

Dmorn --[from base 36]--> 22893539 --[MOD 8713]--> 4488
oc1my --[from base 36]--> 40872778 --[MOD 8713]--> 95
xrnrn --[from base 36]--> 56717843 --[MOD 8713]--> 4926

Bước 3

Tất cả các khóa được sắp xếp theo thứ tự tăng dần:

[ 39, 75, 95, 192, 255, 287, 294, 344, 372, 389, 399, 516, 551, 574, 624, ..., 8635, 8688 ]

Chuyển đổi thành giá trị delta:

[ 39, 36, 20, 97, 63, 32, 7, 50, 28, 17, 10, 117, 35, 23, 50, ..., 83, 53 ]

Và được mã hóa dưới dạng các ký tự ASCII trong phạm vi [ 32, 126 ]. Một số giá trị giả trung gian được chèn khi chênh lệch giữa hai khóa liên tiếp vượt quá cường độ mã hóa tối đa.

Cuối cùng, danh sách các khóa được ánh xạ tới một danh sách các ký hiệu được sắp xếp theo cùng một thứ tự.

Kiểm tra


Theo testsuite của riêng bạn, điều này phân loại sai 5 mục. Tôi đã không điều tra để xem những gì gây ra chúng, nhưng điều đó có thể cần sửa chữa.

@ ais523 Đó là những gì tôi nhận được khi chỉ thử nghiệm trên Firefox. Tôi sẽ cố gắng khắc phục điều đó cho (ít nhất) Chrome.
Arnauld

2
@ ais523 Điều đó giờ sẽ hoạt động tốt trên FF, Chrome & Edge. Xin lỗi vì điều đó.
Arnauld

Tôi đã có ý tưởng trích xuất các bit từ các tên được chuyển đổi thành số, nhưng tôi rõ ràng là suy nghĩ quá nhỏ. Chúc mừng!
Jonas Schäfer

8

Java, 1130 byte

import java.util.*;class G{public static void main(String[]u){BitSet s=BitSet.valueOf(Base64.getDecoder().decode("D94FuoCWYEIhCTEgLWwRNU/CMB1cE7XBhxBsBCusihaASRg14IJpQMOIDJdFx3BOdDcmThdhILVkCgGsEmhII8UE+SB4kDYEEJzw7Tw54oUEQZe0AUHCACH6nAdqgiZgJhASCIPAEAzJBmuMIrBCHE8IiFjgKQwrN4/90B4QFaLBQBEwTArRBMLCLHQOUQs7ZXZ8B8uGC1EbeAMJBdihUDgCIwGUEKgEAu4W2SJkIAhzB1IQSHgNiEAwABQECV5BvAB7eizABXxFLEg5iMA3whhAFXOKHXEURB7UA7PQjgUK7sji8CmIC0FJsTB4tAMFgiARB3hOJATDsBkgGKnGmWIiIWBRwkMgToQJ49G8gTR4IqcB4vJwDBHSVBLQhpwHsUFipqBcWWaEsCBoGBF0AlNAE305HAfdU1AEbELBO0EERAfkmMkgZcEXDIa4MAp4HcENmYAMBB7UBbTwBqQPSMS9kVkEBMhCudAqBAKaR1CzZggDRw8WMAh0FQPEyKAsRAxzBwn0grwDMQMyQMdADRtFUBAsBQetRRBwcUgrlsQ1IkosBc9B6iBcjAkSDDKgEAQ1wgLIMEEwMkYB42ERBCdiEJMAt1wYSIAQkdIEI0UPNhALsDnRQ1AT/HQi1AyCEwiICOICpiAPlB8MwxnBPIk6JYaIgDy8NJHDsiAqzK0JAXpQPXgPLwJuEEbMTAGBYlQbDESvAXJAAQ=="));int i,j,k,c,d,h=u[0].hashCode(),a=(h&4092)>>2|(h>>5)&1024|(h>>7)&2048|(h>>9)&4096;char r='@';for(i=k=0;i<4297;i+=14){for(c=0,j=7;j>=0;j--)c+=c+(s.get(i+j)?1:0);if((k+=c)==a){for(d=0,j=13;j>=8;j--)d+=d+(s.get(i+j)?1:0);r=d<5?" &':;".charAt(d):(char)((d<31?60:66)+d);}}System.out.println(r);}}

Ung dung:

import java.util.*;

class G {
    public static void main(String[] u) {
        BitSet s = BitSet.valueOf(Base64.getDecoder().decode(
                "D94FuoCWYEIhCTEgLWwRNU/CMB1cE7XBhxBsBCusihaASRg14IJpQMOIDJdFx3BOdDcmThdhILVkCgGsEmhII8UE+SB4kDYEEJzw7Tw54oUEQZe0AUHCACH6nAdqgiZgJhASCIPAEAzJBmuMIrBCHE8IiFjgKQwrN4/90B4QFaLBQBEwTArRBMLCLHQOUQs7ZXZ8B8uGC1EbeAMJBdihUDgCIwGUEKgEAu4W2SJkIAhzB1IQSHgNiEAwABQECV5BvAB7eizABXxFLEg5iMA3whhAFXOKHXEURB7UA7PQjgUK7sji8CmIC0FJsTB4tAMFgiARB3hOJATDsBkgGKnGmWIiIWBRwkMgToQJ49G8gTR4IqcB4vJwDBHSVBLQhpwHsUFipqBcWWaEsCBoGBF0AlNAE305HAfdU1AEbELBO0EERAfkmMkgZcEXDIa4MAp4HcENmYAMBB7UBbTwBqQPSMS9kVkEBMhCudAqBAKaR1CzZggDRw8WMAh0FQPEyKAsRAxzBwn0grwDMQMyQMdADRtFUBAsBQetRRBwcUgrlsQ1IkosBc9B6iBcjAkSDDKgEAQ1wgLIMEEwMkYB42ERBCdiEJMAt1wYSIAQkdIEI0UPNhALsDnRQ1AT/HQi1AyCEwiICOICpiAPlB8MwxnBPIk6JYaIgDy8NJHDsiAqzK0JAXpQPXgPLwJuEEbMTAGBYlQbDESvAXJAAQ=="));

        int i, j, k, c, d, h = u[0].hashCode(), 
            a = (h & 4092) >> 2 | (h >> 5) & 1024 | (h >> 7) & 2048 | (h >> 9) & 4096;
        char r = '@';
        for (i = 0, k = 0; i < 4297; i += 14) {
            for (c = 0, j = 7; j >= 0; j--)
                c += c + (s.get(i + j) ? 1 : 0);
            if ((k += c) == a) {
                for (d = 0, j = 13; j >= 8; j--)
                    d += d + (s.get(i + j) ? 1 : 0);
                r = d < 5 ? " &':;".charAt(d) : (char) ((d < 31 ? 60 : 66) + d);
            }
        }
        System.out.println(r);
    }
}

Tên quái vật là:

  • băm bằng hashcodephương thức Java => 32 bit
  • ANDed với mặt nạ 1001001000111111111100 => 13 bit
  • sắp xếp từ nhỏ nhất đến lớn nhất
  • sau đó chúng tôi sử dụng các giá trị delta của danh sách được sắp xếp => 8 bit

Ký tự hiển thị được mã hóa trên 6 bit.

Vì vậy, mỗi bộ (tên quái vật, ký tự) sử dụng 14 bit. Tất cả các bộ dữ liệu được lưu trong một Bitset và mã hóa cơ sở 64.

Tôi mất rất nhiều byte với mã hóa Base64 và các hoạt động BitSet :-)


Bạn có thể giảm kích thước bằng cách làm cho biểu thức lambda : ()->{...}. Câu hỏi nói như vậy trong phần "làm rõ" của nó.
Olivier Grégoire

5

Mathicala, 1067 byte (mã hóa ký tự Mac OS Roman)

FromCharacterCode[Mod[Tr/@{c=ToCharacterCode@#,c^2},216,32],"MacintoshRoman"]/.Inner[Rule,";¤7«´3πœ(ú-UU=6z±'¥ÿ†tƒ|\¢KÛd§≤jSóKWÊ8‰Ñwiøì¡ÛhÓ\‡¨:–*~‚¬æº¢»‘¤Á^∫„·nLÒ¤b|$|ÇòCóÌÈS_Ñä.Ëí 5y«KΔË\Ãò™_E`J’ëΔñTV–N„'„Ÿà¥xpîH#-PP)ÈÊVQ©LrBt}∑WÉ∏dÿå„•Tz∑Âao¿rÃ^bbP¨}ëÖ◇1èÇ&d¢¤ái√,B}±BˆÍdA´íBtæÅ/m√yQ6,uãÊ≤/Î!ïøuΩÒÉ)ë“∕C$RY•ÍÍu£oÉÓå‚Ïl.·1‚40ÃÚ¨ÇÆÅccflÓ8Ï Gáç3EÑ¥fXñ¨Àìz~j÷–ñÓz0~ôWtñ}μÎ◇f||Dd\ ÙH﷿É∑Ì´|¿Ö_»RT8Ûª|Äqü‘&6Ãác›Yˆ¿ô5≈ënÚqΩåVä>∫æ∂p ¨jtöåoÌfløÏÏò§¤flÈ;À∑Ѥ·›9né∕<·ì∕ÿmŸ«Ì»j√üà‰÷“5ïä^Ûe◇kd‡“(Ïö71›iΟÁm„ÈïÒß„kÕπ°ÊÓÒçÓfˆ¨flÁ9k|¶ä∕l~Òød‹jZÏ2[kÎ√3ÛâìÓΔE]ıIÚ>{#ÁÖ‚Üâ;·?l^vàß‹‘jîÙÇÅÉú¥äärÆæ™∏Üi≈mØÂ’-%USÌâ’ı Ê›·Ëÿb‡ıÖ31nh™Δ$~%À0n-À´sflk∑p.o5vz}mè]ÎÅç©lt;Îu„ŸW„›ˆˆÍ﷿Ä*7m8‰πór,„Õш/”Ë∕ªß9±‡¶çÁ•âg˜fló)ÖÔ¡'wúæ0ñ„Kûr"~(a=StringPartition)~2,"AAA&&DH&&&&&D&KKKKH&o&WT&&soV&bEYLDD:DDwDwDDDD&q&WBDyNNPuDj&FPhYss:c'ScAd:LdR&dvhhMZhE;MZv&MZHaEHve'evCdeHgSe:b ZaBa;mMrsZH'pGGMZGGo'NNDPuDFPgxNNdd&Hohoi&ufMZ&Tv:i&'pJdf;AafkMkZk;fdkm'iqlLFd:wtf&i&LhqhHYCnq&:jOODMoZooYf';&SCuwcSQiabrBDFNNrpT'qR:&Ysr eFDZmSajEvH'H'&ifHqtTUBVVF&du&ESnTdrdDugddd'nrWqXDyFYz"~a~1,List]/.x_/;StringLength@x>1->"@"&

Hàm không tên lấy một chuỗi làm đầu vào và trả về một ký tự. Hàm có dạng sau:

1  FromCharacterCode[
2    Mod[Tr/@{c=ToCharacterCode@#,c^2},216,32]
3    ,"MacintoshRoman"] /.
4  Inner[Rule,
5    GIANT_STRING_1 ~(a=StringPartition)~2,
6    GIANT_STRING_2 ~a~1,
7    List]
8  /. x_/;StringLength@x>1 -> "@" &

Ở đây GIANT_STRING_1 là một chuỗi chứa 608 ký tự một byte trong mã hóa ký tự Mac OS Roman (không có ký tự nào trong phạm vi 00-1F), trong khi GIANT_STRING_2 là một chuỗi chứa 304 ký tự ASCII.

Dòng 2 khởi động hàm băm: nó chuyển đổi chuỗi đầu vào thành một danh sách mã ký tự (mã hóa không liên quan vì chúng đều có thể in ASCII), sau đó tính tổng các mã ký tự đó và tổng bình phương của chúng, cả modulo 216 và buộc câu trả lời nằm giữa 32 và 255. Sau đó, dòng 1 và 3 chuyển đổi các cặp số nguyên theo thứ tự đó thành chuỗi hai ký tự, là giá trị băm mà cuối cùng chúng ta sử dụng.

Dòng 5 biến GIANT_STRING_1 thành một danh sách gồm 304 chuỗi hai ký tự; dòng 6 biến GIANT_STRING_2 thành một danh sách gồm 304 chuỗi một ký tự. Sau đó, dòng 4 và 5 chuyển đổi hai danh sách đó thành một bộ gồm 304 quy tắc thay thế: nếu bạn thấy chuỗi hai ký tự tương tự và như vậy, hãy biến nó thành chuỗi một ký tự như vậy. Cuối cùng, dòng 8 biến bất kỳ chuỗi hai ký tự còn lại thành "@".

Có 71 quái vật trong danh sách có biểu tượng "@"và chúng được xử lý mà không cần băm (Tôi đã đánh cắp ý tưởng này từ một nhận xét của ais523 trong một câu trả lời khác). Nó chỉ xảy ra rằng các giá trị băm 304 khác là duy nhất! và vì vậy không cần sửa đổi nào khác cho thuật toán. (Đó là một sự phá vỡ may mắn "human"cần được ánh xạ tới "@", vì tổng các mã ký tự của các chữ cái trong"human" và các chữ cái "shark"giống hệt nhau, cũng như các tổng bình phương của các mã đó là số nguyên, thậm chí không phải là modulo 216!)


3

Python, 2055 byte

def f(s):import re;m=re.search(s[-3:-1]+s[:2]+str(len(s))+"(.)","omgn5Gteen13vligr7glero10'akga12Sanso11aragi9rgoDe10&tiet5HleVl16Vanst11Hmpmo14nubge15brsho5uingo21Nbuin7&gobl11Dgobl12Duaja6faule10lkest7Eolic9Ttawa15EanKo14KanKo12Kviba12&gore10Dimim3iutzr5zingn10Ganhi10Herfr15emmel9Mlele13'guNa6Wkaja6dotco6docvr5&petr7tmaor10oarli6:nhpi7;moma11&icli4Linbl20Nolwa11Titwr6Wtlgi12ateru12Rbign12Zozgr9Plepa11'oufo9vingu23Norhi8onena10&tati5Hiosc8sinre18Nligo6obeki10aeaow7Yeyfl12elewo10'adsh5 anfr11Hapap3Ygrog4Obequ9ahopy6Steki6fgogr11Dgogr12Dpepi9Sngdi5dindw10hlegl11'imgr11Pbava11Bcero12phaOl8Tdoli10dcuwi15dargn14GotIx5Dinbl13Parwa4dpuhe14dtisa9&ilba14:liho9onyer6&euAs8&aupl14Cttle9qmmdw11Molbr10Fmism11mncPe10&mpwa11noror3oispy8caumo16Clest11'haUr8okekr6;bigi12ZbuBa9&gowh12Dbiko13Zbiet12Zmmgn11Molwe8dmowa11&icde8Lbiho6hdola9dleJu7&otMi18&ulum10Uenpi9&luho10ighye12ymamu5qorwh13ughbl11yylga8gKoKe12Knndj6&mmet11Magbl10Narsh5;osgh5 orxo4Xoltr5Tdoma8qopCy7Hceir12pgoba18Dorlo9wgoba16Dbidw12ZinFa6&goor13DeaAl5Aiuba14qloac9bkemo6Yniqu16QteDi8&aufo14Ckesh8Fetye4Yolro9ryema18hersh15eaggo11Nrase9ranig6:ghba12Winbr13Polwi11dgeti5fzoNa6&orga9emmko12Manfi8aorgn10Gatco6Alecl10'goye13Deabu7hinog9Oheli6Feoch9:ynly4fngte5ieeel12;rawe7ricch11caior11ocala9fguvi13Fangi9aangi5Hhepa7fdesa10:cuOr5&rswa8ubrco5Sorva12Vxaxa3xovlu12tbaba3Bilcr9:geAn5Aolwo4dviic9&tafi14Ecegl13pbugr8xorpu11wgoCh16Dicar9Laggu13Ndegi12shoAr6Aolla12kedce9sitma8&erti11qicma11Lbior10Zviho12&test12vbusu8&fofo3ddeca11srara9rolko6kmpwo10ntaea15Ellbl10jgosi13Daksn5Svibo10&tosk8Zicco10cvera5Bgoba15DatDe5&goba17Dpuwu6qkawe10dmmhu11Mdodo3dunhe10dtcsa9Yckge5:tefi11vsiqu6iloqu14bewne4:yoGe6&caho8fucwo9rorMo10oisje9;taai13Eardw5holye11Fordw10hlloc11jough5Zerfl14emila11mtedu11vthro5qteic10vtuLo11Hmmor9Mirva7Vbagi9Bolro10Tmako13kleir10'biel10Zmmgi11Mnema5ilego10'olre8Forbl13usiwa14Sroba6&agre8Nrohe6&orgr12ulefl11'ocja10JghYe8&aumi8HiuSc8sbihu12Zriki6Ayemi11horko11kolgr10Furle6ianfi10Hmigi11monpo4ullsp13jaiKo11Ktedi12Rapca15Yorog9Oylwi15geegi9;orba14worba16w");return m.group(1)if m else'@'

Đây là khai thác thử nghiệm của tôi, trong trường hợp nó giúp bất cứ ai khác.

MAPPING = {
    # as given in the original question
}
def validate_solution(f):
    for k, v in MAPPING.iteritems():
        vv = f(k)
        if vv != v:
            print 'FAIL: f(%s) = %s instead of %s' % (k, vv, v)
    print 'SUCCESS!'

Tôi đã viết một chương trình nhỏ để liệt kê tất cả các cách trích xuất 4 ký tự cộng với độ dài của chuỗi. Kế hoạch ban đầu của tôi là ord()các nhân vật này, thực hiện một số phép toán cho họ và đưa nó xuống một hàm băm hoàn hảo tạo ra các chỉ số thành một bảng kết quả đầu ra. Vì vậy, tôi đã viết một chương trình nhỏ khác để liệt kê tất cả các cách khác nhau để tổng hợp / nhân / điều chỉnh 4 ký tự này với nhau; nhưng các chức năng băm kết quả liên tục gặp cách quá nhiều va chạm. Vì vậy, cuối cùng tôi đã từ bỏ và chỉ làm những gì bạn thấy ở đây, đó chỉ là một bản đồ từ biểu diễn chuỗi nhỏ của tên của mỗi quái vật đến biểu tượng thích hợp.

Đó là: Điều tôi muốn nhận là

def f(monster_name):
    small_string = f1(monster_name)
    integer_index = f2(small_string)
    symbol = "relatively_short_table"[integer_index]
    return symbol

nhưng tôi chỉ cố gắng đi xa

def f(monster_name):
    small_string = f1(monster_name)
    symbol = {relatively_large_dict}[small_string]
    return symbol

trong đó tra cứu dict của tôi {relatively_large_dict}[small_string]được thể hiện như re.match(small_string+"(.)", "relatively_large_string")đối với golf.


2

JavaScript (ES6), 1178

n=>'@0uy1c8@@@@@@2cb7sj0sb5rhcm626435y6js6u651b1nj5jg85g2xj02l4wh31u2py2xl96h5fz6ys46tc7821p2e9c1o1td1cy834@2sq2c055iabn91f82vahc6ytagh5d363i@@@@@@@@@@@@@@@@@@@7hh2wf2nd1bu2d93cm0gu862@144819a6v2h44o41d4@@@@@@0c404806f3fa0z8@04c82o1vfac3@c10a3g08g@82e0lr7bf26p2dibcb11t9y19q6bbh4db7tr3592u2bof4913edawy84p1cr@bap1qzb1o033bt6@8d93v230t4240w9ahh8cy@09u0a60sd1qd@1n23ak1bt614bax0ro7sd57xagg22s1gj@@be0@74l01c28qcdi@1so83t0c068s@2jh7as7ddalq0vxag68pn6b9@0gabu71zp54m6997imb2047h@10s0zo0mv@aww6ixbqgag7@944@bza76b@1a053c2yn6101eh8en@4je6fq97t1py9f0@6co@b3k5my44p@4edb737t9@0tl@00rau75y369z5hk0ot@23d2wicb90uwb54a9l3gw9lv3z51nv@@@@@@@amy81e3kh9yc90e59d@6528z42ic@7uv6bm58t@3av0w004t05aavs3oq3040irawj0ov1n90213h89yn0vs@0mcc284fv6uyaxp@3242ok39h0jd06905v1ia@7zc9659bk@ax30ua0um0652sa65daqd@00z03d2ra1f95751xu@9x10676yz@72w33r24b63d@2d7@ats6f678u@bcg9uf6h6@1b60us2d17ygbxn72106t02g@adublf05q@8xu5wobqb1tc1c73cs7pj@87k3cj2xq6258l379y@0q42qy3vs3y70r9@06v2a9@ast4su12w0ko4y77dn@7oubr07ju1ct5qe81v@0d52kb66t4zj@93508c@af30kj@299'.replace(/@\w*/g,v=>~-v.search((100+h.toString(36)).slice(-3))%3?++i:r=String.fromCharCode(i),i=32,r='@',n.replace(/\w/g,c=>h=parseInt(c,36)^(h*3)&16383,h=0))&&r

Ít chơi gôn

n=>(
'@0uy1c8@@@@@@2cb7sj0sb5rhcm626435y6js6u651b1nj5jg85g2xj02l4wh31u2py2xl96h5fz6ys46tc7821p2e9c1o1td1cy834@2sq2c055iabn91f82vahc6ytagh5d363i@@@@@@@@@@@@@@@@@@@7hh2wf2nd1bu2d93cm0gu862@144819a6v2h44o41d4@@@@@@0c404806f3fa0z8@04c82o1vfac3@c10a3g08g@82e0lr7bf26p2dibcb11t9y19q6bbh4db7tr3592u2bof4913edawy84p1cr@bap1qzb1o033bt6@8d93v230t4240w9ahh8cy@09u0a60sd1qd@1n23ak1bt614bax0ro7sd57xagg22s1gj@@be0@74l01c28qcdi@1so83t0c068s@2jh7as7ddalq0vxag68pn6b9@0gabu71zp54m6997imb2047h@10s0zo0mv@aww6ixbqgag7@944@bza76b@1a053c2yn6101eh8en@4je6fq97t1py9f0@6co@b3k5my44p@4edb737t9@0tl@00rau75y369z5hk0ot@23d2wicb90uwb54a9l3gw9lv3z51nv@@@@@@@amy81e3kh9yc90e59d@6528z42ic@7uv6bm58t@3av0w004t05aavs3oq3040irawj0ov1n90213h89yn0vs@0mcc284fv6uyaxp@3242ok39h0jd06905v1ia@7zc9659bk@ax30ua0um0652sa65daqd@00z03d2ra1f95751xu@9x10676yz@72w33r24b63d@2d7@ats6f678u@bcg9uf6h6@1b60us2d17ygbxn72106t02g@adublf05q@8xu5wobqb1tc1c73cs7pj@87k3cj2xq6258l379y@0q42qy3vs3y70r9@06v2a9@ast4su12w0ko4y77dn@7oubr07ju1ct5qe81v@0d52kb66t4zj@93508c@af30kj@299'
.replace(/@\w*/g, v= > 
   (v.search((100 + h.toString(36)).slice(-3))-1) % 3  
     ? ++i : r = String.fromCharCode(i),
   i=32,
   r='@',
   n.replace(/\w/g,c => h=parseInt(c,36) ^ (h*3) & 16383,h=0)
)
&& r

Kiểm tra

F=
n=>'@0uy1c8@@@@@@2cb7sj0sb5rhcm626435y6js6u651b1nj5jg85g2xj02l4wh31u2py2xl96h5fz6ys46tc7821p2e9c1o1td1cy834@2sq2c055iabn91f82vahc6ytagh5d363i@@@@@@@@@@@@@@@@@@@7hh2wf2nd1bu2d93cm0gu862@144819a6v2h44o41d4@@@@@@0c404806f3fa0z8@04c82o1vfac3@c10a3g08g@82e0lr7bf26p2dibcb11t9y19q6bbh4db7tr3592u2bof4913edawy84p1cr@bap1qzb1o033bt6@8d93v230t4240w9ahh8cy@09u0a60sd1qd@1n23ak1bt614bax0ro7sd57xagg22s1gj@@be0@74l01c28qcdi@1so83t0c068s@2jh7as7ddalq0vxag68pn6b9@0gabu71zp54m6997imb2047h@10s0zo0mv@aww6ixbqgag7@944@bza76b@1a053c2yn6101eh8en@4je6fq97t1py9f0@6co@b3k5my44p@4edb737t9@0tl@00rau75y369z5hk0ot@23d2wicb90uwb54a9l3gw9lv3z51nv@@@@@@@amy81e3kh9yc90e59d@6528z42ic@7uv6bm58t@3av0w004t05aavs3oq3040irawj0ov1n90213h89yn0vs@0mcc284fv6uyaxp@3242ok39h0jd06905v1ia@7zc9659bk@ax30ua0um0652sa65daqd@00z03d2ra1f95751xu@9x10676yz@72w33r24b63d@2d7@ats6f678u@bcg9uf6h6@1b60us2d17ygbxn72106t02g@adublf05q@8xu5wobqb1tc1c73cs7pj@87k3cj2xq6258l379y@0q42qy3vs3y70r9@06v2a9@ast4su12w0ko4y77dn@7oubr07ju1ct5qe81v@0d52kb66t4zj@93508c@af30kj@299'.replace(/@\w*/g,v=>~-v.search((100+h.toString(36)).slice(-3))%3?++i:r=String.fromCharCode(i),i=32,r='@',n.replace(/\w/g,c=>h=parseInt(c,36)^(h*3)&16383,h=0))&&r


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"
}
err = ok = 0

for(name in monsters) {
  code = monsters[name]
  result = F(name)
  if (result != code)
    console.log('ERROR',++err, name, result, code)
  else
    ++ok
}
console.log('Errors',err,'OK', ok)


2

Javascript, 1185 byte

s=>{h=0;for(i of s)h=(h<<5)-h+i.charCodeAt()|0;for(v of "Aqgh201etxitsxy0_&ctpzfekt09j36uafamqw46mz1qcxvnnoego4212nxfivutt09qyac4td1ayiotfh3dvub5fggzjqa58h37bnva3dzy_D9wlywkgkifldlp6t46v97basg905f8wadwt0w49q0gk9c8edz9e33uj6esjl_Hkkt54kr0qdlxs6hxdxxyegrdzcmz8ymvg_Ki0enu0ct1shv_o193ve2y3tpa71xu3pud405o7_We09jfsayx_Tw2gk0spoqab5c9k_s7timco3yh674rp1_Vppq2k9t1q_b3mo3tac13_E0r50a7vi5a0kgim_Y2omnjbkq59mw5njf5t_Lu9z2bj6w2128_:n0gngsocqeuh5czhyiedwd3a_w9lf1hv1rra7r_qmckg7rbhlldbvros4f44h_B32t12yzdci83_yjkb3va_Nt2cbaqd46toc29anic1qq3es_P3mkmtv2l4j8r_ukjb44lwm5vkaz5hwkh_j3oo7uj9ip_Fzuk8mh1rpfw7obl6s9fsq_hzmwz3f7kdhiaj4enlxha1_c0q0yu8tnf_'nf7c1sks8rzgxhw83vjq0s76xhrvppbgn_Slr90h5su3zokncwi2m_doi5t2p4vw6dryycyhtl6eujb1ta26752ta7hr19d9vceq_Rqk8tsy_vuxwglitt4u25zfhj5q_M4j7tjk9cryvqn8101u5h646p_Ztzwr09t8ckxx3hbsl6r7dqv7qxmnwt_;u7r3e9trqqkmdj5tlx_apoj0ngpcqy6r7t8gw9_e2wtyw9oyve8uxlf_C8tpo3hlb3_gxji2n2nl4_ kwft9p_maxcdzat5e_rcy28c360mmndp8ksxh_pegqkkuur3_Gh6f8pheo0nn2_xu6yjdx_iz538jwkbwuh4ge7ymj_f3eytt6khltgxj13itedbz_Jlgk_knskybpe8n69a_llnv_tuxgkxc_nod5ob3cft_Oij0a222q3_Q6af_Uc5x_Xzjn_z6iq".split`_`)if(~v.slice(1).match(/.../g).indexOf(h.toString(36).slice(-3)))return v[0];return"@"}

Sử dụng một phiên bản golf của hàm băm chuỗi Javascript được tìm thấy ở đây. Hàm băm thực tế được lưu trữ trong bảng (chuỗi dài đó) lấy giá trị tuyệt đối của hàm băm được tạo bởi phương thức đó, chuyển đổi nó thành cơ sở-36 và bỏ tất cả trừ ba chữ số có nghĩa nhỏ nhất.


1
Vì vậy, theo như tôi có thể nói, bạn đang băm theo {ký tự đầu tiên, ký tự cuối cùng, độ dài}, sau đó sử dụng bảng tra cứu lớn? Điều đó có ý nghĩa, nhưng tôi nghĩ có một số cải tiến bạn có thể thực hiện: có một số mục trùng lặp trong bảng và bạn có thể tiết kiệm khá nhiều không gian bằng cách xóa các mục nhập @khỏi bảng và chỉ mặc định @nếu đầu vào không tìm thấy.

2
cavewomanchameleoncó cùng char đầu tiên, char cuối cùng và độ dài, đó có thể là một vấn đề?
Arnaud

1
split("_")có thể trở thành split backtick _ backtick
user2428118

@SuperChafouin Đã sửa.
SuperJedi224

1
@ SuperJedi224 Có những người khác: CyclopsCroesus, baluchitheriumbaby long worm, crocodilecentipede, và 24 hơn
Arnaud

1

Python 3, 1915 1900 byte

Thay đổi:

  • Làm việc với và xuất mã ASCII thay vì chính ký tự (đã lưu 15 byte)

Vượt qua tên quái vật làm đối số dòng lệnh đầu tiên, nhận ký tự trên thiết bị xuất chuẩn.

import sys
D=b'`"\x08\x04\x02&@Yx\xf6\x90a\x00Z\x00\x00c\x00X\x00\x00f\x00z\x00\x00hS\x12\x06\t@PSTft?z\x0fnK\nH\x87\xa2ig\t\t\x12 &;@FZkoq\x05\xfc~?\x1b\x80\xc2,z\r\xf3Y\x141\x9cS\x10\x80jU\x06\x08\t&;@BKpqr\x9f\xbe\xbb\xf9O\xcde\x03!kK\x11\x07\x07&:@WYsu\x1boDv\xc9i\x90lS$\x06\r@Sdirw\x1f\x1d\x198\xb3\xb2\x91\x0fm\xa5\x03A@mB#\x07\x07@GPWdiv\x7f;n\xb3Bk\xa5ng\x07\x0c\x16&@EHSVcdfqru\x01\xfen\x83q\xd8\xf3\x1c.\xe5\xac^\x87\t\xaaT\xd4D\x9c\xe1*Io;\x03\x05\x06@desu\x01\xf7\x95R0\x88pc \x08\n:@KMNknq\xfd\xfe\ru\xb2z\xea\\\x9b\x05qC\x08\x07\x06&@AGOfhy\xe2\xbbA\xf2ArS\x1e\x08\x08&@JVYdfi_\x1c\xd8/k\x89\xa8\xe0sw\x08\x0b\x1c&;@Kdfhijou\t\xe0[# \\\x9a\xd3F(L\xfapM\tp\xa8t\xccp\x8d\x11e+\x05\x0c\x8a\x08t+)\x04\x02@PQT\xf2\x94uG\x1c\x06\t&@Uilq\x0f\ryl\xc4`\xa5\x10\x90v\x85\r\x0e$&:@FKLNORSWYry\x9f\x97\xf8\xae\xb8\xdf\xdd\xc1\xcdl\xb2\xc9L|\xbb;\x92\xb8j\xb0\xa99\xdd\x9c\xb8\xd0\x8bh\x95\x88T\xb3;1\xb6\x0bwb\x06\x0c\x11&:;@DGHOVhkm\x02\xfe\x8fO{\xd9u\xac&\xd7\x90\x9fe\xc0\xf44GxW\x07\x07\x0bADHScdv?>\xdd<:\xb7s.\x8cI\x07yR\x07\x07\t&:@bcht;Zx\x16sO\x8d\xab\xc3ze\x0b\x08\x14&@ABCaqs\x01}\xbe=\x15\xc6\xcdL\xa1\xc8\x9e.\xf7\x02\xc1Xq4\x99\t{G\x16\x06\t@Faefg\x1f\x9bU$2P`\xa8\x80|G\x15\x06\x07&\';@Go\x1c1\\\xa7*\x0bS}s\x06\n" &@AHLYZdh\xf6\x1e\t\xb93N2\xc27\xd6\xd8\xd8*\xe5L\xa3\xa4f\x860A\xfa:7.\xdd\x9b)\xb80\x85\xc4\xb4\x83~W\x0e\x07\r&:@ERbd>\x1b\xda\x15\xd4\x92\x0eM\xacJH\x04c\x7fG\x00\x06\x08:@dghx\x1f\xbc\xf4Z\xa1%\xd3C'
R=range
N=sys.argv[1].lower()
B=0
for c in N:B|=ord(c)&0x7f;B<<=7
B%=2**62-1
P=N.split()
F=ord(P[-1][0])^(ord(P[-1][1])>>2)
while D:
 f=D[0];ik,j,h,n=D[1:5];i=ik>>4;k=ik&15;D=D[5:];c=D[:h];m=D[h:h+n];m=int.from_bytes(m,"big");s=1;C=j;b=(h-1).bit_length()
 for x in R(i-1):s<<=k;s|=1
 s<<=j;z=(B&s)>>j;x=0;u=1
 for y in R(i):x<<=1;x|=bool(z&u);u<<=k
 if f!=F:D=D[h+n:];continue
 while m:
  if m&(2**i-1)==x:m>>=i;C=c[m&(2**b-1)];break
  m>>=b+i
 break
print(C)

Khi tôi đọc câu hỏi, tôi nghĩ "Tôi cần nén cái này". Bước đầu tiên là viết thường tất cả các tên.

Nhìn vào dữ liệu, tôi cảm thấy rằng bằng cách nào đó sử dụng chữ cái đầu tiên của từ cuối cùng sẽ thực hiện thủ thuật như một phỏng đoán đầu tiên về những chữ cái mà quái vật có thể có. Hóa ra, đó là một dự đoán ban đầu mạnh mẽ. Bảng sau đây là "ký tự đầu tiên của từ cuối", "số lần truy cập", "nhân vật quái vật":

 'd' (37) => & @ D L R d h
 'g' (31) =>   & ' : @ G H Z g o
 's' (30) =>   & : ; @ E F H K P S Y Z e k o s
 'm' (28) => & @ F H M Q R S Y i m q r
 'c' (24) => : @ A C H S b c d f s v
 'p' (20) => & ; @ P S c d f p u
 'w' (20) => @ G W d q r u w
 'a' (19) => & @ A L Y a t
 'h' (17) => & @ N U d f h i o u
 'l' (17) => : @ F G K L O V f h i k l q y
 'n' (15) => & : @ N W n
 't' (14) => @ H T f i q t
 'b' (14) => & @ B a b h q x
 'k' (13) => ; @ A G K O f h k
 'e' (12) => & ; @ E H e
 'o' (12) => & @ O P T Y o
 'z' ( 9) => Z z
 'v' ( 9) => & @ S V v
 'r' ( 8) => @ B q r
 'j' ( 8) => & ; J d f j
 'f' ( 6) => & F d h
 'i' ( 5) => & : D V i
 'u' ( 4) => o u
 'y' ( 3) => & @ Y
 'x' ( 2) => X x
 'q' ( 1) => i

Để cải thiện hơn nữa tính lan truyền, tôi đã sửa đổi phím một chút, bằng cách XOR-ing ký tự thứ hai của từ cuối cùng, chuyển sang bit sang phải, thành ký tự đầu tiên (chúng ta gọi cấu trúc này first_key):

 '}' (29) =>   & @ A H L Y Z d h
 'v' (25) => & : @ F K L N O R S W Y r y
 'x' (25) => A D H S c d v
 's' (21) => & ; @ K d f h i j o u
 'p' (21) => : @ K M N k n q
 'z' (19) => & @ A B C a q s
 'n' (19) => & @ E H S V c d f q r u
 '|' (18) => & ' ; @ G o
 'l' (17) => @ S d i r w
 '~' (16) => & : @ E R b d
 '{' (15) => @ F a e f g
 'w' (14) => & : ; @ D G H O V h k m
 'i' (14) =>   & ; @ F Z k o q
 'j' (13) => & ; @ B K p q r
 'u' (12) => & @ U i l q
 'm' (12) => @ G P W d i v
 '\x7f' (11) => : @ d g h x
 'o' (11) => @ d e s u
 'h' (11) => @ P S T f t
 'y' (10) => & : @ b c h t
 'r' ( 9) => & @ J V Y d f i
 'k' ( 9) => & : @ W Y s u
 'a' ( 8) => Z
 'q' ( 7) => & @ A G O f h
 't' ( 6) => @ P Q T
 '`' ( 4) => & @ Y x
 'c' ( 1) => X
 'f' ( 1) => z

Như bạn có thể thấy, điều này cung cấp cho chúng tôi chín tên có thể được ánh xạ duy nhất chỉ với thông tin đó. Tốt đẹp!

Bây giờ tôi cần tìm bản đồ còn lại. Đối với điều này, tôi bắt đầu bằng cách chuyển đổi tên đầy đủ (chữ thường) thành số nguyên:

def name_to_int(name):
    bits = 0
    for c in name:
        bits |= ord(c) & 0x7f
        bits <<= 7
    return bits

Điều này chỉ đơn giản là ghép các giá trị ASCII 7 bit của các tên thành một số nguyên lớn. Chúng tôi thực hiện modulo này 4611686018427387903(2⁶²-1) cho các bước tiếp theo.

Bây giờ tôi cố gắng tìm một bitmask mang lại một số nguyên lần lượt phân biệt độc đáo các nhân vật quái vật khác nhau. Các mặt nạ bit bao gồm các mặt phẳng trải đều (như 101010101hoặc 1000100010001) và được tham số hóa bằng số bit ( i>=1) và mức chênh lệch ( k>=1). Ngoài ra, các mặt nạ được dịch chuyển sang trái cho đến 32*ibit. Chúng là AND-ed với tên nguyên và số nguyên kết quả được sử dụng làm khóa trong ánh xạ. Ánh i*number_of_mapping_entriesxạ không xung đột tốt nhất (được ghi bởi ) được sử dụng.

Các số nguyên lấy từ AND-ing mặt nạ và tên integerised được chuyển trở lại bởi jbit và tước zero của họ (chúng tôi lưu trữ i, kjcùng với việc lập bản đồ để có thể tái tạo lại đó), tiết kiệm rất nhiều không gian.

Vì vậy, bây giờ chúng ta có một ánh xạ hai cấp: từ first_keyđến hashmap và hashmap ánh xạ tên đầy đủ đến quái vật char duy nhất. Chúng ta cần lưu trữ bằng cách nào đó. Mỗi mục của ánh xạ cấp cao nhất trông như thế này:

Row = struct.Struct(
    ">"
    "B"  # first word char
    "B"  # number of bits (i) and bit spacing (k)
    "B"  # shift (j) or character to map to if i = 0
    "B"  # number of monster characters
    "B"  # map entry bytes
)

tiếp theo là các nhân vật quái vật và ánh xạ cấp hai.

Ánh xạ mức thứ hai được tuần tự hóa bằng cách đóng gói nó thành một số nguyên lớn và chuyển đổi nó thành byte. Mỗi giá trị và khóa được chuyển liên tiếp vào số nguyên, điều này làm cho ánh xạ có thể được cấu trúc lại (số bit trên mỗi khóa / giá trị không thể lấy được từ số lượng ký tự và icả hai được lưu trữ trong mục nhập hàng).

Nếu một mục nhập chỉ có một ký tự quái vật duy nhất có thể ánh xạ tới, ilà 0, số lượng ký tự và ánh xạ cũng bằng 0 byte. Các ký tự được lưu trữ, nơi jthường sẽ được lưu trữ.

Dữ liệu đầy đủ có kích thước 651 byte, được tuần tự hóa dưới dạng chuỗi python byte 1426 byte của nó.

Chương trình giải mã về cơ bản thực hiện theo cách khác: đầu tiên, nó trích xuất first_keyvà tìm kiếm trong dữ liệu cho mục tương ứng. Sau đó, nó tính toán hàm băm của tên và tìm kiếm thông qua hàm băm cho mục tương ứng.

Bộ giải mã không cố định

#!/usr/bin/python3
import sys
import math

data = b'`"\x08\x04\x02&@Yx\xf6\x90a\x00Z\x00\x00c\x00X\x00\x00f\x00z\x00\x00hS\x12\x06\t@PSTft?z\x0fnK\nH\x87\xa2ig\t\t\x12 &;@FZkoq\x05\xfc~?\x1b\x80\xc2,z\r\xf3Y\x141\x9cS\x10\x80jU\x06\x08\t&;@BKpqr\x9f\xbe\xbb\xf9O\xcde\x03!kK\x11\x07\x07&:@WYsu\x1boDv\xc9i\x90lS$\x06\r@Sdirw\x1f\x1d\x198\xb3\xb2\x91\x0fm\xa5\x03A@mB#\x07\x07@GPWdiv\x7f;n\xb3Bk\xa5ng\x07\x0c\x16&@EHSVcdfqru\x01\xfen\x83q\xd8\xf3\x1c.\xe5\xac^\x87\t\xaaT\xd4D\x9c\xe1*Io;\x03\x05\x06@desu\x01\xf7\x95R0\x88pc \x08\n:@KMNknq\xfd\xfe\ru\xb2z\xea\\\x9b\x05qC\x08\x07\x06&@AGOfhy\xe2\xbbA\xf2ArS\x1e\x08\x08&@JVYdfi_\x1c\xd8/k\x89\xa8\xe0sw\x08\x0b\x1c&;@Kdfhijou\t\xe0[# \\\x9a\xd3F(L\xfapM\tp\xa8t\xccp\x8d\x11e+\x05\x0c\x8a\x08t+)\x04\x02@PQT\xf2\x94uG\x1c\x06\t&@Uilq\x0f\ryl\xc4`\xa5\x10\x90v\x85\r\x0e$&:@FKLNORSWYry\x9f\x97\xf8\xae\xb8\xdf\xdd\xc1\xcdl\xb2\xc9L|\xbb;\x92\xb8j\xb0\xa99\xdd\x9c\xb8\xd0\x8bh\x95\x88T\xb3;1\xb6\x0bwb\x06\x0c\x11&:;@DGHOVhkm\x02\xfe\x8fO{\xd9u\xac&\xd7\x90\x9fe\xc0\xf44GxW\x07\x07\x0bADHScdv?>\xdd<:\xb7s.\x8cI\x07yR\x07\x07\t&:@bcht;Zx\x16sO\x8d\xab\xc3ze\x0b\x08\x14&@ABCaqs\x01}\xbe=\x15\xc6\xcdL\xa1\xc8\x9e.\xf7\x02\xc1Xq4\x99\t{G\x16\x06\t@Faefg\x1f\x9bU$2P`\xa8\x80|G\x15\x06\x07&\';@Go\x1c1\\\xa7*\x0bS}s\x06\n" &@AHLYZdh\xf6\x1e\t\xb93N2\xc27\xd6\xd8\xd8*\xe5L\xa3\xa4f\x860A\xfa:7.\xdd\x9b)\xb80\x85\xc4\xb4\x83~W\x0e\x07\r&:@ERbd>\x1b\xda\x15\xd4\x92\x0eM\xacJH\x04c\x7fG\x00\x06\x08:@dghx\x1f\xbc\xf4Z\xa1%\xd3C'


def name_to_int(name):
    bits = 0
    for c in name:
        bits |= ord(c) & 0x7f
        bits <<= 7
    return bits


def make_mask(nbits, k):
    mask = 1
    for i in range(nbits-1):
        mask <<= k
        mask |= 1
    return mask


def collapse_mask(value, nbits, k):
    bits = 0
    shift = 0
    for i in range(nbits):
        bits <<= 1
        bits |= bool(value & (1<<shift))
        shift += k
    return bits


name = sys.argv[1].casefold()
last_word = name.split()[-1]
last_word_char = chr(ord(last_word[0]) ^ (ord(last_word[1]) >> 2))
while data:
    first_char = chr(data[0])
    ik, j, nchars, nbytes = data[1:5]

    i = ik >> 4
    k = ik & 15

    data = data[5:]
    if first_char != last_word_char:
        # skip this entry
        data = data[nchars+nbytes:]
        continue

    chars, mapping = data[:nchars], data[nchars:nchars+nbytes]
    result = j
    if i == 0:
        break

    mapping = int.from_bytes(mapping, "big")

    name_bits = name_to_int(name) % (2**62-1)
    mask = make_mask(i, k) << j
    key = collapse_mask((name_bits & mask) >> j, i, k)
    bits_per_key = i
    key_mask = 2**(bits_per_key)-1
    bits_per_value = math.ceil(math.log(len(chars), 2))
    value_mask = 2**(bits_per_value)-1
    while mapping:
        if mapping & key_mask == key:
            mapping >>= bits_per_key
            result = chars[mapping & value_mask]
            break
        mapping >>= bits_per_value+bits_per_key

    break
print(chr(result))

Công cụ phân tích

Đây là công cụ tôi đã tạo và sử dụng để tạo dữ liệu - tự chịu rủi ro khi đọc:

#!/usr/bin/python3
import base64
import collections
import math
import json
import struct
import zlib

data = json.load(open("data.json"))

reverse_pseudomap = {}
forward_pseudomap = {}
forward_info = {}
reverse_fullmap = {}
hits = collections.Counter()
monster_char_hitmap = collections.Counter()

for name, char in data.items():
    name = name.casefold()
    parts = name.split()
    monster_char_hitmap[char] += 1

    # if len(parts) > 1:
    #     key = first_char + parts[0][0]
    # else:
    #     key = first_char + last_part[1]

    key = chr(ord(parts[-1][0]) ^ (ord(parts[-1][1]) >> 2))
    # key = parts[-1][0]

    hits[key] += 1
    reverse_pseudomap.setdefault(char, set()).add(key)
    forward_pseudomap.setdefault(key, set()).add(char)
    forward_info.setdefault(key, {})[name] = char
    reverse_fullmap.setdefault(char, set()).add(name)


for char, hit_count in sorted(hits.items(), key=lambda x: x[1], reverse=True):
    monsters = forward_pseudomap[char]
    print(" {!r} ({:2d}) => {}".format(
        char,
        hit_count,
        " ".join(sorted(monsters))
    ))


def make_mask(nbits, k):
    mask = 1
    for i in range(nbits-1):
        mask <<= k
        mask |= 1
    return mask


def collapse_mask(value, nbits, k):
    bits = 0
    shift = 0
    for i in range(nbits):
        bits <<= 1
        bits |= bool(value & (1<<shift))
        shift += k
    return bits


def expand_mask(value, nbits, k):
    bits = 0
    for i in range(nbits):
        bits <<= k
        bits |= value & 1
        value >>= 1
    return bits


assert collapse_mask(expand_mask(0b110110, 6, 3), 6, 3)
assert expand_mask(collapse_mask(0b1010101, 7, 3), 7, 3)


def name_to_int(name):
    # mapped_name = "".join({"-": "3", " ": "4"}.get(c, c) for c in name)
    # if len(mapped_name) % 8 != 0:
    #     if len(mapped_name) % 2 == 0:
    #         mapped_name += "7"
    #     mapped_name = mapped_name + "="*(8 - (len(mapped_name) % 8))
    # print(mapped_name)
    # return base64.b32decode(
    #     mapped_name,
    #     casefold=True,
    # )

    bits = 0
    for c in name:
        bits |= ord(c) & 0x7f
        bits <<= 7
    return bits


compressed_maps = {}
max_bit_size = 0
nmapentries = 0


for first_char, monsters in sorted(forward_info.items()):
    monster_chars = forward_pseudomap[first_char]
    print("trying to find classifier for {!r}".format(first_char))
    print("  {} monsters with {} symbols".format(
        len(monsters),
        len(monster_chars))
    )
    bits = math.log(len(monster_chars), 2)
    print("  {:.2f} bits of clever entropy needed".format(
        bits
    ))

    bits = math.ceil(bits)

    int_monsters = {
        name_to_int(name): char
        for name, char in monsters.items()
    }

    reverse_map = {}
    for name, char in int_monsters.items():
        reverse_map.setdefault(char, set()).add(name)

    solution = None
    solution_score = float("inf")

    if bits == 0:
        char = ord(list(int_monsters.values())[0][0])
        solution = 0, 0, char, {}

    for i in range(bits, 3*bits+1):
        print("  trying to find solution with {} bits".format(i))
        for k in [2, 3, 5, 7, 11]:
            mask = make_mask(i, k)
            for j in range(0, 32*bits):
                bucketed = {}
                for int_name, char in int_monsters.items():
                    bucket = (int_name % (2**62-1)) & mask
                    try:
                        if bucketed[bucket] != char:
                            break
                    except KeyError:
                        bucketed[bucket] = char
                else:
                    new_solution_score = i*len(bucketed)
                    if new_solution_score < solution_score:
                        print("   found mapping: i={}, k={}, j={}, mapping={}".format(
                            i, k, j, bucketed
                        ))
                        solution = i, k, j, bucketed
                        solution_score = new_solution_score
                mask <<= 1

    if solution is not None:
        print("  solution found!")

    chars = "".join(sorted(set(int_monsters.values())))
    i, k, j, mapping = solution

    # sanity check 1
    if i > 0:
        mask = make_mask(i, k) << j
        for int_name, char in int_monsters.items():
            key = (int_name % (2**62-1)) & mask
            assert mapping[key] == char

    compressed_mapping = {}
    for hash_key, char in mapping.items():
        hash_key = collapse_mask(hash_key >> j, i, k)
        max_bit_size = max(hash_key.bit_length(), max_bit_size)
        compressed_mapping[hash_key] = chars.index(char)

    nmapentries += len(compressed_mapping)
    compressed_maps[first_char] = i, k, j, chars, compressed_mapping

    print(" ", compressed_maps[first_char])

    print()

print("max_bit_size =", max_bit_size)
print("nmapentries =", nmapentries)

print("approx size =", (1+math.ceil(max_bit_size/8))*nmapentries)


# first we need to map first word chars to compressed mappings
Row = struct.Struct(
    ">"
    "B"  # first word char
    "B"  # number of bits (i) and bit spacing (k)
    "B"  # shift (j) or character to map to if i = 0
    "B"  # number of characters
    "B"  # map entry bytes
)


def map_to_bytes(i, nchars, mapping):
    bits_per_value = math.ceil(math.log(nchars, 2))
    bits_per_key = i

    bits = 0
    # ensure that the smallest value is encoded last
    for key, value in sorted(mapping.items(), reverse=True):
        assert key.bit_length() <= bits_per_key
        assert value.bit_length() <= bits_per_value

        bits <<= bits_per_value
        bits |= value
        bits <<= bits_per_key
        bits |= key

    return bits.to_bytes(math.ceil(bits.bit_length() / 8), "big")


def bytes_to_map(i, nchars, data):
    data = int.from_bytes(data, "big")

    bits_per_value = math.ceil(math.log(nchars, 2))
    bits_per_key = i
    key_mask = 2**(bits_per_key)-1
    value_mask = 2**(bits_per_value)-1

    mapping = {}
    while data:
        key = data & key_mask
        data >>= bits_per_key
        value = data & value_mask
        data >>= bits_per_value
        assert key not in mapping
        mapping[key] = value

    return mapping


parts = bytearray()
for first_char, (i, k, j, chars, mapping) in sorted(compressed_maps.items()):
    raw_data = map_to_bytes(i, len(chars), mapping)
    recovered_mapping = bytes_to_map(i, len(chars), raw_data)
    assert recovered_mapping == mapping, "{}\n{}\n{}\n{} {}".format(
        mapping,
        recovered_mapping,
        raw_data,
        i, len(chars),
    )
    assert len(raw_data) <= 255

    print(" {!r} => {} {} {} {} {}".format(
        first_char,
        i, k, j,
        len(chars),
        raw_data
    ))

    assert k <= 15
    assert i <= 15

    if i == 0:
        chars = ""

    row = Row.pack(
        ord(first_char),
        (i << 4) | k, j,
        len(chars),
        len(raw_data),
    )
    row += chars.encode("ascii")
    row += raw_data
    parts.extend(row)

parts = bytes(parts)
print(parts)
print(len(parts))
print(len(str(parts)))
print(len(str(zlib.compress(parts, 9))))

Lái thử

#!/usr/bin/python3
import json
import subprocess
import sys

with open("data.json") as f:
    data = json.load(f)

for name, char in data.items():
    stdout = subprocess.check_output(["python3", sys.argv[1], name])
    stdout = stdout.decode().rstrip("\n")
    if char != stdout:
        print("mismatch for {!r}: {!r} != {!r}".format(
            name, char, stdout
        ))

0

awk 73 + 2060 byte

s{while(!(i=index(s,$0)))sub(/.$/,"");print substr(s,i+length(),1)}{s=$0}

Dữ liệu đã được chuẩn bị cho điều này:

  "Aleax": "A",            Al A     # first of alphabet truncate to len=1
  "Angel": "A",            An A
  "Arch Priest": "@",      Arch @   # this needs to come
  "Archon": "A",           Archo A  # before this
  "Ashikaga Takauji": "@", Ash @
  "Asmodeus": "&",         Asm &    

(2060 ký tự) tức là. để rút ngắn chuỗi ký tự duy nhất với ký tự quái vật được gắn vào tên và cuối cùng ở dạng này:

AlAAnAArch@ArchoAAsh@Asm&

(cần phải có một char quay lại ở đầu chuỗi để đánh dấu một trận đấu không khớp) Khi tìm kiếm một trận đấu, tên được rút ngắn char bởi char từ cuối cho đến khi có một trận đấu và char tiếp theo sau khi trận đấu được trả về :

$ cat monster
Aleax
$ awk -f program.awk monsters_string monster
A

Tôi vẫn có thể cạo một vài byte khỏi chuỗi quái vật với một số tổ chức:

AAnArch@ArchoAsh@Asm&

Xem xét kích thước của dữ liệu với tên quái vật bắt đầu bằng Asẽ giảm từ 38 byte xuống còn 22 nghĩa là giảm kích thước dữ liệu từ trung bình 2060 xuống còn 1193.

việc này vẫn đang được tiến hành và chuỗi quái vật sẽ được xuất bản sau đó một chút.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.