Trivia!

A front-end project that allows two players or teams to compete for trivia points

trivia

click preview image for live site,

or view the code on GitHub

Checkout my walk-through!

p.s. realized after recording that the game not starting was a feature, not a bug. Open Trivia Database didn't have enough questions of the format/difficult/category specified, so I was redirected back to the menu. Forgot I had coded that in!

Technology


Languages

  • HTML

  • SCSS

  • TypeScript

Bundlers

  • webpack

Libraries

  • jQuery

  • AJAX

  • Lodash

Formatters

  • ESLint (Airbnb style guide)

  • Prettier


Features


Responsive Design

  • Equally enjoyable on every device

  • Fully playable and fully readable regardless of window height and width

Style

  • Consistent color schemes

  • Advanced animations

  • Themed fonts

  • Font Awesome icons

  • Principled graphic design

  • High rule specificity

  • CSS / SCSS concepts

  • Gradients

  • Font sizing based on vmax and em ratios

  • Flexible Box Layout

  • Grid Layout

  • Media queries

  • Pseudo-elements

  • Cross browser compatibility

User Experience

  • Immersive! No need to ever reload when...

  • Switching between multiple game modes

  • Starting a new quiz before finishing a round

  • Playing with different quiz preferences

  • Choosing quiz preferences that cannot be met by the database

  • Encountering the rare server error

  • Easy to pick up! No instructions needed as...

  • Gameplay is intuitive

  • Every action results in a visible change


Product Highlights


Question Types Supported

  • True / False

  • Multiple Choice

  • Handles text, gifs & colors

  • Fill-in The Blank

  • Handles unlimited fields

  • Dropdown Selection

  • Handles unlimited sections

Multiple Game Modes

  • Dev's Challenge

  • Pick question types

  • Limit number of questions per type

  • Trivia Database

  • Select number of questions

  • Select format

  • Select difficulty

  • Select category


Key Code


Getting & Handling the Right Trivia Data with AJAX


const playTrivia = function (otdbParameters?: OtdbParameters): void {
    let url: string;
    if (otdbParameters) {
        const { amount, category, difficulty, type } = otdbParameters;
        url = `https://opentdb.com/api.php?amount=${amount}${
            category !== 'any' ? `&category=${category}` : ''
        }${difficulty !== 'any' ? `&difficulty=${difficulty}` : ''}${
            type !== 'any' ? `&type=${type}` : ''
        }&encode=url3986`;
    } else {
        url = 'https://cdn.aglty.io/3bikcueb/trivia-cases/challenge.json';
    }

    $.ajax({
        url,
    }).then(
        (data) => {
            if (otdbParameters && data.response_code !== 0) {
                gameOn = false;

                $player1Button.hide();
                $player2Button.hide();
                $activeGameContainer.hide();
                $promptContainer.hide();
                $responseContainer.hide();

                $otdbDescriptionText.text(
                    'Sorry! No quiz in the Database matched these parameters. Try less questions and/or less specificity...'
                );

                $modeContainer.show();
                $inactiveGameContainer.show();
                $otdbPregameContainer.show();

                $playButton.css('border', '1px solid goldenrod');
            } else {
                runQuiz(data.results);
            }
        },
        (error) => {
            console.log('bad request', error);

            $player1Button.hide();
            $player2Button.hide();
            $activeGameContainer.hide();
            $responseContainer.hide();

            $inactiveGameContainer.show();
            $modeContainer.show();

            $devSelectButton.css('border', '1px solid black');
            $otdbSelectButton.css('border', '1px solid black');
            $playButton.css('border', '1px solid goldenrod');

            $promptText.text(
                'Sorry! Server error. Please check your internet connection or try a different game mode.'
            );
            $promptText.fadeIn();

            gameOn = false;
            modeSelected = undefined;
        }
    );
};

Producing Question Elements

Requesting the Appropriate Display

const updateDom: () => void = function (): void {
// refer to github for entire function... below is just the key code
    if (type === 'boolean') {
        $responseEl = makeBoolean();
    } else if (type === 'multiple') {
        $responseEl = makeMultiple(
            _.shuffle(_.concat(correctAnswer, incorrectAnswers)),
            credit,
            datatype
        );
    } else if (type === 'fill') {
        $responseEl = makeFill(incorrectAnswers, credit);
    } else if (type === 'dropdown') {
        $responseEl = makeDropdown(options);
    }

    $responseContainer.append($responseEl.eq(0));
};
updateDom();

Calculating Points Won & Updating State


processResponse(
    lockersName: string,
    correctAnswers: string[],
    incorrectAnswers: string[],
    credit: string
): number {
    let pointsWon = 0;
    let multiplier = 0;

    if (this.currentRound.difficulty === 'easy') {
        multiplier = 1;
    } else if (this.currentRound.difficulty === 'medium') {
        multiplier = 2;
    } else if (this.currentRound.difficulty === 'hard') {
        multiplier = 3;
    }

    let numCorrect: number = 0;
    let numIncorrect: number = 0;
    for (let i = 0; i < correctAnswers.length + incorrectAnswers.length; i++) {
        if (correctAnswers.includes(this.response[i])) {
            numCorrect++;
        } else if (incorrectAnswers.includes(this.response[i])) {
            numIncorrect++;
        }
    }

    if (numCorrect > 0) {
        if (credit === 'single') {
            if (numCorrect === correctAnswers.length && numIncorrect === 0) {
                pointsWon = 1 *multiplier;
            }
        } else if (this.currentRound.type === 'dropdown') {
            pointsWon = multiplier;
        } else {
            pointsWon =
(multiplier / correctAnswers.length)* (numCorrect - numIncorrect);
            if (pointsWon < 0) {
                pointsWon = 0;
            }
        }
    }

    if (this.player1.getName() === lockersName) {
        this.player1.setScore(this.player1.getScore() + pointsWon);
    }
    if (this.player2.getName() === lockersName) {
        this.player2.setScore(this.player2.getScore() + pointsWon);
    }
    this.currentRound = this.rounds.shift();
    this.currentRoundNumber++;
    this.response = [];

    return pointsWon;
}

Coolest Algorithm

Filtering JSON Object by Property: 'type'

if (includeQuestionTypes && maxQuestionsPerType) {
    const separatedQuestions: any[][] = [];

    for (let t = 0; t < includeQuestionTypes.length; t++) {
        separatedQuestions[t] = prettyData;
    }

    prettyData = [];

    separatedQuestions.forEach(function (arr, index) {
        separatedQuestions[index] = arr
            .filter(function (anyQuestion) {
                if (anyQuestion.type === includeQuestionTypes[index]) {
                    return true;
                }
                return false;
            })
            .slice(0, maxQuestionsPerType);

        for (let i = 0; i < separatedQuestions[index].length; i++) {
            prettyData.push(separatedQuestions[index][i]);
        }
    });
}