Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Khyati MajmudarNinad Majmudar
Published © Apache-2.0

UK Treasure Hunt

Find Precious Treasures from across the United Kingdom..!

IntermediateFull instructions providedOver 1 day812

Things used in this project

Story

Read more

Code

package.json

JSON
package.json - for NodeJS modules
{
  "name": "UKTreasureHunt",
  "version": "1.0.0",
  "description": "UKTreasureHunt.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "alexa",
    "skill",
    "UK Treasure Hunt"
  ],
  "author": "Khyati",
  "license": "Apache-2.0",
  "dependencies": {
    "alexa-sdk": "^1.0.9",
    "flickrapi": "^0.6.0",
    "moment": "^2.18.1"
  }
}

Lamda Function - index.js

JavaScript
Main Index.js - Code Entry Point
"use strict";


var questions = require("./questions");
var constants = require('./constants');
var gamelogic = require('./gamelogic');

var moment = require('moment');
var flickrObj = null;
var Flickr = require("flickrapi"),
    flickrOptions = {
        api_key: constants.apiKey,
        secret: constants.apiSecret,
        user_id: constants.userId,
        requestOptions: {
            timeout: 2000,
            /* other default options accepted by request.defaults */
        }
    };
Flickr.tokenOnly(flickrOptions, function (error, flickr) {
    flickrObj = flickr;
});



var languageString = {
    "en": {
        "translation": {
            "GAME_NAME": "U.K. Treasure Hunt", // Be sure to change this for your skill.
            "HELP_MESSAGE": "I will ask you %s multiple choice questions. Respond with the number of the answer. " +
            "For example, say one, two, three, or four. To start a new game at any time, say, start game. ",
            "REPEAT_QUESTION_MESSAGE": "To repeat the last question, say, repeat. ",
            "ASK_MESSAGE_START": "Would you like to start playing?",
            "HELP_REPROMPT": "To give an answer to a question, respond with the number of the answer. ",
            "STOP_MESSAGE": "Are you sure you want to stop the trail?",
            "CANCEL_MESSAGE": "Okies, We will hunt the treasure some other time. <say-as interpret-as='interjection'>ta ta</say-as>.",
            "NO_MESSAGE": "Ok, we\'ll play another time. Goodbye!",
            "TRIVIA_UNHANDLED": "Try saying a number between 1 and %s",
            "HELP_UNHANDLrighED": "Say yes to continue, or no to end the game.",
            "START_UNHANDLED": "Say start to start a new treasure trail.",
            "NEW_GAME_MESSAGE": "<say-as interpret-as='interjection'>Ahoy</say-as>, %s. Welcome to %s. ",
            "WELCOME_MESSAGE": "Find precious lost treasures from across the U.K.! " +
            "Find your treasure before stipulated time. Here is your first trail. ",
            "NEXT_TRAIL_MESSAGE": "Here is your next trail. ",
            "ANSWER_CORRECT_MESSAGE": "correct. ",
            "ANSWER_WRONG_MESSAGE": "wrong. ",
            "CORRECT_ANSWER_MESSAGE": "The correct answer is %s: %s. ",
            "ANSWER_IS_MESSAGE": "That answer is ",
            "TELL_QUESTION_MESSAGE": "Question %s. %s ",
            "GAME_OVER_MESSAGE": "You got %s out of %s questions correct. Thank you for playing!",
            "SCORE_IS_MESSAGE": "Your score is %s. ",
            "UNHANDLED_CLUE": "Sorry, I did not get that. You need to select one of the three helpers to get your clue. ",
            "UNHANDLED_DECIDE": "Sorry, I did not get that.",
            "UNHANDLED_FLY": "Sorry, that's not a valid destination.",
            "DECIDE_PROMPT": "Alright, then, %s. Now, do you wish to Ask for Clue further or Fly to Next Destination?",
            "DECIDE_REPROMPT": "Please say Ask to ask for further Clues or say, Fly to look for destinations?",
            "START_MESSAGE": "Your current location is %s. And your treasure trail will lead you to %s. Right now, it is %s and " +
                "You have 2 days to fetch your treasure. Find your next destination by asking for clues. You can ask %s, %s or %s. Whom do you wish to ask?",
            "ASK_MESSAGE": "Right now, It is %s. " +
                "You can ask %s, %s or %s. Whom do you wish to ask?",
            "ASK_UNHANDLED_MESSAGE":
            "You can ask %s, %s or %s. Whom do you wish to ask?",
            "ASK_UNHANDLED_MESSAGE_PROMPT":
            "For example, You can say, Please ask %s. Whom do you wish to ask?",
            "FLY_MESSAGE": "You have the option to fly to %s. " +
    " Where to next, %s?",
            "NEW_DEST_MESSAGE": "<say-as interpret-as='interjection'>tick-tock. tick-tock. tick-tock.</say-as>You have arrived. Welcome to %s and right now, it is %s. Now, do you wish to Ask for Clue further or Fly to Next Destination?",
            "HELP_START" : "Help asked, and here it is. U.K. Treasure Hunt is a game where you need to follow the trail across cities of U.K. At each stage, you will get the help of three helpers whom you can ask for clues. The clue will point to your next destination and then, you need to choose that destination out of the provided options and fly there. Again ask for clues for next destination, and so on until you reach the treasure. You need to reach the treasure in a stipulated time and so, make sure you do not reach to an incorrect City or spend time asking for more clues! Happy Treasure Hunting!",
            "HELP_DECIDE" : "Help asked, and here it is. At any stage of the game, you can either ask helpers for clues or fly to your next destination. You can ask upto three helpers for clues, which will point you to your next destination. So, to ask for clues, Please say, I want to ask. And to fly to your next destination, Please say, I want to fly.",
            "HELP_FLY" : "Help asked, and here it is. Once you have asked Helpers for clues, you will have to deduce which city those clues point to. You can then fly to that city to continue your trail. You will get option of three to four cities and out of those select the one where the clues lead to. For example, to fly to America, please say, Fly to America.",
            "HELP_CLUE" : "Help asked, and here it is. To complete your trail, you need to identify your next location. To help you do that, you can ask upto three helpers for clues. You may figure out the location based on one clue or you can ask other helpers as well. For example, to ask banker for clue, Please say, Ask Banker.",


        }
    }
};

var Alexa = require("alexa-sdk");

exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.appId = constants.appId;
    alexa.resources = languageString;
    alexa.dynamoDBTableName = constants.dynamoDBTableName;
    alexa.registerHandlers(newSessionHandlers, startStateHandlers
        , clueStateHandlers, flyStateHandlers, decideStateHandlers
        , helpStateHandlers
        );
    alexa.execute();

};

var newSessionHandlers = {

    'NewSession': function () {
        console.log('New Sessioon 1');
        var that = this;
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
                that.handler.state = constants.states.START_MODE;
                that.emitWithState("StartGame", true);
                // result is Flickr's response
    },
    "LaunchIntent": function () {
        console.log('Launch Req 1');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },
    "LaunchRequest": function () {
        console.log('Launch Req 1');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },
    "AMAZON.StartOverIntent": function () {
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },
    "AMAZON.HelpIntent": function() {
        this.handler.state = constants.states.HELP_MODE;
        this.emitWithState("helpTheUser", constants.states.START_MODE);
    },
    "Unhandled": function () {
        var speechOutput = this.t("START_UNHANDLED");
        this.emit(":ask", speechOutput, speechOutput);
    }
};

var startStateHandlers = Alexa.CreateStateHandler(constants.states.START_MODE, {
    "StartGame": function (newGame) {
        console.log('Start Mode: Start Game');
        var speechOutput = newGame ? this.t("NEW_GAME_MESSAGE", this.attributes['level'], this.t("GAME_NAME"))  : "";
        if (this.attributes['newuser'] == 0)
        {
            speechOutput += this.t("WELCOME_MESSAGE");
        }
        else
        {
            speechOutput += this.t("NEXT_TRAIL_MESSAGE");
        }
        var currentCountry = gamelogic.getStartCountry();
        var currentTime = moment();
        var data = questions.data;
        var helper = questions.helper;
        var endTime = moment();
        endTime.add(2, 'd');

        var trail = gamelogic.buildTreasureTrail(currentCountry);
        var treasure = getRandomTreasure();

        var repromptText = this.t("START_MESSAGE", data[currentCountry].country, treasure,
            currentTime.format("Do MMMM h A"), helper[trail[0].helper[0]].helper,
            helper[trail[0].helper[1]].helper, helper[trail[0].helper[2]].helper);

        speechOutput += repromptText;

        Object.assign(this.attributes, {
            "speechOutput": repromptText,
            "repromptText": repromptText,
            "currentCountry": currentCountry,
            "currentTime": currentTime,
            "currentStep": 0,
            "rightStep": 0,
            "newuser": 1,
            "trail": trail,
            "treasure": treasure,
            "endTime": endTime,
            "startCountry": currentCountry,
            "currentState": constants.states.CLUE_MODE,
            "isRightTrack": true
        });

        this.handler.state = constants.states.CLUE_MODE;
        this.emit(":askWithCard", speechOutput, repromptText, this.t("GAME_NAME"), getCardString(repromptText));
    },
    "LaunchIntent": function () {
        console.log('Launch Req start');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },
    "LaunchRequest": function () {
        console.log('Launch Req start');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },


    "AMAZON.HelpIntent": function() {
        this.handler.state = constants.states.HELP_MODE;
        this.emitWithState("helpTheUser", constants.states.START_MODE);
    }

});


var clueStateHandlers = Alexa.CreateStateHandler(constants.states.CLUE_MODE, {
    "AskIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        handleUserClue.call(this);
    },
    "LaunchIntent": function () {
        console.log('Launch CLue 1');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },
    "LaunchRequest": function () {
        console.log('Launch CLue 1');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },


    "AMAZON.StartOverIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", false);
    },
    "AMAZON.RepeatIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.emit(":ask", this.attributes["speechOutput"], this.attributes["repromptText"]);
    },
    "AMAZON.HelpIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.handler.state = constants.states.HELP_MODE;
        this.emitWithState("helpTheUser", constants.states.CLUE_MODE);
    },
    "AMAZON.NoIntent": function() {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        if (this.attributes["speechOutput"] && this.attributes["repromptText"]) {
            this.emitWithState("AMAZON.RepeatIntent");
        } else {
            this.handler.state = constants.states.START_MODE;
            this.emitWithState("StartGame", false);
        }
    },
    "AMAZON.CancelIntent": function () {
        this.handler.state = constants.states.START_MODE;
        this.emit(":tell", this.t("CANCEL_MESSAGE"));
    },
    "AMAZON.YesIntent": function () {
        if(this.attributes['askedCancel'])
        {
            var speechOutput = this.t("CANCEL_MESSAGE");
            this.handler.state = constants.states.START_MODE;
            this.emit(":tell", speechOutput);
        }
        else
        {
            this.emitWithState("Unhandled");
        }
    },
    "AMAZON.StopIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": true
        });
        var speechOutput = this.t("STOP_MESSAGE");
        this.emit(":ask", speechOutput, speechOutput);
    },
    "Unhandled": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        var data = questions.data;
        var helper = questions.helper;
        var currentStep = this.attributes['currentStep'];
        var trail = this.attributes['trail'];
        var currentCountry = this.attributes['currentCountry'];
        var currentTime = moment(this.attributes['currentTime']);

        var speechText = this.t("UNHANDLED_CLUE") + this.t("ASK_UNHANDLED_MESSAGE",
            helper[trail[0].helper[0]].helper,
            helper[trail[0].helper[1]].helper, helper[trail[0].helper[2]].helper);

        var repromptText = this.t("ASK_UNHANDLED_MESSAGE_PROMPT",
            helper[trail[0].helper[0]].helper);

        
        Object.assign(this.attributes, {
            "speechOutput": speechText,
            "repromptText": repromptText
        });

        this.emit(":askWithCard", speechText, repromptText, this.t("GAME_NAME"), getCardString(speechText));
    },
    "SessionEndedRequest": function () {
        this.handler.state = constants.states.START_MODE;
        this.emit(':saveState', true);
        console.log("Session ended in trivia state: " + this.event.request.reason);
    }
});
function handleUserClue() {
    var answerSlotValid = isHelperSlotValid(this.event.request.intent);
    if(!answerSlotValid)
    {
        this.emitWithState("Unhandled");
        return;
    }
    else
    {
        var currentTime = moment(this.attributes['currentTime']);
        currentTime.add(2, 'h');
        Object.assign(this.attributes, {
            "currentTime": currentTime
        });

        //check if gameover
        if (checkIfGameOver.call(this))
        {
            var repromptText = getTimeOutText.call(this);
            repromptText = repromptText.clue.replace('%s', currentTime.format("Do MMMM h A"));

            this.emit(":tellWithCard", repromptText, repromptText, this.t("GAME_NAME"), getCardString(repromptText));
            return;
        }
        // check if back
        var thisNote = this.event.request.intent.slots.Helper.value.toLowerCase().trim();
        thisNote = thisNote.replace(/[^a-zA-Z]/gi, '');
        if (thisNote === 'back') {
            this.handler.state = constants.states.DECIDE_MODE;
            var speechOutput = this.t("DECIDE_PROMPT", this.attributes['level']);
            var repromptText = this.t("DECIDE_REPROMPT");
            Object.assign(this.attributes, {
                "speechOutput": speechOutput,
                "repromptText": repromptText
            });
            this.emit(":askWithCard", speechOutput, repromptText, this.t("GAME_NAME"), getCardString(repromptText));
            return;
        }
        else
        {
            var currentStep = this.attributes['currentStep'];
            var rightStep = this.attributes['rightStep'];

            var clue = '';
            if (currentStep == rightStep)
                clue = getHelperClue.call(this, this.event.request.intent);
            else
                clue = getIncorrectClue.call(this).clue;

            var helperNAme = getHelper.call(this, this.event.request.intent);
            console.log('Clue: ' + clue);
            if(clue !== '')
            {
                this.handler.state = constants.states.DECIDE_MODE;
                var speechOutput = 'The ' + helperNAme + ' says, ' + clue + '. ' + this.t("DECIDE_PROMPT", this.attributes['level']);
                var repromptText = this.t("DECIDE_REPROMPT");
                Object.assign(this.attributes, {
                    "speechOutput": speechOutput,
                    "repromptText": repromptText
                });
                var that = this;
                var trivia = this.attributes['trail'];
                var country = trivia[this.attributes['currentStep']].country;
                var randPage = getRandomNumber(0, 5);
                var randPerPage = getRandomNumber(0, 30);
                Flickr.tokenOnly(flickrOptions, function (error, flickr) {
                    // we can now use "flickr" as our API object,
                    // but we can only call public methods and access public data
                    console.log("Token Recd: " + questions.data[country].country);
                    flickr.photos.search({
                        tags: questions.data[country].country,
                        extras: "url_l", //"url_l,license,owner_name",
                        licenses: "0,7",
                        safe_search: 1,
                        //is_commons: true,
                        page: randPage,
                        per_page: randPerPage
                    }, function (err, result) {
                        console.log('Err: ' + err);
                        var photo = "";
                        if (!err) {
                            try {
                                var l = result.photos.photo.length;
                                if (l > 0)
                                {
                                    var randNo = getRandomNumber(0, l);
                                    console.log(randNo);
                                    photo = result.photos.photo[randNo].url_l;
                                    console.log(photo);
                                }
                            }
                            catch(e)
                            {
                                photo = "";
                            }
                        }
                        if (photo !== "")
                            that.emit(":askWithCard", speechOutput, repromptText, that.t("GAME_NAME"), getCardString(speechOutput),
                                { largeImageUrl: photo, smallImageUrl: photo });
                        else
                            that.emit(":askWithCard", speechOutput, repromptText, that.t("GAME_NAME"), getCardString(speechOutput));

                    });
                });

            }
            else
            {
                this.emitWithState("Unhandled");
            }
        }
    }


}

var decideStateHandlers = Alexa.CreateStateHandler(constants.states.DECIDE_MODE, {
    "DecideIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        handleUserDecide.call(this);
    },
    "LaunchIntent": function () {
        console.log('Launch Decide 1');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },
    "LaunchRequest": function () {
        console.log('Launch Decide 1');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },

    "AMAZON.StartOverIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", false);
    },
    "AMAZON.RepeatIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.emit(":ask", this.attributes["speechOutput"], this.attributes["repromptText"]);
    },
    "AMAZON.HelpIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.handler.state = constants.states.HELP_MODE;
        this.emitWithState("helpTheUser", constants.states.DECIDE_MODE);
    },
    "AMAZON.NoIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        if (this.attributes["speechOutput"] && this.attributes["repromptText"]) {
            this.emitWithState("AMAZON.RepeatIntent");
        } else {
            this.handler.state = constants.states.START_MODE;
            this.emitWithState("StartGame", false);
        }
    },
    "AMAZON.CancelIntent": function () {
        this.handler.state = constants.states.START_MODE;
        this.emit(":tell", this.t("CANCEL_MESSAGE"));
    },
    "AMAZON.YesIntent": function () {
        if(this.attributes['askedCancel'])
        {
            var speechOutput = this.t("CANCEL_MESSAGE");
            this.handler.state = constants.states.START_MODE;
            this.emit(":tell", speechOutput);
        }
        else
        {
            this.emitWithState("Unhandled");
        }
    },
    "AMAZON.StopIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": true
        });
        var speechOutput = this.t("STOP_MESSAGE");
        this.emit(":ask", speechOutput, speechOutput);
    },
    "Unhandled": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.emit(":ask", this.t("UNHANDLED_DECIDE") + ' ' + this.attributes["repromptText"], this.attributes["repromptText"]);
    },
    "SessionEndedRequest": function () {
        this.emit(':saveState', true);
        console.log("Session ended in trivia state: " + this.event.request.reason);
    }
});
function handleUserDecide() {
    var answerSlotValid = isDecideSlotValid(this.event.request.intent);
    console.log('Ans: ' + answerSlotValid);
    if (answerSlotValid === '') {
        this.emitWithState("Unhandled");
        return;
    }
    else {
        if(answerSlotValid == 'fly')
        {
            this.handler.state = constants.states.FLY_MODE;
            var data = questions.data;
            // generate list of countries and ask user

            var sh = [];

            var currentStep = this.attributes['currentStep'];
            var rightStep = this.attributes['rightStep'];

            if (currentStep == rightStep)
                sh = getCountryList.call(this);
            else
                sh = getCountryListIncorrect.call(this);

            var cntName = '';
            for (var x = 0 ; x < sh.length ; x++) {
                var n = data[sh[x]].country;
                cntName += n;
                if (x == sh.length - 1)
                    cntName += '';
                else if (x == sh.length - 2)
                    cntName += ' or ';
                else
                    cntName += ', ';
            }
            console.log('--------=========-----------');
            console.log(JSON.stringify(cntName));
            var repromptText = this.t("FLY_MESSAGE", cntName, this.attributes['level']);
            Object.assign(this.attributes, {
                "speechOutput": repromptText,
                "repromptText": repromptText,
                "flyOptions": sh
            });

            this.emit(":askWithCard", repromptText, repromptText, this.t("GAME_NAME"), getCardString(repromptText));

        }
        else if(answerSlotValid == 'ask')
        {
            this.handler.state = constants.states.CLUE_MODE;
            // get list of current helpers and send
            var data = questions.data;
            var helper = questions.helper;

            var currentStep = this.attributes['currentStep'];
            var trail = this.attributes['trail'];
            var currentCountry = this.attributes['currentCountry'];
            var currentTime = moment(this.attributes['currentTime']);

            var repromptText = this.t("ASK_MESSAGE", 
                currentTime.format("Do MMMM h A"), helper[trail[currentStep].helper[0]].helper,
                helper[trail[currentStep].helper[1]].helper, helper[trail[currentStep].helper[2]].helper);


            Object.assign(this.attributes, {
                "speechOutput": repromptText,
                "repromptText": repromptText
            });

            this.emit(":askWithCard", repromptText, repromptText, this.t("GAME_NAME"), getCardString(repromptText));
        }
    }


}

var flyStateHandlers = Alexa.CreateStateHandler(constants.states.FLY_MODE, {
    "FlyIntent": function () {
        console.log('FlyIntent');
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        handleUserFly.call(this);
    },
    "LaunchIntent": function () {
        console.log('Launch Fly 1');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },
    "LaunchRequest": function () {
        console.log('Launch Fly 1');
        if (Object.keys(this.attributes).length === 0) {
            console.log('New Session');
            this.attributes['level'] = 'Treasure Hunter';
            this.attributes['trail'] = [];
            this.attributes['history'] = [];
            this.attributes['score'] = 0;
            this.attributes['newuser'] = 0;
        }
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", true);
    },

    "AMAZON.StartOverIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", false);
    },
    "AMAZON.RepeatIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.emit(":ask", this.attributes["speechOutput"], this.attributes["repromptText"]);
    },
    "AMAZON.HelpIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.handler.state = constants.states.HELP_MODE;
        this.emitWithState("helpTheUser", constants.states.DECIDE_MODE);
    },
    "AMAZON.NoIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        if (this.attributes["speechOutput"] && this.attributes["repromptText"]) {
            this.emitWithState("AMAZON.RepeatIntent");
        } else {
            this.handler.state = constants.states.START_MODE;
            this.emitWithState("StartGame", false);
        }
    },
    "AMAZON.CancelIntent": function () {
        this.handler.state = constants.states.START_MODE;
        this.emit(":tell", this.t("CANCEL_MESSAGE"));
    },
    "AMAZON.YesIntent": function () {
        if(this.attributes['askedCancel'])
        {
            var speechOutput = this.t("CANCEL_MESSAGE");
            this.handler.state = constants.states.START_MODE;
            this.emit(":tell", speechOutput);
        }
        else
        {
            this.emitWithState("Unhandled");
        }
    },
    "AMAZON.StopIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": true
        });
        var speechOutput = this.t("STOP_MESSAGE");
        this.emit(":ask", speechOutput, speechOutput);
    },
    "Unhandled": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        console.log('UnahnIntent');
        this.emit(":ask", this.t("UNHANDLED_FLY") + ' ' + this.attributes["repromptText"], this.attributes["repromptText"]);
    },
    "SessionEndedRequest": function () {
        this.emit(':saveState', true);
        console.log("Session ended in trivia state: " + this.event.request.reason);
    }
});
function handleUserFly() {
    var answerSlotValid = isDestinationSlotValid.call(this, this.event.request.intent);
    console.log('>>> ' + answerSlotValid);
    if (!answerSlotValid) {
        this.emitWithState("Unhandled");
        return;
    }
    else {
        var currentTime = moment(this.attributes['currentTime']);
        var timeAdd = getRandomNumber(4, 8);
        currentTime.add(timeAdd, 'h');

        // check if time over


        Object.assign(this.attributes, {
            "currentTime": currentTime
        });

        if (checkIfGameOver.call(this))
        {
            var repromptText = getTimeOutText.call(this);
            repromptText = repromptText.clue.replace('%s', currentTime.format("Do MMMM h A"));

            this.emit(":tellWithCard", repromptText, repromptText, this.t("GAME_NAME"), getCardString(repromptText));
            return;
        }

        // check if back
        var thisNote = this.event.request.intent.slots.Destination.value.toLowerCase().trim();
        thisNote = thisNote.replace(/[^a-zA-Z]/gi, '');
        console.log('NOTES: ' + thisNote);
        if (thisNote === 'back') {
            this.handler.state = constants.states.DECIDE_MODE;
            var speechOutput = this.t("DECIDE_PROMPT", this.attributes['level']);
            var repromptText = this.t("DECIDE_REPROMPT");
            Object.assign(this.attributes, {
                "speechOutput": speechOutput,
                "repromptText": repromptText
            });
            this.emit(":askWithCard", speechOutput, repromptText, this.t("GAME_NAME"), getCardString(repromptText));
            return;
        }
        else {
            var destination = getDestination.call(this, this.event.request.intent);
            console.log('Destination: ' + JSON.stringify(destination));
            var currentStep = this.attributes['currentStep'];
            var rightStep = this.attributes['rightStep'];
            var trail = this.attributes['trail'];
            var currentCountry = this.attributes['currentCountry'];
            var currentTime = moment(this.attributes['currentTime']);

            var isRightTrack = destination[0].countryId == trail[rightStep].country;
            if (rightStep == 0 && rightStep != currentStep)
            {
                isRightTrack = destination[0].countryId == this.attributes['startCountry'];
            }
            console.log('>> ' + rightStep + ' ' + currentStep)
            console.log('>>> ' + isRightTrack + ' ' + destination[0].countryId + ' : ' + trail[rightStep].country);
            if (isRightTrack)
            {
                if (destination[0].countryId == this.attributes['startCountry'])
                    rightStep = 0;
                for (var tt = 0 ; tt < trail.length ; tt++) {

                    console.log('>>>>>> ' + tt + ' ' + destination[0].countryId + ' : ' + trail[tt].country);
                    if (trail[tt].country == destination[0].countryId) {
                        rightStep = tt + 1;
                        break;
                    }
                }
            }

            if (rightStep == 5) {
                // game over
                var winText = getWinText.call(this);
                console.log(">>> " + questions.data[trail[rightStep - 1].country] + '  ' + winText);
                var score = this.attributes['score'];
                var level = this.attributes['level'];
                var left = 0;
                var lastLevel = level;
                var addLevelText = true;
                score = score + 1;

                if (score < 5)
                {
                    level = questions.level[0].level;
                    left = 5 - score;
                }
                else if (score < 10)
                {
                    level = questions.level[1].level;
                    left = 10 - score;
                }
                else if (score < 15)
                {
                    level = questions.level[2].level;
                    left = 15 - score;
                }
                else if (score < 20)
                {
                    level = questions.level[3].level;
                    left = 20 - score;
                }
                else if (score < 25)
                {
                    level = questions.level[4].level;
                    left = 25 - score;
                }
                else 
                {
                    addLevelText = false;
                }
                Object.assign(this.attributes, {
                    "score": score,
                    "level": level
                });
                console.log('>>' + score + ' ' + level);
                var repromptText = winText.clue.replace('%s', questions.data[trail[rightStep - 1].country].country);

                repromptText += ' The ' + this.attributes['treasure'] + ' is all yours! ';
                if (addLevelText)
                {
                    if (level === lastLevel) {
                        repromptText += ' You have completed ' + score + ' Trails successfully! And, your current rank is ' +
                            level + '. ' + left + ' more trails until your next promotion! Happy Treasure Hunting!<say-as interpret-as="interjection">ta ta</say-as>';
                    }
                    else
                    {
                        repromptText += ' <say-as interpret-as="interjection">woo hoo</say-as>. You have earned a promotion. Your current rank is ' + level + '. And, you have completed ' + score +
                            ' Trails successfully! ' + left + ' more trails until your next promotion! Happy Treasure Hunting!<say-as interpret-as="interjection">ta ta</say-as>';

                    }
                }
                this.emit(":tellWithCard", repromptText, this.t("GAME_NAME"), getCardString(repromptText));
                return;

            }
            else {
                currentStep++;
                console.log('curr: ' + currentStep + '  : right ' + rightStep + ' ' + isRightTrack);
                if (isRightTrack) {
                    currentStep = rightStep;
                }
                this.handler.state = constants.states.DECIDE_MODE;
                currentCountry = trail[rightStep].country;
                var repromptText = this.t("NEW_DEST_MESSAGE", destination[0].country,
                currentTime.format("Do MMMM h A"));


                Object.assign(this.attributes, {
                    "speechOutput": repromptText,
                    "repromptText": repromptText,
                    "currentTime": currentTime,
                    "currentStep": currentStep,
                    "isRightTrack": isRightTrack,
                    "rightStep": rightStep,
                    "currentCountry": currentCountry
                });

                this.emit(":askWithCard", repromptText, repromptText, this.t("GAME_NAME"), getCardString(repromptText));
            }
        }
    }


}


var helpStateHandlers = Alexa.CreateStateHandler(constants.states.HELP_MODE, {
    "helpTheUser": function (state) {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.attributes["currentState"] = state;
        this.handler.state = state;
        var speechOutput = this.t("HELP_MESSAGE");
        if(state == constants.states.START_MODE)
        {
            speechOutput = this.t("HELP_START");
        }
        else if(state == constants.states.CLUE_MODE)
        {
            speechOutput = this.t("HELP_CLUE");
        }
        else if(state == constants.states.DECIDE_MODE)
        {
            speechOutput = this.t("HELP_DECIDE");
        }
        else if(state == constants.states.FLY_MODE)
        {
            speechOutput = this.t("HELP_FLY");
        }
        
        this.emit(":ask", speechOutput + ' Now, ' + this.attributes['speechOutput'], speechOutput + ' Now, ' + this.attributes['repromptText']);
    },
    "AMAZON.StartOverIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.handler.state = constants.states.START_MODE;
        this.emitWithState("StartGame", false);
    },
    "AMAZON.RepeatIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        this.emitWithState("helpTheUser", this.handler.state);
    },
    "AMAZON.HelpIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });

        this.emitWithState("helpTheUser", this.handler.state);
    },
    "AMAZON.YesIntent": function () {
        if (this.attributes['askedCancel']) {
            var speechOutput = this.t("CANCEL_MESSAGE");
            this.handler.state = constants.states.START_MODE;
            this.emit(":tell", speechOutput);
        }
        else {
            this.emitWithState("Unhandled");
        }
    },
    "AMAZON.StopIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": true
        });
        var speechOutput = this.t("STOP_MESSAGE");
        this.emit(":ask", speechOutput, speechOutput);
    },
    "AMAZON.NoIntent": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        if (this.attributes["speechOutput"] && this.attributes["repromptText"]) {
            this.emitWithState("AMAZON.RepeatIntent");
        } else {
            this.handler.state = constants.states.START_MODE;
            this.emitWithState("StartGame", false);
        }
    },
    "AMAZON.CancelIntent": function () {
        this.handler.state = constants.states.START_MODE;
        this.emit(":tell", this.t("CANCEL_MESSAGE"));
    },
    "Unhandled": function () {
        Object.assign(this.attributes, {
            "askedCancel": false
        });
        var speechOutput = this.t("HELP_UNHANDLED");
        this.emit(":ask", speechOutput, speechOutput);
    },
...

This file has been truncated, please download it to see its full contents.

gamelogic.js

JavaScript
Contains the Code for building the Game
"use strict";

var questionCount = 7;  // The number of questions available per city.
module.exports =
{
    buildTreasureTrail : function (startCountry) {
        
        return buildTrail(startCountry);
    },
    getStartCountry : function() {
        return getRandomCountry([]);
    }
};
var start = function()
{
    debugger;
    var cnt = getRandomCountry([]);
    var trail = buildTrail(cnt);
    document.getElementById('dd').innerHTML = 'Staring > ' + cnt + '<br/><br/>Trail:<br/><br/>' + JSON.stringify(trail);
}
var buildTrail = function (startCountry)
{
    debugger;
    var trail = [];
    var country = [];
    var helpers = [];
    country.push(startCountry);
    for (var i = 0 ; i < 5 ; i++) {
        var cnt = getRandomCountry(country);
        country.push(cnt);
        var helper = getRandomHelper(helpers);
        for(var x = 0 ; x < helper.length ; x++)
        helpers.push(helper[x]);
        var clue = getRandomClues(cnt);
        trail.push({
            'country': cnt,
            'helper': helper,
            'clue': clue
        });
    }

    return trail;
}

var getRandomNumber = function(min, max)
{
    return Math.floor(Math.random()*(max-min)+min);
}

var getRandomCountry = function(cnt)
{
    var cntFound = false;
    var cntName = 'Not Found';
    var data = questions.data;
    var id = [];
    for (var i = 0 ; i < data.length ; i++) {
        var thisId = getRandomNumber(0, data.length);
        while (id.filter(function (e) { return e == thisId }).length > 0)
        {
            thisId = getRandomNumber(0, data.length);
        }
        id.push(thisId);
        var thisCountry = data[thisId];
        if (cnt.filter(function (e) { return e == thisCountry.countryId }).length == 0) {
            cntName = thisCountry.countryId;
            break;
        }
    }

    return cntName;

}

var getRandomHelper = function (helper) {

    var cntFound = 0;
    var cntName = [];
    var data = questions.helper;
    var id = [];

        for(var i = 0 ; i < data.length ; i ++)
        {
            var thisId = getRandomNumber(0, data.length);
            while (id.filter(function (e) { return e === thisId }).length > 0) {
                thisId = getRandomNumber(0, data.length);
            }
            id.push(thisId);

            var thisHelper = data[thisId];
            if (helper.filter(function (e) { return e == thisHelper.helperId }).length == 0) {
//                if (helper.indexOf(thisHelper) == -1) {
                //Not In the array!
                cntName.push(thisHelper.helperId);
                cntFound++;
                if(cntFound >= 3)
                break;
            }
        }

    return cntName;

}

var getRandomClues = function (cnt) {

    var data = questions.data;
    var id = [];
    var clues = [];
    var thisCountry = data.filter(function (e) { return e.countryId == cnt });
    if (thisCountry.length > 0) {
        for(var i = 1 ; i <= questionCount ; i++)
        {
            var thisId = getRandomNumber(0, questionCount);
            while (id.filter(function (e) { return e == thisId }).length > 0) {
                thisId = getRandomNumber(0, questionCount);
            }
            id.push(thisId);
            clues.push(thisId);
            //clues.push(thisCountry[0]["q" + thisId]);
            if(clues.length >= 3)
            {
                break;
            }
        }
    }

    return clues;

}

var questions = {
    data: [
        { 'countryId': 0, 'country': 'India', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },
        { 'countryId': 1, 'country': 'America', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },
        { 'countryId': 2, 'country': 'United Kingdom', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },
        { 'countryId': 3, 'country': 'Algeria', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },
        { 'countryId': 4, 'country': 'Egypt', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },
        { 'countryId': 5, 'country': 'Kuwait', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },
        { 'countryId': 6, 'country': 'Singapore', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },
        { 'countryId': 7, 'country': 'Australia', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },
        { 'countryId': 8, 'country': 'Sri Lanka', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },
        { 'countryId': 9, 'country': 'China', 'q0': 'q0', 'q1': 'q1', 'q2': 'q2', 'q3': 'q3', 'q4': 'q4', 'q5': 'q5', 'q6': 'q6' },


    ],

    helper:
        [
            { 'helperId': 0 , 'helper': 'banker'},
            { 'helperId': 1 , 'helper': 'historian'},
            { 'helperId': 2 , 'helper': 'librarian'},
            { 'helperId': 3 , 'helper': 'teacher'},
            { 'helperId': 4 , 'helper': 'doctor'},
            { 'helperId': 5 , 'helper': 'shopkeeper'},
            { 'helperId': 6 , 'helper': 'police'},
            { 'helperId': 7 , 'helper': 'artist'},
            { 'helperId': 8 , 'helper': 'traveller'},
            { 'helperId': 9 , 'helper': 'architect'},
            { 'helperId': 10 , 'helper': 'hotelier'},
            { 'helperId': 11 , 'helper': 'manager'},
            { 'helperId': 12 , 'helper': 'pilot'},
            { 'helperId': 13 , 'helper': 'businessman'},
            { 'helperId': 14, 'helper': 'professor' }

        ]

};

constants.js

JavaScript
To define static data for the Game Engine
"use strict";

module.exports = Object.freeze({
    //Amazon Alexa Skill App Id
    appId: '',
    //Flickr API Key
    apiKey: '',
    //Flickr API Secret
    apiSecret: '',
    //Flicker User ID
    userId: '',
    //  DynamoDB Table name
    dynamoDBTableName: 'UKTreasureHuntTable',

    states : {
        START_MODE : '',
        CLUE_MODE: '_CLUE_MODE',
        DECIDE_MODE: '_DECIDE_MODE',
        FLY_MODE: '_FLY_MODE',
        HELP_MODE: '_HELP_MODE'
        
    },

    appname: 'U.K. Treasure Hunt'

});

Utterances

Plain text
Utterances used in Alexa Skill
DecideIntent I want to {Decision}
DecideIntent {Decision} for Clue further
DecideIntent {Decision} to Next Destination
DecideIntent I want to {Decision} for Clue Further
DecideIntent I want to {Decision} for Clue
DecideIntent I want to {Decision} to Next Destination
AskIntent I want to ask {Helper}
AskIntent Ask {Helper}
AskIntent Please Ask {Helper}
FlyIntent I want to fly to {Destination}
FlyIntent Fly to {Destination}
FlyIntent To {Destination}
FlyIntent Next {Destination}
FlyIntent Next destination is {Destination}
FlyIntent Take me to {Destination}
FlyIntent Fly me to {Destination}
LaunchIntent Launch Lost Treasure
LaunchIntent Ask U.K. Treasure Hunt to start a new trail
LaunchIntent Tell U.K. Treasure Hunt to start a new trail
LaunchIntent Open U.K. Treasure Hunt
LaunchIntent Start U.K. Treasure Hunt

Intent Schema

JSON
Intent Schema used for Alexa Skill
{
  "intents": [
    {
      "slots": [
        {
          "name": "Decision",
          "type": "DECIDE"
        }
      ],
      "intent": "DecideIntent"
    },
    {
      "slots": [
        {
          "name": "Helper",
          "type": "HELPER"
        }
      ],
      "intent": "AskIntent"
    },
    {
      "slots": [
        {
          "name": "Destination",
          "type": "COUNTRY"
        }
      ],
      "intent": "FlyIntent"
    },
    {
      "intent": "LaunchIntent"
    },
    {
      "intent": "AMAZON.StartOverIntent"
    },
    {
      "intent": "AMAZON.RepeatIntent"
    },
    {
      "intent": "AMAZON.HelpIntent"
    },
    {
      "intent": "AMAZON.StopIntent"
    },
    {
      "intent": "AMAZON.CancelIntent"
    },
    {
      "intent": "AMAZON.YesIntent"
    },
    {
      "intent": "AMAZON.NoIntent"
    }
  ]
}

Credits

Khyati Majmudar

Khyati Majmudar

1 project • 1 follower
Mobility and Data Analytics Entrepreneur
Ninad Majmudar

Ninad Majmudar

1 project • 1 follower

Comments