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!
vincent wong
Published © GPL3+

Digestive System Quiz

This trivia game tests your knowledge of the human digestive system.

BeginnerProtip697

Things used in this project

Story

Read more

Code

package.json

snippets
{
  "name": "digestive_system",
  "version": "1.0.0",
  "description": "A digestive system Alexa app",
  "main": "index.js",
  "author": "vincent wong @ wesee",
  "license": "ISC",
  "alexa": {
    "applicationId":"amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe"
  },
  "dependencies": {
    "alexa-app": "^2.3.2"
  }
}

index.js

JavaScript
'use strict';

var Skill = require('alexa-app');
var skillService = new Skill.app('digestive_system');

var questions = [

{
"What is the digestive system?": [
"The body's food-processing system", "The body's breathing system", "The body's system of nerves", "The body's blood-transporting system"
] },


{
"Digestion begins in the mouth. Which of the following statement is INCORRECT?": [
"The tongue aids in the digestion of the food.", "The saliva changes some of the starches in the food to sugar.", "The tongue keeps the food in place in the mouth while the food is being chewed.", "The digestive juices can react more easily with the food when chewed."
] },


{
"Where does food pass through between the mouth and the stomach?": [
"The gullet", "The rectum", "The small intestine", "The large intestine"
] },


{
"Our throat divides into two separate tubes: the windpipe and the gullet. What prevents food from entering the windpipe?": [
"The epiglottis", "The uvula", "The tongue", "The trachea"
] },


{
"What happens when food reaches the stomach?": [
"Juices mix with the food and stomach muscles squeeze it.", "Nothing. No digestion occurs in the stomach.", "The food moves quickly into the small intestine.", "The food is completely digested and is absorbed by tiny blood vessels in the walls of the stomach."
] },


{
"Where does the partly-digested food (in liquid form) go after it leaves the stomach?": [
"The small intestine", "The gullet", "The appendix", "The large intestine"
] },


{
"How does digested food finally reach the bloodstream?": [
"It is absorbed into the blood through blood vessels.", "It passes through the gullet into the blood.", "It is absorbed into the blood through the walls of the lungs.", "It passes from the small intestine into the large intestine, then into the blood."
] },


{
"The digestive system processes food into usable and unusable materials. The usable materials are sent to the body's cells as food. What happens to unusable materials?": [
"It goes into the large intestine to await disposal.", "It goes into the pancreas to await disposal.", "It goes to the right ventricle to await disposal.", "It goes into the small intestine to await disposal."
] },


{
"Solid waste leaves the body through the rectum then the anus. Liquid waste leaves the body after passing through the ...": [
"kidneys and bladder", "blood vessels and lungs", "large intestine and bowel", "small intestine and large intestine"
] },


{
"Digestion takes place in a long tube-like canal called the alimentary canal, or the digestive tract. Food travels through these organs in the following order:": [
"Mouth, gullet, stomach, small intestine, large intestine and rectum", "Mouth, oesophagus, stomach, large intestine, small intestine and rectum", "Mouth, stomach, oesophagus, small intestine, large intestine and rectum", "Mouth, stomach, gullet, small intestine, large intestine and rectum"
] },


{
"Which of the following does NOT manufacture digestive juices?": [
"Kidneys", "Liver", "Stomach", "Pancreas"
] },


{
"The liver is located in the abdomen and performs many functions. Which of the following is NOT a function of the liver?": [
"Manufacturing insulin", "Storing food", "Producing digestive juices", "Healing itself when it is damaged"
] }


];

var ANSWER_COUNT = 4;
var GAME_LENGTH = 5;
var CARD_TITLE = "Digestive System"; // Be sure to change this for your skill.

skillService.pre = function(request, response, type) {
	if (request.sessionDetails.application.applicationId != "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe") {
		// Fail ungracefully
		response.fail("Invalid applicationId");
    }
};

skillService.launch(function(request, response) {
    getWelcomeResponse(response);
});

skillService.intent('AnswerIntent', 
	{
		"slots": {"ANSWER": "LIST_OF_ANSWERS"},
		"utterances": ["{the|my} answer is {-|ANSWER}", "is it {-|ANSWER}", "{-|ANSWER} is my answer"]		
	},
	function(request, response){
        handleAnswerRequest(request, response);
	}
);

skillService.intent('AnswerOnlyIntent', 
	{
		"slots": {"ANSWER": "LIST_OF_ANSWERS"},
		"utterances": ["{-|ANSWER}"]
	},
	function(request, response){
        handleAnswerRequest(request, response);
	}
);

skillService.intent('DontKnowIntent', 
    {
        "utterances": [ "{i|} don't know {that|that one|this question|}", "skip", "who knows", "dunno"]  
    },
	function(request, response){
        handleAnswerRequest(request, response);
	}
);

skillService.intent('AMAZON.YesIntent', {},
	function(request, response){
        if (request.session('userPromptedToContinue')) {
            response.clearSession('userPromptedToContinue'); 
            handleRepeatRequest(request, response);
        } else {
            handleAnswerRequest(request, response);
        }
	}
);

skillService.intent('AMAZON.NoIntent', {},
	function(request, response){
        if (request.session('userPromptedToContinue')) {
            response.clearSession('userPromptedToContinue'); 
            handleFinishSessionRequest(response);
        } else {
            handleAnswerRequest(request, response);
        }
	}
);

skillService.intent('AMAZON.HelpIntent', {},
	function(request, response){
        handleGetHelpRequest(response);
	}
);

skillService.intent('AMAZON.StopIntent', {},
	function(request, response){
        handleFinishSessionRequest(response);
	}
);

skillService.intent('AMAZON.StartOverIntent', 
    {
        "utterances":[ "start", "{start|new} game", "start new game" ]    
    },
	function(request, response){
        getWelcomeResponse(response);
	}
);

skillService.intent('AMAZON.CancelIntent', {},
	function(request, response){
        handleFinishSessionRequest(response);
	}
);

skillService.intent('AMAZON.RepeatIntent', {},
	function(request, response){
        handleRepeatRequest(request, response);
	}
);

skillService.sessionEnded(function(request,response) {
    // Clean up the user's server-side stuff, if necessary

    // No response necessary
});



// ------- Skill specific business logic -------


function writeSessionData(response, sessionAttributes) {
	for (var k in sessionAttributes) {
		if (sessionAttributes.hasOwnProperty(k)) {
			response.session(k, sessionAttributes[k]);
		}
	}
}

function populateGameQuestions() {
    var gameQuestions = [];
    var indexList = [];
    var index = questions.length;

    if (GAME_LENGTH > index){
        throw "Invalid Game Length.";
    }

    for (var i = 0; i < questions.length; i++){
        indexList.push(i);
    }

    // Pick GAME_LENGTH random questions from the list to ask the user, make sure there are no repeats.
    for (var j = 0; j < GAME_LENGTH; j++){
        var rand = Math.floor(Math.random() * index);
        index -= 1;

        var temp = indexList[index];
        indexList[index] = indexList[rand];
        indexList[rand] = temp;
        gameQuestions.push(indexList[index]);
    }

    return gameQuestions;
}

function populateRoundAnswers(gameQuestionIndexes, correctAnswerIndex, correctAnswerTargetLocation) {
    // Get the answers for a given question, and place the correct answer at the spot marked by the
    // correctAnswerTargetLocation variable. Note that you can have as many answers as you want but
    // only ANSWER_COUNT will be selected.
    var answers = [],
        answersCopy = questions[gameQuestionIndexes[correctAnswerIndex]][Object.keys(questions[gameQuestionIndexes[correctAnswerIndex]])[0]],
        temp, i;

    var index = answersCopy.length;

    if (index < ANSWER_COUNT){
        throw "Not enough answers for question.";
    }

    // Shuffle the answers, excluding the first element.
    for (var j = 1; j < answersCopy.length; j++){
        var rand = Math.floor(Math.random() * (index - 1)) + 1;
        index -= 1;

        var temp = answersCopy[index];
        answersCopy[index] = answersCopy[rand];
        answersCopy[rand] = temp;
    }

    // Swap the correct answer into the target location
    for (i = 0; i < ANSWER_COUNT; i++) {
        answers[i] = answersCopy[i];
    }
    temp = answers[0];
    answers[0] = answers[correctAnswerTargetLocation];
    answers[correctAnswerTargetLocation] = temp;
    return answers;
}

function handleAnswerRequest(request, response) {
	// console.log("data->" + JSON.stringify(request.data));
	// console.log("intent->" + request.data.request.intent.name);

    var speechOutput = "";
    var reprompt = "";
    var card = {}; 
    var sessionAttributes = {};
    var gameInProgress = request.session('questions');
    var answerSlotValid = isAnswerSlotValid(parseInt(request.slot('ANSWER', 0)));
    var userGaveUp = request.data.request.intent.name === "DontKnowIntent";

	
    if (!gameInProgress) {
        // If the user responded with an answer but there is no game in progress, ask the user
        // if they want to start a new game. Set a flag to track that we've prompted the user.
        sessionAttributes.userPromptedToContinue = true;
        speechOutput = "There is no game in progress. Do you want to start a new game? ";
        card = {
            type: "Simple",
            title: CARD_TITLE,
            content: speechOutput
        };
        
        writeSessionData(response, sessionAttributes);
        buildSpeechletResponse(response, speechOutput, speechOutput, card, false);
    } else if (!answerSlotValid && !userGaveUp) {
        // If the user provided answer isn't a number > 0 and < ANSWER_COUNT,
        // return an error message to the user. Remember to guide the user into providing correct values.
        reprompt = request.session('speechOutput');
        speechOutput = "Your answer must be a number between 1 and " + ANSWER_COUNT + ". " + reprompt;
        card = {
            type: "Simple",
            title: CARD_TITLE,
            content: speechOutput
        };
        
		writeSessionData(response, sessionAttributes);
        buildSpeechletResponse(response, speechOutput, speechOutput, card, false);
    } else {
        var gameQuestions = request.session('questions'),
            correctAnswerIndex = parseInt(request.session('correctAnswerIndex')),
            currentScore = parseInt(request.session('score')),
            currentQuestionIndex = parseInt(request.session('currentQuestionIndex')),
            correctAnswerText = request.session('correctAnswerText');

        var speechOutputAnalysis = "";

        if (answerSlotValid && parseInt(request.slot('ANSWER')) == correctAnswerIndex) {
            currentScore++;
            speechOutputAnalysis = "correct. ";
        } else {
            if (!userGaveUp) {
                speechOutputAnalysis = "wrong. "
            }
            speechOutputAnalysis += "The correct answer is " + correctAnswerIndex + ": " + correctAnswerText + ". ";
        }
        // if currentQuestionIndex is 4, we've reached 5 questions (zero-indexed) and can exit the game session
        if (currentQuestionIndex == GAME_LENGTH - 1) {
            speechOutput = userGaveUp ? "" : "That answer is ";
            speechOutput += speechOutputAnalysis + "You got " + currentScore.toString() + " out of "
                + GAME_LENGTH.toString() + " questions correct. Thank you for playing!";
                
            card = {
                type: "Simple",
                title: CARD_TITLE,
                content: speechOutput
            };

            writeSessionData(response, sessionAttributes);
            buildSpeechletResponse(response, speechOutput, "", card, true);
        } else {
            currentQuestionIndex += 1;
            var spokenQuestion = Object.keys(questions[gameQuestions[currentQuestionIndex]])[0];
            // Generate a random index for the correct answer, from 0 to 3
            correctAnswerIndex = Math.floor(Math.random() * (ANSWER_COUNT));
            var roundAnswers = populateRoundAnswers(gameQuestions, currentQuestionIndex, correctAnswerIndex),

                questionIndexForSpeech = currentQuestionIndex + 1,
                repromptText = "Question " + questionIndexForSpeech.toString() + ". " + spokenQuestion + " ";
            for (var i = 0; i < ANSWER_COUNT; i++) {
                repromptText += (i+1).toString() + ". " + roundAnswers[i] + ". "
            }
            speechOutput += userGaveUp ? "" : "That answer is ";
            speechOutput += speechOutputAnalysis + "Your score is " + currentScore.toString() + ". " + repromptText;

            card = {
                type: "Simple",
                title: CARD_TITLE,
                content: speechOutput
            };
            sessionAttributes = {
                "speechOutput": repromptText,
                "repromptText": repromptText,
                "currentQuestionIndex": currentQuestionIndex,
                "correctAnswerIndex": correctAnswerIndex + 1,
                "questions": gameQuestions,
                "score": currentScore,
                "correctAnswerText":
                    questions[gameQuestions[currentQuestionIndex]][Object.keys(questions[gameQuestions[currentQuestionIndex]])[0]][0]
            };
            
            writeSessionData(response, sessionAttributes);
            buildSpeechletResponse(response, speechOutput, speechOutput, card, false);
        }
    }
}


function getWelcomeResponse(response) {
    var sessionAttributes = {},
        speechOutput = "I will ask you " + GAME_LENGTH.toString()
            + " questions, try to get as many right as you can. Just say the number of the answer. Let's begin. ",
        shouldEndSession = false,

        gameQuestions = populateGameQuestions(),
        correctAnswerIndex = Math.floor(Math.random() * (ANSWER_COUNT)), // Generate a random index for the correct answer, from 0 to 3
        roundAnswers = populateRoundAnswers(gameQuestions, 0, correctAnswerIndex),

        currentQuestionIndex = 0,
        spokenQuestion = Object.keys(questions[gameQuestions[currentQuestionIndex]])[0],
        repromptText = "Question 1. " + spokenQuestion + " ",

        i, j;

    for (i = 0; i < ANSWER_COUNT; i++) {
        repromptText += (i+1).toString() + ". " + roundAnswers[i] + ". "
    }
    speechOutput += repromptText;
    var card = {
        type: "Simple",
        title: CARD_TITLE,
        content: speechOutput
    };
    sessionAttributes = {
        "speechOutput": repromptText,
        "repromptText": repromptText,
        "currentQuestionIndex": currentQuestionIndex,
        "correctAnswerIndex": correctAnswerIndex + 1,
        "questions": gameQuestions,
        "score": 0,
        "correctAnswerText":
            questions[gameQuestions[currentQuestionIndex]][Object.keys(questions[gameQuestions[currentQuestionIndex]])[0]][0]
    }; 
    
	writeSessionData(response, sessionAttributes);
    buildSpeechletResponse(response, speechOutput, repromptText, card, shouldEndSession);

}

function handleGetHelpRequest(response) {
    // Provide a help prompt for the user, explaining how the game is played. Then, continue the game
    // if there is one in progress, or provide the option to start another one.

    // Set a flag to track that we're in the Help state.
    response.session('userPromptedToContinue', true);

    // Do not edit the help dialogue. This has been created by the Alexa team to demonstrate best practices.

    var speechOutput = "I will ask you " + GAME_LENGTH + " 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. "
        + "To repeat the last question, say, repeat. "
        + "Would you like to keep playing?",
        repromptText = "To give an answer to a question, respond with the number of the answer . "
        + "Would you like to keep playing?";
    var shouldEndSession = false;
    
    buildSpeechletResponseWithoutCard(response, speechOutput, repromptText, shouldEndSession);
}

function handleRepeatRequest(request, response) {
    // Repeat the previous speechOutput and repromptText from the session attributes if available
    // else start a new game session
    if (!request.session('speechOutput')) {
        getWelcomeResponse(response);
    } else {
        buildSpeechletResponseWithoutCard(response, request.session('speechOutput'), request.session('repromptText'), false);
    }
}

function handleFinishSessionRequest(response) {
    // End the session with a "Good bye!" if the user wants to quit the game
    buildSpeechletResponseWithoutCard(response, "Good bye!", "", true);
}

function isAnswerSlotValid(answer) {
    return answer < (ANSWER_COUNT + 1) && answer > 0;
}

// ------- Helper functions to build responses -------


function buildSpeechletResponse(response, speechOutput, repromptText, card, shouldEndSession) {
    response.say(speechOutput);
    response.reprompt(repromptText);
    response.card(card);
    response.shouldEndSession(shouldEndSession);
}

function buildSpeechletResponseWithoutCard(response, speechOutput, repromptText, shouldEndSession) {
    response.say(speechOutput);
    response.reprompt(repromptText);
    response.shouldEndSession(shouldEndSession);
}


module.exports = skillService;

Intent Schema

snippets
{
  "intents": [
    {
      "intent": "AnswerIntent",
      "slots": [
        {
          "name": "ANSWER",
          "type": "LIST_OF_ANSWERS"
        }
      ]
    },
    {
      "intent": "AnswerOnlyIntent",
      "slots": [
        {
          "name": "ANSWER",
          "type": "LIST_OF_ANSWERS"
        }
      ]
    },
    {
      "intent": "DontKnowIntent",
      "slots": []
    },
    {
      "intent": "AMAZON.YesIntent",
      "slots": []
    },
    {
      "intent": "AMAZON.NoIntent",
      "slots": []
    },
    {
      "intent": "AMAZON.HelpIntent",
      "slots": []
    },
    {
      "intent": "AMAZON.StopIntent",
      "slots": []
    },
    {
      "intent": "AMAZON.StartOverIntent",
      "slots": []
    },
    {
      "intent": "AMAZON.CancelIntent",
      "slots": []
    },
    {
      "intent": "AMAZON.RepeatIntent",
      "slots": []
    }
  ]
}

Utterances

snippets
AnswerIntent	the answer is {ANSWER}
AnswerIntent	my answer is {ANSWER}
AnswerIntent	is it {ANSWER}
AnswerIntent	{ANSWER} is my answer
AnswerOnlyIntent	{ANSWER}
DontKnowIntent	i don't know that
DontKnowIntent	don't know that
DontKnowIntent	i don't know that one
DontKnowIntent	don't know that one
DontKnowIntent	i don't know this question
DontKnowIntent	don't know this question
DontKnowIntent	i don't know
DontKnowIntent	don't know
DontKnowIntent	skip
DontKnowIntent	who knows
DontKnowIntent	dunno
AMAZON.StartOverIntent	start
AMAZON.StartOverIntent	start game
AMAZON.StartOverIntent	new game
AMAZON.StartOverIntent	start new game

LIST_OF_ANSWERS

snippets
Slot Types
1
2
3
4

Credits

vincent wong

vincent wong

81 projects • 205 followers

Comments