Forum.iFiction.Ru

iFiction.Ru · ifHub · FAQ · IFWiki · QSP · URQ · INSTEAD · AXMA

форум об interactive fiction, текстовых приключенческих играх и всём таком...

Вы не зашли.

0    0    #1
05.12.2007 12:55

Flint
Участник
Зарегистрирован: 06.09.2007
Сообщений: 148

---

Универсальный spellchecker для RTADS

Людям свойственно ошибаться. При наборе, к примеру, мы частенько делаем опечатки. В этом случае текстовые процессоры подчеркивают неизвестные слова, а поисковая система Google предлагает вариант исправления для неправильного слова.

Интерфейс всех парсерных игр основан на вводе текста, причем количество введенного игроком текста за всю игру может быть весьма существенным. Естественно, не обходится без ошибок. «К сожалению, слово "деференциал" мне неизвестно.» Довольно странным выглядит то, что эта проблема никак не решается в этой абсолютно текстовой среде, где, вроде бы, сам бог велел (а ведь сейчас проверку орфографии встраивают даже в браузеры), тем более, что никаких словарей для проверки таскать за собой не надо – словарь, вот ведь он, заботливо объявленный автором в самой игре!

Поэтому я решил написать нечто вроде spellchecker для RTADS.
Он исправляет опечатки следующих типов:
1) пропустили букву
>о холдильник

2) набрали лишнюю букву
>о холодлильник

3) перепутали буквы местами (частенько случается при наборе)
>о холдоильник

4) ошиблись в букве
>о халодильник

После того, как вы нажмете Энтер, на экране, вместо раздражающего

«К сожалению, слово "халодильник" мне неизвестно.»

вы увидите

Возможно, вы имели в виду «холодильник».

Высокий белый холодильник. На боковую стенку налеплен магнит в виде головы панды (довольно дурацкий, верно?).


Мой spellchecker исправляет только одну ошибку на слово (т.е. «халодильник» он исправит, а «халадильник» уже нет). Это связано с объемом вычислений. Для слова из 7 букв выполняется 478 проверок на одну ошибку, соответственно, для проверки на 2 ошибки необходимо производить 478^2 = 228484 проверок, что конкретно затормаживает игру. К тому же, по статистике Google, из всех ошибок при вводе ошибки в одну букву составляют более 90% случаев, так что большинство опечаток мой spellchecker исправит.

Спелчекер универсален – он использует активный словарь текущей игры, обеспечивая тем самым 100% релевантность исправлений и минимум ложных срабатываний :-).

Чтобы подключить spellchecker к вашей игре на RTADS, сделайте следующее (процесс описан для файлов 24 релиза библиотек, но, скорее всего, заработает и на более ранних):

1. Откройте файл advr.t, найдите там функцию preparse и в самый ее конец, но перед “return comStr;” воткните
global.prevCommand := comStr;

2. Откройте файл erroru.t и в самый конец файла добавьте

parseErrorParam: function(errornum, string, ...)
{
    local correct, pos, newstr;
   
    if (errornum != 2)
        return parseError(errornum, string);
   
    correct := spellcheck(getarg(3));
    if (correct)
    {
        pos := reSearch(getarg(3), global.prevCommand);
        newstr := substr(global.prevCommand, 1, pos[1] - 1) + correct + substr(global.prevCommand, pos[1] + pos[2], length(global.prevCommand));
       
        "Возможно, вы имели в виду <b>&laquo;<<correct>>&raquo;</b>.<br><br>";
        parserReplaceCommand(newstr);
    }
   
    return parseError(errornum, string);
}

spellcheck: function(word)
{
    local ruslet := 'абвгдежзиклмнопрстуфхцчшщьыъэюя';
    local englet := 'abcdefghijklmnopqrstuvwxyz';
    local curlet := ruslet;
    local i, j;
   
    local variants = [];
    local found;
   
    //узнаем, английский ли это текст или нет
    for (i := 1; i <= length(word); i++)
    {
        for (j := 1; j <= length(englet); j++)
        {
            if (substr(word, i, 1) = substr(englet, j, 1))
            {
                curlet := englet;
                goto next;
            }
        }
    }
    next: ;
   
    //пропущенные буквы
    for (i := 1; i <= length(curlet); i++)
    {
        for (j := 0; j <= length(word); j++)
        {
            variants += substr(word, 1, j) + substr(curlet, i, 1) + substr(word, j + 1, length(word));
        }
    }
   
    //лишние буквы
    for (i := 0; i < length(word); i++)
    {
        variants += substr(word, 1, i) + substr(word, i + 2, length(word));
    }
   
   
    //неправильные буквы
    for (i := 1; i <= length(curlet); i++)
    {
        for (j := 0; j < length(word); j++)
        {
            variants += substr(word, 1, j) + substr(curlet, i, 1) + substr(word, j + 2, length(word));
        }
    }
   
    //перепутанные местами буквы
    for (i := 0; i < length(word) - 1; i++)
    {
        variants += substr(word, 1, i) + substr(word, i + 2, 1) + substr(word, i + 1, 1) + substr(word, i + 3, length(word));
    }
   

    for (i := 1; i <= length(variants); i++)
    {
        found := parserDictLookup([] + variants[i], [PRSTYP_NOUN]);
        if ( length(found) > 0)
            return variants[i];
    }
   
    for (i := 1; i <= length(variants); i++)
    {
        found := parserDictLookup([] + variants[i], [PRSTYP_VERB]);
        if ( length(found) > 0)
            return variants[i];
    }

    for (i := 1; i <= length(variants); i++)
    {
        found := parserDictLookup([] + variants[i], [PRSTYP_ADJ]);
        if ( length(found) > 0)
            return variants[i];
    }
   
    return nil;
}

Все! Компилируйте игру и можете пробовать.

Из недостатков спелчекера стоит отметить то, что он не занимается подбором форм слов, поэтому возможно неграмотное с точки зрения русского языка исправление написания, например «осмотреть сантехникв» (хотели написать «осмотреть сантехника») будет исправлено на «осмотреть сантехник». К сожалению, это никак не исправить.

Просьба протестировать работу на имеющихся у вас проектах RTADS. Пожелания и предложения приветствуются.

И напоследок, может кто-нибудь знает, как убрать вывод сообщения [TADS-1014: 'abort' statement executed] при каждом вызове функции parserReplaceCommand?

RTADS – лучшая платформа!

Неактивен

0    0    #2
06.12.2007 13:44

Flint
Участник
Зарегистрирован: 06.09.2007
Сообщений: 148

---

Re: Универсальный spellchecker для RTADS

А нельзя ошибку просто как-нибудь заныкать? У нее же есть код.
То есть что-то вроде:

Код:

parseErrorParam: function(errornum, string, ...)
{
...

if (errornum = 1014)
    return ''; //пустая строка

}

Я так пробовал, но почему-то не заработало (наверное, потому что номер больше 100).

Отредактировано Flint (06.12.2007 13:45)

Неактивен

0    0    #3
21.01.2008 02:50

Flint
Участник
Зарегистрирован: 06.09.2007
Сообщений: 148

---

Re: Универсальный spellchecker для RTADS

И снова здравствуйте.

uux, снимаю шляпу. Я бы сам ни за что не полез в эту внутреннюю кухню парсера, потому что практически ничего в ней не понимаю, ограничиваюсь лишь самыми поверхностными слоями.

Мое молчание было обусловлено лишь нехваткой времени из-за подготовки своей игры на КРИЛ, а ни в коем случае не потерей интереса или недооценке сделанного uux-ом. Да и приходить с пустыми руками не хотелось.

Поэтому представляю вашему вниманию обновленную версию spellchecker. Я изменил лишь саму функцию spellcheck, чтобы ничего не поломать в коде uux’а.

Что нового:
- убран спойлерный потенциал, теперь спеллчекер проверяет только видимые персонажу объекты;
- теперь не проверяются слова короче 4-х букв;
- мелкие улучшения;

Прошу любить и жаловать:

Код:

spellcheck: function(word)
{
    local ruslet := 'абвгдежзиклмнопрстуфхцчшщьыъэюя';
    local englet := 'abcdefghijklmnopqrstuvwxyz';
    local curlet := ruslet;
    local i, j, k;
    
    local TYPES = [PRSTYP_NOUN PRSTYP_VERB PRSTYP_ADJ];
    local variants = [];
    local found;
    
    //Если 4 символа или короче, то не обрабатываем
    if (length(word) <= 4)
        return nil;

    //узнаем, английский ли это текст или нет    
    if ( reSearch('[a-z]', word) )
        curlet := englet;
    
    //пропущенные буквы
    for (i := 1; i <= length(curlet); i++)
    {
        for (j := 0; j <= length(word); j++)
        {
            variants += substr(word, 1, j) + substr(curlet, i, 1) + substr(word, j + 1, length(word));
        }
    }
    
    //лишние буквы
    for (i := 0; i < length(word); i++)
    {
        variants += substr(word, 1, i) + substr(word, i + 2, length(word));
    }
    
    
    //неправильные буквы
    for (i := 1; i <= length(curlet); i++)
    {
        for (j := 0; j < length(word); j++)
        {
            variants += substr(word, 1, j) + substr(curlet, i, 1) + substr(word, j + 2, length(word));
        }
    }
    
    //перепутанные местами буквы
    for (i := 0; i < length(word) - 1; i++)
    {
        variants += substr(word, 1, i) + substr(word, i + 2, 1) + substr(word, i + 1, 1) + substr(word, i + 3, length(word));
    }
    
    for (i := 1; i <= length(TYPES); i++)
    {
        for (j := 1; j <= length(variants); j++)
        {
            found := parserDictLookup([] + variants[j], [] + TYPES[i]);
            for (k := 1; k <= length(found); k++)
            {
                if (TYPES[i] = PRSTYP_VERB)
                    return variants[j];
                if ( found[k].isVisible(parserGetMe()) )
                    return variants[j];
            }
        }
    }
    
    return nil;
}

Осилит дорогу идущий!

Неактивен

0    0    #4
22.01.2008 01:05

Flint
Участник
Зарегистрирован: 06.09.2007
Сообщений: 148

---

Re: Универсальный spellchecker для RTADS

Сегодня день у меня выдался продуктивный.
Первую половину дня наполнял ifwiki.ru, вторую половину вникал в дао парсера. Путем меганапряжения своего мозга я, кажется, нашел работающее решение для spellchecker.
Вместо того, чтобы идти в атаку глубже, как попробовал uux, я, наоборот, отступил и вставил проверку ЕЩЕ РАНЬШЕ – в функцию preparse. В конце-концов, невелика разница, получаем ли мы уже готовое слово (предоставляемое функцией parseErrorParam) или сами находим его в строке, введенной игроком (по флагу PRSTYP_UNKNOWN).

Поэтому описываю еще раз процесс установки:

0. (если у вас уже стоит спеллчекер)
- в функции preparse (advr.t) удалить строку “global.previousCommand := comStr;”
- в файле errorru.t удалить нафиг функции parseErrorParam и spellcheck

1. В файл errorru.t добавить следующий код:

Код:

checkString: function(str)
{
    local wordlist, typelist;
    local i;
    local correct, pos;
    
    wordlist := parserTokenize(str);
    typelist := parserGetTokTypes(wordlist);
    
    for (i := 1; i <= length(typelist); i++)
    {    
        if ((typelist[i] & PRSTYP_UNKNOWN) != 0)
        {
            correct := spellcheck(wordlist[i]);
            if (correct)
            {
                pos := reSearch(wordlist[i], lower(str));
                str := substr(str, 1, pos[1] - 1) + correct + substr(str, pos[1] + pos[2], length(str));
            
                if (systemInfo(__SYSINFO_HTML) = 1)
                      "Возможно, вы имели в виду <b>&laquo;<<correct>>&raquo;</b>.<br><br>";
                else
                      "Возможно, вы имели в виду \"<<correct>>\"\b";
            }
        }
    }
    
    return str;
}

spellcheck: function(word)
{
    local ruslet := 'абвгдежзиклмнопрстуфхцчшщьыъэюя';
    local englet := 'abcdefghijklmnopqrstuvwxyz';
    local curlet := ruslet;
    local i, j, k;
    
    local TYPES = [PRSTYP_NOUN PRSTYP_VERB PRSTYP_ADJ];
    local variants = [];
    local found;
    
    //Если 4 символа или короче, то не обрабатываем
    if (length(word) <= 4)
        return nil;

    //узнаем, английский ли это текст или нет    
    if ( reSearch('[a-z]', word) )
        curlet := englet;
    
    //пропущенные буквы
    for (i := 1; i <= length(curlet); i++)
    {
        for (j := 0; j <= length(word); j++)
        {
            variants += substr(word, 1, j) + substr(curlet, i, 1) + substr(word, j + 1, length(word));
        }
    }
    
    //лишние буквы
    for (i := 0; i < length(word); i++)
    {
        variants += substr(word, 1, i) + substr(word, i + 2, length(word));
    }
    
    
    //неправильные буквы
    for (i := 1; i <= length(curlet); i++)
    {
        for (j := 0; j < length(word); j++)
        {
            variants += substr(word, 1, j) + substr(curlet, i, 1) + substr(word, j + 2, length(word));
        }
    }
    
    //перепутанные местами буквы
    for (i := 0; i < length(word) - 1; i++)
    {
        variants += substr(word, 1, i) + substr(word, i + 2, 1) + substr(word, i + 1, 1) + substr(word, i + 3, length(word));
    }
    
    for (i := 1; i <= length(TYPES); i++)
    {
        for (j := 1; j <= length(variants); j++)
        {
            found := parserDictLookup([] + variants[j], [] + TYPES[i]);
            for (k := 1; k <= length(found); k++)
            {
                if (TYPES[i] = PRSTYP_VERB)
                    return variants[j];
                if ( found[k].isVisible(parserGetMe()) )
                    return variants[j];
            }
        }
    }
    
    return nil;
}

2. В файле advr.t найдите функцию preparse и в самом конце, но перед return comStr; добавьте

Код:

comStr := checkString(comStr);

3. В файле advr.t найдите функцию preparseExt и в самом конце, но перед строками
if (comStr=str) return true;
return comStr;


добавьте

Код:

comStr := checkString(comStr);

Ура-ура, ошибок 1014 больше нет!

Я очень прошу людей, у которых есть игры на RTADS, найти 20 минут времени, добавить спеллчекер и попробовать пройти с ним собственные игры, чтобы проверить, не дает ли он паразитных наводок на процесс разбора текста и т.п.

Если ошибок не обнаружится, то можно даже будет добавить этот код в стандартную библиотеку (или лучше пусть будет отдельным модулем? ГрАнд, что думаешь?).

Всем спасибо!

Отредактировано Flint (22.01.2008 01:09)

Неактивен

0    0    #5
22.01.2008 22:41

Flint
Участник
Зарегистрирован: 06.09.2007
Сообщений: 148

---

Re: Универсальный spellchecker для RTADS

Готово.
http://ifwiki.ru/index.php/Модуль_прове … _для_RTADS

Отредактировано Flint (22.01.2008 22:43)

Неактивен

Powered by PunBB
© copyright 2001–2024 iFiction.Ru