Kapitola 18: Interakce – Vstup, Výstup a Textové hry

Doposud jsme s Prologem komunikovali spíše jednostranně. My jsme se ptali, on odpovídal. Ale program, který nemluví a nereaguje na uživatele, je jako socha – krásný, ale nudný. V této kapitole se naučíme, jak Prologu vdechnout život. Ukážeme si, jak číst vstup od uživatele, jak zapisovat výsledky na obrazovku a jak pracovat se soubory. A co je nejdůležitější, všechny tyto znalosti spojíme dohromady a vytvoříme si vlastní textovou dobrodružnou hru!

18.1 Základní vstup a výstup – Mluvíme s Prologem

Prolog má jednoduché predikáty pro komunikaci s vnějším světem.

ZÁKLADNÍ VSTUP/VÝSTUP (I/O) Predikáty, které umožňují programu číst data od uživatele (klávesnice) a zapisovat data na obrazovku.

Příklad: Jednoduchý chatbot "Psycholog"

start_chatbot :-
    write('Ahoj, jak se cítíš? '), nl,
    read(Pocit), % Uživatel musí napsat např. 'smutny.'
    reakce(smutny) :- write('To mě mrzí. Dej si čokoládu.'), nl.
    reakce(stastny) :- write('To je skvělé! Jen tak dál.'), nl.
    reakce(_) :- write('Zajímavé... pověz mi víc.'), nl.

18.2 Práce se soubory – Paměť na disku

Dynamická databáze (Kapitola 15) je skvělá, ale její obsah zmizí, když program vypnete. Co když chceme uložit stav hry nebo data pro příští spuštění? K tomu slouží práce se soubory.

PRÁCE SE SOUBORY Predikáty, které umožňují programu číst data ze souborů na disku a zapisovat data do nich.

Příklad: Uložení a načtení skóre

% Uloží aktuální skóre do souboru
uloz_skore(Skore) :-
    tell('skore.txt'),
    write(Skore), write('.'), nl, % Uložíme jako term s tečkou
    told.

% Načte skóre ze souboru
nacti_skore(Skore) :-
    see('skore.txt'),
    read(Skore), % Přečte term
    seen.

18.3 Textová dobrodružná hra – Váš první interaktivní svět

Teď spojíme vše, co jsme se naučili: fakta, pravidla, proměnné, dynamickou databázi a vstup/výstup. Vytvoříme jednoduchou textovou hru, kde hráč prozkoumává místnosti, sbírá předměty a řeší jednoduché hádanky. Prolog je pro takové hry ideální, protože jeho logická povaha se skvěle hodí pro popis světa a jeho pravidel.

Textová hra (Vektorová)
Obrázek 18.1: Obrazovka textové adventury. Hráč zadává příkazy a prozkoumává svět.

18.3.1 Svět hry – Fakta a dynamické stavy

Náš svět bude popsán fakty. Některá fakta budou statická (popis místností), jiná dynamická (kde je hráč, co má v inventáři).

% --- Statická fakta o světě ---

% mistnost(ID, Popis).
mistnost(jeskyne, 'Jsi v temné jeskyni. Slyšíš kapání vody.').
mistnost(les, 'Jsi v hustém lese. Slunce prosvítá skrz koruny stromů.').
mistnost(reka, 'Stojíš u břehu divoké řeky.').

% cesta(Odkud, Smer, Kam).
cesta(jeskyne, sever, les).
cesta(les, jih, jeskyne).
cesta(les, vychod, reka).
cesta(reka, zapad, les).

% objekt(ID, Popis).
objekt(mec, 'Starý, rezavý meč.').
objekt(klic, 'Malý, lesklý klíč.').
objekt(truhla, 'Stará dřevěná truhla.').

% umisteni_objektu(ObjektID, MistnostID).
umisteni_objektu(mec, jeskyne).
umisteni_objektu(klic, les).
umisteni_objektu(truhla, reka).

% --- Dynamické stavy (mění se během hry) ---
% Musíme je deklarovat jako dynamické!
:- dynamic umisteni_hrace/1.
:- dynamic inventar/1.

% Počáteční stav hráče
umisteni_hrace(jeskyne).

18.3.2 Základní herní mechanismy

% --- Popis aktuální místnosti ---
popis_mistnosti :-
    umisteni_hrace(AktualniMistnost),
    mistnost(AktualniMistnost, Popis),
    write(Popis), nl,
    write('Vidíš: '),
    (umisteni_objektu(Objekt, AktualniMistnost) ->
        objekt(Objekt, PopisObjektu),
        write(PopisObjektu), write(' ')
    ;
        write('nic zajímavého.')
    ), nl,
    write('Můžeš jít: '),
    (cesta(AktualniMistnost, Smer, _) ->
        write(Smer), write(' ')
    ;
        write('nikam.')
    ), nl.

% --- Pohyb hráče ---
jdi(Smer) :-
    umisteni_hrace(AktualniMistnost),
    cesta(AktualniMistnost, Smer, NovaMistnost),
    retract(umisteni_hrace(AktualniMistnost)),
    assert(umisteni_hrace(NovaMistnost)),
    popis_mistnosti.
jdi(_) :-
    write('Tudy cesta nevede.'), nl.

% --- Vzít předmět ---
vezmi(Objekt) :-
    umisteni_hrace(AktualniMistnost),
    umisteni_objektu(Objekt, AktualniMistnost),
    retract(umisteni_objektu(Objekt, AktualniMistnost)),
    assert(inventar(Objekt)),
    write('Vzal jsi '), write(Objekt), nl.
vezmi(Objekt) :-
    write('Nemůžeš vzít '), write(Objekt), nl.

% --- Zobrazit inventář ---
ukaz_inventar :-
    write('Tvůj inventář:'), nl,
    (inventar(X) ->
        write('- '), write(X), nl,
        fail
    ;
        write('Je prázdný.'), nl
    ).
ukaz_inventar. % Pro případ, že inventář je prázdný a fail selže

% --- Hlavní herní smyčka ---
herni_smycka :-
    popis_mistnosti,
    write('> '),
    read(Prikaz), % Přečte příkaz (např. jdi(sever).)
    (Prikaz == konec ->
        write('Konec hry.'), nl
    ;
        call(Prikaz), % Zavolá predikát odpovídající příkazu
        herni_smycka
    ).

18.3.3 Jak hrát

1. Zkopírujte kód do souboru hra.pl. 2. Spusťte Prolog a načtěte soubor: ?- [hra]. 3. Začněte hru: ?- herni_smycka. 4. Zadávejte příkazy (nezapomeňte na tečku!): * jdi(sever). * vezmi(mec). * ukaz_inventar. * konec. (pro ukončení hry)

Klíčový koncept: Interaktivita: Oživení programu

Vstupní a výstupní operace, spolu s dynamickou databází, mění Prolog z pouhého "logického stroje" na interaktivní program. Umožňují mu reagovat na uživatele, měnit svůj vnitřní stav a vytvářet tak dynamické a poutavé aplikace, jako jsou hry nebo chatboti.

Otázky k zamyšlení

  1. Proč je důležité, aby predikáty jako umisteni_hrace/1 a inventar/1 byly deklarovány jako dynamic?
  2. Jak byste rozšířili hru, aby hráč mohl "použít" klíč na truhlu? (Nápověda: Potřebujete nové pravidlo pouzij(Objekt1, Objekt2) a fakt ma_klic(truhla, klic)).
  3. Jaký je rozdíl mezi write(Term) a write('Text')?
  4. Proč je v Prologu nutné ukončovat vstup z klávesnice tečkou?

🧪 Laboratoř: Rozšiřujeme hru