| ||||
Рассказы с фронта кодированияНожницы, брюки, очки и орангутанги© Andrew Pontiousoriginal from: http://www.xyzzynews.com Вступление от переводчика: Следует заметить, что проблема, с которой столкнулся автор данной статьи, не актуальна для пользователей RTADS. Конечно, в английском языке нет падежей, но ради наглядности я немного адаптировал текст под российские реалии. В целом же статья интересна не самой проблемой, а поиском ее решения, тем, насколько модифицируемым может оказаться TADS при условии понимания механизмов его работы. Вступление от GrAndrey: Для меня доставило большое удовольствие чтение этой статьи, открывшей мне историю развития любимой мною системы, в результате которых стал возможным её перевод на русский язык. Статья показывает, насколько важным является создание и настройка стандартных библиотек, и сколь долгий и извилистый путь они проходят, прежде чем достигают своего должного состояния. Всякий, кто пишет новую платформу, должен помнить, что компилятор и интерпретатор - это только первый, и, возможно, самый простой шаг! Когда я начал программировать свою первую игру TADS, еще в семнадцатом веке... ладно, не так уж и давно, но то, что я делал, было похоже на длинный и мучительный процесс. Я был новичком, пока не решил добавить несколько свойств, которые оказались из категории для продвинутых. В этой статье идет речь об одном маленьком изменении, которое я сделал в словаре, чтобы неизмененная версия TADS 2.2 иным образом распознавала игровые конструкции "множественный-единственный", которые являются едиными объектами, но обращаются к ним во множественном числе. В некоторой мере этим могут быть ножницы, которые - единая вещь в реальном мире, или для групп вещей, которые вы хотите объединить для удобства игры, подобно "двадцати орангутангам, угрожающим вам с деревьев". Сначала я создам один из тех множественных-единственных объектов. Я должен заметить, это довольно тяжело для не-пользователя TADS: scissors: item // "item" является классом TADS для // чего-то, что вы можете поднять sdesc = "ножницы" // "краткое" описание объекта adesc = "ножницы" // описание, использующееся, когда // игра хочет назвать предмет // с использованием артикля "a" noun = 'ножницы' // термин, которым игрок может // воспользоваться, чтобы назвать предмет location = startroom // где объект расположен вначале игры ;Комментарий Г.А. №1 Это позволит нам начать. Я также создаю ящик в startroom (специфический класс, названный "контейнер"), чтобы вы могли поместить в него предметы. >посмотреть Вы в стартовой комнате вашей тестовой игры. Здесь вы видите ящик и ножницы. Правильной командой в этом случае, грамматически, но не логически, будет: >взять ножницы из ящика. Игра разумно отвечает: Ножницы не находится ящике. Ага! Вот начало проблемы. Я ищу с целью обнаружить, откуда выходит это сообщение. Оно - в ADV.T, файле, который обычно входит в каждую игру (этот файл похож, но не аналогичен Inform'овским словарю, грамматике и синтаксическому анализатору), чтобы образовать игровую среду TADS, определяя такие прототипы объектов, как "комната", "открываемый контейнер" и "контейнер". На самом верху иерархии - объект-элементарная запись, которой фактически является всякий объект - потомок "вещи". Все стандартные глаголы игры описаны здесь и унаследованы любыми объектами, в которых они не перекрыты своими собственными глаголами. Например, код, касающийся глагола "взять" для прямого объекта, выглядит примерно так: verDoTakeOut( actor, io ) = { if ( io <> nil and not self.isIn( io )) { caps(); self.thedesc; "не находится в"; io.thedesc; ". "; } self.verDoTake(actor); /* убедиться, что объект вообще можно брать */ }Комментарий Г.А. №2 Атрибут "actor" относится к тому персонажу, который выполняет действие (обычно "Я", игрок), и "io" - косвенный объект, в этом случае то, из чего вы берете прямой объект. "Self" здесь - ножницы. Обратите внимание, как код сообщает, есть ли косвенный объект ("io <> nil"), и если "self" (ножниц) в нем нет ("isIn" - специальный метод, который проверяет это), выдает вам сообщение, с которым мы уже столкнулись. То, что я должен сделать - найти способ "убедить" эту программу распознавать этот объект как множественный и выдавать верный отклик на это. Моя первая попытка в решении включала создание нового свойства в нашем объекте "ножницы", названное "plural", путем добавления этой строки где-нибудь в определении ножниц: plural = true Затем я использую команду "modify", которая позволяет мне переопределить отдельные методы или свойства объекта после того, как объект уже был объявлен, в этом случае - в ADV.T -- однако, вы не можете изменить отдельные строки метода или свойства, и должны повторить их все. Как, например: modify thing verDoTakeOut( actor, io ) = { if ( io <> nil and not self.isIn( io )) { caps(); self.thedesc; // НОВЫЙ КОД! if (self.plural) " не находятся в"; else " не находится в"; // КОНЦОВКА СТАРОГО КОДА " в "; io.thedesc; ". "; } self.verDoTake(actor); /* убедиться, что объект вообще можно брать */ } ;Комментарий Г.А. №3 Итак, я пытаюсь откомпилировать это и получаю сообщение "error TADS-335: invalid vocabulary property value" (неверное значение свойства). Что это означает? "Plural = true" - вполне правильно! После недолгого поиска, я обнаружил, что ADV.T уже определяет свойство, названное "plural", но как строку. Мой последующий код пытался переопределить переменную после того, как она была объявлена, причем со строкового типа в логический. Ни-ни. Тогда взамен я меняю "plural" на менее изящный "pluralflag" (все же я бы мог быть злонамеренным и использовать "Plural", так как TADS чувствителен к регистру (Комментарий Г.А. №4) ) и перекомпилирую. Теперь программа выполняется следующим образом: >взять ножницы из ящика. Ножницы - не находятся ящике. Теперь, все же, я выучил этот урок. Я осознаю, что мне нужно просмотреть ADV.T на наличие подобных примеров "не лежит" или "не лежат", "надет" или "не надеты", или чего-нибудь еще. Я дам вам еще несколько примеров, однако полный список значительно длиннее. Из предметов: verDoPutOn: "{прямой объект} уже лежит/лежат на {косвенный объект}!" verDoTellAbout: "Кажется, {прямой объект} вас не интересует/интересуют". И из персонажа, другого прототипа-объекта, который имеет методы, подходящие для ваших NPC: verDoWalkon: "{Прямой объект} возражает/возражают против этого" Итак, теперь я обнаружил все места, где сообщения должны быть изменены, и для этого модифицировал все объекты и методы из ADV.T в своем собственном коде. И даже лучше для вас, я сохраняю свои изменения в отдельном файле, который другие люди могли бы включать в свои игры для того, чтобы использовать его преимущества, исправляя "из ящика", как оно было, просто включая "pluralflag = true" (или новое имя - см. ниже) в нужные объекты. Все же запомните - не модифицируйте те же методы снова в вашем коде после того, как вы включили мою утилиту, или вы уничтожите мои изменения в ADV.T! Лучшим способом, если вам нужно изменить те же методы, будет не вызов файла утилиты отдельно из вашей игры (технически: #include), а полная вставка моего кода в вашу игру (и там с ним возиться). Вскоре вы найдете его на IF архиве под именем plurals.t в Programming/TADS/Examples, или на прилагаемом диске XYZZYnews, если вы подписались на него, вместе с небольшой тестовой игрой, названной pluralstest.t, показывающей много примеров этих объектов. Итак, мы это сделали, не так ли? Я подумал так же, пока не попытался: >поместить ножницы Во что вы хотите его поместить? Теперь, это сообщение ошибки, сгенерированное синтаксическим анализатором в ответ на отсутствие указанного косвенного объекта (грамматически - предложной группы, но давайте не будем придираться), когда требуется один единственный существующий шаблон или образец для глагола. Вы не можете только что-то помещать, вы должны помещать это во что-то или на что-то. Проблема в этом надоедливом "это". Давала бы версия 2.2 больше контроля над сообщениями ошибки синтаксического анализатора, может быть, я мог бы модифицировать также и их. Шаг первый: "The New Book of the Parser" в Upgrade Manual TADS версии 2.2, страница 90: "Если глагол требует прямой или косвенный объект, но команда не находит ни то, ни другое, и синтаксический анализатор не может подставить подходящий по умолчанию... синтаксический анализатор проверяет, определена ли в вашей игровой программе функция, названная parseAskobjActor [перед тем, как продолжать свой алгоритм]". Окей, круто, я определю эту функцию, но потом я нахожу что, даже если это тот косвенный объект, который вы ищете, метод не дает никаких параметров прямого объекта - только персонаж, глагол, и предлог, если такой есть. Следовательно, у вас нет способа передать вашему новому методу parseAskobjActor определения, какой же именно прямой объект и единственный или множественный он, до того, как вы зададите вопрос, поставленный выше (Прим. переводчика: "Во что вы хотите его поместить?"). Итак, мы не можем изменить вопрос с точки зрения единственного-множественного тем способом, которым хотели. Тупик. Я смотрю на другой, более общий метод, который TADS обеспечивает, чтобы изменять сообщения ошибки, функцию parseError. Она позволяет вам заменять любое стандартное сообщение ошибки, которое генерируется, новым, например, сообщением №6, "Я ожидал существительное после 'of'.". И здесь мы очень ограничены; мы просто получаем номер сообщения, которое синтаксический анализатор должен выдать, как параметр (есть их целая таблица), и можем также заменять сообщение в каждом случае, когда синтаксический анализатор использует его, или оставить его без изменений. Сообщения №140-149 имеют дело с частями сообщений, с которыми мы могли бы целиком провозиться, используя parseAskobjActor, например, 140: "что вы хотите" или 143: "?". Почему это вы должны хотеть заменить, скажем, знак вопроса, я не уверен, но вы можете это сделать. Синтаксический анализатор собирает эти части вокруг того, что набрал игрок, например, Во+ "что вы хотите " + поместить + "это" + "?". Номер 141 - то самое надоедливое "это", но постойте, есть же и №147, "их". Когда оно используется? Страница 91: "Если нужно местоимение для прямого объекта, синтаксический анализатор проверяет каждый объект в списке прямых объектов. Если игрок явно задал множественные прямые объекты (используя "все" или список фраз существительных), синтаксический анализатор отображает сообщение 144 ("их")." Текст продолжает объяснять, как используются свойства isHim и isHer объектов, определенных игроком ("поместить тетушку Хелен", например), чтобы было возможно заменять местоимения "его" или "ее". Итак, единственный путь сделать так, чтобы синтаксический анализатор использовал "их" в этом сообщении-франкенштейне,- определить два или больше объектов ("поместить яблоко и апельсин"); другой тупик. Пока синтаксический анализатор делает всю обработку, не спрашивая нашего совета (как оскорбительно!), мы не можем на это влиять. Мой способ, по общему признанию грубый,- заменить сообщение 141 "этим". Но, это имеет смысл. Поход Дэвида Бэджетта при написании эквивалента advr.t, - названного WorldClass, решает проблему, полностью исключая местоимение; его parseAskobjActor задаёт вопросы типа "Какими?" или "В котором?", что, я должен признать, весьма умно, пусть и звучит резко. Лучшее решение - иметь синтаксический анализатор, проверяющий мое свойство, которое ради логического соответствия я теперь назвал "isThem", прежде чем решить, какое местоимение использовать. Это будет даже более идеальным, если в следующей версии TADS (если есть следующая версия TADS) синтаксический анализатор позволит вам заменять всю работу по определению местоимений на свой собственный метод, на этот раз с верными параметрами. Гибкость является королем. Это то, чем по-настоящему блистает Inform, с тех пор как все подобные внутренние обработки синтаксического анализатора доступны для правки со стороны игрока (вот почему они уже получили полную утилиту обработки множественно-единственных конструкций). Комментарий Г.А. №5 И это все изменения, необходимые для того, чтобы обеспечивать множественно-единственные конструкции! Может быть, есть другие, на которые я не обратил внимания, но во время детального тестирования своей собственной игры я не обнаружил ни одного (знаменитые последние слова...). Моя игра имеет другие серьезные изменения в ADV.T, вносящие новые возможности и заставляющие многие вещи работать более гладко и логично, из которых я хочу составить утилиты, как только выйдет моя игра. У вас будет возможность использовать всё это, если вы не хотите массово переходить на WorldClass. Есть также возможность, что сам TADS включит аналогичные улучшения в следующую версию. Майкл Робертс, вы слушаете? ( Комментарий Г.А. №6) Как вы можете видеть, я люблю говорить на узкопрофессиональные темы, так что если у кого-нибудь есть любые комментарии, свободно пишите мне по e-mail на адрес, указанный в начале этой статьи. КомментарииКомментарий от ГрАнда №1: В RTADS это выглядит так (использовал генератор, что ускорило процедуру до 7 секунд) nozhnitsy : item location = startroom sdesc = "ножницы" // именительный падеж rdesc = "ножниц" // родительный падеж ddesc = "ножницам" // дательный падеж vdesc = "ножницы" // винительный падеж tdesc = "ножницами" // творительный падеж pdesc = "ножницах" // предложный падеж noun = 'ножницы' 'ножниц' 'ножницам' 'ножницами' 'ножницах' 'ножницам#d' 'ножницами#t' isThem = true // Вот это чудо, которое // родилось после этой статьи ;Вернуться к основному тексту Комментарий от ГрАнда №2: В RTADS, где файл adv.t называется advr.t, это выглядит так: verDoTakeOut(actor, io) = { if (io <> nil and not self.isIn(io)) { ZAG(self,&sdesc); " "; " не наход<<glok(self,2,1)>>ся в "; io.pdesc; ". "; } else self.verDoTake(actor); /* убедиться, что объект вообще можно брать */ } Т.е. у нас вообще нет thedesc, а буквы делаем заглавными другой функцией. Функция glok(self,2,1) подбирает нужное окончание для глагола второго спряжения и с гласной, не включающей "й". При этом, глагол должен согласовываться с объектом self. Таким образом, так как разнообразие окончаний у нас больше(только тут: -ишь -ит -им -ите -ят), мы не могли обойтись таким перебором, каким обойдётся далее автор статьи. Вернуться к основному тексту Комментарий от ГрАнда №3: Позже Майк Робертс преобразует этот фрагмент, назначив каждому предмету строковое свойство "isntdesc" (в оригинале, проблема состоит в правильной подстановке "is not" или "are not") Теперь код таков: verDoTakeOut(actor, io) = { if (io <> nil and not self.isIn(io)) { caps(); self.thedesc; " "; self.isntdesc; " in "; io.thedesc; ". "; } else self.verDoTake(actor); /* убедиться, что объект вообще можно брать */ }Вернуться к основному тексту Комментарий от ГрАнда №4: Чувствительность к регистру теперь опциональна. Вернуться к основному тексту Комментарий от ГрАнда №5: Поздние версии TADS позволили это сделать для РТАДС. Как видите, теперь передаются все возможные параметры. Но при этом ряд проблем ещё остаётся. Обработчик РТАДС сейчас выглядит подобным диким образом: parseAskobjActor: function(a, v, ...) { if (argcount = 3) { if (getarg(3)!=toPrep or v.type!=2) ZA(dToS(getarg(3),&sdesc)); if (getarg(3)=onPrep or getarg(3)=thruPrep or getarg(3)=inPrep or getarg(3)=atPrep) " что "; else if ((getarg(3)=underPrep) or (getarg(3)=behindPrep) or (getarg(3)=overPrep)or (getarg(3)=betweenPrep) or (getarg(3)=aboutPrep)) " чем "; else if (getarg(3)=goonPrep) " чему "; else if (getarg(3)=toPrep) { if (v.type=2) "Кому "; else " чему "; } else " чего "; if (a <> parserGetMe() or a.lico=3) { a.sdesc;" ";"долж<<ok(a,'ны','ен','но','на')>> "; } else "Вы хотите "; v.sdesc;" "; if (getarg(3)!=aboutPrep) { if (v.pred) v.pred.sdesc; " это?"; } else (v=tellVerb)?"этому?":"этого?"; //Тоже звучит некрасиво :( } else { ZA(dToS(v,&vopr)); if (a <> parserGetMe() or a.lico=3) { a.sdesc;" ";"долж<<ok(a,'ны','ен','но','на')>> "; } else "<<parserGetMe().sdesc>> хо<< (parserGetMe().isThem)?"тите":"чешь">> ";"<<v.sdesc>>?"; } }Впрочем, часть дикости объясняется моими нововведением - различается второе и третье лицо персонажа. В английском TADS это по-прежнему требует изменения кода. Вернуться к основному тексту Комментарий от ГрАнда №6: О, да! Он слушал! Вернуться к основному тексту © Andrew Pontious Перевод: Wizard Nash Правка и комментарии: Андрей Гранкин aka GrAndrey 2004 |