Script structure
At the previous step, we’ve created a chatbot.yaml configuration file and a main.sc script for the bot.
Now, let’s work with the structure of the script. We will create intents for navigating states, and will also use the functions that we’ve created previously.
Start
The script starts from the Start state. The bot sends a welcome message and asks the user to choose who will start the game first.
state: Start || modal = true
q!: $regex</start>
intent!: /LetsPlay
script:
$session = {}
$client = {}
$temp = {}
$response = {}
a: Let's play "Cities" Game. Who will start first: bot or user?
state: User
intent: /user
a: Name the city
script:
$session.keys = Object.keys($Cities);
$session.prevBotCity = 0;
go!: /LetsPlayCitiesGame
state: Computer
intent: /computer
script:
$session.keys = Object.keys($Cities);
var city = $Cities[chooseRandCityKey($session.keys)].value.name
$reactions.answer(city)
$session.prevBotCity = city
go!: /LetsPlayCitiesGame
state: LocalCatchAll
event: noMatch
a: I don't understand you. Please try again.
Under the script tag, initialize all session variables to zero. We use them to save user’s and bot’s replies.
Let’s create a Letsplay intent, so we can get to the Rules state from any other state when the user sends a message Let's play.
To view the intents settings, go to the NLU > Intents tab on the control panel. Create an intent and specify phrases in the Training phrases field: let's play, play, I want to play.

Let’s set modal flag to true in the Start state. It is used when you need the user to provide some information for the game to go on. In our case, the game cannot go on until the user chooses who starts the game first.
The state includes three nested states: Computer, User and LocalCatchAll.
User
The transition to the User state is triggered by the /user intent. Then the message Name the city is displayed.
state: User
intent: /user
a: Name the city
script:
$session.keys = Object.keys($Cities);
$session.prevBotCity = 0;
go!: /LetsPlayCitiesGame
Create a /user intent and specify phrases in the Training phrases field: user, me, not you, I.

In the script tag, assign an array of id from the city/cities-en.csv list to the $session.keys variable.
$session.keys = Object.keys($Cities);
Clear the $session.prevBotCity variable in which we store the city named by the bot. Then move to LetsPlayCitiesGame state using go! tag.
Computer
The bot moves to the Computer state when the /computer intent is triggered.
state: Computer
intent: /computer
script:
$session.keys = Object.keys($Cities);
var city = $Cities[chooseRandCityKey($session.keys)].value.name
$reactions.answer(city)
$session.prevBotCity = city
go!: /LetsPlayCitiesGame
Create a /computer intent and specify phrases in the Training phrases field: computer, you, not me.

In the script tag, assign an array of id from the city/cities-en.csv list to the $session.keys variable.
Write a randomly selected city into the city variable using the chooseRandCityKey() function. Let’s use the built-in function $reactions.answer() to display the city from city as the bot’s response.
Save the city named by the bot to the $session.prevBotCity variable. Then move to LetsPlayCitiesGame state.
LocalCatchAll
If modal=true, the system will process the request in the context of that state, i.e. the request can only be switched to one of its nested states.
If the nested states have no matching answer and the local LocalCatchAll state is missing, the system returns an error. The error log tells you that no state to switch to was found in the script.
So, let’s create a LocalCatchAll state. The bot switches to this state if it receives a message that differs from all the considered options in the states.
state: LocalCatchAll
event: noMatch
a: I don't understand you. Please try again.
LetsPlayCitiesGame
The LetsPlayCitiesGame state consists of two nested states: CityPattern and NoMatch.
state: LetsPlayCitiesGame
state: CityPattern
q: * $City *
script:
if (isAFullNameOfCity()) {
if (checkLetter($parseTree._City.name, $session.prevBotCity) == true
|| $session.prevBotCity == 0) {
var removeCity = findByName($parseTree._City.name, $session.keys, $Cities)
if (checkCity($parseTree, $session.keys, $Cities) == true) {
$session.keys.splice(removeCity, 1)
var key = responseCity($parseTree, $session.keys, $Cities)
if (key == 0) {
$reactions.answer("I give up")
} else {
$reactions.answer($Cities[key].value.name)
$session.prevBotCity = $Cities[key].value.name
removeCity = findByName($Cities[key].value.name, $session.keys, $Cities)
$session.keys.splice(removeCity, 1)
}
} else $reactions.answer("You can't name the same city twice. Try again.")
}
} else $reactions.answer("Please use only full cities name")
state: noMatch
event: noMatch
a: I don't know this city. Try again.
CityPattern
The transition to the CityPattern state is based on the * $City * pattern specified in the q tag.
The script tag implements the game logic:
- In the first condition, we check that the user has entered the full name of the city. If
false, the following message is displayed:
Please use only full cities name
- If
true, we check whether the first letter of the city entered by the user matches the last letter of the city entered by the bot, or the$session.prevBotCityvariable equals to0. - If
true, save the entered city in theremoveCity. - Then we check whether the city has been named earlier. If so, the following message is displayed:
You can't name the same city twice. Try again.
- Otherwise, remove the entered city from the list. Assign the result of
responseCity()function to thekeyvariable. - If the
keyvariable equals to0, then the bot has run out of city names starting with the last or the second to last letter. The bot displays the messageI give up. - Otherwise, we display the city generated by the bot and remove it from the city list.
NoMatch
If the user makes a mistake when entering a city or names a non-existent city, the noMatch event is triggered. The bot displays a message:
I don't know this city. Try again.
EndGame
The EndGame state is used to process the end of the game in case the user chooses to finish the game.
state: EndGame
intent!: /endThisGame
a: That's a pity! If you change your mind, just text me "Let's play"
Create an /endThisGame intent that will process the transition to the EndGame state from any other state. There can be two cases when the users want to finish the game: they don’t want to play anymore or they give up to continue the city names chain. So, specify phrases in the Training phrases field: stop, give up, tired, finish.

Next, move on to testing the script.