diff --git a/docs/source/notes/designing_new_game.rst b/docs/source/notes/designing_new_game.rst new file mode 100644 index 00000000..da70fe3b --- /dev/null +++ b/docs/source/notes/designing_new_game.rst @@ -0,0 +1,116 @@ +Chapter 3 : How To Design A New Game +============================================== +To design a new game, TextWorld framework provides a library to make a game called `GameMaker`. This library requires +to use game logic and text grammar files which we already discussed about them in previous two chapters. By default, +it employs the built-in versions of these two files. If the new design of the game requires the customized version of +them, they should be addressed and imported to the GameMaker. Thus, the first action, needs to be taken, is importation +of the path in which the customized files are stored. The following three lines of code provide this information for +GameMaker library. + +.. code-block:: bash + + from textworld.generator.data import KnowledgeBase + + PATH = pjoin(os.path.dirname(__file__), 'textworld_data') + kb = KnowledgeBase.load(target_dir=PATH) + +To handcraft a new game, the next is to create an object of GameMaker and start creating rooms and elements of the game. +Assume that our handcrafting game is happening in a spaceship and includes two rooms and there is two doors in the game, +one connects these two rooms and one is on the second room opening to outside (nowhere in this scenario). The `player` +is in the first room and both doors are locked. There is a key and a push button matched with each of these doors. The +goal of the game is to find the keys and go out from the second room. The two rooms and the doors are added to the +world by following block of codes. + +.. code-block:: bash + + from textworld import GameMaker + + # ===== World, Rooms, and Doors Design ============================================================================= + gm = GameMaker(kb=kb, theme='Spaceship') + room_1 = gm.new_room("Sleep Station") + room_2 = gm.new_room("Control Room") + room_3 = gm.new_room("Space") + + corridor1 = gm.connect(room_1.east, room_2.west) + doorA = gm.new_door(corridor1, name="door A") + gm.add_fact("locked", doorA) # Add a fact about the door, e.g. here it is locked. + + corridor2 = gm.connect(room_2.east, room_3.west) + doorB = gm.new_door(corridor2, name="door B") + gm.add_fact("locked", doorB) + +which the `new_room` method is designed to create and add a room to the world; the assigned name of the room should be +imported to the method. `Connect` method is used to connect two rooms to each other. The two inputs of this method +are those rooms which are connected in addition to the direction that the connection is occurred; e.g. here room_1 is +connected to room_2 from its "east" side. If there is a door between two rooms, i.e. the connection includes a door; +`new_door` defines this door and asks to describe the location of the door and its name. The final step is adding the +initial state (i.e. fact) of the door to the game, by using `add_fact`. Facts are basically the state of each element +when the game starts. + +Now, let's define a key and a table in the Sleep station, in which the key is on the table and it is matched with door +A. The method to create both of these elements is `new`. In this method, the type of new element is defined according +to the type definition of the corresponding element at logic files (.twl file). Also, if the designer wants to add some +description to the element it can add it by the `.info.desc` code. This description is displayed any time that the +player imports `look {.}` command. The newly created item should be located somewhere at the world and if it requires +initial state setup, that is defined as well (see `add` and `add_fact` methods). Look the following example + +.. code-block:: bash + + # ===== Box and Key Design ========================================================================================= + table = gm.new(type='s') # Table is a supporter which is fixed in place in the Sleep Station. + table.infos.desc = "It is a metal sturdy table." + room_1.add(table) + + key = gm.new(type='k', name="electronic key") + key.infos.desc = "This key opens the door into the control room area." + table.add(key) + + gm.add_fact("match", key, doorA) # Tell the game 'Electronic key' is matching with door A's lock + +From above code, the key is similarly designed and it is on the table. The key and its corresponding door should be +defined by `add_fact` function again, while the fact is `match`, be advised that match fact accepts two inputs. as a +recall, we defined `on(o, s)` as a predicate which means the object is on the supporter (table here), since the key is +defined as a sub-category of object, the `table.add(key)` syntax models this predicate. For the push button, we assume +that it is in room_2. As we observed so far, adding a push button is similar to the rest and just requires `new` method. + +.. code-block:: bash + + # ===== Push Button Design ========================================================================================= + push_button = gm.new(type='b', name="exit push button") + push_button.infos.desc = "This push button is a an object which opens door B."\ + "This push button is installed on the wall at the control room." + gm.add_fact("unpushed", push_button) + room_2.add(push_button) + +The player and its inventory are also two important parts of the world which should be defined for the game. If the +designer doesn't define where the player is, then the game will put it automatically at the first designed room. When a +player is set by `set_player` command its location is assigned. The graphical representation of the world is also +available by using `render` method. We recommend the readers of this tutorial, to check all above mentioned methods to +find out more details of how they can be employed more flexible within the design of a new game. The last but not least, +the `quest_record` method compiles the new game, generates the world, and starts the game to play. At each state the game, +the player can see the description of the scene and all the corresponding avaialble actions which transforms the player +to another state. + +.. code-block:: bash + + # ===== Player and Inventory Design ================================================================================ + gm.set_player(room_1) + + gm.render(interactive=True) + + gm.record_quest() + +The visualization of this game is illustrated at below figure, + +.. |game_sample| image:: ./images/GameDesign.png + :alt: Game version + ++------------------+ +| |game_sample| | ++==================+ +| Game | ++------------------+ + +This tutorial tried to shed a light to Textworld framework and elaborates how new games can be handcrafted using this +framework and cooperate with the customized logic and grammar files. +For further questions please communicate with the technical team of TextWorld project at Microsoft, via ... diff --git a/docs/source/notes/designing_new_grammar.rst b/docs/source/notes/designing_new_grammar.rst new file mode 100644 index 00000000..78f559d8 --- /dev/null +++ b/docs/source/notes/designing_new_grammar.rst @@ -0,0 +1,184 @@ +Chapter 2 : How To Design A New Text Grammar +============================================== +In addition to logic files, each game requires a grammar file in which it describes various sentences to be used within +the game. For example, when the player enters a room, a few lines of the greetings to the game, introduction of the +room, the items inside the room, etc. are displayed for the player. This text is compiled by a word parser at Inform7 +using a customized text-grammar file. This file also should be designed by the designer of the game and stored with +`.twg` extension. + +To describe this file, it is better to start with greetings. Greetings is a set of various sentences which will be +picked by the parser randomly and will be displayed on output anytime the player either enters into the game or enters +into a room. The greeting should include a general description about the state of the game, the room, or the current +elements of the game. If the generated sentence requires importation of elements or status of the game, that should +also be coded into the sentence. The question is how? Here, we practice writing such sentences. + +Let's first explore a fixed type of greetings. + +.. code-block:: bash + + GREETING : GREETING!;GREETINGS TREKKIE!;HELLO ASTRONAUT!;ALRIGHT THEN!; HEY TREKKIE + +Here, `GREETING` is the code to call the greeting sentence and the right side of semicolon is an array-like of different +options that the parser can pick between them; e.g. in above example, there are five different options and each time the +parser randomly picks one of them as the greeting of the game. Each of these five has the same probability; thus, if we +want to increase the probability of one item, we can repeat that as much as we wish. Moreover, each phrase above is +fixed and will be used as it is. However, it is possible to have a sentence which can be reformed based on the +situation. Following is an example of flexible greeting, + +.. code-block:: bash + + Flex_greeting : #GREETING#, it is TextWorld + +This example explains that we may have "Greeting!, it is TextWorld" or "HEY TREKKIE!, it is TextWorld", or any other +combinations that we can mix and match from "Flex_greeting" and "GREETING". In other words, any word (or combination of +words which are attached by hyphen) and comes in between "#", is like a symbol of another vector of phrases and is +replaced by the parser with one of the phrases from that vector. Following is another example of the creation of the +flexible sentence: + +.. code-block:: bash + + dec : #GREETING# #dec_type##suffix_(r)#;#dec_type##suffix_(r)# + + dec_type : #reg-0#;#difficult-0# + suffix_(r) : . Okay, just remember what is your mission here to do, and everything will go great.; \ + . You try to gain information on your surroundings by using a technique you call 'looking.'; \ + . You can barely contain your excitement.; + . The room seems oddly familiar, as though it were only superficially different from the other rooms in the spacecraft.; \ + . You decide to just list off a complete list of everything you see in the module, because hey, why not?; + + reg-0 : #01#;#02# + difficult-0 : #03# + + 01 : #dec_find-yourself# in a (name);#dec_guess-what# (name) + 02 : Well, here we are in #dec_a_the# (name) + 03 : You're now in #dec_a_the# (name) + + dec_find-yourself : You #dec_what# + dec_guess-what : #dec_well-guess#, you are in #dec_a_the# place we're calling #dec_a_the# + dec_a_the : a;the + dec_what : are;find yourself;arrive + dec_well-guess : Guess what;Well how about that;Well I'll be + +In this example, assume that the #GREETING# #dec_type##suffix_(r)# is randomly picked, to replace GREETING, dec_type, +and suffix_(r) variables, respectively, the "HELLO ASTRONAUT!", "reg-0", and ". Okay, just remember what is your mission +here to do, and everything will go great." are chosen. To replace reg-0, the parser randomly picks "02", and to replace +"02", the "Well, here we are in #dec_a_the# (name)" is selected. In the latter choice of phrase we have two type of +variables, one is "dec_a_the" and the other is "(name)". The first has already described and let's assume that "the" is +picked. For the second, the (name) is replaced by the name of a room that the player is in. Rooms and their names are +described in next chapter. Finally, the created sentence is as follows: + +.. code-block:: bash + + HELLO ASTRONAUT! Well, here we are in the (name). Okay, just remember what is your mission here to do, and everything will go great. + +This sentence is made for a sample state and anytime the game reaches to this state the (name) is replace with the +corresponding room's name and is printed on the screen. To increase the variety of the outputs, a designer can expand +those sentence block to more and more options. However, it is always important to notice that these sentences +should comply with the scenario of the game in general and the specific scene of the game in each state. We recommend +to make a good use of general sentences, and specific type of sentences which can be fed by variable from the game +state (between parentheses variables). Following this advice can give better sentence in accordance to the game story. + +Although the design of a text-grammar file is more depend on the designer's preference rather than the logic file, yet, +there are some sections which should be considered in .twg file. The fundamental sections are named as + + 1. Expandables : All required combinations, structures, etc of words, letters, and numbers which are used in the whole text of the grammar. + 2. Verbs : All verbs which are used as action or simply as verb in the text are collected. + 3. Types & Variables : Type of objects and variables of the game are defined and coded. + 4. Objects Garmmar : The grammar of each object of the game is defined in this section. + 5. Room Description Grammar : All the texts which are used to describe the game inside different rooms are defined and expanded. + 6. Instructions Grammar : The grammar of instructions for compound commands, etc are described. + +Expandables are all the variables which comes in between "#"s and expand to create a sentence. Verbs are also some sort +of expandable in which different synonyms and tense of the verb and its corresponding synonyms are clarified to be used +in text creation and hesitate from repeating a verb frequently, see below example for "take" verb, + +.. code-block:: bash + + take : #take_syn_v# the #obj_types# from the (r).;#take_syn_v# the #obj_types# that's in the (r). + take_syn_v : take;retrieve;grab + take_syn_pp : taken;got;picked + taking : taking;getting;picking + + take/s : #take_syn_v# the #obj_types# from the #on_var#. + + take/c : #take_syn_v# the #obj_types# from the #on_var#. + +"take_syn_v" and "take_syn_pp" respectively refer to the list of synonyms and the past participle of those +synonyms; the ing-form of the verb is the following line. Similar to the logic file description, if we have to assign a +word in different application, like take vs. take from a table, these two can be distinguished by assigning different +code words for each set. To understand this, take a look at above example and compare the definition of "take" with +"take/s". + +Types of all elements in the game can be coded for the grammar to address much easier. For example, "obj_types : (o|k|f)" +indicates all the object, key, or food with the `obj_types`, while "on_types : (c|s)" refers to container or supporter +types which object-like can be put `on` it. Recall that the left-side of the semicolon is just a symbolic way of +representing something which comes on the left-side; so, it is just for text generation and there is no logic behind it. + +In "Objects Grammar" section, every element of the game can have their own grammar and customized nouns and adjectives +to create more sense of the world that the designer tries to build. As an instance, a room can generally be expanded by +an adjective and a noun; if the game refers to an office (work type of room), then the list of adjective-noun pairs +could be different, and based on the game story, the designer can add as much as combinations she/he wishes, to add +more flavour to her/his game. Below is a good example of how different rooms can be assigned with their +own grammar, + +.. code-block:: bash + # --- Rooms --------------------------------------------------------------------- + ## List each type of room with a ';' between each + ## Each roomType must have specific rooms + ### Creating a room: first, take the name of the roomtype as listed under #room_type# (let's call it X for now). + ### Then, create three symbols with this: X_(r), X_(r)_noun, and X_(r)_adj. + ### X_(r) will always be composed of X_(r)_adj | X_(r)_noun. If you want to subdivide a roomtype into two or more variants, you can add _type1, _type2, etc at the end of the noun and adj symbols. + ### Make sure that these changes are also accounted for in the X_(r) token. + + room_type : clean;cook;rest;work;storage + + (r) : #(r)_adj# | #(r)_noun# + (r)_noun : sleep station;crew cabin;washroom;closet;kitchenette;module;lab;lounge + (r)_adj : nondescript;plain + + ### > Rest Room + ### >> Sleep Room + rest_(r) : #rest_(r)_adj_type_1# | #rest_(r)_noun_type_1#;#rest_(r)_adj_type_2# | #rest_(r)_noun_type_2# + + rest_(r)_noun_type_1 : sleep station;sleep station;sleep station;sleeping bag;crew cabin + rest_(r)_adj_type_1 : cozy;relaxing;pleasant;sleepy + ### >> fun with friends + rest_(r)_noun_type_2 : lounge;playroom;recreation zone;crew cabin;crew cabin;crew cabin + rest_(r)_adj_type_2 : fun;entertaining;exciting;well lit;silent + +Majority of the text which is created by the parser belongs to the description of a room. The Room Description Grammar +expands all the grammar which is used for a room to describe the room as well as the scenario at that room. This process +is very similar to what we described in Greetings section. + +Last but not least is the "Instructions Grammar". This part basically includes all the required grammatical structures +which the text-based game needs to compound two actions (like unlock and open), separate two sentence from each other or +to connect them with a word, etc. which are important in the expansion of the sentences all over the game. Following is +a few examples of what is designed for the Spaceship game: + +.. code-block:: bash + # --- Compound Command Description Functions ------------------------------------ + ig_unlock_open : open the locked #lock_types# using the (k).; \ + unlock and open the #lock_types#.; \ + unlock and open the #lock_types# using the (k).; \ + open the #lock_types# using the (k). + ig_unlock_open_take : open the locked #lock_types# using the (k) and take the #obj_types_no_key#.; \ + unlock the #lock_types# and take the #obj_types_no_key#.; \ + unlock the #lock_types# using the (k), and take the #obj_types_no_key#.; \ + take the #obj_types_no_key# from within the locked #lock_types#. + + # --- Separators ----------------------------------------------------------------- + ## *--- Action separators + action_separator_take : #afterhave# #take_syn_pp# the #obj_types#, ; \ + #after# #taking# the #obj_types#, ; \ + With the #obj_types#, ; \ + If you can get your hands on the #obj_types#, ; \ + #emptyinstruction#; + action_separator_eat : #afterhave# #eat_syn_pp# the #eat_types#, ; \ + #after# #eating# the #obj_types#, ; \ + #emptyinstruction#; + + ## *--- Separator Symbols + afterhave : After you have;Having;Once you have;If you have + after : After; + +For further details on these expandables, please check the TextWorld's Spaceship game. diff --git a/docs/source/notes/designing_new_logic.rst b/docs/source/notes/designing_new_logic.rst new file mode 100644 index 00000000..51c16259 --- /dev/null +++ b/docs/source/notes/designing_new_logic.rst @@ -0,0 +1,402 @@ +Chapter 2 : Extending the Game Logic +===================================== + +TextWorld is a framework to train and test reinforcement learning agents to play text-based games. It enables +generating games from a game distribution parameterized by the map size, the number of objects, quest length and +complexity, richness of text descriptions, and more. Each generated game will follow the same dynamics as +defined by the TextWorld's Game Logic (i.e. entity types, predicates, rules, and contraints). + +TextWorld comes with a basic Game Logic that can be used to build games. For instance, it has a limited +set of entity types such as rooms, doors, supporters, containers, objects, etc. +While the builtin Game Logic is very basic, TextWorld allows users to extend it by +modifying or writing new logic files `*.twl`. The basic logic files are collected at following directory: +`TextWorld \\ textworld \\ generator \\ data \\ logic`. These files are built-in logic files and cannot be modified by +the designer for any new theme. Hence, usually designers collect their own files in the following directory: +`TextWorld \\ textworld \\ challenges \\ (new_game_name) \\ textworld_data \\ logic`. Any changes into the basic +entities or any newly designed entity is provided here, and should be called during the "new game design" (This process +is described in `designing_new_game.rst` with more details). For convenience, game logic can be split into multiple +`.twl` files which are going to be aggregated to each other at runtime. + +Figure 1 depicts a sample of the `thing` entity. + + +.. figure:: ./images/thing.twl.png + :align: center + :alt: Logic example - Thing entity + + Part of the logic file `thing.twl` describing the `thing` entity. + +As we can see in the figure, there are different sections in a logic file which need to be provided. +Notably, there's a whole section about Inform7_, a programming language and design system for interactive fiction. +TextWorld has to deal with two different languages simultaneously: the TextWorld's Logic Language (TWLL) +and the Inform7 Language (I7L). TextWorld's engine interprets TWLL while Inform7 compiles I7L into either +Z-Machine (.z8) or Glulx (.ulx) games. +Any additions or modifications to the logic should be defined in **both** languages. + +This tutorial goes over the steps needed to add a new type of entity - a push button - in TextWorld. + +A. Declaring a new type +----------------------- +World in any text-based game contains various entities which create the world. Each entity within a TextWorld +environment has a *type*. A *type* defines the possible interactions the player can have with different objects (e.g., +open door), as well as their states (e.g. open/closed or unlock/locked door). +Each type must be represented by a unique identifier composed of letters and dashes. + +Let's define the a push button type as follows + +.. code-block:: bash + + type push-button { + } + +The basic logic entities, which are categorized as built-in entities in TextWorld framework, are defined and +organized as follows: + +.. code-block:: bash + + - room (as "r") + - Player (as "P") + - INVENTORY (as "I") + - thing (as "t") ----------> supporter (as "s") + |---> door (as "d") + |---> container (as "c") + |---> object (as "o") ---------->> food (as "f") + |--->> key (as "k") + +NB: notice how `INVENTORY` is all uppercase. This is used to indicate that there should only be one +object of that type per game. + +A *type* can be a subtype (e.g. `food` is a subtype of `portable_object`) which means it will inherit all +of its parent behaviors (e.g. `food` is also *portable*) and can also define new ones (e.g. `food` is edible). + +Let's our push button be a subtype of `thing`, so we can interact with it in a game: + +.. code-block:: bash + + type push-button : thing { + } + +NB: the type `thing` represents everything that is tangible in a game (e.g. `food`, `containers`, etc.) +opposed to abstract concept like `rooms` and `INVENTORY`. + +B. Defining a type +------------------ + +After declaring the type of an entity, its definition needs to be put between curly braces `{...}`. Its +definition includes various sections. + +So far, the `push-button` type will only be recognized by the TextWorld Engine but not the Inform7 compiler. +To fix that, a mapping between TWLL's type and I7L's counterpart - a `kind` - must be establish. + +Let's add the relevant I7L code for the `push-button`: + +.. code-block:: bash + + type push-button : thing { + + inform7 { + type { + kind :: "push-button"; + } + } + } + +The above example presents the type in both languages. Another relevant and simple example is a built-in entity inside +the TextWorld framework, called `door`. Below is the details of the door entity: + +.. code-block:: bash + + # door + type door : thing { + + inform7 { + type { + kind :: "door"; + definition :: "door is openable and lockable. A door is usually locked."; + } + } + } + +The designer, here, prefers to add two features to the `door` type, as openable and lockable. Any feature which is +supposed to be as general feature of this type can be mentioned in `definition`. Moreover, if there is an initial state +that the designer prefers to define to start the game with, should be mentioned in the definition part. For instance, +the designer of our example game likes to have all doors in the game locked by default, when the game starts. Then "A +door is usually locked." is added to definition. Similarly for a cloth, we can say "cloth are cleanable. A cloth is +usually clean.". + +Each game usually include a few rooms with various entities in them. The combination of the initial state of each +entity of the game (like a door which is either locked or open) creates the initial state of the game in general. The +game continues state-to-state when the current state of any entity of the game changes, e.g. the locked door changes +to unlocked (but closed). This is how the Text-based games perform. + + +C. Defining a predicate +------------------------- + +The next section of a logic file is a "Predicate", which describes various state definition of the logic of the entity +using the TWLL. For instance, the states of a door can be either open/close or locked/unlocked. Another feature that +can be defined as predicate is the connectivity of this entity with other entity(ies) in the environment. For example, +a door can link two rooms, described as `link(r, door, r')`, or an object like a glass can be put on a supporter like a +table, described as `on(o, s)`, which `o` stands for object type and `s` stands for supporter type. Any other feature +which defines the state of the entity (in some way it also describes the state of the game), and designer would like +to add it, should be described for the framework in TWLL format in the predicate part. Following is an example of the +possible states of a door, in TWLL: + +.. code-block:: bash + + predicates { + open(door); + closed(door); + locked(door); + + link(room, door, room'); + } + +In this example the door can be locked, closed, or open. The unlock state is implicitly defined by closed state. This +door also links two rooms as described. After defining predicates in TWLL, similar to previous part, the predicates +should be described in I7L as well. The following code presents this in both languages + +.. code-block:: bash + + # door + type door : thing { + predicates { + open(door); + closed(door); + locked(door); + + link(room, door, room'); + } + + inform7 { + type { + kind :: "door"; + definition :: "door is openable and lockable. A door is usually locked."; + } + + predicates { + open(door) :: "The {door} is open"; + closed(door) :: "The {door} is closed"; + locked(door) :: "The {door} is locked"; + + link(room, door, room') :: ""; # No equivalent in Inform7. + } + } + } + +There are important notes in this example which is good to elaborate them: + + a. The Inform7 language is very close to simple English language. However, some conditions and exceptions are + applied. For details of Inform7 language please check `http://inform7.com`. + + b. Each type has a unique name which can contain a letter, a word, or combination of either with dash. A door also + have a specific name in the game which helps to identify it among all the doors in the game e.g. "wooden door". + Assigning a specific name to each entity of the game is generally an appropriate solution to distinguish between + different entities of the same type (like wooden door vs. glass door). This name is assigned to `{door}` in the + I7L-based text; i.e. "The wooden door is open". + + c. There might be some predicates that the designer would like to define as the logic of the game and they don't + have an I7L equivalent necessarily, like `link(.)` in this example. The framework basically understands it by + defining as empty I7L-based description and performs as designer's wish with no harm. + + d. To make a line as comment, just put a `#` sign at the beginning of the line. + +Basically a door links two rooms and if it is lockable, there is a key in the game which is +matched with this door. Key is also a built-in entity. As we have already mentioned, TextWorld has some built-in +designed entities and many games may require other entity(ies) in addition to the built-in ones. Our push-button example +is one relevant example which is NOT already defined in the built-in entities list. The framework doesn't know what is +a push button. Moreover, let's assume that a game designer likes to create a game in which a door can be unlock and open +by a push button. What does she/he do, since the framework doesn't know that the entity, like push button, can unlock +and open a door? Similar to the door, some primary predicates is required to be defined for a push button, as described +here: + +.. code-block:: bash + + # push button + type push-button : thing { + predicates { + pushed(push-button); + unpushed(push-button); + + pair(push-button, door); + } + + inform7 { + type { + kind :: "button-like"; + definition :: "A button-like can be either pushed or unpushed. A button-like is usually unpushed. A button-like is fixed in place."; + } + + predicates { + pushed(push-button) :: "The {push-button} is pushed"; + unpushed(push-button) :: "The {push-button} is unpushed"; + + pair(push-button, door) :: "The {push-button} pairs to {door}"; + } + } + } + + +D. Defining a command +----------------------- + +A command (or rule) activates an action in the game and should be defined in both languages. "Rule" section is another +section of logic file. It describes how the game transforms from one state to another by using the command; see the +below example for a simple `open` rule which is used to open a door, + +.. code-block:: bash + + open/d :: $at(P, room) & $link(room, door, room') & $link(room', door, room) & closed(door) -> open(door) & free(room, room') & free(room', room); + +where `at(P, room)` means "the player is at the room" and `free(room, room')` means the path from firs room (called +`room` here) to the second room (which is called `room'`) is clear. This rule includes two columns which are separated +by a `::`. The left column presents the rule's name. This name should be unique for each rule, thus, if we have two +states with different conditions, then their names should be different, for instance "open/d" vs "open/c" which stand +for open door and open container, consecutively. + +The right column of the above rule describes the state change of the game according to the current change and the next +state which the game will turn to, by using this command. As it is depicted, each state contains some predicates +which describe the conditions applied to the entities of the game at that moment (or state) of the game, and provides +eligibility for the defined rule to be applied/called. After calling the rule, it is activated and makes some changes +into the state of the game (or equivalently some selected entities of the game) as it is described on the right side of +the arrow. By these changes, the game will finally transit to the next state. Please be notified that any predicate +which is supported by `$` sign will be kept as unchanged at the next state. Therefore, it is not necessary to repeat +them at the right side of the arrow. + +Equivalently, the I7L version of this rule should be coded in the inform7 part, which is translated again as `open {d}`. +When this command is imported by the player, the inform7 will return a response as the game state, which in this example +is `opening {d}`. This inform-based output is important for the TextWorld framework to identify that the inform +compiler has taken the action of the command (here opening the door) and has transited to the next state. This can be +assumed as acknowledgment to the framework to change the status. All these information are coded in `command` +sub-section inside the inform7 part in the logic file, which is given as + +.. code-block:: bash + + inform7 { + ... + + commands { + open/d :: "open {d}" :: "opening {d}"; + } + } + } + + +E. Defining a new command to Inform7 +------------------------------------- + +The push button is basically a sub-set of thing, and it is paired with a door. However, "pair" action is not defined in +I7L, thus it should be defined and described for Inform7 that what it means when a door and a push button are paired. +The description of new concept to I7L is always provided in "code" sub-section within the inform7 section in the logic +file. As a continuum to our example, below the "pair" and corresponding action, `push`, is defined for I7L: + +.. code-block:: bash + + # push button + type push-button : thing { + ... + + inform7 { + ... + + code :: """ + + connectivity relates a button-like to a door. The verb to pair to means the connectivity relation. + + Understand the command "push" as something new. + Understand "push [something]" as _pushing. + _pushing is an action applying to a thing. + + Carry out _pushing: + if a button-like (called pb) pairs to door (called dr): + if dr is locked: + Now the pb is pushed; + Now dr is unlocked; + Now dr is open; + otherwise: + Now the pb is unpushed; + Now dr is locked. + + Report _pushing: + if a button-like (called pb) pairs to door (called dr): + if dr is unlocked: + say "You push the [pb], and [dr] is now open."; + otherwise: + say "You push the [pb] again, and [dr] is now locked." + """ + } + } + +In this example, the "push" command is defined; the compiler expects to have a syntax such as "push [something]" which +the [something] usually is replaced by the name assigned to the push button. It is also described that what changes +are expected to happen when the button is pushed; the button state should change from `unpushed` to `pushed`, the door +state also should be changed from `unlocked` to `open`. The last block is for human interaction and prints out these +changes, thus, it is not mandatory. + +To open a locked door with a push button, it is necessary to have the player at the same room as the push button is. +Then the door is paired with the button, and the two rooms that this door connects to each other should be declared +(note: the door and the button can be located in two different rooms, compare the first and the second set of rules +in below example). +Also the door is locked and the button is unpushed. From the `code` section, we realized that the defined command for +this state transition is "push {b}". After this action, the door is unlocked and open and the button is changed to +pushed. The rest of the conditions (predicates) are unchanged. This process is presented in following example for two +scenarios: a) the button and the door ar at the same room, b) the push button is in separate room than the door. + +.. code-block:: bash + + rules { + lock/close/db :: $at(P, room) & $at(push-button, room) & $pair(push-button, door) & $link(room', door, room) & $link(room, door, room') & pushed(push-button) & open(door) & free(room, room') & free(room', room) -> unpushed(push-button) & locked(door); + unlock/open/db :: $at(P, room) & $at(push-button, room) & $pair(push-button, door) & $link(room', door, room) & $link(room, door, room') & unpushed(push-button) & locked(door) -> pushed(push-button) & open(door) & free(room, room') & free(room', room); + + lock/close/d/b :: $at(P, room) & $at(push-button, room) & $pair(push-button, door) & $link(room', door, room'') & $link(room'', door, room') & pushed(push-button) & open(door) & free(room', room'') & free(room'', room') -> unpushed(push-button) & locked(door); + unlock/open/d/b :: $at(P, room) & $at(push-button, room) & $pair(push-button, door) & $link(room', door, room'') & $link(room'', door, room') & unpushed(push-button) & locked(door) -> pushed(push-button) & open(door) & free(room', room'') & free(room'', room'); + } + + reverse_rules { + lock/close/d/b :: unlock/open/d/b; + lock/close/db :: unlock/open/db; + } + + inform7 { + ... + + commands { + lock/close/d/b :: "push {push-button}" :: "_pushing the {push-button}"; + unlock/open/d/b :: "push {push-button}" :: "_pushing the {push-button}"; + + lock/close/db :: "push {push-button}" :: "_pushing the {push-button}"; + unlock/open/db :: "push {push-button}" :: "_pushing the {push-button}"; + } + } + +As this example illustrates, since the same command is used for an action in two different situations, the rule names +are different, although the command and the inform7 acknowledgement are all the same. + +Have you noticed the reverse_rule section? In this section, the reverse rules are connected to each other to inform the +framework that after taking an action what would be the reverse action to get back to the current state. This provides +the possibility of getting back to a state after moving from it, also provides back and forth exploration within the +environment. + + +F. Defining constraints +----------------------- + +The last section of a logic file is the `constraints` which defines the failure rules; i.e. describes that which +predicates cannot occur simultaneously in a state. This section is only required to be defined in TWLL. Following is an +example of some selected constraints applied to our example, + +.. code-block:: bash + + constraints { + # Predicate conflicts + d1 :: open(door) & closed(door) -> fail(); + d2 :: open(door) & locked(door) -> fail(); + d3 :: closed(door) & locked(door) -> fail(); + + # A door can't be used to link more than two rooms. + link1 :: link(room, door, room') & link(room, door, room'') -> fail(); + link2 :: link(room, door, room') & link(room'', door, room''') -> fail(); + } + + +.. _Inform7: http://www.inform7.com/ diff --git a/docs/source/notes/images/GameDesign.png b/docs/source/notes/images/GameDesign.png new file mode 100644 index 00000000..efd6c3d5 Binary files /dev/null and b/docs/source/notes/images/GameDesign.png differ diff --git a/docs/source/notes/images/thing.twl.png b/docs/source/notes/images/thing.twl.png new file mode 100644 index 00000000..0184240b Binary files /dev/null and b/docs/source/notes/images/thing.twl.png differ