Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS

Introduction

 
Here is another article on custom Alexa skills. In this article I am going to demonstrate how to create a custom Alexa skill on Alexa Hosted node.js servers. For that I will be creating a custom Alexa skill named rock paper scissors. With the help of this skill, users can play a rock paper scissor game with Alexa as an opponent.
 
The users can tell what the next move of the user is and Alexa will generate random options on each turn. Based on the user’s input and Alexa’s input, a winner will be decided. The backend code required for the Alexa skill to work will be stored in a lambda function inside the Amazon developer console. To understand the concept of Alexa skills and the components required to create an Alexa skill in a simpler way you can visit my previous article
 

Creating Alexa Skills

 
To create a new skill, first we need to login into the Alexa developer console. We need to mention the unique skill name and select the default language according to our location.
 
Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS
 
After that we can choose a model to add to our skill. To create a custom skill, we can select custom model.
 
Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS
 
We can also choose a method or a template to host the skill’s backend code inside a lambda function.
 
Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS
 
We can choose Alexa hosted node.js or Python template. We can also mention our own endpoint or server to store backend resources for the required Alexa skills.
 
The next step is to choose a template to add to our skill, which we customize later according to our need and click on create skill button.
 
Now as the skill has been created, we need to make adjustments to the skill’s frontend. Now I will be creating intents, slots and custom slot types to create the skill’s frontend. First we need to mention the invocation name. Users say a skill's invocation name to begin an interaction with a particular custom skill.
 
Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS
 
Now we have to create intents.
 
Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS
 
Here I have added a new intent named GameIntent along with some sample utterances such as choose {action}, I would like to choose {action}. Inside the utterance “I would like to choose {action}”, action is a slot defined for the skill. Slots are defined within curly brackets.
 
Slots and slot type of each slot is defined as follows:
 
Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS
 
 
Here action is a slot created for intent named GameIntent.
 
Custom slot type can also be created by clicking on Add Slot Type button under slot types menu.
 
Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS
 
To create a custom slot type, type the name of the custom slot and aalso fill in the required slot values along with Id and synonyms.
 

Defining Dialogs

 
We can also create dialogs such as Alexa speech prompts and user utterances for each and every slot being created. When the slot is required to fulfill the intent is not mentioned in the sample utterance of a user then dialogs are created to ask users to mention the slot values to fulfill the intent.
 
To create a dialog under each slot:
  • Enable option “is this slot required to fulfil the intent”.
  • Type Alexa speech prompts and user utterances accordingly.
After creating a model for a particular skill, we can save and build the model by clicking on save model and build model button on the top.
 
The json code for the above frontend code  is as follows,
  1. {  
  2.     "interactionModel": {  
  3.         "languageModel": {  
  4.             "invocationName""rock paper scissor",  
  5.             "intents": [  
  6.                 {  
  7.                     "name""AMAZON.CancelIntent",  
  8.                     "samples": []  
  9.                 },  
  10.                 {  
  11.                     "name""AMAZON.HelpIntent",  
  12.                     "samples": []  
  13.                 },  
  14.                 {  
  15.                     "name""AMAZON.StopIntent",  
  16.                     "samples": []  
  17.                 },  
  18.                 {  
  19.                     "name""AMAZON.NavigateHomeIntent",  
  20.                     "samples": []  
  21.                 },  
  22.                 {  
  23.                     "name""GameIntent",  
  24.                     "slots": [  
  25.                         {  
  26.                             "name""action",  
  27.                             "type""ActionType"  
  28.                         }  
  29.                     ],  
  30.                     "samples": [  
  31.                         "choose {action}",  
  32.                         "i would like to choose {action}",  
  33.                         "my choice is {action}",  
  34.                         "{action}",  
  35.                         "i choose {action}"  
  36.                     ]  
  37.                 }  
  38.             ],  
  39.             "types": [  
  40.                 {  
  41.                     "name""ActionType",  
  42.                     "values": [  
  43.                         {  
  44.                             "name": {  
  45.                                 "value""scissor"  
  46.                             }  
  47.                         },  
  48.                         {  
  49.                             "name": {  
  50.                                 "value""paper"  
  51.                             }  
  52.                         },  
  53.                         {  
  54.                             "name": {  
  55.                                 "value""rock"  
  56.                             }  
  57.                         }  
  58.                     ]  
  59.                 }  
  60.             ]  
  61.         }  
  62.     }  
  63. }  

Creating the backend resource for the Alexa skill

 
To create backend code inside lambda function, we can write code inside index.js node.js file. The code for the custom Alexa skill is as follows,
  1. // This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).  
  2. // Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,  
  3. // session persistence, api calls, and more.  
  4. const Alexa = require('ask-sdk-core');  
  5.   
  6. const ACTIONS=[  
  7.             'paper',  
  8.             'rock',  
  9.             'scissor'  
  10.         ];  
  11.   
  12. const LaunchRequestHandler = {  
  13.     canHandle(handlerInput) {  
  14.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';  
  15.     },  
  16.     handle(handlerInput) {  
  17.         const speakOutput = 'Welcome to rock paper scissor. What do you choose?';  
  18.         return handlerInput.responseBuilder  
  19.             .speak(speakOutput)  
  20.             .reprompt(speakOutput)  
  21.             .getResponse();  
  22.     }  
  23. };  
  24. const GameIntentHandler = {  
  25.     canHandle(handlerInput) {  
  26.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'  
  27.             && Alexa.getIntentName(handlerInput.requestEnvelope) === 'GameIntent';  
  28.     },  
  29.     handle(handlerInput) {  
  30.           
  31.         const userAction = handlerInput.requestEnvelope.request.intent.slots.action.value;  
  32.           
  33.         let speakOutput = '';  
  34.         let repromptOutput =' What is your next move?';  
  35.           
  36.         const alexaAction=ACTIONS[Math.floor(Math.random()*ACTIONS.length)];  
  37.   
  38.         const combo = userAction+alexaAction;  
  39.   
  40.         switch(combo)  
  41.         {  
  42.             case 'rockrock':  
  43.                 speakOutput+="you played rock and i played rock, it is a tie! ";  
  44.                 break;  
  45.             case 'rockpaper':  
  46.                 speakOutput+="you played rock and i played paper, I win! ";  
  47.                 break;  
  48.             case 'rockscissor':  
  49.                 speakOutput+="you played rock and i played scissor, you win! congratulations ";  
  50.                 break;  
  51.             case 'paperrock':  
  52.                 speakOutput+="you played paper and i played rock, you win! congratulations ";  
  53.                 break;  
  54.             case 'paperpaper':  
  55.                 speakOutput+="you played paper and i played paper, it is a tie! ";  
  56.                 break;  
  57.             case 'paperscissor':  
  58.                 speakOutput+="you played paper and i played scissor, I win! ";  
  59.                 break;  
  60.             case 'scissorrock':  
  61.                 speakOutput+="you played scissor and i played rock, I win! ";  
  62.                 break;  
  63.             case 'scissorpaper':  
  64.                 speakOutput+="you played scissor and i played paper, you win! congratulations ";  
  65.                 break;  
  66.             case 'scissorscissor':  
  67.                 speakOutput+="you played scissor and i played scissor, it is a tie! ";  
  68.                 break;  
  69.             default:  
  70.                  break;  
  71.         }  
  72.         return handlerInput.responseBuilder  
  73.             .speak(speakOutput + repromptOutput)  
  74.             .reprompt(repromptOutput)  
  75.             .getResponse();  
  76.     }  
  77. };  
  78. const HelpIntentHandler = {  
  79.     canHandle(handlerInput) {  
  80.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'  
  81.             && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';  
  82.     },  
  83.     handle(handlerInput) {  
  84.         const speakOutput = 'This is a skill to play rock paper scissor. What do you choose?';  
  85.   
  86.         return handlerInput.responseBuilder  
  87.             .speak(speakOutput)  
  88.             .reprompt(speakOutput)  
  89.             .getResponse();  
  90.     }  
  91. };  
  92. const CancelAndStopIntentHandler = {  
  93.     canHandle(handlerInput) {  
  94.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'  
  95.             && (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'  
  96.                 || Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');  
  97.     },  
  98.     handle(handlerInput) {  
  99.         const speakOutput = 'Goodbye!';  
  100.         return handlerInput.responseBuilder  
  101.             .speak(speakOutput)  
  102.             .getResponse();  
  103.     }  
  104. };  
  105. const SessionEndedRequestHandler = {  
  106.     canHandle(handlerInput) {  
  107.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';  
  108.     },  
  109.     handle(handlerInput) {  
  110.         // Any cleanup logic goes here.  
  111.         return handlerInput.responseBuilder.getResponse();  
  112.     }  
  113. };  
  114.   
  115. // The intent reflector is used for interaction model testing and debugging.  
  116. // It will simply repeat the intent the user said. You can create custom handlers  
  117. // for your intents by defining them above, then also adding them to the request  
  118. // handler chain below.  
  119. const IntentReflectorHandler = {  
  120.     canHandle(handlerInput) {  
  121.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest';  
  122.     },  
  123.     handle(handlerInput) {  
  124.         const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);  
  125.         const speakOutput = `You just triggered ${intentName}`;  
  126.   
  127.         return handlerInput.responseBuilder  
  128.             .speak(speakOutput)  
  129.             //.reprompt('add a reprompt if you want to keep the session open for the user to respond')  
  130.             .getResponse();  
  131.     }  
  132. };  
  133.   
  134. // Generic error handling to capture any syntax or routing errors. If you receive an error  
  135. // stating the request handler chain is not found, you have not implemented a handler for  
  136. // the intent being invoked or included it in the skill builder below.  
  137. const ErrorHandler = {  
  138.     canHandle() {  
  139.         return true;  
  140.     },  
  141.     handle(handlerInput, error) {  
  142.         console.log(`~~~~ Error handled: ${error.stack}`);  
  143.         const speakOutput = `Sorry, I had trouble doing what you asked. Please try again.`;  
  144.   
  145.         return handlerInput.responseBuilder  
  146.             .speak(speakOutput)  
  147.             .reprompt(speakOutput)  
  148.             .getResponse();  
  149.     }  
  150. };  
  151.   
  152. // The SkillBuilder acts as the entry point for your skill, routing all request and response  
  153. // payloads to the handlers above. Make sure any new handlers or interceptors you've  
  154. // defined are included below. The order matters - they're processed top to bottom.  
  155. exports.handler = Alexa.SkillBuilders.custom()  
  156.     .addRequestHandlers(  
  157.         LaunchRequestHandler,  
  158.         GameIntentHandler,  
  159.         HelpIntentHandler,  
  160.         CancelAndStopIntentHandler,  
  161.         SessionEndedRequestHandler,  
  162.         IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers  
  163.     )  
  164.     .addErrorHandlers(  
  165.         ErrorHandler,  
  166.     )  
  167.     .lambda();  
To receive requests from the user, request handlers are created for each intent to handle. Inside each handlers, canHandle and handle functions are defined.
 
The canHandle() function is where you define what requests the handler responds to. The handle() function returns a response to the user. If your skill receives a request, the canHandle() function within each handler determines whether or not that handler can service the request.
 
In this case, the user wants to launch the skill, which is a LaunchRequest. Therefore, the canHandle() function within the LaunchRequestHandler will let the SDK know it can fulfill the request. In computer terms, the canHandle returns true to confirm it can do the work.
 
The LaunchRequestHandler will greet the user by saying “Welcome to rock paper scissors. What do you choose?”.
 
After that GameIntentHandler is defined to handle each and every user’s reservation request. This handler will receive input action as requested slot parameter and give a response accordingly.
 
Output
 
Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS
 
As we can see from the output above, to invoke a skill, users can say "open" followed by the skill name. Once the skill is invoked, Alexa will greet the user by saying “welcome to rock paper scissor” and ask the user to take his/her turn.
 
Once the user takes his/her turn, Alexa will also generate a random input. Alexa will compare user requests with  randomly generated input and based on some condition, a winner is decided.
 
If a user fails to mention slot values while making a request to Alexa then Alexa will prompt a message “what is your next move” each time and ask the user to provide specific slot values as well.
 
Creating Custom Rock Paper Scissor Game Alexa Skill Using Alexa Hosted NodeJS
 

Summary

 
In this article, I created a custom Alexa skill. I also defined intents, slots and custom slot types for each slot. I demonstrated the method to create custom slot types and slot type values. Proper coding snippets along with output for backend of skill is also provided.