Consignes
La collaboration orale est encouragée, mais le partage de code et le copier/coller (via Discord, github, etc.) n'est pas autorisé et pourra être pénalisé.
Le rendu de ce TP se fera uniquement par TPLab et consistera en une archive (zip, tar, etc.) contenant un répertoire TP3-NOM avec :
-
un fichier cartes-NOM.pl
-
un fichier zebra-NOM.pl
-
des fichiers mon_jeu.pl et mon_jeu.sol
-
un fichier texte README contenant les réponses aux questions du TP.
Vous aurez compris que NOM devra être remplacé par votre nom (ou vos noms en cas de binômes) !
Tous vos fichiers doivent comporter une entête avec votre noms et votre groupe de TP.
Tout non respect d'une ou plusieurs de ces consignes entrainera automatiquement un retrait de points sur votre note !
Liens utiles
-
le deuxième fichier de test (arbre généalogique),
-
un mini jeu le_talisman_perdu.pl avec sa solution le_talisman_perdu.sol
-
un squelette de jeu mon_jeu.pl pour la deuxième partie du TP
-
Adventure in Prolog, version en ligne du livre de Dennis Merritt
Si SWI-Prolog n'est pas installe dans votre salle (et seulement dans ce cas), vous pouvez utiliser l'interpréteur SWI-Prolog en ligne.
Préliminaires : Prolog
Utilisation de l'interpréteur
Nous allons utiliser SWI Prolog, une distribution libre et gratuite du langage de programmation Prolog.
Vous pouvez l'installer sous Ubuntu (paquet swi-prolog) et même (il parait) sous Windows ou Mac.
Le plus simple est d'utiliser un éditeur de texte pour écrire des fichiers Prolog (extension .pl). Vous pouvez ensuite lancer l'interpréteur avec le nom d'un fichier en argument :
$ swipl test.pl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.0.2)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit http://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
?-
Le prompt "?-" indique que l'interpréteur attend une requête de votre part. Toute requête doit se terminer par un point "." et un retour charriot. Lorsqu'une requête occupe plusieurs lignes, le prompt devient "|".
Vous pouvez quitter l'interpréteur avec Control-d pour fermer l'entrée standard.
Un exemple simple
Le fichier suivant contient l'exemple standard :
% pour montrer qu'un "truc" est mortel, il suffit de montrer que c'est un humain
% Autrement dit "tous les humains sont mortels"
mortel(X) :- humain(X).
% pour montrer qu'un "truc" est un humain, il suffit de montrer que c'est un philosophe
% Autrement dit "tous les philosophes sont des humains"
humain(X) :- philosophe(X).
% Louis XIV est un humain
humain(louis_XIV).
% Socrate est un philosophe
philosophe(socrate).
% Milou est un chien !
chien(milou).
Quelques points importants
-
Les variables commencent par une majuscule.
-
Les constantes, fonctions, prédicats et relations commencent par une minuscule. (Il est possible d'avoir des noms de prédicats plus généraux si on les écrit entre apostrophes simples :
'Louis XIV'
). -
Les commentaires sont donnés en commençant une ligne par un symbole "
%
". (On peut aussi avoir des commentaires sur plusieurs lignes avec/* ... */
. À la différence du C, il est possible d'imbriquer ces commentaires.)
Téléchargez le fichier de test et lancez l'interpréteur dessus.
-
Vérifiez que
milou
est un chien avec la requête?- chien(milou).
-
Vérifiez que
louis_XIV
est mortel avec la requête?- mortel(louis_XIV).
-
Quelles sont les réponses aux requêtes suivantes ?
?- chien(socrate). ?- chien(toto). ?- souris(milou).
-
Cherchez tous les atomes qui satisfont le prédicat
mortel
avec la requête?- mortel(X).
-
vous pouvez arrêter la recherche des solutions suivantes avec "Entrée", et demander d'autres solutions avec "Espace"
-
pour une requête comme celle ci, le choix de la variable
X
n'est pas important. (Testez avecUne_autre_variable
...)
Le point important est que bien qu'aucun atome ne soit explicitement déclaré
mortel
, Prolog arrive à inférer cette information. -
Vocabulaire
- fait
-
c'est une ligne de la forme
quelque_chose(..., ...).
C'est une manière d'affirmer que quelque chose est vrai. Cela permet d'ajouter une formule dans la base de connaissances.
Les
...
sont des termes (éventuellement zéro) qui peuvent contenir des variables (elles sont quantifiées universellement) et des atomes (comme danschien(milou).
). - règle
-
c'est un morceau du fichier de la forme
quelque_chose(..., ...) :- autre_chose(..., ...), et_autre_chose(..., ...), ... un_dernier_truc(..., ...).
Le symbole
:-
peut se lire "si". La règle énonce des conditions pour quequelque_chose(..., ...)
soit vrai. Les virgules ",
" dans le corps de la règle sont des conjonctions. - prédicat (ou relation)
-
c'est une séquence de faits et règles consécutives portant sur le même nom (
quelque_chose
dans les exemples précédents.) - atome
-
c'est une constante (comme
socrate
dans l'exemple). - terme
-
ce sont les arguments d'un prédicat / relation. Ils sont constitués de variables, d'atomes et de symboles fonctions.
-
Lorsqu'une variable n'apparait qu'une seule fois dans une règle, on parle de variable singleton. SWI Prolog affiche un warning dans ce cas, car il pourrait s'agir d'une faute de frappe (
Une_Variable
etUne_variable
par exemple). On peut utiliser_
comme variable "bidon" dans ce cas, ou_une_variable
si on souhaite lui donner un nom... -
Formellement, un nom contient aussi l'arité du prédicat, c'est à dire son nombre d'arguments. Les noms sont souvent écrits comme
quelque_chose/2
(arité 2) ouquelque_chose/0
, etc. Vous ne devez pas préciser l'arité lorsque vous définissez un prédicat ou lorsque vous l'utiliser. Vous n'en aurez besoin que lorsque vous déclarez un prédicat commediscontiguous
(cf point suivant) oudynamic
(cf Prédicats dynamiques). -
SWI Prolog insiste pour que les différentes clauses d'un prédicat soient consécutives. Si ce n'est pas le cas, il faut le déclarer avant la définition avec
:- discontiguous quelque_chose/2.
-
Les symboles de fonction sont inertes : le calcul est uniquement fait avec les prédicats.
-
Il y a une différence entre
-
'Quelque chose'
: c'est un atome, -
"Quelque chose"
: c'est un élément du typestring
de SWI Prolog, -
`Quelque chose`
: c'est une liste de points Unicode.
-
Unification
Le symbole =
en Prolog est utilisé pour l'unification,
c'est à dire la recherche de valeurs pour les variables qui permettent de
rendre 2 choses égales.
Cette action est symétrique : on peut toujours inverser la droite et la gauche d'une unification.
?- X = f(banane).
X = f(banane).
?- f(banane) = Y.
Y = f(banane).
?- f(X) = f(banane).
X = banane.
?- f(X) = g(Y).
false.
Quelques exercices faciles
Le fichier test-genealogie.pl contient un morceau d'arbre
généalogique. Ce fichier définit 2 relations pere/2
et
mere/2
avec des lignes de la forme
pere(theophile, louise).
mere(eugenie, louise).
pere(theophile, jean_francois).
mere(eugenie, jean_francois).
...
Nous avons donc :
swipl TP3/test-genealogie.pl
...
?- pere(theophile, louise).
true
(Attention, une fois que Prolog a trouvé que la relation était vérifiée, il continue de chercher d'autres justifications...)
L'arbre généalogique est également donné de manière "graphique" en haut du fichier.
À votre avis, quel sera le résultat de
swipl TP3/test-genealogie.pl
...
?- X = pere(theophile, louise).
?????
?- pere(theophile, louise) = X.
?????
Testez ces requêtes !
L'unification permet d'imposer des "égalités". On peut par exemple écrire une
relation grand_pere/2
avec
grand_pere(GP, PF) :-
pere(GP, P1),
pere(P2, PF),
P1 = P2.
Il est en général plus élégant de faire les unifications de manière implicite en réutilisant des variables :
grand_pere(GP, PF) :-
pere(GP, P),
pere(P, PF).
Testez les deux définitions de grand_pere/2
(en les ajoutant
dans le fichier test-genealogie.pl
) et vérifiez qu'elles donnent
le même résultat.
Profitez on pour corriger le bug suivant :
swipl TP3/test-genealogie.pl
...
?- grand_pere(theophile, thomas).
false.
N'utilisez pas l'opérateur "OU" de Prolog (|
) pour résoudre le
problème. Vous pouvez le faire simplement en ajoutant des règles.
L'unification Prolog ne fait pas de "occur check" (test d'apparition). Normalement, il n'est pas possible d'unifier X avec f(X) ou tout autre terme contenant X. Ce test est assez couteux et Prolog ne le fait pas pour gagner du temps.
En pratique, cela ne pose pas de problème, mais formellement parlant, l'unification n'est pas toujours correcte.
La négation
Prolog a un concept de négation :
-
le prédicat
fail/0
échoue tout le temps,?- fail. false
-
le prédicat
\+ quelque_chose(...)
échoue lorsque le prédicatquelque_chose(...)
réussit, et vice versa, -
attention, une négation ne peut jamais provoquer d'instantiation !
Toujours avec le fichier test-genealogie.pl, une première définition de la
relation fratrie/2
(pour "frère ou sœur") pourrait être
fratrie(X, Y) :-
mere(P, X),
mere(P, Y).
Cela fonctionne
swipl TP3/test-genealogie.pl
...
?- fratrie(edith, louise).
true.
?- fratrie(edith, jean_francois).
true.
?- fratrie(edith, pierre).
false.
... presque
?- fratrie(edith, X).
X = edith ;
X = louise ;
X = jean_francois.
On trouve que edith
est sa propre sœur !
Utiliser la négation permet de s'en sortir.
Testez la définition suivante qui ajoute la contrainte que X
et
Y
ne sont pas unifiables.
fratrie(X, Y) :-
\+ X = Y,
pere(P, X),
pere(P, Y).
Testez les requêtes suivantes
?- fratrie(edith, edith).
...
?- fratrie(X, edith).
...
?- fratrie(edith, X).
...
Qu'en pensez-vous ?
Expliquez ce phénomène et proposez une solution.
on peut écrire X \= Y
à la place de \+ X = Y
.
Récursion et listes
Prolog a un type de données listes, assez similaire au type liste de Caml.
-
une liste est donnée par ... la liste de ces éléments
[X, Y, Z]
pour une liste à 3 éléments, -
en interne, une liste est représentée
-
soit par la liste vide, notée
[]
-
soit par une tête de liste suivi d'une autre liste, notée
[Tete | Queue]
.
Attention à ne pas confondre
[X, Y]
et[X | Y]
. Dans le premier cas,Y
est un élément de la liste (le second et dernier élément) et dans le deuxième cas,Y
est une liste contenant potentiellement de nombreux éléments.La notation
[X, Y, Z]
est en fait un raccourci pour[X | [Y | [Z | []]]]
. Un autre raccourci pour cette même liste serait[X, Y | [Z]]
. -
On peut utiliser l'unification pour définir des prédicats. Voici par exemple
un prédicat deuxieme/2
dont le sens est X
est le
deuxième élément de la liste L
:
deuxieme(X, []) :- fail.
deuxieme(X, [Y]) :- fail.
deuxieme(X, [Y, Z | L]) :- X = Z.
Une manière beaucoup plus concise et élégante de définir le même prédicat est
deuxieme(X, [_, X | _]).
Bien que l'on définisse des relations, le mécanisme de recherche de solution de Prolog permet de faire des calculs : il suffit de mettre une variable à la place de l'argument que l'on cherche :
?- deuxieme(X, [17, -1, 42]).
X = -1.
Testez les 2 définitions de deuxieme/2
.
Plus intéressant, on peut définir des prédicats récursifs. Voici le
prédicat contient/2
:
contient([X | _], X).
contient([_ | L], X) :- contient(L, X).
Testez le prédicat contient/2
pour comprendre son comportement.
Dans un fichier listes-NOM.pl :
-
Écrivez un prédicat
dernier/2
dont le sens estX
est le dernier élément de la listeL
. -
Écrivez un prédicat
voisins/3
dont le sens estX
etY
sont 2 éléments consécutifs deL
(dans n'importe quel ordre). -
Écrivez un prédicat
devant/3
dont le sens estX
apparait avantY
dansL
, maisX
etY
ne sont pas forcément consécutifs.
Fonctionnalités "extra-logiques"
Affichage
Prolog possède également des "prédicats" qui n'ont pas de sens logique. Par exemple,
test(X) :-
write("La valeur de X est "), write(X), nl.
write/1
est un prédicat qui affiche quelque chose à l'écran, et
qui réussi toujours. nl/0
est un prédicat qui va à la ligne ("nl"
= "new line").
Prédicats dynamiques
Deux autres prédicats non logiques sont :
-
assert/1
qui permet d'ajouter un fait dans la base de donnée Prolog actuelle :?- mere(edith, arthur). false. ?- assert(mere(edith, arthur)). true. ?- mere(edith, arthur). true.
-
retract/1
qui permet de supprimer un fait dans la base de données.
Ces fonctionnalités seront très utiles dans la deuxième partie du TP !
SWI Prolog impose de déclarer les prédicats dynamiques. Il faut donc inclure une ligne
:- dynamic(mere/2).
avant la définition de mere
.
Comme write/1
, les prédicats assert/1
et
retract/1
ne sont pas "défaits" lors du backtracking.
Il est donc important de ne les faire qu'au dernier moment !
La coupure
Le symbole !
s'appelle la coupure. Il permet de dire au
moteur Prolog de ne pas faire de backtracking à ce niveau là : la coupure
bloque le backtracking.
L'utilisation de la coupure permet parfois d'améliorer l'efficacité, mais son utilisation n'est pas toujours facile.
Voici 4 versions du prédicat contient/2
:
contient1([A|_],A).
contient1([_|L],A) :- contient1(L,A).
contient2([_|L],A) :- contient2(L,A).
contient2([A|_],A).
contient3([A|_],A) :- !.
contient3([_|L],A) :- contient3(L,A).
contient4([_|L],A) :- contient4(L,A).
contient4([A|_],A) :- !.
-
Comparez les sur des requête du style
?- contient([1,2,3], 2). ?- contient([1,2,3], X). ?- contient(L, 2).
-
Quels sont les avantages et les inconvénients des versions avec coupure ?
-
La bibliothèque standard de Prolog contient un prédicat
member/2
. À quelle version correspond il ? (Attention, l'ordre des arguments demember/2
est inversé par rapport à celui decontient/2
.)
1. Problèmes logiques
1.1. Le problème des as
On peut résoudre le problème des as du TP1 en Prolog.
-
Écrivez, dans un fichier cartes-NOM.pl un prédicat
solution/1
qui donne la solution au problème des as.Votre prédicat pourra par exemple ressembler à
solution(L) :- L = [_,_,_,_], % la solution contient 4 éléments contient(L, pique), % la solution contient les 4 as contient(L, coeur), % ... contient(L, trefle), % ... contient(L, carreau), % ... % en position 2, il n'y a ni l'as de trefle, ni l'as de pique ... % l'as de trefle est plus a droite que l'as de carreau ... % l'as de carreau et l'as de coeur ne sont pas voisins ...
-
Vérifiez que Prolog ne trouve qu'une seule solution, et qu'elle est bien identique à celle que vous aviez trouvé au TP1.
-
Vous pouvez redéfinir les prédicats
voisins/3
oudevant/3
pour les utiliser. -
Attention, utilisez une version de
contient/2
sans coupure !
1.2. Le problème de Einstein
-
Dans un fichier zebra-NOM.pl, écrivez un prédicat
solution/1
qui recherche une solution au problème de Einstein du TP1. -
Vérifiez que Prolog ne trouve qu'une seule solution, et qu'elle est bien identique à celle que vous aviez trouvé au TP1.
Attention, la version donnée au TP1 contenait un bug volontaire et avait 2 solutions quasi identiques. (Le propriétaire du zèbre était en particulier le même dans ces 2 solutions.)
-
Comme pour le problème des as, votre prédicat peut commencer par
solution(L) :- L = [_, _, _, _, _], % il y a 5 maisons ...
-
Chaque élément de la solution représente un type de maison, avec un métier, un animal, un style de musique et une boisson. On peut représenter un tel élément en utilisant un symbole de fonction :
maison(Type, Metier, Animal, Musique, Boisson)
.On peut par exemple modéliser le premier indice "Le musicien a une tortue." par
contient(L, maison(_,musicien,tortue,_,_)),
-
Attention, utilisez une version de
contient/2
sans coupure !
2. Un jeu d'aventure en Prolog
L'objectif de ce TP est maintenant d'écrire un jeu d'aventure textuel en Prolog. Le prototype de ce type de jeux est Colossal Cave Adventure, dont les premières versions datent de 1975.
Voila un exemple typique d'interaction
You are standing at the end of a road before a small brick building. Around
you is a forest. A small stream flows out of the building and down a gully.
>go building
Inside Building
You are inside a building, a well house for a large spring.
There are some keys on the ground here.
There is tasty food here.
There is a shiny brass lamp nearby.
There is an empty bottle here.
>take lamp
Taken.
>light lamp
You switch the brass lantern on.
>...
Les versions originales du jeu étaient écrites en Fortran, et les jeux d'aventure textuels modernes sont souvent écrits en utilisant des langages de descriptions spécifiques (Z-machine ou Glulx).
En utilisant les concepts de base de Prolog, il est assez facile de créer un petit jeu d'aventure de ce style. Pour ceux que ça intéresse, vous trouverez de nombreux détails dans le livre de Dennis Merritt Adventure in Prolog.
2.1. Un exemple
Téléchargez le fichier le_talisman_perdu.pl et lancez le jeu :
$ swipl le_talisman_perdu.pl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.0.2)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit http://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
?- jouer.
Les commandes doivent être données avec la syntaxe Prolog habituelle.
Les commandes existantes sont :
jouer. -- pour commencer une partie.
n. s. e. o. -- pour aller dans cette direction (nord / sud / est / ouest).
aller(direction) -- pour aller dans cette direction.
prendre(objet). -- pour prendre un objet.
lacher(objet). -- pour lacher un objet en votre possession.
regarder. -- pour regarder autour de vous.
instructions. -- pour revoir ce message !.
fin. -- pour terminer la partie et quitter.
Vous êtes à l'entrée de la grotte.
À l'est se trouve la salle de vie commune où les membres du clan se réchauffent..
À l'ouest se trouve un petit passage sombre.
Tout droit, au nord, la grotte s'enfonce dans la montagne.
Votre grande sœur est là. Elle doit remplacer le shaman, emporté par une maladie.
Elle n'ose pas rejoindre le reste clan, car elle n'a toujours pas trouvé le cailloux scintillant,
talisman de la tribu.
true.
?-
Si vous devez utiliser l'interpréteur en ligne de SWI-prolog, vous devrez :
-
ouvrir le fichier dans un éditeur de texte et faire un copier-coller dans la fenêtre de votre navigateur
-
modifier les lignes 149-151 en remplaçant
fin :- nl, write("La partie est finie."), nl, halt.
par
fin :- nl, write("La partie est finie."), nl. %halt.
-
le jeu recommence au début a chaque requête. Pour faire une partie, vous devrez donc donner la liste de toutes les commandes, séparées par des virgules. (Lorsque vous lancez une requête avec "Controle-Entree", la requête n'est pas effacée. Il est donc facile d'ajouter une nouvelle commande.)
Vous allez donc exécuter les requêtes suivantes :
-
aller(est).
-
aller(est), prendre(torche).
-
aller(est), prendre(torche), aller(ouest).
-
...
-
Passez quelques minutes à jouer pour vous familiariser avec le style de jeux. (Indice: la solution est donnée dans le fichier le_talisman_perdu.sol)
Le fichier le_talisman_perdu.pl contient environ 200 lignes de code, et la plupart sont des descriptions. En voici un extrait :
decrire(espace_de_vie) :-
write("Cette grande salle est éclairée par un grand feu."), nl,
write("Tout le clan est présent et les chasseurs préparent une sortie."), nl,
write("Votre grande sœur est absente."), nl,
write("La sortie se trouve à l'ouest."), nl.
2.2. À vous !
Écrivez votre propre jeu d'aventure en Prolog !
Vous pouvez partir du fichier mon_jeu.pl, mais ce n'est pas obligatoire.
Ce TP est très fortement inspiré du livre - Adventure in Prolog, de Dennis Merritt et d'un TP de David Matuszek
N'hésitez pas à vous en inspirer, sans pour autant copier ce que vous trouvez.
Un autre jeu d'aventure en Prolog (un peu plus complexe) du même type se
trouve ici : Jolly
Roger (Lancez le prédicat play/0
pour commencer la partie.)
Quelques détails
Le non respect des consignes suivantes entrainera automatiquement un retrait de points de votre note finale !
-
Le chargement de votre jeux ne devra pas provoquer d'erreur ou de warning de la part de SWI-prolog.
-
Votre fichier devra comporter une commande
jouer.
qui permet de lancer la partie. Si votre jeu est paramétrable, documentez le dans votre fichier README, et la commandejouer.
devra utiliser des valeurs par défaut raisonnables. -
Ma première impression de votre jeu va se faire en lançant la commande
jouer.
, avant même de lire votre fichier README. L'ergonomie de votre jeu est donc un facteur important.-
Fournissez des raccourcis pour les commandes courantes (
n/0
pournord/0
,r/0
pourregarder/0
, etc.). -
Il faut pouvoir obtenir la liste des commandes existantes facilement (
instructions/0
). -
Affichez les déplacements possibles (sauf éventuellement pour les passages secrets).
-
N'imposez pas de contrainte inutile. Par exemple, si vous avez une commande
combiner/2
, il faut quecombiner(pile, lampe).
etcombiner(lampe, pile)
soient équivalentes.
-
-
L'intérêt de votre jeu aura un impact sur la note. Soyez originaux.
-
Le formatage de votre code aura un impact sur la note. Ne soyez pas trop originaux.
-
En plus du fichier MON_JEU.pl, vous devez fournir un fichier MON_JEU.sol contenant une solution. Ce fichier contiendra une suite des commandes (une par ligne) pour terminer votre jeux. (Regardez le fichier le_talisman_perdu.sol.)
Je testerais avec la commande
$ swipl MON_JEU.pl < MON_JEU.sol
N'hésitez pas à fournir plusieurs fichiers solution si vous souhaitez illustrer plusieurs fins (succès, échec, fin alternative, etc.)
-
Votre jeu devra comporter plusieurs fonctionnalités "avancées" (voir plus bas).
Les fonctionnalités implémentées doivent être listées dans votre fichier README. Pour chacune d'entre elles, vous devez également fournir quelques explications et fournir une suite de commandes pour que je puisse les tester facilement.
Voici un extrait possible d'un fichier README :
### Gestion des objets cachés Certains objets peuvent satisfaire un prédicat "hidden/1". Par exemple, la clé du coffre, dans le bureau, est déclarée avec position(cle, bureau). hidden(cle). L'action "fouiller/0" utilise le prédicat Prolog "retract/1" pour rendre un objet visible. Il sera donc ensuite affiché. Exemple : jouer. n. prendre lampe. o. % On est dans le bureau. regarder. % La clé n'est pas listée. fouiller. % On a le message "Vous trouvez une clé cachée derrière le bibelot, % sur la cheminée." regarder. % La clé est maintenant listée. ...
Fonctionnalités avancées
Plusieurs points du barème sont réservés à l'implémentation de quelques fonctionnalités "avancées". Toutes n'ont pas la même difficulté, et je vous conseille d'en choisir au moins 3 si vous souhaitez avoir les points associés.
Je vous rappelle que vous devez lister et décrire chacune des fonctionnalités implémentées dans votre fichier README.
Voici quelques exemples de telles fonctionnalités. La liste suivante n'est pas exhaustive. N'hésitez pas à faire d'autres choses !
- présence d'objets cachés
-
une clé dans un pot de fleurs, un document dans un coffre fort, un code dans une boule de cristal, etc.
- présence d'objets incomplets
-
un stylo auquel il faut ajouter une cartouche d'encre, une radio sans pile, un arc sans flèches, etc.
- existence de passages secrets
-
une trappe sous un tapis, une porte derrière une armoire, etc.
- présence de personnages non joueurs
-
un dragon qu'il faut tuer, une voyante qu'il faut interroger, etc.
- un mécanisme de ressources
-
un nombre de pièces d'or à trouver, des points de vie qui diminuent, un décompte avant une explosion, etc.
Il vous faudra probablement rechercher comment gérer les expressions arithmétiques en Prolog pour faire ceci.
- présence d'aléatoire
-
Attention, dans ce cas, votre fichier solution devra initialiser la graine du générateur (avec
setrand/1
) pour pouvoir fonctionner ! - gestion des objets plus sophistiquée
-
distinction entre les objets en main, et les autres (dans un sac à dos, une poche ventrale, etc.) ; commande
inventaire
, etc.Attention, il faut que le jeu reste fluide. Il est très frustrant d'avoir une interaction comme
?- boire(potion). Vous n'avez pas potion. true. ?- prendre_dans_main(potion). Votre main contient déjà : épée true. ?- poser(épée). épée est maintenant par terre. true. ?- prendre_dans_main(potion). Vous avez potion en main. true. ?- boire(potion). Vous devenez invisible. true. ?- prendre(épée). Vous ramasser épée. true. ?- prendre_dans_main(épée). Vous avez épée en main. true.
- raccourcis
-
possibilité de se déplacer aux endroits déjà visités en une seule commande
aller(chambre)
pour retourner dans la chambre en passant par les emplacements intermédiaires.(Bien entendu, les contraintes de déplacement doivent être vérifiées comme lors de déplacement "atomiques".)
- définir une petite interface en langue naturelle
- ...