Задний план
Компьютерная игра NetHack датируется 1987 годом, до того, как было широко распространено использование графики в компьютерных играх. В игре много монстров, и потенциально много нужно разместить сразу на экране, поэтому монстры нарисованы очень минимальным образом: монстр просто рисуется как ASCII-персонаж на экране.
В дополнение к тому, что есть много монстров, есть много типов монстров. Может быть важно знать, что есть что; вам придется реагировать иначе, увидев котенка и дракона. Таким образом, большая часть ASCII используется для представления монстров; например, котенок есть f
, а красный дракон есть D
. Это означает, что может быть очень полезно знать, как будет выглядеть данный монстр, поскольку это поможет вам распознать его, если вы столкнетесь с ним позже в игре. (Обратите внимание, что типов монстров больше, чем персонажей ASCII, поэтому некоторые из них имеют общие свойства: красный дракон и синий дракон оба D
.)
задача
Ваша программа должна взять имя монстра NetHack в качестве входных данных и создать символ ASCII, который представляет его в игре в качестве выходных данных. Программа может предполагать, что на самом деле это имя монстра NetHack; это может, если он желает сбой, привести к бессмысленным результатам и т. д., если ввод неверен.
Следующий фрагмент стека представляет собой объект JSON, дающий полное сопоставление возможных входов и их соответствующих выходов:
Таким образом, в основном, задача здесь «дать ключ в словаре, представленном объектом JSON выше, вернуть соответствующее значение».
Обратите внимание, что эта проблема, в некотором смысле, обратная колмогоровская сложность ; вместо перехода от маленького / пустого ввода к большому выводу вы переходите от большого ввода к маленькому выводу. (Таким образом, на входе много избыточной информации, которую вы можете игнорировать или использовать по своему усмотрению). Это также довольно похоже на регулярное выражение для гольфа, за исключением того, что а) разрешен любой язык (не только регулярное выражение), и б) существует более двух возможных выходных данных. (У нас было несколько подобных задач, таких как эти две , но эта задача несколько отличается, потому что указанное поведение ввода / вывода имеет более строгие шаблоны).
Разъяснения
Вы можете использовать любой приемлемый формат ввода и вывода (например, вы можете создать вывод в виде символа, или в виде его кода ASCII, или в виде строки длиной один символ). Вы можете отправить функцию вместо полной программы, если хотите.
Это уже упоминалось в стандартных лазейках, но только для повторения: вы не можете хранить соответствие между вводом и выводом нигде, кроме исходного кода вашей программы. Эта задача в основном заключается в представлении поведения ввода / вывода в наименьшем возможном пространстве, поэтому вы не должны делать такие вещи, как загрузка списка из Интернета, сохранение корреспонденции во внешнем файле, запуск NetHack в режиме отладки и порождение соответствующего монстра. чтобы посмотреть, как это выглядит, и т. д. (Кроме того, я не хочу отбиваться от монстров, чтобы проверить ваши представления.)
Состояние победы
Это код-гольф , поэтому выигрышной записью будет самая короткая запись, подсчитанная в байтах. Удачи!
источник
mail daemon
> _ <Ответы:
Jelly , 309 байт в кодировке Jelly
Попробуйте онлайн!
Я решил, что пришло время попробовать свои силы. Использование Jelly (и его 8-битной кодовой страницы) дает мне преимущество в 12,5% по сравнению с языками только для ASCII, и Jelly удобен для этой задачи благодаря наличию встроенных базовых операторов преобразования с короткими именами, но большей части экономии из-за лучшего алгоритма сжатия (эта программа в среднем составляет менее одного байта на тип монстра).
Алгоритм и объяснение
Словесная классификация
Я решил, что для того, чтобы получить хороший результат, необходимо использовать преимущества структуры ввода больше, чем другие записи. Одна вещь, которая очень заметна, - то, что у многих монстров есть названия формы " прилагательное разновидности "; a
red dragon
и ablue dragon
оба типа дракона, и поэтому выглядят какD
. У некоторых других монстров есть названия вида " работа вида ", такие как ; будучи типом орка, это выглядит как . Сложные вопросы - это нежить; a является и кобольдом, и зомби, и последнее состояние имеет приоритет в именовании монстров NetHack, поэтому мы хотели бы классифицировать это как .orc shaman
o
kobold zombie
Z
Таким образом, я классифицировал слова, которые появляются в именах монстров, следующим образом: индикатор - это слово, которое настоятельно рекомендует соответствующий класс монстров (например,
sphere
настоятельно предполагает, что монстр находится в классеe
); неоднозначное слово это слово , которое делает гораздо меньше предложения (lord
не сказать вам много), а все остальные слова псевдослов , что мы не заботимся о. Основная идея заключается в том, что мы смотрим на слова в названии монстра от конца к началу и выбираем первый индикатор, который мы видим. Таким образом, необходимо было обеспечить, чтобы каждое имя монстра содержало хотя бы один индикатор, за которым следовали двусмысленные слова. Как исключение, слова, которые появляются в именах монстров, которые выглядят как@
(самая большая группа) все классифицируются как неоднозначные. Все может появиться перед индикатором; например, имена цветов (такие какred
) всегда появляются в имени раньше, чем индикатор, и, таким образом, считаются не словами (так как они никогда не проверяются при определении личности монстра).В конце концов, эта программа сводится к хеш-таблице, как и другие программы. Однако таблица не содержит записей для всех имен монстров или для всех слов, которые появляются в именах монстров; скорее он содержит только показатели. Хеши неоднозначных слов не отображаются в таблице, но должны быть назначены пустым слотам (попытка найти неоднозначное слово всегда будет пустой). Для не-слов не имеет значения, появляется ли слово в таблице или нет, или хэш сталкивается или нет, потому что мы никогда не используем значение поиска не-слова. (Таблица довольно разреженная, поэтому большинство слов не появляется в таблице, но некоторые из них, например
flesh
, найдены в таблице в результате коллизий хешей.)Вот несколько примеров того, как работает эта часть программы:
woodchuck
это длинное слово (таким образом, индикатор), и поиск в таблицеwoodchuck
дает нам ожидаемый ответr
.abbot
также одно слово долго, но выглядит как@
. Как таковое,abbot
считается неоднозначным словом; поиск по таблице пуст, и мы возвращаем ответ@
по умолчанию.vampire lord
состоит из индикатора (vampire
соответствуетV
) и неоднозначного слова (lord
которого нет в таблице). Это означает, что мы проверяем оба слова (в обратном порядке), а затем даем правильный ответV
.gelatinous cube
состоит из не-слова (gelatinous
соответствуетH
из-за коллизии хэшей) и индикатора (cube
соответствуетb
). Поскольку мы берем только последнее слово, найденное в таблице, это возвращаетb
, как и ожидалось.gnome mummy
состоит из двух показателей,gnome
соответствующихG
иmummy
соответствующихM
. Мы берем последний индикатор и получаем тоM
, что мы хотим.Код для обработки классификации на основе слов является последней строкой программы Jelly. Вот как это работает:
Есть два реальных случая; если входные данные полностью состоят из неоднозначных слов,
t0
удаляются все выходные данные поиска в таблице, и мы получаем@
результат по умолчанию; если на входе есть индикаторы,t0
то удалит что-нибудь справа от крайнего правого индикатора иṪ
выдаст соответствующий результат для этого индикатора.Сжатие таблицы
Конечно, разбиение ввода на слова само по себе не решает проблему; нам все еще нужно закодировать соответствие между индикаторами и соответствующими классами монстров (и отсутствие соответствия от неоднозначных слов). Чтобы сделать это, я построил разреженную таблицу с 181 использованными записями (что соответствует 181 индикатору; это большое улучшение по сравнению с 378 монстрами!) И всего 966 записей (что соответствует 966 выходным значениям хэш-функции). Таблица кодируется в программе с использованием двух строк: первая строка указывает размеры «пробелов» в таблице (которые не содержат записей); и вторая строка определяет класс монстров, который соответствует каждой записи. Они оба представлены в сжатой форме через базовое преобразование.
В программе Jelly код поиска по таблице вместе с самой программой представлен во второй строке, начиная с первой
µ
. Вот как работает эта часть программы:Биективное основание 21 похоже на основание 21, за исключением того, что 21 является допустимой цифрой, а 0 - нет. Для нас это более удобная кодировка, потому что мы считаем, что две соседние записи имеют пробел 1, поэтому мы можем найти действительные индексы с помощью накопленной суммы. Когда дело доходит до той части таблицы, которая содержит значения, у нас есть 58 уникальных значений, поэтому мы сначала декодируем в 58 последовательных целых чисел, а затем снова декодируем, используя таблицу поиска, которая сопоставляет их с реальными используемыми символами. (Большинство из них - буквы, поэтому мы начинаем эту вторичную таблицу поиска с
&;:'
небуквенных записей, а затем просто добавляем константу Jelly, начинающуюся с прописных и строчных букв; у нее также есть какой-то другой мусор, но нам все равно об этом.)Стражное значение Jelly «index not found», если вы используете его для индексации списка, возвращает последний элемент списка; таким образом, я добавил ноль (целое ноль, даже если таблица в основном состоит из символов) к таблице поиска, чтобы дать более подходящий сторож, чтобы указать отсутствующую запись.
Хэш-функция
Оставшаяся часть программы - хеш-функция. Это начинается достаточно просто, с
OḌ
; это преобразует входную строку в ее ASCII-коды, а затем вычисляет последний код, плюс 10-кратный предпоследний код, плюс 100-кратный код до и т. д. (это очень короткое представление в Jelly, потому что оно чаще используется как строка → целочисленная функция преобразования). Однако, если бы мы просто сократили этот хэш напрямую с помощью операции модуля, нам бы понадобилась довольно большая таблица. Поэтому вместо этого я начинаю с цепочки операций по сокращению таблицы. Каждый из них работает следующим образом: мы берем пятую степень текущего хеш-значения; затем мы уменьшаем значение по модулю константы (эта константа зависит от того, какую операцию мы используем). Эта цепочка дает большую экономию (с точки зрения уменьшения размера результирующей таблицы), чем она стоит (с точки зрения необходимости кодирования самой цепочки операций), двумя способами: она может создать таблицугораздо меньше (966, а не 3529 записей), а использование нескольких этапов дает больше возможностей для создания полезных коллизий (такого не было много, но есть одно такое коллизирование:Death
иYeenoghu
хеш, и 806, что позволяет нам удалить один запись из таблицы, так как они оба идут в&
). Здесь используются модули [3529, 2163, 1999, 1739, 1523, 1378, 1246, 1223, 1145, 966]. Между прочим, причина повышения до пятой степени заключается в том, что если вы просто берете значение непосредственно, промежутки, как правило, остаются одинаковыми, тогда как возведение в степень перемещает пробелы вокруг и может позволить распределению таблицы более равномерно распределяться после цепочка, а не застревание в локальном минимуме (более равномерно распределенные промежутки позволяют более сжатое кодирование размеров промежутков). Это должно быть нечетной степенью, чтобы предотвратить тот факт, что x ² = (- x ) ², приводящее к столкновениям, и 5 работал лучше, чем 3.Первая строка программы кодирует последовательность модулей с использованием дельта-кодирования:
Оставшаяся часть программы, начало второй строки, реализует хеш-функцию:
верификация
Это скрипт Perl, который я использовал для проверки правильности работы программы:
источник
JavaScript (ES6),
915...902890 байтотформатирован
Ниже приведена отформатированная версия кода с усеченными данными полезной нагрузки.
Как это работает
Шаг 1
Сначала мы уменьшаем имя монстра на:
1
's.Примеры:
Это приводит к нескольким столкновениям. Например,
"Master Assassin"
и"Master Kaen"
оба уменьшены до"Mst1n"
. К счастью, все имена сталкивающихся монстров имеют один и тот же символ (@
в данном случае).Шаг 2
Затем мы интерпретируем эту 5-символьную строку как основную 36-ю величину, чтобы преобразовать ее в десятичную (эта операция не учитывает регистр), и применяем модуль
8713
, который был выбран эмпирически для создания списка ключей без столкновений.Примеры:
Шаг 3
Все ключи отсортированы в порядке возрастания:
Преобразовано в значения дельты:
И закодированы как символы ASCII в диапазоне
[ 32, 126 ]
. Некоторые промежуточные фиктивные значения вставляются, когда разница между двумя последовательными ключами превышает максимальную кодируемую величину.Наконец, список ключей отображается на список символов, расположенных в том же порядке.
Тест
Показать фрагмент кода
источник
Java, 1130 байт
Ungolfed:
Имена монстров:
hashcode
метода Java => 32 битаСимвол дисплея кодируется в 6 битах.
Таким образом, каждый кортеж (имя монстра, персонаж) использует 14 бит. Все кортежи сохраняются в BitSet и кодируются в base 64.
Я теряю много байтов с кодировкой base64 и операциями BitSet :-)
источник
()->{...}
. Так говорится в разделе «Разъяснения».Mathematica, 1067 байт (кодировка символов Mac OS Roman)
Безымянная функция, принимающая строку в качестве ввода и возвращающая символ. Функция имеет следующий вид:
Здесь GIANT_STRING_1 - это строка, содержащая 608 однобайтовых символов в римской кодировке Mac OS (ни один из которых не находится в диапазоне 00-1F), а GIANT_STRING_2 - это строка, содержащая 304 символа ASCII.
Строка 2 запускает хеш-функцию: она преобразует входную строку в список кодов символов (кодировка не имеет значения, поскольку все они печатаются в формате ASCII), затем вычисляет сумму этих кодов символов и сумму их квадратов, как по модулю 216, так и по форсированию. ответ лежит между 32 и 255. Затем строки 1 и 3 преобразуют эти упорядоченные пары целых чисел в двухсимвольные строки, что является значением хеша, которое мы в конечном итоге используем.
Строка 5 превращает GIANT_STRING_1 в список из 304 двухсимвольных строк; строка 6 превращает GIANT_STRING_2 в список из 304 односимвольных строк. Затем строки 4 и 5 преобразуют эти два списка в набор из 304 правил замещения: если вы видите такую-то двухсимвольную строку, превращайте ее в такую-то такую-односимвольную строку. Наконец, строка 8 превращает любую оставшуюся двухсимвольную строку в
"@"
.В списке 71 монстр с символом
"@"
, и они обрабатываются без хеширования (я украл эту идею из комментария ais523 к другому ответу). Просто так получилось, что все остальные 304 значения хешей являются уникальными! и поэтому никакие другие модификации алгоритма не требуются. (Это удачный разрыв, который"human"
нужно сопоставить"@"
, поскольку суммы кодов символов букв в"human"
и букв в"shark"
них идентичны, равно как и суммы квадратов этих кодов - как целые числа, даже не по модулю 216!)источник
Питон, 2055 байт
Вот мой тестовый комплект, на случай, если он кому-нибудь поможет.
Я написал небольшую программу для перечисления всех различных способов извлечения 4 символов плюс длина строки. Мой первоначальный план состоял в том, чтобы затем использовать
ord()
эти символы, сделать с ними некоторые математические расчеты и свести их к идеальной хэш-функции, которая вывела бы индексы в таблицу результатов. Поэтому я написал еще одну небольшую программу для перечисления всех различных способов суммирования / умножения / модуляции этих четырех символов вместе; но в результате хэш - функция продолжала иметь сторонние слишком много столкновений. Так что в конце концов я сдался и просто сделал то, что вы видите здесь, это просто карта из строкового представления имени каждого монстра к соответствующему символу.То есть: то, что я хотел получить, было
но мне удалось добраться до
где мой продиктованный поиск
{relatively_large_dict}[small_string]
выражен какre.match(small_string+"(.)", "relatively_large_string")
для игры в гольф.источник
JavaScript (ES6), 1178
Меньше гольфа
Тест
источник
Javascript, 1185 байт
Использует гольф-версию строкового хэша Javascript, найденную здесь. Фактический хеш, хранящийся в таблице (эта длинная строка), принимает абсолютное значение хеша, созданного этим методом, преобразует его в base-36 и удаляет все, кроме трех наименее значащих цифр.
источник
@
из таблицы и просто установив значение по умолчанию,@
если ввод не найденcavewoman
иchameleon
иметь тот же первый символ, последний символ и длину, что может быть проблемой?split("_")
может статьsplit
backtick_
backtickCyclops
иCroesus
,baluchitherium
иbaby long worm
,crocodile
иcentipede
, и еще 24Питон 3,
1915 г.1900 байтChangelog:
Передайте имя монстра в качестве первого аргумента командной строки, получите символ на стандартный вывод.
Когда я прочитал вопрос, я подумал: «Мне нужно это сжать». Первым шагом было сделать строчными все имена.
Глядя на данные, я почувствовал, что каким-то образом использование первой буквы последнего слова должно помочь в качестве приблизительного первого предположения о том, какие буквы могут быть у монстра. Как оказалось, это было мощное первоначальное предположение. В следующей таблице приведены «первый символ последнего слова», «количество попаданий», «символы монстра»:
Чтобы еще больше улучшить распространение, я немного изменил ключ, добавив XOR второго символа последнего слова, смещенного в биты вправо, в первый символ (назовем эту конструкцию
first_key
):Как видите, это дает нам девять имен, которые могут быть однозначно сопоставлены только с этой информацией. Ницца!
Теперь мне нужно было найти оставшееся отображение. Для этого я начал с преобразования полного (в нижнем регистре) имени в целое число:
Это просто объединяет 7-битные значения ASCII-имен в огромное целое число. Мы возьмем это по модулю
4611686018427387903
(2⁶²-1) для следующих шагов.Теперь я пытаюсь найти битовую маску, которая выдает целое число, которое, в свою очередь, хорошо различает различных персонажей монстров. Битовые маски состоят из равномерно распределенных (например,
101010101
или1000100010001
) и параметризуются числом битов (i>=1
) и расширением (k>=1
). Кроме того, маски сдвинуты влево на величину до32*i
битов. Это AND-ed с целочисленным именем, и полученное целое число используется в качестве ключа в отображении. Лучшее (оцененноеi*number_of_mapping_entries
) бесконфликтное отображение используется.Целые полученные из И-инги маски и integerised имени сдвинуты назад
j
битами и раздели их нули (мы хранимi
,k
иj
вместе с отображением , чтобы иметь возможность восстановить это), экономя много места.Итак, теперь у нас есть двухуровневое отображение: from
first_key
на hashmap, и hashmap однозначно отображает полное имя на monster char. Нам нужно как-то хранить это. Каждая запись отображения верхнего уровня выглядит следующим образом:затем следуют персонажи монстров и отображение второго уровня.
Отображение второго уровня сериализуется путем упаковки его в большое целое число и преобразования его в байты. Каждое значение и ключ последовательно сдвигаются в целое число, что делает преобразование восстанавливаемым (количество битов на ключ / значение выводится из числа символов и
i
оба хранятся в записи строки).Если запись имеет только один возможный символ монстра для сопоставления,
i
равен нулю, количество символов и отображение также равны нулю байтов. Символ хранится там, гдеj
обычно хранится.Полные данные имеют размер 651 байт, сериализованный как строка байтов питона, его 1426 байт.
Программа декодирования, по сути, делает это наоборот: сначала она извлекает
first_key
и ищет в данных соответствующую запись. Затем он вычисляет хеш имени и выполняет поиск в хеш-карте соответствующей записи.Необъяснимый декодер
Инструмент анализа
Это инструмент, который я сделал и использовал для генерации данных - читайте на свой страх и риск:
Тестовый водитель
источник
awk 73 + 2060 байт
Данные были подготовлены к этому:
(2060 символов) т.е. кратчайшую уникальную строку с символом монстра, добавленным к имени и, наконец, к этой форме:
(в начале строки должен быть отступающий символ, чтобы пометить несоответствие). При поиске совпадения имя сокращается на символ символом с конца, пока не будет найдено совпадение, а следующий символ после совпадения возвращается :
Я все еще могу сбрить несколько байтов из строки монстров с некоторой организацией:
Учитывая, что размер данных с именами монстров, начиная с
A
, уменьшится с 38 байтов до 22, это означает уменьшение размера данных в среднем с 2060 до 1193.это все еще в стадии разработки, и строка монстров будет опубликована чуть позже.
источник