ABSTRACT: Developing of Polish interactive fiction game had not been an easy easy task. The biggest challanges were coming from parser and dictionary systems. Originally conceived in English speaking countries and tailored for English parser and dictionary level of IF had to be adapted for the conditions of Polish language, where the change of syntactic order is frequent, words undergo several declinations and letters have diacritics. Paweł Aleksander Fedoryński, in fragments of his MA thesis, gives an overview of these challenges and also decsribes his new parser system for inputing and outputing Polish language.
KEYWORDS: computer linguistics, interactive fiction, parser models, dictionary in IF, retro games,
W tekstowych grach przygodowych (ang. interactive fiction) gracz steruje zachowaniem postaci, w którą się wciela, poprzez wydawanie tekstowych poleceń w języku naturalnym. Informacje o skutkach podejmowanych działań otrzymuje również w postaci tekstowej. Pierwsza w historii interactive fiction to stworzona przez Willa Crowthera w 1975 roku Adventure (znana później pod nazwą Colossal Cave Adventure). Fabuła polegała na eksploracji połączonych jaskiń (Crowther wykorzystał swoje doświadczenie grotołaza). Gra była na- pisana w języku FORTRAN, na komputerze PDP-10 [1]. Przełom lat siedemdziesiątych i osiemdziesiątych to okres świetności gier tekstowych. W Stanach Zjednoczonych największe znaczenie miała na tym rynku firma Infocom, wydawca m.in. klasycznej gry Zork. Infocom wydał dziesiątki gier tekstowych (w tym szereg sequeli do “Zorka”), które sprze- dawały się znakomicie. Jednak od połowy lat osiemdziesiątych, między innymi w związku z niezbyt udaną próbą rozszerzenia działalności na aplikacje biznesowe, firma zaczęła przy- nosić straty i ostatecznie została przejęta przez Activision, i w 1989 roku zlikwidowana
Poza Ameryką najważniejszym ośrodkiem rozwoju gier tekstowych była australijska firma Beam Software. Jej pierwszym i największym przebojem okazała się opublikowana w 1982 roku gra Hobbit [2], oparta na powieści J.R.R. Tolkiena. Stworzony przez pracujących na pół etatu studentów program wzbudził niezwykłe uwielbienie [3]. Hobbit jest — w każdym razie w Europie — najbardziej klasyczną tekstową grą przygodową wszechczasów; większość graczy właśnie dzięki niemu po raz pierwszy zetknęła się z tym gatunkiem.
Źródeł sukcesu można dociekać różnie. Po pierwsze, Hobbit działał na ośmiobitowych domowych komputerach ZX Spectrum i Commodore 64, i był pierwszą podobnie rozbudowaną grą dla tego kręgu odbiorców. Dodatkowym czynnikiem przyciągającym klientów było włą- czenie grafiki, ilustrującej niektóre lokacje. Niezwykła, jak na tamte czasy, była samodzielność innych postaci (NPC — Non-Player Character s) — mogły się przemieszczać, otwierać i zamy kać drzwi, walczyć pomiędzy sobą, i wykonywać inne działania, według podobnych reguł jakie dotyczyły działań gracza, ale zupełnie niezależnie od niego. Zdarzało się na przykład, że rozstawaliśmy się z Gandalfem, by po pewnym czasie nieoczekiwanie znów go spotkać w Górach Mglistych lub w lochu goblinów. Albo natrafialiśmy w drodze na zwłoki postaci, które zadar- ły z kimś silniejszym od siebie. Zwiększało to bardzo poczucie realności świata gry. Postacie reagowały na działania gracza (i swoje wzajemnie) — zaatakowane broniły się i atakowały odwetowo w następnych turach. Można było też rozmawiać z innymi postaciami, czasem na- wet okazywało się to konieczne (‘Say to Bard “Shoot the dragon”’). Fabuła była dość złożona, nieliniowa (tzn. większość zagadek miała co najmniej dwa różne rozwiązania) i raczej wierna książce. Ciekawą cechą było też wprowadzenie namiastki czasu rzeczywistego — jeśli przez dłuższą chwilę gracz nie dotykał klawiatury, to program automatycznie wprowadzał polecenie “Wait”; taka chwila nieuwagi mogła się źle skończyć (jak pokazuje ilustracja 1.2)
Jednak chyba najbardziej przełomowym aspektem był sposób wprowadzania poleceń. Standardem dla gier tekstowych w tamtych czasach — przynajmniej jeśli chodzi o kom- putery domowe — było ubogie słownictwo i jedno- lub dwuwyrazowe polecenia typu “Look” czy “Open door”. “Hobbit” znał ponad 500 słów i rozumiał złożone zdania w rodzaju “Open everything except the wooden chest”, “Go east then north”, “Take the rope and the sword” czy “Throw the barrel through the large trap door and jump onto it”. Autorzy byli bardzo dumni [3] z możliwości językowych gry. Określili obsługiwany przez niego podzbiór języka angielskiego mianem “Inglish” i w dołączanej do gry książeczce zamieścili krótkie wprowadzenie do języka Inglish. Dozwolone były czasowniki i rzeczowniki (“Open the door”), przymiotniki (“Kill the hideous troll”), ale także przysłówki (“Carefully attack the goblin”), i różnego rodzaju przyimki (“Climb into the boat”, “Kill the goblin with the sword”, . . . ). Inglish zawierał też (co pokazują poprzednie przykłady) różne znaczenia słowa “and”. Program pozwalał na wydawanie poleceń w formie skróconej, o ile tylko były one jednoznaczne. Na przykład, jeśli jedynym przedmiotem w polu widzenia gracza był klucz, można było go wziąć pisząc po prostu “Take”. Jeśli w zasięgu był klucz i lina, konieczna była większa precyzja: “Take the key” . Gdyby akurat gracz mógł wziąć dwa klucze, należałoby dodatkowo użyć przymiotnika: “Take the golden key” [4]
Gry tekstowe po polsku pojawiły się znacznie później niż angielskie i nigdy nie osiągnęły podobnej popularności. Pierwsze poważne próby zaatakowania problemu podjęto w drugiej połowie lat 80-tych. Ówczesne podejście do zagadnienia opisuje artykuł Andrzeja Kadlofa [5]. Autor przedstawił w nim budowę silnika polskojęzycznych gier przygodowych, z kompletnym omówieniem algorytmu, formatów danych, a nawet wskazówkami dotyczącymi implementacji; artykuł jest zilustrowany przykładami i fragmentami kodu w języku BASIC. Większość tego materiału ma dziś znaczenie czysto historyczne, warto jednak przyjrzeć się fragmentowi, omawiającemu analizę poleceń gracza.
Pomysł polegał na pozostawieniu z każdego słowa rdzenia, otrzymywanego przez odrzucenie ewentualnego prefiksu (“ZA”, “NA”, “PO”, . . . ) i wzięcie pierwszych czterech (lub mniej, jeśli wyraz jest krótszy) liter z reszty; np. “Zamknij bramę” → “Mkni bram”. Przetworzone w ten sposób wyrazy były następnie, od lewej do prawej, sprawdzane w słowniku. Wyrazy nierozpoznane algorytm ignorował. Pętla kończyła się w momencie natrafienia na drugi znany programowi wyraz. W ten sposób np. każde ze zdań: “Zamknij bramę”, “Zamknij żelazną bramę”, “Przymknij bramę”, “Domknij ostrożnie żelazną bramę”, dawało ten sam wynik “mkni bram”; podobnie np. każde ze zdań “Biegnij na wschód”, “Pobiegnij szybko w kierunku wschodnim”, “Biegnij w stronę wschodu” sprowadzało się do “bieg wsch” (wielkość liter była ignorowana). Tak uproszczona instrukcja była dalej sprawdzana w słowniku instrukcji i zależnie od stanu gry, wykonywana lub nie (każde akceptowane przez grę polecenie musiało się składać z dokładnie dwóch wyrazoẃ)...
Pierwszą opublikowaną grą tekstową po polsku był Smok wawelski [6] na komputer ZX Spectrum. Wydaje się, że autorzy zastosowali algorytm podobny, co do idei, do opisanego w zacytowanym artykule. Nie został wykorzystany pomysł odrzucania przedrostków — widocznie w praktyce nie dawało to istotnych oszczędności. Pewnym usprawnieniem jest też zmienna długość wyrazu w słowniku — zamiast brać zawsze czteroliterowe prefiksy, program porównuje słowo ze słownika z prefiksem wprowadzonego wyrazu, o długości równej długości słowa ze słownika. Np. “otwórz” jest rozpoznane jako “otw”, ale “drzwi” jako pełne “drzwi”. Wydaje się to rozsądne — zauważmy np. że przy prefiksach czteroliterowych, “wodę”, w zdaniu “wypij wodę z butelki”, oraz “wody”, w zdaniu “napij się wody”, musielibyśmy traktować jako różne wyrazy (za to “wypij” i “napij” przy odrzucaniu przedrostków zostałyby utożsamione).
Pod względem fabuły i złożoności świata gry Smok Wawelski ustępował zachodnim tytułom, jednak niewątpliwie stanowi jedno ze szczytowych osiągnięć gatunku. Najwybitniejszym dziełem tego typu była prawdopodobnie późniejsza gra Mózgprocesor. Miała ona rozleglejszą mapę oraz bardziej rozbudowaną fabułę i była dobrze przyjęta przez graczy (ukazała się wersja na ZX Spectrum oraz na Atari). Wydaje się jednak, że Mózgprocesor był stworzony w oparciu o ten sam, lub podobny, silnik co Smok Wawelski i od strony możliwości językowych te gry różnią się niewiele.
Niestety, polskojęzyczna interactive fiction właściwie zupełnie przestała istnieć wraz ze zmierzchem gier komercyjnych. Przez pewien czas pojawiały się niekomercyjne produkcje hobbystów (można tu wymienić komediową grę Pięć gówien Eepcha na komputer Atari) lecz z czasem i one wymarły [7]
Analiza polskojęzycznych poleceń, opierająca się na obcinaniu końcówek wyrazów i wybieraniu dwóch znaczących słów z każdego zdania, sprawdzała się znakomicie. Niemniej jest to rozwiązanie pod pewnymi względami niesatysfakcjonujące. Akceptowanie poleceń w rodzaju “Otwabcxyz qwerty drzwiuiop aaa” na równi z “Otwórz drewniane drzwi” to nie jest zdrowa sytuacja — wyraźnie widać, że przedstawione rozwiązanie (choć sprytne i skuteczne) stanowi zaledwie pierwszy krok. Nie bardzo wiadomo, jak można by je rozbudowywać w kierunku rozumienia bardziej skomplikowanych zdań, gdyż w takich zdaniach większość informacji o funkcji wyrazu pochodzi właśnie z jego końcówki
Wiąże się to ze wspólnym dla wszystkich wymienionych w poprzednim podrozdziale silników— nierozumieniem szyku przestawnego. Nie jest ono może wielkim problemem samo w sobie — raczej rzadko ktoś, w dobrej wierze, napisze “Drzwi otwórz” — jednak jest symptomem czegoś poważniejszego: oznacza, iż algorytm nie odzwierciedla podstawowej cechy języka polskiego, w którym — inaczej niż np. w języku angielskim — rola wyrazu zależy nie od jego położenia w zdaniu, lecz od formy gramatycznej. Co więcej, problem nabiera na znaczeniu wraz ze wzrostem komplikacji wypowiedzi: zdanie “Drzwi otwórz” jest bardzo nienaturalne, ale już “Zabij mieczem trolla” brzmi dużo lepiej, a zdania “Przełóż pieniądze ze skrzyni do kuferka” i “Przełóż pieniądze do kuferka ze skrzyni” są niemal równie dobre.
Stąd wziął się pomysł na pracę magisterską, obronioną przeze mnie 2005 roku w Instytucie Informatyki UW (promotor dr Krzysztof Szafran), na zaprezentowanie teoretyczne, oraz zaimplementowanie, innego, nowego rozwiązania, które umożliwiałoby analizę bardziej skomplikowanych zdań niż klasyczne silniki, a przy tym było łatwe do dalszej rozbudowy. Przedstawiony w pracy algorytm doprowadza pobłażliwość dla szyku przestawnego do skrajności — czerpie informacje niemal wyłącznie z form gramatycznych wyrazów, zwracając uwagę na ich położenie w zdaniu tylko wtedy, gdy jest to konieczne. Zaakceptuje na przykład na równi zdania “Otwórz zielone drzwi”, “Otwórz drzwi zielone”, “Drzwi zielone otwórz”, “Zielone otwórz drzwi” itd. Za wzór siły wyrazu i semantyki implementowanego podzbioru języka polskiego przyjąłem Inglish, czyli podzbiór języka angielskiego obsługiwany przez grę Hobbit. Udało się odtworzyć wiele jego cech; przedstawiony program rozumie polecenia takie jak “Zabij ohydnego trolla mieczem”, “Wejdź do beczki”, “Otwórz wszystko oprócz drewnianej skrzyni” czy “Wyjmij ze skrzyni wszystkie klucze oprócz ogromnego”. Możliwa jest też, jak w “Hobbicie”, interakcja między postaciami (‘Powiedz do Gandalfa “Daj mi mapę”’, ‘Powiedz Thorinowi “Zabij wszystkie trolle”’). Również na wzór Hobbita program umożliwia wydawanie poleceń skróconych, w sytuacji gdy nie prowadzi to do niejednoznaczności: można napisać “Otwórz okrągłe, zielone drzwi”, ale jeśli te drzwi są jedyną rzeczą, jaką postać może otworzyć, wystarczy polecenie “Otwórz”. Jeśli można otworzyć drzwi lub skrzynię, to gracz musi wpisać “Otwórz drzwi”, itd. Jednak sukces nie jest całkowity — głównym brakiem, w porównaniu z wersją angielską, jest brak obsługi spójnika “i”.
W dalszych częściach pracy opisuję zaproponowany silnik w sposób oderwany od implementacji, koncentrując się na koncepcyjnym modelu danych i algorytmach, nie wnikając, jak konkretnie te dane będą przechowywane, ani jaka będzie struktura kodu czy użyty język programowania. Omawiam też przygotowaną w ramach pracy przykładową implementację. Wreszcie prezentuje, działającą w oparciu o zaimplementowany silnik, grę wzorowaną na oryginalnym Hobbicie. Cały stworzony w ramach pracy kod objęty jest licencją GNU GPL, praca magisterska także jest dostępna online.
Na poniższej ilustracji zamieszczono zastosowany w mojej pracy model, który przedstawia używane przez program zbiorniki z danymi i związki pomiędzy nimi. Na górze każdej prostokątnej ramki znajduje się nazwa zbiornika (np. postacie), a poniżej wymienione są nazwy pól w należących do tego zbiornika rekordach (np. gdzie_lok, gdzie_prz, gdzie_osoba, nazwa, . . . ). Linie ze strzałką oznaczają, że rekord z jednego zbiornika jest skojarzony z co najwyżej jednym rekordem z innego zbiornika [8]. Linie bez strzałek oznaczają, że rekordy z połączonych zbiorników mogą być skojarzone dowolnie. Dane są w tym przypadku trwałe, tzn. są wczytywane na początku gry i istnieją przez cały czas jej trwania (oczywiście, mogą być modyfikowane), w przeciwieństwie do tymczasowych struktur danych, powoływanych do istnienia podczas obróbki pojedynczego polecenia gracza. Przyjrzyjmy się teraz każdej z występujących na rysunku kolekcji.
Rekordy z tego zbiornika reprezentują występujące w grze postacie. Pola gdzie_lok
, gdzie_prz
i gdzie_osoba
mówią, w jakiej lokacji, lub w jakim przedmiocie (np. w skrzyni, w beczce) znajduje się dana postać, lub przez kogo jest niesiona. Dokładnie jedno z pólgdzie_lok
, gdzie_prz
i gdzie_osoba
powinno być niepuste. nazwa wskazuje na rzeczownik opisujący postać; może to być imię własne (“Gandalf”, “Thorin”), lub zwykły rzeczownik (“troll”, “goblin”). Pole to — podobnie jak pola nazwa we wszystkich omawianych typach danych — wskazuje na rzeczownik bez określenia jego rodzaju. Zatem strzałki wskazujące na zbiornik rzeczowniki mogą, wyjątkowo, oznaczać skojarzenie z jednym, dwoma lub trzema rekordami z tego zbiornika. Pole zywy
mówi, czy dana postać żyje. Pole sila
mówi, ile dana postać może naraz unieść. spr_boj
to sprawność bojowa — im wyższa, tym trudniej zabić daną postać i tym łatwiej ona może zabijać innych. ciezar mówi, jak trudno daną postać unieść. Siła, sprawność bojowa i ciężar nie są wyrażone w żadnych realnych jednostkach w rodzaju kilogram; są to umowne, bezwymiarowe liczby.
Wreszcie funkcja zawiera odniesienie do fragmentu kodu odpowiedzialnego za zachowanie postaci.
Postać może być określana przez dowolną liczbę przymiotników (co ilustruje linia bez strzałek).
Rekordy z tego zbiornika reprezentują drzwi, prowadzące z jednych lokacji do innych. Każde drzwi powinny być wskazywane przez dokładnie dwie lokacje. Pole klucz zawiera odniesienie do przedmiotu, który jest kluczem do danych drzwi; może ono być puste, jeśli drzwi nie da się zamknąć na klucz. Pola otwarte
, locked
i rozbite
są typu logicznego i mówią, czy drzwi są odpowiednio otwarte, zamknięte na klucz lub rozbite. twardosc
to liczba związana z tym, jak trudno jest rozbić (wyłamać) dane drzwi. Pole nazwa
wskazuje na rzeczownik, który jest nazwą danych drzwi (najczęściej, ale nie obowiązkowo, będzie to rzeczownik “drzwi”). Ponadto każde drzwi mogą być określane przez dowolną liczbę przymiotników.
Występujące w grze przedmioty. Pola nazwa
, gdzie_lok
, gdzie_prz
i gdzie_osoba
mają takie samo znaczenie, jak w przypadku postaci. Pole z_wnetrzem
mówi, czy coś może znajdować się w środku danego przedmiotu. Pole otwieralne
mówi, czy przedmiot można otwierać i zamykać (np. miecz będzie miał zarówno z_wnetrzem
jak i otwieralne
ustawione na false; łódka będzie mieć z_wnetrzem
równe true, zaś otwieralne
równe false; skrzynia będzie mieć oba pola równe true; kombinacja z_wnetrzem = false, otwieralne = true nie jest możliwa). klucz
wskazuje na przedmiot będący kluczem do danego przedmiotu; może być niepuste tylko gdy otwieralne = true. otwarte
i locked
mówi, czy przedmiot jest otwar ty/zamknięty na klucz. Pole ciezar mówi, jak trudno unieść dany przedmiot. przyd_boj
mó wi, jak bardzo użycie przedmiotu do zaatakowania kogoś zwiększa szanse zwycięstwa w walce. objetosc
to objętość przedmiotu, objwewn
to objętość wnętrza. Nie można włożyć przedmiotu do czegoś, co ma objętość wnętrza mniejszą niż objętość przedmiotu. Przedmiot może być określany przez dowolną liczbę przymiotników
Kolekcja rzeczowników. rodz i liczba mówią, jaki jest rodzaj (męski, żeński, nijaki, męskoosobowy, niemęskoosobowy) i liczba (pojedyncza, mnoga) danego rzeczownika (znaczy to, że będą osobne rekordy dla tego samego rzeczownika w liczbie pojedynczej i mnogiej). Pozostałe pola to rzeczownik odmieniony przez przypadki.
Kolekcja przymiotników. Znaczenia pól takie same, jak dla kolekcji rzeczowniki
. Będą osobne rekordy dla różnych rodzajów — “ładny”, “ładna”, “ładne” (dziecko), “ładne” (kobiety), ...
dzialania
to zbiornik możliwych do wykonywania przez postacie działań, takich jak podnoszenie i wyrzucanie przedmiotów, otwieranie drzwi, atakowanie innych postaci, itp. Pole funkcja
zawiera odniesienie do fragmentu kodu, wołanego aby wykonać działanie. Pozostałe pola zawierają informacje o tym, jakich argumentów oczekuje ów kod. czycytat może być równe true lub false i mówi, czy działanie wymaga przekazania fragmentu tekstu, wprowadzonego przez użytkownika w cudzysłowach — ściśle mówiąc, jest dokładnie jedno działanie z czy cytat ustawionym na true: rozmawianie z innymi postaciami (“Powiedz do Gandalfa ‘Daj mi mapę’ ”). Pozostałe pola są to miejsca składniowe. Ograniczmy się tu do stwierdzenia, że nazwy pól to nazwy argumentów kodu z pola funkcja
; jeśli pole jest puste, to znaczy, że funkcja nie oczekuje takiego argumentu; jeśli jest niepuste, to mówi, jakiego typu powinien być argument — typ może to być przedmiot, postać lub drzwi. Każde działanie może być nazwane wieloma różnymi czasownikami (“Zaatakuj trolla”, “Zabij trolla”, “Uderz trolla”).
Zbiornik warunków, które muszą być spełnione, aby działania dało się wykonywać (np. aby wyrzucić jakiś przedmiot trzeba go nieść, aby dało się otworzyć drzwi nie mogą one być już otwarte, ani też zamknięte na klucz, ani wyłamane, itp.). Pole dzialanie
wskazuje na działanie, którego dotyczy warunek. warunek
to odniesienie to fragmentu kodu, który sprawdza, czy warunek jest spełniony. komunikat
to odniesienie do fragmentu kodu, generującego, w razie niespełnienia warunku, odpowiedni komunikat o błędzie. kolejnosc
to kolejność sprawdzania.
Jeśli dwa działania są identyczne, a różnią się jedynie przyjmowanymi argumentami, to w zbiorniku aliasydz
znajduje się rekord mówiący, które działanie należy zastąpić którym. Jest to przydatne aby identyfikować, że np. “Powiedz Thorinowi ‘...’ ” i “Powiedz do Thorina ‘...’ ” znaczy jedno i to samo. zastepowane
i zastepujace
to odniesienia do rekordów ze zbiornika dzialania
, pozostałe pola mówią, w jaki sposób podczas takiej zamiany należy przemapować argumenty
Kolekcja czasowników. Każdy czasownik może nazywać wiele działań. Pola bezokol
, rozkaz
, os_2
i os_3
to forma danego czasownika w bezokoliczniku, w trybie rozkazującym, oraz w drugiej i trzeciej osobie w czasie teraźniejszym. Brak pola os_1
wynika z faktu, że nigdzie podczas gry czasownik w takiej formie się nie pojawia (rozkaz występuje w poleceniach gracza, os_2
np. w zdaniu “Otwierasz drewnianą skrzynię”, os_3
np. w zdaniu “Gandalf idzie na północ”).
Przedstawiony w mojej pracy algorytm rozpoznaje podzbiór języka polskiego mający trochę mniejszą siłę wyrazu niż, służący za wzór, Inglish. Jednak został tak pomyślany, aby dodawanie rozszerzeń było jak najłatwiejsze. Na przykład, angielska wersja rozumie przysłówki: “Quickly open the door”. Nic nie stoi na przeszkodzie, aby wprowadzić nową kolekcję przyslowki na modelu danych i dodać do elementu listy wariantów w strukturze gramtab pole przyslowki, zawierające listę odwołań do rekordów z tej kolekcji. Wiązałoby się to oczywiście z dodaniem nowego sposobu identyfikacji słowa. Oczywiście, pozostaje pytanie, jak traktowany byłby taki przysłówek na etapie interpretacji. Prawdopodobnie chcielibyśmy, aby modyfikował w jakiś sposób skojarzone z czasownikiem z pola rozkaz działanie (np. “Ostrożnie wyważ drzwi mieczem” wiązałoby się z mniejszym ryzykiem złamania miecza niż samo “Wyważ drzwi mieczem”). Inne możliwe rozszerzenie to rzeczowniki w funkcji przydawki. W oryginalnym Hobbicie występowały np. “Goblins back door”, co można przetłumaczyć jako “Tylne drzwi goblinów”. Opisany algorytm nie daje możliwości wprowadzenia do gry takiego obiektu. Jednak można bez problemu dodać taką funkcjonalność. W podobny sposób można by dodawać inne rozszerzenia, np. umożliwić wprowadzenie obiektu “drzwi w podłodze” (ang. trap door). Istotnie trudniejsze wydaje się dodanie obsługi spójnika “i”. Tutaj prawdopodobnie dobrym punktem wyjścia byłoby zauważenie, że “i” zawsze łączy dwa konkretne wyrazy znajdujące się po różnych jego stronach (np. w zdaniu “Weź jabłko i zjedz” wyrazy “weź” i “zjedz”; w zdaniu “Weź miecz i linę” wyrazy “miecz” i “linę”), oraz, że brakujące części zdania można uzupełniać wyrazami z przeciwnej strony “i”, przetwarzając jedno zdanie z “i” na dwa zdania połączone kropką (np. “Weź jabłko i zjedz” → “Weź jabłko. Zjedz jabłko”; “Weź miecz i linę” → “Weź miecz. Weź linę”). Kolejnym krokiem po dodaniu obsługi “i” mogłoby być dodanie zaimków (“Przeczytaj mapę i daj ją Gandalfowi”). Zapewne w tym celu należałoby pamiętać, jakie obiekty wypełniały miejsca składniowe w poprzednich poleceniach.
Aby zaprezentować możliwości stworzonego silnika, przygotowana została korzystająca z niego kompletna gra, o treści opartej na treści klasycznej gry Hobbit. Założenie było takie, aby osoba znająca Hobbita z czasów ZX Spectrum mogła grać w remake odczuwając, poza zmianą języka, tak mało różnic w stosunku do oryginału jak to możliwe. Dla osób, które nie miały kontaktu z oryginalną grą, w dodatku A znajduje się krótka instrukcja. Występujące w grze fragmenty dialogów, o ile były zaczerpnięte z książki, zostały przełożone w oparciu o tłumaczenie Marii Skibniewskiej [11].
Od strony technicznej przygotowanie gry wymagało dwóch rzeczy: po pierwsze, dosta czenia odpowiednich plików z danymi; po drugie, napisania kodu, odpowiedzialnego za specyficzne dla gry działania, w pliku dzialania.lisp
, za zachowanie postaci, w pliku npc.lisp
, oraz za zdarzenia, w pliku zdarzenia.lisp
. Pliki npc.lisp
i zdarzenia.lisp
są całkowicie zależne od konkretnej gry (choć najprostsze, generyczne zachowania z pliku npc.lisp
, jak np. losowe błąkanie się po mapie i atakowanie kogo popadnie, w zasadzie mogłyby być ponownie wykorzystane w innej grze). Plik dzialania.lisp
zawiera wszystkie działania; część z nich (poruszanie się, otwieranie i zamykanie drzwi i przedmiotów, podnoszenie i wyrzucanie przedmiotów, itd.) jest uniwersalna; inne są typowe dla konkretnej gry. Przykładem działania, mającego sens w jednej tylko grze, jest występujące w Hobbicie branie do niewoli. Gdy goblin bierze do niewoli (ang. captures) jakąś postać, wtedy jest ona przeniesiona do lochu goblinów. Może to zrobić także elf, wtedy postać zostaje przeniesiona do lochu elfów. Inne postacie nie mogą tego robić - w ich przypadku polecenie “Weź do niewoli ...” będzie równoważne z “Zaatakuj ...”. Kod odpowiedzialnej za to działanie funkcji capture
zależy od istnienia odpowiednich typów postaci i odpowiednich lokacji (rozpoznawanych po etykietach), i raczej nie uda się go wykorzystać w jakiejkolwiek innej grze.
Lokacje i połączenia między nimi są dokładnie takie same jak w oryginale. Występujące postacie odpowiadają postaciom z oryginalnej gry, a ich zachowanie zostało tak zaprogramowane, aby przypominać zachowanie wzorów z angielskiej wersji. Są to Thorin, Gandalf, Elrond, Bard, sześć goblinów, dwa trolle, leśny elf (wood elf), podczaszy (butler), Gollum, wredny warg (vicious warg), czerwonozłoty smok (red golden dragon). Występujące przedmioty również odpowiadają przedmiotom występującym w oryginale. Z istotniejszych można wymienić skarb, którego zdobycie jest celem gry, oraz drewnianą skrzynię, do której trzeba włożyć skarb po powrocie do domu, aby gra została uznana za wygraną. Z innych ciekawych faktów można dodać, że występujące w grze rzeki z punktu widzenia silnika są nietypowym rodzajem drzwi.
W grze Hobbit wcielasz się w rolę hobbita, Bilbo Bagginsa.
Będziesz swobodnie poruszać się po Śródziemiu, odkrywać i badać ten wspaniały, zaczarowany świat. Spotkasz wiele różnych stworzeń; niektóre z nich będą przyjazne, inne znacznie mniej. Czeka Cię niebezpieczna i fascynująca przygoda.
Być może nie orientujesz się w sprawach związanych z hobbitami — są to małe istoty, wysokie mniej więcej na metr, mniejsze niż na przykład krasnoludy. Nie umieją też czarować. Bardziej kompletny opis znajdziesz w książce “Hobbit czyli tam i z powrotem”, lecz to co powiedzieliśmy powinno wystarczyć do zrozumienia, że na ogół istoty które napotkasz podczas tej przygody, włącznie z krasnoludami, będą większe i silniejsze od Ciebie. Musisz więc wykorzystać cały swój spryt.
Gandalf, czarodziej, namówił Cię do wzięcia udziału w nowej i ekscytującej przygodzie; zamierzasz pomóc Thorinowi (który jest krasnoludem). Zadanie polega na odnalezieniu nikczemnego smoka, odebraniu mu zagrabionego skarbu i umieszczeniu go w Twojej skrzyni (gdzie będzie bezpieczny). Gra rozpoczyna się w momencie, gdy opuszczacie bezpieczną norkę w Shire i wyruszacie w drogę.
Grasz po prostu wydając komputerowi polecenia. Aby na przykład otworzyć skrzynię, napisz “Otwórz skrzynię”. Komputer przeanalizuje polecenie i — jeśli to, czego żądasz, da się wykonać — zobaczysz komunikat w rodzaju “Otwierasz drewnianą skrzynię”. Jeśli jednak coś stoi na przeszkodzie, zobaczysz odpowiednie wyjaśnienie — np. “Skrzynia jest zamknięta na klucz”. Zobaczysz też opisy różnych zdarzeń i działań innych osób: Otwierasz drewnianą skrzynię. Thorin ogląda interesującą mapę.
Poruszasz się wydając polecenia takie jak “Idź na północ”. Dla wygody możesz też użyć skrótu — “Pn”. Gdy po raz pierwszy odwiedzisz jakieś miejsce, komputer przedstawi Ci jego dłuższy opis, za następnym razem opis skrócony. Aby znów zobaczyć dłuższy opis użyj polecenia “Patrz”. Inne ważne komendy to “Weź ...”, “Wyrzuć ...”, “Obejrzyj ...”, “Daj ...”, “Otwórz ...”, “Zamknij ...”, “Zabij ...”, “Wyłam ...”, “Wejdź do ...”, “Wyjdź z ...”.
Aby zobaczyć listę przedmiotów, które niesiesz, napisz “kieszeń”. Aby zakończyć grę, napisz “koniec”. Podczas drogi może tak się zdarzyć, że nie będziesz w stanie poradzić sobie bez pomocy innych osób. Nic prostszego niż o nią poprosić: możesz napisać na przykład “Powiedz do Gandalfa ”Przeczytaj mapę””. Oczywiście, może się zdarzyć i tak, że ktoś nie będzie chciał (lub nie będzie w stanie) zrobić tego, o co poprosisz.