August 04, 2019

Using Prolog for creating text adventures


The Prolog language works different from normal programming languages like Python which makes it hard to for the newbies to get familiar with it. To overcome the issue a small example make sense which is a text adventure similar to the example at https://gist.github.com/JosephLenton/3191695
It's a small prolog program which contains of facts and rules. The facts are used to store the worldstate of the adventure which means, it contains the rooms and the objects in the room. The rules allow the player to take actions in the game, for example he can pick up an object. Basically spoken, Prolog was invented to create text adventure with less effort over normal programming languages like C.
Let us investigate what will happen after the game gets started. The user interacts with the system interactively. He can show the current situation and he can execute an action. If the player likes to take an object but the object is not there an error message gets printed to the screen. What makes the Prolog program different from procedural programming languages is, that no variables but only a fact list is available. It is a long list of information which can become true or false. If the operators (=rules) are executed the facts gets modified, very similar to the STRIPS action language. This formal description allows a solver to bring the system into a goal state. All what the solver has to do is to find the sequence of actions which will modify the facts into a certain direction.
OOP?
Until now, the question remains open if it make sense to use Prolog facts and rules to implement a text adventure. From the perspective of the AI language Prolog it's the prefered choice in doing so, because Prolog was invented around the idea to add and delete facts from a list and apply rules ontop of the knowledge base. The more interesting question is, if it's make sense for the ordinary programmer.
To understand what is maybe wrong with Prolog we have to ask which programming techniques is normally used for game programming. It's called object oriented programming and the idea is to create a simulation with the help of classes which can call each other. The fact storage of Prolog makes object oriented programming obsolete. But is avoiding objects useful for real applications? Let us make a thought experiment. The idea is to program a larger text adventure which contains of 5000 lines of code. Which programming style is the better choice, using prolog facts or using object oriented classes?
Perhaps it make sense to go a step backward. What is the reason why object oriented programming was invented? From a technical perspective there is no need for doing so. In the C language it's possible to store all the variables direct into the memory and get access with a pointer to the memory adress. If the programmer likes to store a new information, he reserves a new memory block at the end, and stores the value to that position. But real programming projects doesn't use this technique, instead it is more convenient to store the information in structs and classes. Not because the program would run faster, but because programming will be much easier.
The main question is, which programming technique is the right choice for creating large scale text adventures: the Prolog style which is based on rules and facts or the object oriented style which is using classes and methods.
If we are asking an expert programming which paradigm he can recommend for realizing a larger project for example a computer game or a desktop application he will recommend in 99% of the cases an object oriented programming language like C++, Java or C#. What he will recommend not is procedural language like C or a AI language like LISP. The reason for this clear voting is, that object oriented programming has become the standard in modern software development. But what is the case of a text adventure? Sure it is possible to use Prolog or Lisp but then it won't become a software project but an AI course at the university. That means, the goal is not to develop a text adventure but to explain the student who the Prolog interpreter works. If the idea is to develop a text adventure with maximum productive, an OOP language like C# or C++ is the better choice.
OOP textadventure
To understand why object oriented programing is the perfect choice for creating a text adventure we have to take a look into a real project. I've selected a template written in Python available under https://github.com/dexbarrett/python-text-adventure. It is working with classes for items, Non player characters and rooms. The idea is to take a class as a template and create subclass which contains all the needed variables. The text adventure will contains hundred of theses classes which are sending messages back and forth. It's the same principle used in normal graphics computer game, but only the rendering engine for painting the information to the screen is missing.
The interesting point is, that in the gaming industry nobody asks if object orientated programming make sense. All which are 99% of the developers are using this style. The only difference is, that programmer A prefers Python, programmer B is a fan of C# and programmer C likes C++ because C++ provides the maximum performance. But all these languages are using OOP. And the gaming industry is right. It is not possible to replace a language like C++ with Prolog. It would result into lower productivity.
mezzano os
Is a small project which is lisp based operating system. The problem is, that no clear advantage over C/C++ is visible. That means, the software looks like a barebone OS written in C code and it didn't even have a webbrowser. That means, it's easy to predict, that a LISP based operating system won't be successful in the future. Mezzano is similar to most Lisp projects a proof of concept but can't be recommended for practical applications. The problem with Lisp and Prolog is, that it doesn't support object oriented programming which makes it hard to program larger libraries in that language. Using existing libraries is the success factor why existing Windows and Linux operating systems are so successful. It allows to program large scale systems which contains of 10 million lines of code. This is not possible with the Lisp ecosystem.
The best example for a failed Lisp project is the Emacs texteditor. From a technical perspective it is well written software project, but Emacs isn't more powerful than other IDE tools for example Visual studio or Eclipse. Let us go back to the initial question. What is the best way to program a text adventure? If the idea is to show how Prolog is working in a practical application, then it's possible to use this language. But if the idea is to not focus on the technology and create a text adventure in a small amount of time, that an object oriented language is the better choice.
Avoid OOP?
Let us make a short thought experiment to investigate the advantages of object oriented programming. The work hypothesis is, that the concept of classes is overestimated and the better idea is to store all the code into a single main class. From a technical perspective the text adventure would run great. The class provides the world state which contains of 300 variables, and it provides all the actions which are round about 150. The methods have access to the variables and no problems are there with inheritance.
The main reason why such programming style isn't used in reality is because the resulting project is hard to maintain. The compiler is not the bottleneck but the programmer who should write the code. He will get confused after a short amount of time, because it's unclear which methods are allowed to access which variables. He will perhaps try to build modules which have a smaller problem space. This is equal to introduce object oriented programming. Using a single class for storing all the 300 variables is an anti pattern in software development. It will make the workflow much harder.
The problem with Prolog and LIsp is, that both languages are close to the antipattern. Either no OOP features are available or their importance is ignored. Instead other ideas are utilized for example the ability to modify the own code or to store facts as RDF-triple. The problem with these techniques is, that they can't replace OOP.
One to one comparison
A direct comparison between a Prolog style program and a object oriented technique is shown in the sourcecode. Instead of a Prolog interpreter a normal Python interpreter was used, but the programing style was oriented on Prolog. That means, the text adventure contains of facts stored in a list, and it contains of rules which have preconditions. in contrast, the second example was realized with normal Python classes which is more easier to read.

Sourcecode at the end of the blogpost 


From the game itself, both are the same. The world contains of a robot, a box and a gripper. The variables can be changed with actions like “moveto” and “setdirection”. The difference is, that in the first example with a fact database no object oriented advantages were used. Instead all the fucntions have access to all the variables. Even the game is small this can become a problem. It's unclear which function is allowed to change which variable.
In the second example, this question was solved with the well known OOP feature. That means, the variables are hold in different objects and the functions in the object can only modify the variables with within the object.
But let us talk about the first version of the program which is using a Prolog style. The interesting point is, that the program itself can be explained very well. At the beginning a fact database gets initialized and then some rules are implemented which are operating on the facts. After executing the program it works quite well and it's possible to solve the game autonomously with a graph search algorithm. The only thing what is missing is any kind of object oriented programming. Instead of using classes, the sourcecode is distributed around the fact storage and the rulelist. If the concept of OOP is unknown it will make sense, but under the perspective of OOP the question is what is the advantage?
From a perspective of computing history the reason why in the past non oop techniques were used was, that the problems were small. A blocksworld problem can be easily implemented in Prolog and a text adventure with 300 lines of code perhaps too. The reason, why OOP was developed is because the technique of storing all the variables in the same space will make programming too complicated in the amount of sourcecode is larger. This is especially true, if the facts are stored in a RDF triple storage and dedicated fucntions were implemented to add new facts. For the program and for the Prolog compiler as well the system is always in a healthy state, but which programmer is able to fix the sourcecode and adapt it to the domain?

"""
prototype for text adventure
- fact list
- tasks
"""
class Game():
  def __init__(self):
    self.facts=[]
    self.tasks=["setdirection","moveto"]
    self.updateworld()
    self.show()
    self.task("setdirection","south")
    self.show()
  def updateworld(self):
    self.facts.append(("gripperstatus","open"))
    self.facts.append(("robotdirection","north"))
    self.facts.append(("robotposition","waypoint1"))
    self.facts.append(("boxposition","waypoint2"))
  def changefact(self,action,name):
    if action=="remove":
      for i in self.facts:
        if i[0]==name:
          self.facts.remove(i)
    if action=="get":
      for i in self.facts:
        if i[0]==name: return i[1]
  def task(self,name,parameter):
    result=True
    if name==self.tasks[0]: # setdirection
      if True: # no precondition
        self.changefact("remove","robotdirection")
        self.facts.append(("robotdirection",parameter))
      else: result=False
    if name==self.tasks[1]: # moveto  
      p1=self.changefact("get","robotposition")
      p2=parameter
      angle1=self.changefact("get","robotdirection")
      angle2=settings.angle_between_two_points(p1,p2)
      diff=settings.anglediff(angle1,angle2)
      if abs(diff)<5: font="">
        self.changefact("remove","robotposition")
        self.facts.append(("robotposition",parameter))
      else: result=False

    return result
  def show(self):
    for i in range(len(self.facts)):
      print(i,self.facts[i])
    print(self.tasks)
    
if __name__ == "__main__":
  g=Game()



"""
second attempt for text adventure
  - using object oriented programming
"""
import sys
sys.path.insert(1, '..') # different folder for import
import settings

class Box():
  def __init__(self):
    self.pos=(100,100)
    self.angle=0
    
class Robot():
  def __init__(self):
    self.gripper="open" 
    self.angle=90
    self.pos=(200,100)
  def setdirection(self,angle):
    self.angle=angle
  def moveto(self,pos):
    a1=settings.angle_between_two_points(self.pos,pos)
    self.setdirection(a1)
    self.pos=pos

class Game():
  def __init__(self):
    self.myrobot=Robot()
    self.mybox=Box()
    self.plan1()
  def plan1(self):
    self.myrobot.moveto(self.mybox.pos)
    print(self.myrobot.pos,self.myrobot.angle)


if __name__ == "__main__":
  g=Game()    


No comments:

Post a Comment