A
kalandjátékok utasításaira általában jellemző, hogy az alany hiányzik
belőlük ill. rejtett (első vagy második személyű), s csaknem kivétel
nélkül valamilyen igei állítmányra épülnek, amihez gyakran különféle
tárgyak, jelzők és határozók csatlakozhatnak. Utóbbiak önmagukban
egyszerűek, egy-két szóból állanak, s ami még nagyon jelentős
egyszerűsítés, hogy alá-, fölérendelt tagmondatok sosem
fordulhatnak elő. (Legalábbis ezidáig nem volt rá még példa…)
Tipikusan egyszerű parancs pl. az alábbi: VEDD FEL A KULCSOT.
Ebben föllelhető mindkét alapvető szófaj: az ige és a főnév –
tulajdonképpen az összes többi szófaj fölfogható úgy is, mintha
ezek speciális esetei lennének. Az ige fontos tartozéka (vagy
inkább: része) még az igekötő is, hiszen ha azt mondjuk, hogy
VEDD FEL, VEDD LE vagy VEDD MEG, azok teljesen eltérő cselekvésre
utalnak. E kettő együttesen dönti el, hogy mit akarunk csinálni,
a főnevek és társaik pedig a cselekvésünk célját határozzák meg,
azáltal, hogy az elérhető tárgyak és szereplők közül konkrétan
kijelölnek valamit. Ez nem mindig egyértelmű: egyrészt létezhet
a játékban több hasonló nevű tárgy is, másrészt használhatunk
különféle gyűjtőneveket. Előbbi esetre példa lehet az, ha többféle
különböző kulcs van: ilyenkor mindegyiknek van valamilyen sajátos
jelzője, mely megkülönbözteti az összes többitől (pl. „kis kulcs”
vagy „nagy kulcs” stb.); a főnevek elé melléknevek járulnak (VEDD
FEL A NAGY KULCSOT). Gyűjtőnév esetén pedig az illető szó ugyan
pontosan megjelöli a tárgyat, ám egyszerre akár többet is – legjobb
példa erre a MINDEN szónak az alkalmazása: egy VEGYÉL FEL MINDENT
utasítás egyszerre vonatkozik a közelünkben látható valamennyi
tárgyra. Melléknevek szintjén is elképzelhető hasonló, pl. így:
VEDD FEL AZ ÖSSZES KULCSOT. (Hogy miért is célszerű a melléknevet
is speciális főnévként nyilvántartani, az kitűnik abból, hogy
alkalomadtán ezek is a főnevekhez hasonlóan ragozódhatnak. Egy
jólnevelt kalandjáték ugyanis, ha nem egészen biztos a dolgában,
további kérdéseket tesz fel: megkérdezi tőlünk, hogy melyik kulcsot
szándékozunk fölvenni. Erre mi felelhetjük azt is, hogy a NAGY
KULCSOT – vagy egyszerűen ennyit: NAGYOT. A melléknév ilyenkor
kifejezetten úgy viselkedik, mintha főnév lenne; fordított esetben
pedig egyes főnevek is állhatnak a melléknévi jelzők helyén.)
Mivel igencsak ritka eset (úgy értem, egy kalandjátékban…) az,
hogy teljesen azonos tárgyakból legyen több példány ugyanazon
a helyen, s ráadásul ezek közül többre, de nem valamennyire akarunk
hivatkozni egyidejűleg, ezért számnevek csak elvétve fordulnak
elő; ilyenkor, mint sejthető, a melléknévhez hasonlóak, csak funkciójuk
más. Ugyancsak fehér holló egy másikfajta jelzős szerkezet, nevezetesen
a birtokos jelző: egyfelől erre is csak viszonylag ritkán kerül
szükség, másfelől meg akkor is kiválthatjuk más, egyszerűbb jelzőkkel
a szerepét. Pl. ha az iménti nagy kulcs egy őr kezében van: egy
VEDD EL AZ ŐR KULCSÁT helyett a FOGD A NAGY KULCSOT utasítás sokkal
egyszerűbb. Jóllehet ezek kezelése eléggé bonyolult eljárást igényel,
mindenesetre sokat emel egy játék színvonalán, ha – mint értékes
egzotikumot – ilyesmit is beleépítünk. Annál nagyobb igény lehet
ezzel szemben (márminthogy a játékosok oldaláról) egy másik, teljes
főnévi értékű szófajra, a névmásra: ha leírjuk, hogy BESZÉLGESS
VELE vagy NÉZZ RÁ, miközben a legutóbbi parancsunk
egy KÖSZÖNJ A MADÁRIJESZTŐNEK volt, ez kétségkívül azt jelenti,
hogy ezúttal is a madárijesztőre gondolunk – az utolsóként előfordult
főnevet helyettesíthetjük valamilyen névmással. Érthetetlen, hogy
ezt is csak alig néhány program ismeri, amikor pedig a megvalósítása
egyszerű (hisz’ mindössze egy szócseréről, behelyettesítésről
van szó), és igen nagy könnyebbség, ha nem kell a hosszadalmas
MADÁRIJESZTŐ szót mindig újra és újra bepötyögni, hanem helyette
ezt a „kézreálló” pár betűt…
A
határozók már nem önálló szófajok, hanem csak mondattani egységek
– lényegében a főnevek eltérő ragozású alakjai. Miután a szórend
szabad, és más támpontunk nincsen, egyedül a ragozásuk alapján
tudjuk csoportosítani a különféle kifejezéseket. Túlságosan körülményes
és fölösleges is volna azonban valamennyi raghoz külön határozót
rendelni, így legcélravezetőbb öt csoportba sorolni őket (az egy
csoportba tartozó ragok nemigen szoktak előfordulni egy parancsban
együtt, és körülbelül hasonló dolgot jelentenek, így nyugodtan
tekinthetjük azonosnak őket): 1.) tárgy: -t raggal vagy rag nélkül;
2.) helyhatározó: -n, -ban, -nál, -ról, -ból, -tól; 3.) célhatározó:
-ra, -ba, -nak, -hoz; 4.) eszközhatározó: -val; 5.) egyéb: -ért,
-ig… stb. Pl. az ADJ PÉNZT A KOLDUSNAK utasítás három mondatelemet
tartalmaz: egy igét, egy tárgyat és egy célhatározót (az, hogy
ezt a nyelvtanban részeshatározónak nevezik, ne zavarjon túlzottan
bennünket…). A ragokon túl használhatunk névutókat is a főnév
jelentésének módosítására, így helyhatározó lehet az is, hogy
ASZTAL ALATT, vagy a KULCCSAL helyett eszközhatározó, hogy KULCS
RÉVÉN vagy KULCS SEGÍTSÉGÉVEL stb. stb.
Összefoglalva
tehát, minden egyes begépelt parancs lényegében a következő részekből
tevődik össze: az ige (a beleértett igekötőjével együtt), valamint
a felsorolt ötféle tárgy ill. határozó. A parancsot akár fölfoghatjuk
úgy is, mint egy rekordot, aminek ez a hatféle mezője van. A programon
belül persze a szavak nem szövegként jelennek meg, hanem minden
egyes dolgot valamilyen sorszám azonosít. Pl. ha a „kinyitni”
ige a 13-as sorszámot kapta az előzetes tervezés során, akkor
ez azt jelenti, hogy az ige változójába egy 13-as szám kerül.
Amelyik fajta eleme hiányzik a mondatnak, az természetesen a 0-ás
értéket kapja. Ez gyanúsan egyszerűnek látszik, holott valójában
egyáltalán nem az: ugyanis ez a végeredményül kapott egyszerűség
a valóságban egy hosszú és bonyolult értelmezési folyamatot takar,
melynek folyamán a különféle szókapcsolatok és más szerkezetek
fokozatosan a lehető legtömörebb viszonyokká bomlanak le. Ahhoz,
hogy ez megvalósuljon, tucatnyi eltérő algoritmust kell egymás
után lefuttatni, amelyek mindegyikének feladata a megfelelő szavak
vagy szópárok cseréje, helyettesítése valamilyen másik,
egyszerűbb szóval. Ezek a cserék táblázatokban való keresések
útján történnek. Efféle algoritmus lehet például az, amelyik az
igék és igekötők különálló párosát egyetlen összetett igére cseréli
ki (a NYISD és a KI szavakat törli, és helyükre egy KINYITNI szót
ír – ha pedig nemlétező párosítást talál, pl. azt írtuk, hogy
NYISD ÖSSZE, ami nincs benne a táblázatban, akkor hibát jelez,
és az ige értelmezhetetlen). Vagy egy másik, mely a névmás helyére
elegánsan becsempészi az utolsóként alkalmazott főnevet. Egy olyan,
mely a „melléknév + főnév” szókapcsolatot egyetlen új főnévvel
helyettesíti be (a NAGY és a KULCS szavak sorszámait kiiktatva,
helyettük egy másik, a NAGY KULCS szó sorszámát illeszti a mondatba).
És így tovább; minél több ilyen funkcióval ellátjuk, annál intelligensebb
lesz a programunk. (És annál nehezebb lesz megírni…) Természetesen
az sem mindegy, milyen sorrendben hajtjuk végre ezeket az egyes
szabályokat a mondatban. Pl. ha azt találjuk leírni, hogy NAGYOT,
akkor az először is felbomlik a – kissé idegenszerűen hangzó –
NAGY AZT szókapcsolatra, majd az AZ helyére bekerül a KULCS főnév
(mint névmás-helyettesítés!), s csupán ezután lehet belőle NAGY
KULCSOT. Egy szöveges kalandjáték értelmező programját elkészíteni
körülbelül hasonló feladat, mintha mondjuk egy PROLOG fordítóprogramot
óhajtanánk írni…
A
parancs és a mondat kifejezéseket az eddigiekben
egy kissé összekeverten használtuk, úgyhogy lassanként ideje lenne
szétválasztanunk őket egymástól: míg egy parancs mindig egyetlen
meghatározott cselekvésre utal, vagyis lényegében tagmondat (a
fentiekben hozott példák mind parancsok voltak), addig egy mondat
nyugodtan akár több parancsból is állhat. (Már amelyik program
hajlandó ezt tudomásul venni…) Vagy az ellenkező irányból megközelítve
a dolgot: egy mondat az a szövegmennyiség, amit a játékos egy
szuszra begépel a bevitel során, s a parancs ennek egy-egy önállóan
végrehajtható része. A mondaton belüli parancsok elválasztására
írásjeleket (pont, vessző és társaik), valamint az ÉS szócskát
vagy annak szinonimáit (MAJD, TOVÁBBÁ, VALAMINT… stb.) használjuk.
A játék a mondatban lépésről lépésre halad előre; mihelyt egy
parancsot végrehajtott, utána kezdi el értelmezni a következőt.
Pl. egy tipikus összetett mondat így hangzik: VEDD FEL A KULCSOT
ÉS NYISD KI VELE AZ AJTÓT. Ebben az esetben a két tagmondatot
akár egy-egy külön mondatként is le lehetne írni, de előfordul
olyan is, hogy szorosabb összefüggés kapcsolja őket össze, miáltal
szétválaszthatatlanok lesznek – ez akkor állhat fenn, mikor a
második parancsból kihagyunk valamit, amit már tartalmaz az első.
Pl.: GYÚJTSD MEG A GYERTYÁT ÉS A MÉCSEST. Itt a második parancsból
kimaradt az ige. Ahhoz, hogy ezt értelmezni tudjuk, egy aprócska
trükköt kell alkalmazni a programban, nevezetesen amíg a mondatnak
nincs vége, addig az aktuális parancs változóit sohasem töröljük,
hanem csak az újabbik parancs szavai mindig felülírják a régit
– így tehát a teljes első parancs változatlanul megmarad, mindössze
a MÉCSES, mint tárgy fogja felülírni az előző tárgyat, a GYERTYÁ-t.
Ezáltal egyetlen állítmány mögé tetszőleges számú tárgyat fölsorakoztathatunk.
Sokkalta nehezebb volna – éppenhogy a lépésenkénti végrehajtás
miatt! – ugyanezt az ellenkező irányból is megcsinálni, valahogy
ilyképpen: NYISD KI ÉS CSUKD BE AZ AJTÓT. Ebben az esetben ugyanis
a programnak szinte „előre kéne gondolkodnia”: amikor odáig jut,
hogy NYISD KI, még fogalma sincsen róla, hogy mire is vonatkozik
ez a dolog – jóllehet a keresett főnév benne van a mondatban,
csak jóval később volna esélye megtalálni azt. Az eredmény: a
számítógép megkérdi, hogy „Mit akarsz kinyitni?”. Ennek feloldására
be lehetne vezetni egy ún. öröklési rendszert, ami abból állna,
hogy még mielőtt hozzálátna a legelső lépés végrehajtásához, a
program végigértelmezné a teljes mondatot, és mindegyik tagmondat
állapotát eltárolná valahol, egy rekordokból álló láncban úgy,
hogy az aktuális rekord mindig örökölné – az iméntiekben leírt
módon – az előzőnek azt a részét, ami nem változott. Azután ugyanezt
végigcsinálná fordított irányban, az utolsótól az első felé haladva
is – és ami valahonnan hiányzik, azt mindig a másikból vett megfelelő
részekkel pótolná és kiegészítené benne ekkor is. Eközben az első
parancsba, ahonnan hiányzik a tárgy, beíródna az utána következő
tagmondat tárgya (ami esetleg szintén a következőből örökölte
azt, és így tovább). Csupán ezt követően hajtódnának végre a parancsok.
Amivel
aztán el is érkeztünk az értelmezés következő állomásához: mi
történjék, amikor valamilyen szó végképp hiányzik a mondatból.
Erre több megoldás is kínálkozik. Lehet egyszerűen a felhasználó
képébe vágni, hogy márpedig ez nem egy értelmes mondat, és legközelebb
szíveskedjék jobban megválogatni a szavait… Ennél valamivel udvariasabb
(és intelligensebb) megoldás, amikor a homályos részekre vonatkozó
kérdéseket teszünk fel, és további kiegészítéseket várunk. Pl.
ha csak annyit kaptunk parancsként, hogy NYISD, akkor feltesszük
neki a következő kérdést: „Mit akarsz kinyitni?” – ami után következő
bevitelként kétféle felelet várható. A játékos vagy begépel egy
teljesen új mondatot, nagylelkűen elfelejtve az előzőt – vagy
pedig a kérdésünkre válaszol, és mindössze a hiányzó tárgyat adja
meg. Hogyan készítsük fel a játékot a helyes reagálásra mindkét
esetben, amikor e kettőt tulajdonképpen nem is igazán lehet megkülönböztetni
egymástól? Hát úgy, hogy nem is különböztetjük őket meg. Az előbb
láttuk, hogy az esetleg összefüggő részmondatok miatt a parancsok
változóit mindig a mondat végén töröljük, amikor egy új mondatba
kezdünk. Egyszerűen annyit kell tennünk, hogy mikor valamilyen
kérdést intézünk a kalandorhoz, olyankor az újabb bevitel megkezdése
előtt nem töröljük ezeket a változókat, azaz nyitva hagyjuk
a mondatot – akármit begépel a tisztelt felhasználó, az a jelenleg
meglévő parancsot fogja felülírni, éppúgy, mintha az a következő
parancs volna. Teljesen mindegy, hogy töredékeket kapunk-e tőle,
vagy egy új mondatba kezd. Így akár több, eltérő tartalmú kérdés
is követheti egymást, és mégis valamennyi válasz ugyanazt a parancsot
fogja fokozatosan gazdagítani – szabályos kis párbeszéd alakulhat
ki a program és a felhasználója között. Komolyabb probléma akkor
adódik, ha már bevezettük az előzőkben vázolt örökléses rendszert
– ilyenkor ugyanis a kérdés feltevése előtt meg kell jegyezünk
az egész mondat állapotát, majd a kiegészítés megtétele után ismételten
végigfuttatni rajta oda-vissza a parancsokat összefűző eljárást.
Harmadik,
és egyben legintelligensebb megoldása a hiányos mondatok esetének,
ha a program maga automatikusan megkísérel behelyettesíteni a
kimaradt részek helyére valamit, és ha csak ez végképp nem sikerül
neki, akkor teszi fel a kérdést. Pl. a NYISD megadása esetén sorjában
végignézi az összes elérhető tárgyat, s csak ha nincs közöttük
egyetlen olyan sem, ami nyitható, akkor kérdi meg a kezelőjét,
hogy voltaképpen mire is gondolt. (Vagy ha többet talált, akkor
azt, hogy melyiket.) Hasonlít ez némiképpen a gyűjtőnevek kezeléséhez.
Leggyakoribb gyűjtőnév a korábban már megemlített MINDEN szó:
ilyenkor a parancs végrehajtását egy külső ciklusba kell ágyazni
– sorban egymás után behelyettesíteni a kívánt szó helyére az
összes elérhető tárgynevet, és mindegyikkel egyesével végrehajtani
ugyanazt a parancsot. (Érdemes elgondolkodni rajta, hogy mi történik
akkor, ha netán a huncut játékos egyazon parancson belül többször
is alkalmazott valamilyen gyűjtőnevet. Pl. így: MUTASS MEG MINDENT
MINDENKINEK. Ez az, ami a programozó idegeit végképp próbára teszi…)