Both sides previous revision
Poprzednia wersja
Nowa wersja
|
Poprzednia wersja
|
pl:dydaktyka:ggp:gdl [2016/04/27 01:45] msl [3 Przykład: Kółko i krzyżyk] |
pl:dydaktyka:ggp:gdl [2019/06/27 15:50] (aktualna) |
Przedstawmy GDL na przykładzie gry [[http://playtictactoe.org/|kółko i krzyżyk]]. | Przedstawmy GDL na przykładzie gry [[http://playtictactoe.org/|kółko i krzyżyk]]. |
| |
== Gracze == | ==== Gracze ==== |
| |
Zacznijmy od tego, że w grze występuje dwóch graczy o pięknych imionach "kolko" i "krzyzyk". Służy do tego predefiniowany fakt ''role'': | Zacznijmy od tego, że w grze występuje dwóch graczy o pięknych imionach "kolko" i "krzyzyk". Służy do tego predefiniowany fakt ''role'': |
Składnia jest taka sama jak w języku [[pl:prolog:start|Prolog]] (lub Datalog) z tą różnicą, że na końcu nie ma kropki. Ponownie stałe pisane są małą, a zmienne wielką literą. | Składnia jest taka sama jak w języku [[pl:prolog:start|Prolog]] (lub Datalog) z tą różnicą, że na końcu nie ma kropki. Ponownie stałe pisane są małą, a zmienne wielką literą. |
| |
== Definiowanie fluentów == | ==== Definiowanie fluentów ==== |
| |
Następnie należy zdefiniować fluenty, które będą opisywać stan gry. W przypadku kółka i krzyżyk stan gry opisany jest przy pomocy 29 fluentów: | Następnie należy zdefiniować fluenty, które będą opisywać stan gry. W przypadku kółka i krzyżyk stan gry opisany jest przy pomocy 29 fluentów: |
Reguły w GDL'u różnią się od reguł Prologowych pod jednym ważnym aspektem: musi być zapewniony fakt, że się nie zapętlą. Więcej szczegółów na ten temat w [[http://www.inf.tu-dresden.de/content/institutes/ki/cl/study/winter09/ggp/kapitel2.pdf|slajdach z uniwersytetu z Dresden]]. | Reguły w GDL'u różnią się od reguł Prologowych pod jednym ważnym aspektem: musi być zapewniony fakt, że się nie zapętlą. Więcej szczegółów na ten temat w [[http://www.inf.tu-dresden.de/content/institutes/ki/cl/study/winter09/ggp/kapitel2.pdf|slajdach z uniwersytetu z Dresden]]. |
| |
== Dostępne akcje == | ==== Dostępne akcje ==== |
| |
Teraz należy się zastanowić nad tym, jakie akcje są wykonywalne w grze. W przypadku kółka i krzyżyk jest ich 20: | Teraz należy się zastanowić nad tym, jakie akcje są wykonywalne w grze. W przypadku kółka i krzyżyk jest ich 20: |
Predykat ''input'' przyjmuje dwa argumenty, pierwszy to nazwa gracza, który może wykonać akcję, drugim jest sama akcja. | Predykat ''input'' przyjmuje dwa argumenty, pierwszy to nazwa gracza, który może wykonać akcję, drugim jest sama akcja. |
| |
== Dozwolone ruchy == | ==== Dozwolone ruchy ==== |
| |
Teraz, znając już możliwe akcje, wypadałoby powiedzieć, kiedy dany ruch jest dozwolony. Służy do tego predykat ''legal''. Aby sprawdzić, czy dany fluent jest aktualnie prawdziwy posługujemy się predykatem ''true''. | Teraz, znając już możliwe akcje, wypadałoby powiedzieć, kiedy dany ruch jest dozwolony. Służy do tego predykat ''legal''. Aby sprawdzić, czy dany fluent jest aktualnie prawdziwy posługujemy się predykatem ''true''. |
Innymi słowy - gracz może zaznaczyć dane pole, o ile jest puste i jest jego tura. Gracz może też wykonać akcję ''noop'', jeżeli tura należy do przeciwnika. | Innymi słowy - gracz może zaznaczyć dane pole, o ile jest puste i jest jego tura. Gracz może też wykonać akcję ''noop'', jeżeli tura należy do przeciwnika. |
| |
== Stan początkowy == | ==== Stan początkowy ==== |
| |
Podobnie jak w PDDL'u, musimy zdefiniować stan początkowy świata. Służy do tego tego predykat ''init''. Możemy przy jego pomocy określić, które fluenty są prawdziwe u zarania dziejów. Dla kółka i krzyżyk, wszystkie komórki są puste, a tura należy do kółka: | Podobnie jak w PDDL'u, musimy zdefiniować stan początkowy świata. Służy do tego tego predykat ''init''. Możemy przy jego pomocy określić, które fluenty są prawdziwe u zarania dziejów. Dla kółka i krzyżyk, wszystkie komórki są puste, a tura należy do kółka: |
</code> | </code> |
| |
== Zmiana stanu == | ==== Zmiana stanu ==== |
| |
Teraz przechodzimy do części najtrudniejszej --- w jaki sposób zamodelować zmiany stanu świata. Zakładamy, że czas w grze jest dyskretny i w każdej kolejnej turze gry obliczany jest nowy stan świata. Zależy on jedynie od poprzedniego stanu świata oraz akcji wykonanych przez graczy. Pojawia się tutaj [[https://en.wikipedia.org/wiki/Frame_problem#Fluent_calculus_solution|problem ramki (lub klatki)]]. Nazwa pochodzi od analogii między następującymi po sobie stanami świata z klatkami na taśmie filmowej. Wszystkie klatki filmowe są od siebie niezależne, różnią się od swoich poprzedników jedynie szczegółami --- reszta jest taka sama. Podobnie tutaj chcielibyśmy, żeby poza //explicite// zamodelowanymi zmianami, reszta świata pozostała bez zmian. W naszym przypadku oznacza to, że musimy zamodelować również brak zmiany :-( | Teraz przechodzimy do części najtrudniejszej --- w jaki sposób zamodelować zmiany stanu świata. Zakładamy, że czas w grze jest dyskretny i w każdej kolejnej turze gry obliczany jest nowy stan świata. Zależy on jedynie od poprzedniego stanu świata oraz akcji wykonanych przez graczy. Pojawia się tutaj [[https://en.wikipedia.org/wiki/Frame_problem#Fluent_calculus_solution|problem ramki (lub klatki)]]. Nazwa pochodzi od analogii między następującymi po sobie stanami świata z klatkami na taśmie filmowej. Wszystkie klatki filmowe są od siebie niezależne, różnią się od swoich poprzedników jedynie szczegółami --- reszta jest taka sama. Podobnie tutaj chcielibyśmy, żeby poza //explicite// zamodelowanymi zmianami, reszta świata pozostała bez zmian. W naszym przypadku oznacza to, że musimy zamodelować również brak zmiany :-( |
<code prolog> | <code prolog> |
next(cell(M,N,x)) :- | next(cell(M,N,x)) :- |
does(kolko,mark(M,N)) & | does(krzyzyk,mark(M,N)) & |
true(cell(M,N,b)) | true(cell(M,N,b)) |
| |
next(cell(M,N,o)) :- | next(cell(M,N,o)) :- |
does(krzyzyk,mark(M,N)) & | does(kolko,mark(M,N)) & |
true(cell(M,N,b)) | true(cell(M,N,b)) |
| |
| |
next(cell(M,N,b)) :- | next(cell(M,N,b)) :- |
does(W,mark(J,K)) | does(W,mark(J,K)) & |
true(cell(M,N,b)) & | true(cell(M,N,b)) & |
distinct(M,J) | distinct(M,J) |
| |
Akcje te kolejno oznaczają: | Akcje te kolejno oznaczają: |
- jeżeli komórka jest pusta, a gracz kółko ją zaznaczył, to w kolejnej turze w tej komórce będzie kółko | |
- jeżeli komórka jest pusta, a gracz krzyżyk ją zaznaczył, to w kolejnej turze w tej komórce będzie krzyżyk | - jeżeli komórka jest pusta, a gracz krzyżyk ją zaznaczył, to w kolejnej turze w tej komórce będzie krzyżyk |
| - jeżeli komórka jest pusta, a gracz kółko ją zaznaczył, to w kolejnej turze w tej komórce będzie kółko |
- jeżeli dana komórka nie była pusta (pomocniczy predykat ''distinct'' sprawdza tożsamość argumentów), to w kolejnej turze będzie miała te samą zawartość | - jeżeli dana komórka nie była pusta (pomocniczy predykat ''distinct'' sprawdza tożsamość argumentów), to w kolejnej turze będzie miała te samą zawartość |
- jeżeli dana komórka była pusta, ale była komórką, która została zaznaczona, to w kolejnej turze też będzie pusta (aż dwie reguły!) | - jeżeli dana komórka była pusta, ale nie była komórką, która została zaznaczona, to w kolejnej turze też będzie pusta (aż dwie reguły!) |
- jeżeli dana tura należała do kółka, to kolejna będzie należała do krzyżyka | - jeżeli dana tura należała do kółka, to kolejna będzie należała do krzyżyka |
- jeżeli dana tura należała do krzyżyka, to kolejna będzie należała do kółka | - jeżeli dana tura należała do krzyżyka, to kolejna będzie należała do kółka |
Warto zauważyć, że akcje 3 i 4 służą właśnie walce z problemem ramki. | Warto zauważyć, że akcje 3 i 4 służą właśnie walce z problemem ramki. |
| |
== Warunki końca gry == | ==== Warunki końca gry ==== |
| |
Każdy wie, że celem gry w kółko i krzyżyk jest ułożenie linii. Żeby sprawdzić, czy w danym stanie ułożona jest linia, należy zdefiniować odpowiednie reguły: | Każdy wie, że celem gry w kółko i krzyżyk jest ułożenie linii. Żeby sprawdzić, czy w danym stanie ułożona jest linia, należy zdefiniować odpowiednie reguły: |
true(cell(M,3,Z)) | true(cell(M,3,Z)) |
| |
column(M,Z) :- | column(N,Z) :- |
true(cell(1,N,Z)) & | true(cell(1,N,Z)) & |
true(cell(2,N,Z)) & | true(cell(2,N,Z)) & |
Operator '~' oznacza negację. Podobnie jak w prologu działa ona na zasadzie [[https://en.wikipedia.org/wiki/Closed-world_assumption|świata zamkniętego]], tzn. zdanie uznajemy za fałszywe, jeżeli nie potrafimy udowodnić, że jest inaczej. W GDL'u istnieją specjalne obostrzenie ograniczające stosowanie negacji, ale na razie nie musimy się tym przejmować (więcej info na [[http://www.inf.tu-dresden.de/content/institutes/ki/cl/study/winter09/ggp/kapitel2.pdf|slajdach z uniwersytetu z Dresden]]). | Operator '~' oznacza negację. Podobnie jak w prologu działa ona na zasadzie [[https://en.wikipedia.org/wiki/Closed-world_assumption|świata zamkniętego]], tzn. zdanie uznajemy za fałszywe, jeżeli nie potrafimy udowodnić, że jest inaczej. W GDL'u istnieją specjalne obostrzenie ograniczające stosowanie negacji, ale na razie nie musimy się tym przejmować (więcej info na [[http://www.inf.tu-dresden.de/content/institutes/ki/cl/study/winter09/ggp/kapitel2.pdf|slajdach z uniwersytetu z Dresden]]). |
| |
== Kryterium zwycięstwa == | ==== Kryterium zwycięstwa ==== |
| |
{{ :pl:dydaktyka:ggp:tyle_wygrac.jpg?200|}} | {{ :pl:dydaktyka:ggp:tyle_wygrac.jpg?200|}} |
| |
<code prolog> | <code prolog> |
goal(kolko,100) :- line(x) & ~line(o) | goal(krzyzyk,100) :- line(x) & ~line(o) |
goal(kolko,50) :- ~line(x) & ~line(o) | |
goal(kolko,0) :- ~line(x) & line(o) | |
| |
goal(krzyzyk,100) :- ~line(x) & line(o) | |
goal(krzyzyk,50) :- ~line(x) & ~line(o) | goal(krzyzyk,50) :- ~line(x) & ~line(o) |
goal(krzyzyk,0) :- line(x) & ~line(o) | goal(krzyzyk,0) :- ~line(x) & line(o) |
| |
| goal(kolko,100) :- ~line(x) & line(o) |
| goal(kolko,50) :- ~line(x) & ~line(o) |
| goal(kolko,0) :- line(x) & ~line(o) |
</code> | </code> |
| |
Powyższe reguły mówią, że w przypadku zwycięstwa gracz dostaje całe 100 (tyle wygrać!), w przypadku remisu 50, a w przypadki porażki 0. Nie wiemy, co ta liczba symbolizuje, ale uznajmy, że są to ciasteczka, albo coś, czego nigdy nie za wiele. | Powyższe reguły mówią, że w przypadku zwycięstwa gracz dostaje całe 100 (tyle wygrać!), w przypadku remisu 50, a w przypadki porażki 0. Nie wiemy, co ta liczba symbolizuje, ale uznajmy, że są to ciasteczka, albo coś, czego nigdy nie za wiele. |
| |
W GDL nie ma arytmetyki --- liczby nie różnią się niczym od innych stałych. Co gorsza, jest ich tyle, co kot napłakał | <WRAP center round tip 60%> |
| W GDL nie ma arytmetyki --- liczby nie różnią się niczym od innych stałych. Stosowane są głównie do zliczania kroków oraz rozdawania nagród. |
| </WRAP> |
| |
| |
== Koniec == | ==== Koniec ==== |
| |
To by było na tyle, właśnie udało się zamodelować prostą grę w kółko i krzyżyk. | To by było na tyle, właśnie udało się zamodelować prostą grę w kółko i krzyżyk. |
Proszę zamodelować grę w "kamień, papier, nożyce, spocka i jaszczurkę". W razie problemów ze zrozumieniem reguł, proszę obejrzeć [[https://www.youtube.com/watch?v=iapcKVn7DdY|instrukcję video]]. Obrazek obok przedstawia użyteczną referencję. . Poćwiczyć można na żywo w sali, lub, jak na prawdziwego człowieka XXI wieku przystało, [[http://www.playmycode.com/play/game/cainy393/rock-paper-scissors-lizard-spock|online...]]. | Proszę zamodelować grę w "kamień, papier, nożyce, spocka i jaszczurkę". W razie problemów ze zrozumieniem reguł, proszę obejrzeć [[https://www.youtube.com/watch?v=iapcKVn7DdY|instrukcję video]]. Obrazek obok przedstawia użyteczną referencję. . Poćwiczyć można na żywo w sali, lub, jak na prawdziwego człowieka XXI wieku przystało, [[http://www.playmycode.com/play/game/cainy393/rock-paper-scissors-lizard-spock|online...]]. |
| |
===== Knowledge Interchange Format ===== | ===== - Knowledge Interchange Format ===== |
| |
O ile wszystko, co zostało powyżej napisane, jest prawdą, o tyle większość systemów GGP nie wspiera reprezentacji w stylu Prolog. Na potrzeby wysyłania i zapisania wiedzy (reprezentowanej w sposób regułowy) powstał format KIF (Knowledge Interchange Format). Podobnie jak w przypadku PDDL, bazuje on na lispie i używa notacji prefixowej. Ponadto symbol '':-'' zestępowany jest przez strzałkę ''<='', symbol koniunkcji ''&'' przez ''and'', symbol negacji ''~'' przez ''not''. Nazwy zmiennych poprzedzane są znakiem zapytania ''?''. Poniżej przedstawiony jest przykład translacji z wersji a'la Prolog: | O ile wszystko, co zostało powyżej napisane, jest prawdą, o tyle większość systemów GGP nie wspiera reprezentacji w stylu Prolog. Na potrzeby wysyłania i zapisania wiedzy (reprezentowanej w sposób regułowy) powstał format KIF (Knowledge Interchange Format). Podobnie jak w przypadku PDDL, bazuje on na lispie i używa notacji prefixowej. Ponadto symbol '':-'' zestępowany jest przez strzałkę ''<='', symbol koniunkcji ''&'' przez ''and'', symbol negacji ''~'' przez ''not''. Nazwy zmiennych poprzedzane są znakiem zapytania ''?''. Poniżej przedstawiony jest przykład translacji z wersji a'la Prolog: |
(<= (q ?y) (p a ?y) (p ?y c)) | (<= (q ?y) (p a ?y) (p ?y c)) |
</code> | </code> |
| |
| === - Zad 1 === |
| |
| Dla każdej z poniższych par określ, czy wersja prefixowa, jest wiernym tłumaczeniem wersji Prologowej. |
| |
| <code lisp> |
| r(a,b) :- p(a) & q(b) |
| (<= (r a b) (and (p a) (q b))) |
| </code> |
| <code lisp> |
| r(a,b) :- p(a) & q(b) |
| (<= (r a b) (p a) (q b)) |
| </code> |
| <code lisp> |
| r(x,y) :- p(x) & q(y) |
| (<= (r ?x ?y) (p ?x) (q ?y)) |
| </code> |
| <code lisp> |
| r(X,Y) :- p(X) & q(Y) |
| (<= (r ?x ?y) (p ?x) (q ?y)) |
| </code> |
| |
| === - Zad 2 === |
| |
| Zapoznaj się z {{:pl:dydaktyka:ggp:tictactoe.kif.zip|wersją 'kółka i krzyżyk' zapisaną w KIF}}. |
| Jakie różnice widzisz między nią a naszym poprzednim modelem? |
| |
| === - Zad 3 === |
| |
| Zapoznaj się z {{:pl:dydaktyka:ggp:blocks.kif.zip|modelem prostego świata klocków w GDL}}. |
| * jakie są kryteria zakończenia tej gry? |
| * w jaki sposób liczone są tury? |
| |
| === - Zad 4 === |
| |
| Przepisz modele 'kólka i krzyżyka', 'krzyżyka i krzyżyka' oraz 'papier, kamień, nożyce, spocka i jaszczurki' do postaci KIF. |
| |
| ===== - Walidacja modeli ===== |
| |
| <WRAP center round tip 60%> |
| Poniższe instrukcje przygotowane są z myślą o laboratorium C2 316, gdzie każdy komputer ma Eclipse. |
| </WRAP> |
| |
| Proszę uruchomić Eclipse i następnie: |
| * ''File -> Import Project'' |
| * Wybrać opcję importowania z repozytorium git |
| * Jeżeli tej opcji nie ma (Eclipse jest za stare), to trzeba ręcznie sklonować repo i zaimportować projekt z katalogu. Reszta tutejszej instrukcji nie ma sensu FIXME |
| * Wybrać opcję klonowania z zewnętrznego URI |
| * Wpisać URI: ''https://github.com/ggp-org/ggp-base.git'' |
| * Wybrać gałąź ''master'' |
| * Wybrać jakiś //rozsądny// katalog dla projektu |
| * Wybrać projekt ''ggp-base'' do zaimportowania |
| |
| ==== - Dodanie gier do środowiska ==== |
| |
| W katalogu projektu proszę dostać się do ścieżki ''games/games'' i stworzyć tam trzy katalogi ''kolko-krzyzyk'', ''krzyzyk-krzyzyk'' i ''spock''. Każdy katalog powinien zawierać dwa pliki: |
| * plik ''.kif'' z modelem gry |
| * plik ''METADATA'' z zawartością: |
| |
| <code javascript> |
| { |
| "gameName": "<nazwa gry>", |
| "rulesheet": "<nazwa pliku kif>" |
| } |
| </code> |
| |
| ==== - Walidacja gier ==== |
| |
| Z poziomu Eclipse proszę uruchomić aplikację ''Validator'' (rozwijana lista przy guziki ''play''). |
| |
| ''Validator'', jak sama nazwa wskazuje, ma na celu sprawdzenie, czy dany model jest prawidłowym modelem GDL. |
| |
| === Ćwiczenia === |
| |
| - W polu ''Repository'' wybrać ''Local Game Repository'' |
| - W polu ''Game'' wybrać ''Tic-Tac-Toe'' |
| - Wciśnąć przycisk ''Validate'' --- program powinien pokazać same sukcesy |
| - Powtórzyć to samo dla własnych modeli |
| - Poprawić własne modele |
| - Zdobyć kolejne sukcesy. |
| |
| |
| |
| ===== - Zabawa ===== |
| |
| Z poziomu Eclipse proszę uruchomić aplikację ''Kiosk'' (rozwijana lista przy guziki ''play''). |
| |
| === Ćwiczenia === |
| |
| - W polu ''Opponent'' proszę wybrać ''SimpleMonteCarloPlayer'' |
| - Wybrać jakąś znajomą grę |
| - Wygrać! |
| |
| {{ :pl:dydaktyka:ggp:have-fun.jpg?400 |}} |
| |