Difference between revisions of "Language"
(6 intermediate revisions by the same user not shown) | |||
Line 102: | Line 102: | ||
The <code>CALL</code> command will cause the whole code contained in ''horribleAdventure'' loop (including all the iterations of ''in-the-loop'') to execute before displaying "Done". | The <code>CALL</code> command will cause the whole code contained in ''horribleAdventure'' loop (including all the iterations of ''in-the-loop'') to execute before displaying "Done". | ||
== Invoking AI == | |||
Invoking AI is the central concept in GamesLoops, but notwithstanding, it's basic use is very simple. It simply requires us to use <code>AI</code> command: | |||
AI "This is an adventure game. I am the player with the name %2, class %1. I'm on a quest to save the princess in the world full of dragons. Describe to me what is going on in at most five sentences." | |||
DISPLAY "%1" | |||
The code above invokes the AI, providing it with a description of the game (in a chat-like convention). The AI generates its answer, which is placed at the top of the stack, and then displayed to the player by the <code>DISPLAY</code> command. | |||
There are, however, some more advanced points about using AI. Three of them can even be seen in the above example: | |||
* the argument should be formulated carefully like said by a person chatting with an AI ("I am the player..."; alternative being trying to avoid any personal remarks like "The player tries"), | |||
* the size of the answer should be controlled ("in at most five sentences"), because generating long messages takes long time, and requires the player to read a lot, | |||
* the context matters ("This is an adventure game."), it's always good to include some contextual information for the AI. | |||
=== AI for data generation === | |||
As you noticed in the previous part, the AI-generated text is not automatically displayed to the player. There's a reason for it - AI can be used in your game for many purposes, not only for direct interaction. One of the uses is to "generate" data, like event cards: | |||
AI "Generate a list of about 15 short random events (like adventure cards) that may happen in a fast food restaurant. Example events 'hot frying oil is spilling!', 'an unpleasant customer is making trouble', 'one of the tables is damaged'. Include only the list in your answer." | |||
This can be stored and passed to the AI later to draw a random card: | |||
AI "Here is a list of random events: %1, pick one." | |||
None of those results should be passed directly to the player, they should be stored and processed first instead. | |||
=== AI contexts and stories === | |||
The AI machines (or models) have something that is called a context window, which basically means that they "remember" just as much of the earlier conversation. Whenever you call <code>AI</code> command, you open a new context, making the AI forget about what has been said earlier. To avoid it you can use <code>AI FOLLOWUP</code> command instead: | |||
AI "The player tries to slice at the monster! Describe what happens in at most four sentences." | |||
DISPLAY "%1" | |||
AI FOLLOWUP "Enumerate the new ailments the player got affected by as a result." | |||
In this case use of <code>AI</code> would make no sense, as the phrase "as a result" would be meaningless to the freshly reset AI. | |||
The context window, however, is not infinite, so some actions have to be taken to ensure the story so far is not forgotten. One of the simplest strategies, still very effective, might be to ask the AI to make a summary of the story so far and store it in the storage. Then, it can be provided to the AI as a fresh context: | |||
AI FOLLOWUP "Condense the story so far in at most nine sentences, prioritizing the most recent events." | |||
STORE "/story" | |||
... | |||
RETRIEVE "/story" | |||
AI "This is an adventure game. Story so far: %1. ..." | |||
The more advanced variants could involve dividing the story into segments and picking the relevant ones when needed. | |||
=== AI controlling the execution of the game === | |||
If you are versed in programming, you might have noticed we have not discussed any conditional commands yet. Conditional commands are the points in the standard programming where the execution might go one of the several paths, depending on something happening. It is also possible in GamesLoops, but it is done with some help from the AI: | |||
AI FOLLOWUP "Tell me which sentence describes the current state of the story the best: (1) We are sailing around the swamp. (2) We have found will o'wisps. (3) We escaped will o'wisps. (4) We left the swamp. (5) We got caught by will o'wisps after a successful escape. (6) The will o'wisps let us leave. Use only a single digit in your answer." | |||
CALL "adventure%1" | |||
The code above relies on the user defining 6 loops: ''adventure1'', ''adventure2'' and so on, to guide the story further. | |||
We can also think of much simpler yes/no scenarios: | |||
AI FOLLOWUP "Has the player guessed correctly? Use only Yes or No in the answer." | |||
CALL "result%1" | |||
This, however, forces us to define two loops: ''resultYes'' and ''resultNo'', while we might be only interested in taking a special action in one of the cases. For this, a special variant of <code>CALL</code> command exists <code>CALL IFYES</code>: | |||
AI FOLLOWUP "Has the player guessed correctly? Use only Yes or No in the answer." | |||
CALL IFYES "finishGame" | |||
== List of all commands == | |||
The following table gathers all the commands of GamesLoop. | |||
{| | |||
|- | |||
! Command !! Description !! Arguments !! Stack state (top) | |||
|- | |||
| STATUS || Changes the status line || ''text'' || - | |||
|- | |||
| AI || Sends the text to the AI || ''text'' || The answer | |||
|- | |||
| AI FOLLOWUP || Sends the text to the AI without resetting the context. || ''text'' || The answer | |||
|- | |||
| DISPLAY || Displays the text as the element of game conversation. || ''text'' || - | |||
|- | |||
| STORE || Stores the data in the given path of the storage tree. If data is not provided, uses <code>%1</code>. || ''path'', [''data''] || - | |||
|- | |||
| STORE ADD || Adds the data to the node in the given path of the storage tree. If data is not provided, uses <code>%1</code>. || ''path'', [''data''] || - | |||
|- | |||
| RETRIEVE || Retrieves data from the given path in the storage and puts it on the top of the stack. || ''path'' || Data retrieved | |||
|- | |||
| PROMPT || Prompts the player to enter a text, optionally within the given amount of seconds, after that uses the failure text. || ''prompt'', [''seconds'', [''failure text'']] || Text entered or failure text | |||
|- | |||
| CALL || Calls the given loop. || ''loop name'' || - | |||
|- | |||
| CALL IFYES || Calls the given loop if the top value on the stack is 'yes' or 'true'. || ''loop name'' || - | |||
|- | |||
| FINISH || Finishes the game. || - || - | |||
|- | |||
| LOOP N TIMES || Specifies that the loop should be executed n number of times. || ''n'' || - | |||
|- | |||
| LOOP OVER || Specifies that the loop should be executed as many times as there are subnodes of the given node in the storage tree. || ''path'' || - | |||
|} |
Latest revision as of 09:59, 19 May 2025
Language basics
The language used in Pry with AI is called GameLoops. A game consists of one more game loops, and each game loops contains one or more commands, possibly with arguments, like:
DISPLAY "Hello world!"
By convention command names are written with capitals. All command arguments should be quoted, being formally strings of characters (texts).
Stack
The key concept in GameLoops is a global stack. Some commands can "return" values, and in such a case they push a value at the top of the stack. Like:
PROMPT "Tell me your name:"
asks the player for their name, and puts them at the top of the stack (denoted %1
). So, assuming that the player answered "John", the stack will look like:
%1 "John"
If we then ask the player about their class:
PROMPT "Tell me your class:"
the class will jump on the top of the stack, pushing all the other values "down", and resulting in something like this:
%1 "Warrior" %2 "John"
The thing to remember is that the return values go to the top of the stack, subsequently pushing the existing values "lower".
The values on the stack can be referred to in command arguments. So the command:
DISPLAY "Hello %2, mighty %1!"
will display the text Hello John, mighty Warrior!
The stack in GameLoops can be only pushed to, there is no way to revoke/pop values from it (although they can be retrieved like shown above).
Storing values
Values in GameLoops can also be stored in a single hierarchical storage organized like a tree. Since the values on the stack constantly move downward, the storage provides more predictable way to refer to the most important game data. There are basically two commands for using the storage: STORE
and RETRIEVE
. STORE
puts a value in the tree:
PROMPT "Tell me your name:" STORE "/player/name", "%1" PROMPT "Tell me your class:" STORE "/player/class", "%1"
The first argument above specifies a path (sequence of nodes) in the tree-like storage. So the player name is stored in the subnode "name" of the top-level node "player" and the player class in its subnode "class" in the storage. Both those values can be retrieved at any time to the top of the stack with the RETRIEVE
command:
RETRIEVE "/player/name" DISPLAY "Be greeted %1!"
The storage is hierarchical, so you can also retrieve the whole top-level node "/player":
RETRIEVE "/player" DISPLAY "%1"
The result would be {name: "John", class: "Warrior"}
(as the storage is basically a JSON tree).
Note that it is also perfectly valid to use parameters like %1
in the path, like in the example below:
PROMPT "Tell me your name:" PROMPT "Tell me your class:" STORE "/characters/%2/class", "%1" ... PROMPT "Which character would you like to play?" RETRIEVE "/characters/%1/class" DISPLAY "Hello mighty %1!"
Loops
The whole game code is organized in loops. Each loop has a name and three blocks of commands: before-the-loop, in-the-loop, after-the-loop, executed accordingly. The game begins in the loop named main.
The loop itself
In-the-loop is a special block of commands that are executed iteratively. Also it should contain a special command as its first command, called loop specifier. There are currently two loop classifiers: LOOP N TIMES
and LOOP OVER
.
LOOP N TIMES
simply specifies that the commands in the loop should be executed the number of times specified in the argument:
LOOP N TIMES "20" DISPLAY "Hi!"
This will display "Hi!" 20 times. From within the loop you can also refer to the iteration number (1-based) using %
, so the code:
LOOP N TIMES "20" DISPLAY "%"
will display numbers from 1 to 20.
The other specifier, LOOP OVER
, is a bit more involved, as it iterates over all the subnodes of the specified node in the storage. Therefore, this:
LOOP OVER "/characters" DISPLAY "%"
will display the data of every character in the game, assuming that we stored them there earlier.
Calling other loops
Loops apart from being... loops, also organize our code into routines that can be invoked from one another. The appropriate command is CALL
:
CALL "horribleAdventure" DISPLAY "Done"
The CALL
command will cause the whole code contained in horribleAdventure loop (including all the iterations of in-the-loop) to execute before displaying "Done".
Invoking AI
Invoking AI is the central concept in GamesLoops, but notwithstanding, it's basic use is very simple. It simply requires us to use AI
command:
AI "This is an adventure game. I am the player with the name %2, class %1. I'm on a quest to save the princess in the world full of dragons. Describe to me what is going on in at most five sentences." DISPLAY "%1"
The code above invokes the AI, providing it with a description of the game (in a chat-like convention). The AI generates its answer, which is placed at the top of the stack, and then displayed to the player by the DISPLAY
command.
There are, however, some more advanced points about using AI. Three of them can even be seen in the above example:
- the argument should be formulated carefully like said by a person chatting with an AI ("I am the player..."; alternative being trying to avoid any personal remarks like "The player tries"),
- the size of the answer should be controlled ("in at most five sentences"), because generating long messages takes long time, and requires the player to read a lot,
- the context matters ("This is an adventure game."), it's always good to include some contextual information for the AI.
AI for data generation
As you noticed in the previous part, the AI-generated text is not automatically displayed to the player. There's a reason for it - AI can be used in your game for many purposes, not only for direct interaction. One of the uses is to "generate" data, like event cards:
AI "Generate a list of about 15 short random events (like adventure cards) that may happen in a fast food restaurant. Example events 'hot frying oil is spilling!', 'an unpleasant customer is making trouble', 'one of the tables is damaged'. Include only the list in your answer."
This can be stored and passed to the AI later to draw a random card:
AI "Here is a list of random events: %1, pick one."
None of those results should be passed directly to the player, they should be stored and processed first instead.
AI contexts and stories
The AI machines (or models) have something that is called a context window, which basically means that they "remember" just as much of the earlier conversation. Whenever you call AI
command, you open a new context, making the AI forget about what has been said earlier. To avoid it you can use AI FOLLOWUP
command instead:
AI "The player tries to slice at the monster! Describe what happens in at most four sentences." DISPLAY "%1" AI FOLLOWUP "Enumerate the new ailments the player got affected by as a result."
In this case use of AI
would make no sense, as the phrase "as a result" would be meaningless to the freshly reset AI.
The context window, however, is not infinite, so some actions have to be taken to ensure the story so far is not forgotten. One of the simplest strategies, still very effective, might be to ask the AI to make a summary of the story so far and store it in the storage. Then, it can be provided to the AI as a fresh context:
AI FOLLOWUP "Condense the story so far in at most nine sentences, prioritizing the most recent events." STORE "/story" ... RETRIEVE "/story" AI "This is an adventure game. Story so far: %1. ..."
The more advanced variants could involve dividing the story into segments and picking the relevant ones when needed.
AI controlling the execution of the game
If you are versed in programming, you might have noticed we have not discussed any conditional commands yet. Conditional commands are the points in the standard programming where the execution might go one of the several paths, depending on something happening. It is also possible in GamesLoops, but it is done with some help from the AI:
AI FOLLOWUP "Tell me which sentence describes the current state of the story the best: (1) We are sailing around the swamp. (2) We have found will o'wisps. (3) We escaped will o'wisps. (4) We left the swamp. (5) We got caught by will o'wisps after a successful escape. (6) The will o'wisps let us leave. Use only a single digit in your answer." CALL "adventure%1"
The code above relies on the user defining 6 loops: adventure1, adventure2 and so on, to guide the story further.
We can also think of much simpler yes/no scenarios:
AI FOLLOWUP "Has the player guessed correctly? Use only Yes or No in the answer." CALL "result%1"
This, however, forces us to define two loops: resultYes and resultNo, while we might be only interested in taking a special action in one of the cases. For this, a special variant of CALL
command exists CALL IFYES
:
AI FOLLOWUP "Has the player guessed correctly? Use only Yes or No in the answer." CALL IFYES "finishGame"
List of all commands
The following table gathers all the commands of GamesLoop.
Command | Description | Arguments | Stack state (top) |
---|---|---|---|
STATUS | Changes the status line | text | - |
AI | Sends the text to the AI | text | The answer |
AI FOLLOWUP | Sends the text to the AI without resetting the context. | text | The answer |
DISPLAY | Displays the text as the element of game conversation. | text | - |
STORE | Stores the data in the given path of the storage tree. If data is not provided, uses %1 . |
path, [data] | - |
STORE ADD | Adds the data to the node in the given path of the storage tree. If data is not provided, uses %1 . |
path, [data] | - |
RETRIEVE | Retrieves data from the given path in the storage and puts it on the top of the stack. | path | Data retrieved |
PROMPT | Prompts the player to enter a text, optionally within the given amount of seconds, after that uses the failure text. | prompt, [seconds, [failure text]] | Text entered or failure text |
CALL | Calls the given loop. | loop name | - |
CALL IFYES | Calls the given loop if the top value on the stack is 'yes' or 'true'. | loop name | - |
FINISH | Finishes the game. | - | - |
LOOP N TIMES | Specifies that the loop should be executed n number of times. | n | - |
LOOP OVER | Specifies that the loop should be executed as many times as there are subnodes of the given node in the storage tree. | path | - |