АГРЕГАТЫ ВРЕМЕНИ
“Оторвать и выбросить!” – ворочался Ватрушкин на равнодушных пухлостях подушек. Вредный сон как солнечный зайчик прятался в складках простыни, под подушки, мимо кровати. Стоило Ватрушкину, наконец, расслабиться и ощутить, что вот, – о, неужели!– кажется, мысли улетучиваются, и томная вуаль забытья рябится на подкорке мозга … И тут сон опять выскакивает, как чертик, из под изголовья, лупит Ватрушкина по голове, и смывается обратно в складки.
Верткие и чужие мысли топтались по ушам и макушке, беззвучно гогоча и щелкая мебелью и половицами; сон, играя с ними в поддавки, делал испуганный вид и шарахался от каждого такого щелчка.
“Когда же это кончится!” – бормотал Ватрушкин, наматываясь на подушку.
Зазвонил телефон.
[highlight]— Где телефон[/highlight]
Телефон стоял на унылой тумбочке. Глаза туда все равно смотреть не хотят.
[highlight]— Хочу телефон[/highlight]
Рука - отдельно от Ватрушкина - сопротивлялась мысленному приказу "лежать спокойно", и взяла трубку.
“Господин Ватрушкин? Беспокоит служба отключения времени. Вы знаете, что у вас задолженность за десять с половиной дней?”
– Да, знаю, знаю. – отвиноватился Ватрушкин. Заспанная взъерошенность смотрела перед собой, держа трубку в ухе. – Что, уже даже десять дней?
“Эти десять дней у вас идут по штрафному тарифу. Если вы не уплатите в течение одного дня, тариф будет удвоен. Если вы не уплатите в течение двух дней, ваши данные будут переданы…”
– Понятно, ясно. Понял. – Ватрушкин зевнул и подтянул брови, рассеянно примеряя удивленное лицо. “Откуда вы узнали мой номер?” – думал было пошутить он, но ограничился подумкой.
Трубка смачно кляцнула в телефон. “Понятно теперь, откуда эта дурацкая бессонница.” Ему грозили отключить время. “Вот зараза.” Пора было идти на работу.
[highlight]— Хочу встать [/highlight]
Пятки отметили холодный пол. Ватрушкин зажмурился и стал наискивать связь со своим организмом. "Интересно, а сколько сейчас времени?"
[highlight]— Какое время [/highlight]
“Так, что там у нас…” – Ватрушкин глянул на “наручники” – часы, которые он носил на запястье. Там, черные острые стрелки нарочито тыкали в критическую область циферблата. Хозяин часов ощутил, как эти стрелки царапали ему нервы. Боковым зрением Ватрушкин увидел, как со стула обреченно слетела мятая рубашка. Там же оставались висеть мятые штаны, а на сиденье дремал пиджак.
[highlight]— Хочу одежду[/highlight]
Ватрушкин завернул себя в двухдневную рубашку и намазал на нее галстук. При близком рассмотрении рубашка несла на себе следы как минимум недели.
[highlight]— Хочу дезодорант [/highlight]
Перебрав все одеколоны, он решил, что сегодня день без запаха.
[highlight]— Какая комната[/highlight]
На стене висячий календарь уже целый месяц безучастно демонстрировал одну и ту же картину, заставляя, как в лотерее, выискивать выпавшее число.
[highlight]— Хочу выйти[/highlight]
Уже почти одетого, Ватрушкина понесло было к входной двери, но шлепанье собственных пяток об еще не нагретые плитки пола, сбило его с толку.
[highlight]— Хочу обуться[/highlight]
Заплетая шнурки на туфлях, Ватрушкин чувствовал, как где-то за спиной украдкой сочатся штрафные минуты и секунды; он уже почти опаздывал.
Надеюсь, вы поняли, что речь идет о новой игре. Пока я размышляю, в каком интерпретаторе его писать (Луа или Ртадс), предлагаю себя в этой ветке форума в качестве парсера. Можете писать фразы, словно уже играете. А я буду реагировать развитием сюжета.
Предупреждение: в игре я намереваюсь использовать модель мотивации обычного человека в повседневной ситуации (в отличии от мотивации игрока в адвенчуры, типа "взять, открыть, применить, добыть и т.п.".
По моему мнению, обычный человек в основном движим желаниями, а уж потом он задается (вербально или невербально) вопросами, и ищет на них ответы. Прямые же указания к действию (себе) он применяет относительно редко (проследите за собой, забавы ради.) В силу этих соображений, эта игра будет реагировать на следующие формы сентенций:
а) Простое вопросительное предложение КАКОЙ(ая-ое),ГДЕ, ПОЧЕМУ, КТО, ОТКУДА, ЗАЧЕМ, КОГДА + ОБЪЕКТ или ПЕРСОНАЖ
б) глагол ХОЧУ (или БУДУ) + ОБЪЕКТ или НЕПЕРЕХОДНЫЙ ГЛАГОЛ (спать, идти, стоять, лежать, прыгать, отвечать, говорить ...)
Как такового применения объекта к объекту ("открыть дверь ключом") не планируется, т.к. загадки будут несколько другого рода.
Поскольку основные куски лит-части лежат и ждут своего применения, осталось продумать и написать код. (ничего себе, "осталось", подумал я. Тем более, что в первый раз. ) Но прежде чем это делать, я хочу немного поиграть с реальными людьми. Чтобы вам было интереснее, немного о сюжете.
Как немного ясно из вступления, героя отключат от услуги под названием "время". То есть, уже довольно скоро он ощутит на себе последствия этого отключения (как нам в реальной жизни урезают интернет, мобильную связь и т.п.)
Местами есть параллели с одним из путешествий Ийона Тихого (надеюсь, помните такого?), где он попал в темпоральный вихрь.
Местами также есть параллели с моей любимой текстовой игрой - "Шрапнель" А.Кадра. В ней обычная логика "поиска и применения предметов" не имела никакого смысла. Главное - ощущение ситуации и момента, в котором оказался герой. А также ход его мыслей и точки приложения внимания, - вот и все.
Итак, кому интересно, вперед. Вводите фразы, как если бы вы общались с парсером, например после символа > .
Неактивен
pureasm написал:
[font=Courier][color=#ff9900] Надеюсь, вы поняли, что речь идет о новой игре. Пока я размышляю, в каком интерпретаторе его писать (Луа или Ртадс), предлагаю себя в этой ветке форума в качестве парсера. Можете писать фразы, словно уже играете. А я буду реагировать развитием сюжета.
А можно по несколько команд сразу? А то прямо шахматы по переписке получаются;).
Итак:
—ОТКУДА ЗАДОЛЖЕННОСТЬ?
—КАКОЙ, НА ФИГ, ШТРАФНОЙ ТАРИФ?
—ЧТО ЗА УСЛУГА ВРЕМЯ?
—ХОЧУ ПОГАСИТЬ ЗАДОЛЖЕННОСТЬ
Неактивен
(Я правда, имел в виду, что все предыдущие команды были введены после того, как Ватрушкин положил трубку, ну да ладно, можно и так)
—ХОЧУ УЗНАТЬ СУММУ ЗАДОЛЖЕННОСТИ
—ХОЧУ ПОПОЛНИТЬ ФОНД СЕЙЧАС
(если пополнить прямо сейчас не получается)
—ХОЧУ ОТКЛЮЧИТЬ УСЛУГУ
(О, блин, а ведь интересно, так, глядишь, и втянусь;).
Неактивен
oleksus написал:
[color=#ff9900][font=Arial]
ЖЕЛАЕТЕ ВОССТАНОВИТЬ ИГРУВЫЙТИ ИЗ ИГРЫ?
—ХОЧУ ВОССТАНОВИТЬ ИГРУ
—КАКОЙ КОД?
—ХОЧУ ВВЕСТИ КОД
—ХОЧУ ПОГАСИТЬ ЗАДОЛЖЕННОСТЬ
(Ну, и вопросы, поднятые zer'ом, меня тоже волнуют, конечно же
Неактивен
oleksus написал:
Я хочу разнообразить выводимые ldesc и sdesc для объектов и локаций. Как оформить несколько описаний в список и выводить случайным образом одно из них методом shuffleList? Когда все длинные описания выйдут, далее выводятся только короткие, тоже вперемежку.
Честно говоря, не понял про shuffleList; я бы данную задачу решил вот так:
class MyRoom: room
// Общее кол-во длинных описаний
NrOfLongDescs=3
// Кол-во показанных длинных описаний
NrOfLongDescsDisplayed=0
// Общее кол-во коротких описаний
NrOfShortDescs=3
//Список длинных описаний
LongDescs=['Это большая комната бла бла бла' 'Знакомая тебе комната бла бла' 'Ничего в этой комнате не поменялось бла бла']
//Список коротких описаний
ShortDescs=['Известно какая' 'Просто комната' 'Комната и все тут']
DisplayedLDs=[]
ldesc={local i, FoundFree, count, PosInList;
if(self.NrOfLongDescsDisplayed<self.NrOfLongDescs)
{// Выводим длинные описания
FoundFree:=nil;
count:=0;
while((not FoundFree) and (count<1500)) // Условие count - защита от непредвиденного зацикливания
// (вообще-то перестраховка)
{count++;
i:=rand(self.NrOfLongDescs);
PosInList:=find(self.DisplayedLDs,i);
if(PosInList=nil)
{// Нашли описание, которое еще не использовалось; выводим его
say(self.LongDescs[i]);
// Устанавливаем флаг выхода из цикла, добавляем номер отображенного описания в
// соответствующий список, увеличиваем число отображенных длинных описаний
self.DisplayedLDs:=self.DisplayedLDs+i;
self.NrOfLongDescsDisplayed++;
FoundFree:=true;
}
}
}
else
{// Длинные описания кончились; выводим короткие
i:=rand(self.NrOfShortDescs);
say(ShortDescs[i]);
}
}
Согласен, выглядит громоздко, зато это - готовое решение (ну, если кто-нибудь бага не найдет . В дальнейшем достаточно будет ввести что-то типа
Komnata: MyRoom
;
и переопределить первые четыре свойства "по месту".
Неактивен
Да, и молчаливо предполагаем, что генератор случайных чисел уже запущен инструкцией randomize() (скажем, где-нибудь в init )
Неактивен
oleksus написал:
Спасибо за код, начну строить первую локацию...
Появилась еще одна мысль на предмет парсера...
Не хочу показаться занудой, но лично я бы рекомендовал воздержаться от каких-либо действий по написанию кода, пока все мысли на предмет парсера, да и всех прочих аспектов игры не утрясутся, и на свет не появится такой документ, как общий план игры (от *самого* начала до *самого* конца): это может быть, например, список локаций с перечнем содержащихся в них хотя бы основных объектов, влияющих на ход игры, и их основных реакций (опять-таки влияющих на развитие сюжета). Понимаю, что занимаюсь практически самоцитированием, но рекомендую по данному поводу перечитать главу 6 (R)TADS-мануала. Очень жизненно
Неактивен
oleksus написал:
Сегодня надо набраться свободных электронов, и утром уже толкну полноценные ответы по игре.
Ну вот, только, можно сказать, втянулся, а парсер завис...
Неактивен
oleksus написал:
Вот по РТАДС есть несколько вопросов.
1) Я хочу заменить функцию look at на простое введение названия объекта. То есть, игрок видит в описании интересующее существительное, вводит его, и получает описание объекта, как если бы он ввел "осмотреть предмет".
Как это сделать?
Да, вопрос сильно нетривиальный. Надо сказать, что по умолчанию TADS заточен под шаблоны команд, начинающихся с глагола, поэтому реализовать вышеописанный ввод, не влезая в парсер, не получится.
Но это не значит, что в RTADS этого сделать вообще нельзя;).
Возможны два варианта: простой, но неэлегантный и кривоватый, а также посложнее, но (надеюсь;)) попрямее.
Неэлегантный вариант - просто переопределить для всех предметов в игре глаголы с тем же набором лексических свойств, что и для предметов. Недостатки: 1) сильно утомительно; 2) трудновато будет реализовать прилагательные (к предмету можно обратиться сочетанием прилагательное-существительное (два лексическиз свойства - adjective и noun), а у глаголов на все про все одно-единственное лексическое свойство verb).
Вариант поэлегантнее - см. следующее сообщение.
Неактивен
Для разбора используем функцию parseUnknownVerb (применяется в тех случаях, когда встроенный парсер не может понять формат команды).
parseUnknownVerb: function(actor, wordlist, typelist, errnum)
{// В общем случае функция осуществляет дополнительный разбор команды игрока в случае,
// если в этой команде отсутствует глагол или в качестве глагола используется слово,
// отсутствующее в словаре игры, либо для разбора команд нестандартного формата.
// В данном случае приспосабливаем ее для того, чтобы выводить описание объекта в том
// случае, если игрок ввел только название объекта без глагола
//
local i, listsize, RetObj, IncorrectFirstTry, ResultList, BuffObj;
// Прежде всего проверяем, ввел ли игрок просто название объекта. Данная проверка носит
// предварительный характер - просто чтобы отбросить заведомо неподходящие варианты.
//
listsize:=length(typelist);
i:=1;
while(i<=listsize)
{if(((typelist[i] & PRSTYP_ADJ)=0) and ((typelist[i] & PRSTYP_NOUN)=0))
{// Команда заведомо не является простым словосочетанием (одно из составляющих команду
// слов - не прилагательное и не существительное (для простоты не заморачиваемся с
// множественными числами); дальше возиться с ним смысла не имеет -
// возвращаем nil (после чего система сама выведет стандартное сообщение об ошибке)
//
return nil;
}
i++;
}
// Итак, у нас - словосочетание, состоящее только из существительных и прилагательных;
// пытаемся подобрать под него объект в игре
//
IncorrectFirstTry:=nil;
RetObj:=parseNounList(wordlist, typelist, 1, nil, nil, true);
// Проверяем результат подбора:
//
if(RetObj=nil)
{// Синтаксически некорректное словосочетание; вообще-то этот вариант должен был
// быть отсечен на предыдущей стадии обработки, но на всякий случай предусматриваем
// этот вариант и здесь: устанавливаем флаг, указывающий на то, что в первом варианте
// подбора словосочетание было признано некорректным
//
IncorrectFirstTry:=true;
}
else
{if((length(RetObj)>1) and (RetObj[1]>length(wordlist)))
{// Словосочетание корректное, и удалось подобрать объекты для него; продолжаем подбор
// в интерактивном режиме
//
ResultList:=parserResolveObjects(actor, inspectVerb, nil, nil, PRO_RESOLVE_DOBJ, &verDoInspect,
wordlist, RetObj, nil);
if (ResultList[1] = PRSERR_DISAMBIG_RETRY)
{// В процессе подбора (устранения неопределенности) игрок ввел новую команду;
// запускаем ее
//
parserReplaceCommand(ResultList[2]);
return true;
}
else
{if(ResultList[1] = PRS_SUCCESS)
{// Удачное завершение; выводим длинное описание подобранного объекта
//
ResultList[2].ldesc;
}
// В любом случае, удачное завершение или нет, дальнейшей обработки не
// требуется (при неудачном завершении сообщение об ошибке было выведено parserResolveObjects)
//
return true;
}
}
}
(Окончание см. следующее сообщение)
Неактивен
(Окончание)
// Если мы оказались здесь, то это означает, что либо в предыдущем варианте словосочетание
// было признано некорректным (тогда IncorrectFirstTry=true), или для него просто не
// нашлось объектов в игре (тогда IncorrectFirstTry=nil), либо оно было разобрано не до конца
// (тогда также IncorrectFirstTry=nil). Пытаемся использовать альтернативный
// вариант подбора (для словосочетаний вида "собака Павлова", которые стандартному
// подборщику TADS не по зубам - по крайней мере, без дополнительной подготовки)
//
if(length(wordlist)>1)
{// Длина списка >1, т. е. теоретически возможно, что игрок ввел словосочетание вида "часы брата";
// дополнительно проверяем наличие соответствущего определения для последнего слова в словосочетании
//
RetObj:=[]+(wordlist[length(wordlist)]+'#r');
BuffObj:=parserDictLookup(RetObj, [PRSTYP_ADJ]);
if(BuffObj=[])
{// Такого определения нет, так что нечего и пытаться дальше разбирать словосочетание
//
if(IncorrectFirstTry)
{// Предоставляем системе самой вывести сообщение об ошибке
//
return nil;
}
else
{// Выводим сообщение сами
//
"Я не вижу здесь этого.";
return true;
}
}
else
{// Меняем порядок элементов в исходных списках слов и типов и пытаемся выполнить разбор еще раз
//
BuffObj:=wordlist[length(wordlist)];
wordlist[length(wordlist)]:=wordlist[length(wordlist)-1];
wordlist[length(wordlist)-1]:=BuffObj;
BuffObj:=typelist[length(typelist)];
typelist[length(typelist)]:=typelist[length(typelist)-1];
typelist[length(typelist)-1]:=BuffObj;
RetObj:=parseNounList(wordlist, typelist, 1, nil, nil, true);
// Вновь проверяем результат
//
if(RetObj=nil)
{// Синтаксически некорректное словосочетание; проверяем, был ли синтаксически некорректным
// предыдущий порядок слов, и в зависимости от этого выводим сообщение сами или
// предоставляем это сделать системе
//
if(IncorrectFirstTry)
{// Предоставляем системе самой вывести сообщение об ошибке
//
return nil;
}
else
{// Выводим сообщение сами
//
"Я не вижу здесь этого.";
return true;
}
}
else
{if((length(RetObj)>1) and (RetObj[1]>length(wordlist)))
{// Словосочетание корректное, и удалось найти объекты; завершаем подбор
// в интерактивном режиме
ResultList:=parserResolveObjects(actor, inspectVerb, nil, nil, PRO_RESOLVE_DOBJ, &verDoInspect,
wordlist, RetObj, nil);
if(ResultList[1] = PRSERR_DISAMBIG_RETRY)
{// В процессе подбора (устранения неопределенности) игрок ввел новую команду;
// запускаем ее
//
parserReplaceCommand(ResultList[2]);
return true;
}
else
{if(ResultList[1] = PRS_SUCCESS)
{// Удачное завершение; выводим длинное описание подобранного объекта
//
ResultList[2].ldesc;
}
// В любом случае, удачное завершение или нет, дальнейшей обработки не
// требуется (при неудачном завершении сообщение об ошибке было выведено parserResolveObjects)
//
return true;
}
}
else
{// Словосочетание корректное, но объектов для него нет; выводим соответствующее
// сообщение и завершаем обработку
//
"Я не вижу здесь этого.";
return true;
}
}
}
}
else
{// В словосочетании всего одно слово - значит, формат "собака Павлова" неприменим по определению
//
if(IncorrectFirstTry)
{// Предоставляем системе самой вывести сообщение об ошибке
//
return nil;
}
else
{// Выводим сообщение сами
//
"Я не вижу здесь этого.";
return true;
}
}
}
Объем кода пусть не пугает: во-первых, он раздут за счет подробных комментариев, а во-вторых, я сознательно ничего не оптимизировал (в частности, там некоторые фрагменты кода повторяются по нескольку раз).
Ограничения данного варианта: не будут корректно работать множественные числа (свойство plural), а также перечисление объектов через запятую (например, если игрок введет "шкаф и стол"). В остальном, насколько я могу судить по короткому тесту - вполне работоспособный вариант.
Информацию по "точке входа" parseUnknownVerb см. в разделе 4.3 руководства по (R)TADS, по используемым в ходе разбора функциям parseNounList, parserResolveObjects, parserReplaceCommand, parserDictLookup - в разделе 4.2 того же руководства (подраздел "Обработка введенной строки непосредственно из игры").
Неактивен
Unreal написал:
Ух ты... Впечетляет. :-) Хотя, лучше бы свою игру понемногу разрабатывал. По-моему, не такая уж и проблема вместо "забор" написать "о забор" — всего 2 символа лишних.
Ага. А дальше понеслось: "синонимы - фиг с ними, не такая уж проблема вместо "здание" набрать "дом", падежи - да на хрен они нужны? Обойдемся распознаванием первых трех букв слова" и т. п. Кто там у нас главный враг самодельных парсеров?
Не, ну на самом деле я просто прикалываюсь, пожалуйста, без обид. А желание автора сделать все стилистически законченным до мелочей мне очень даже понятно - я сам такой же:).
Неактивен
oleksus написал:
Я в шоке. Я просто в ужасе от того, как просто делается в LUA все то, чему посвящается масса гемора в английских изобретениях типа inform и TADs.
Мдяя... я конечно уважаю все предложенные советы и помощь, но на обустройство нужного мне парсера в ЛУА у меня ушло вдесятеро меньше строк кода. ТАДС мне напоминает чугунный скафандр для бальных танцев. ;D
Все, еще несколько дней без лени и ЛУА версия игры готова. Выложу на свой ФТП и сразу дам ссылку.
6days рулит! 8-)
Ну, уважаемому oleksus'у, наверное, уже все равно, он свою игру уже написал на LUA, а вот вниманию всех остальных любителей простых решений предлагаю на будущее RTADS'овский код, решающий ту же задачу (напомню, речь шла о том, чтобы игра распознавала введенное название объекта как синоним команды "осмотреть <объект>"), но "кривее" и с большим количеством ограничений.
class HybridItem: thing
action(actor)={if(self.isVisible(parserGetMe().location))
{self.ldesc;
}
else
{"Ты не видишь этого здесь.";
}
}
;
Shkaf: fixeditem, HybridItem
verb='шкаф'
noun='шкаф'
sdesc="шкаф"
ldesc="Это просто шкаф."
;
Ограничения по сравнению с "развесистым" вариантом, опубликованным ранее:
- затруднена оддержка прилагательных (если у нас есть черный шкаф, большой черный шкаф, шкаф старшей сестры и т. п., реализовать все возможные комбинации будет тяжеловато);
- трудности возникают, когда у нас имеется несколько объектов с одинаковыми названиями в игре (например, тех же шкафов). Связано это с тем, что свойство verb должно быть уникальным для каждого глагола. Причем, если эти объекты разбросаны по разным локациям (например, шкафы стоят в разных комнатах и не могут быть перемещены), то это ограничение обходится, хотя и кривовато, путем использования динамических лексических свойств (addword/delword) - просто при входе в определенную комнату мы "переключаем" дублирующееся лексическое свойство verb c "ненужного" (оставшегося в другой комнате) объекта на "нужный". Сложнее будет обстоять дело, если у вас в игре, например, имеется куча дверей и куча ключей к ним, которые игрок может собирать. При этом данный "простой" способ перестает работать.
Резюмируя, можно сказать: данный короткий способ будет приемлемо работать в небольшой игре, не балующей игрока обилием прилагательных и синонимов в описаниях объекта. При росте размеров игры и увеличении числа объектов, в том числе совпадающих по лексическим свойствам, затраты на обход ограничений данного способа также начнут довольно быстро возрастать, а в ряде случаев и они не помогут.
Предложенный ранее "сложный" способ свободен от этих ограничений. Он будет работать одинаково для любого размера игры/числа объектов в ней, поскольку действует на иных принципах.
Отредактировано uux (06.04.2007 19:13)
Неактивен
HIman написал:
А для особо любопытных, ещё и исходник игры не для того чтобы найти подсказки, а для познавательных целей движка.
Насколько я понимаю (и пусть Рыцарь в Серой Фланели поправит меня, если это не так), на данном этапе развития платформы "6 days" (основанной на языке LUA) игры не шифруются, т. е. исходник будет доступен для чтения по-любому.
Неактивен
zerrr написал:
Валя, а где он игру-то выложил? Задумка интересная, язык богатый - очень хочется глянуть, что же вышло.
Я же сказал "наверное, написал", т.е. это мое предположение. Просто я исходил из того, что заявленные автором "пара дней без лени" за месяц простоя форума точно должны были состояться;). А на игру мне и самому было бы любопытно взглянуть...
zerrr написал:
И где этот самый LUA посмотреть ? обрыл интернет - не нашел рабочих ссылок:)
Посмотри сайт Belial'а: http://questtext.narod.ru/run.html. Игру "Клара - расхитительница варенья" с КРИЛ-06, надеюсь, не забыл? Вот она написана в системе "6 days", которая, в свою очередь, основана на языке LUA.
Кстати, рад, что у тебя проблемы с доступом к форуму решились;).
Отредактировано uux (07.04.2007 08:07)
Неактивен
mihawww написал:
Народ, oleksus,
Если эта игра существует, дайте мне ссылку на неё.
О судьбе "Агрегатов" можно почитать вот здесь: https://forum.ifiction.ru/viewtopic.php?id=524&p=2
Только читать надо внимательнее - там куча тем затронута, а "АВ" посвящен один абзац.
Неактивен