Click here to Skip to main content
Click here to Skip to main content
Go to top

Random Number Guessing Game using HTML5, CSS3 and JavaScript (knockoutjs)

, 18 Jul 2014
Rate this:
Please Sign up or sign in to vote.
A simple JavaScript program "Random Number Guessing Game" using knockoutjs
Best viewed with Firefox / Chrome

Introduction

This article will give you some tips to write a simple Javascript program using knockoutjs. The program allows the player to select the game level and it generates a (n) digit random number based on the level. The player will be given (n) number of attempts depending on the level to guess the random number.

Background

The code is written using HTML5, CSS3 and Javascript (knockoutjs). knockoutjs is a JavaScript framework that offers dynamic data binding, dependency tracking and templating features and also it supports MVVM pattern.

Code

JavaScript - ViewModel

   var RandomNumberGameViewModel = function () {
            var self = this;

            Level = function (id, identifier) {
                return {
                    id: ko.observable(id),
                    identifier: ko.observable(identifier)
                };
            }

            self.GenerateRandomNumber = function () {
                var number = '';
                for (var i = 0; i < self.digitsLimit() ; i++) {
                    var randomNumber = Math.floor((Math.random() * self.digitsLimit()) + 1);
                    number += randomNumber;
                }
                return number;
            }

            self.GetAttemptsLimit = function (levelValue) {
                return levelValue == 2 ? 7 :
                       levelValue == 3 ? 8 : 5;
            }

            self.GetDigitsLimit = function (levelValue) {
                return levelValue == 2 ? 7 :
                       levelValue == 3 ? 9 : 4;
            }

            self.checkInput = function (data, event) {
                return (event.charCode >= 49 &&
                   event.charCode < 49 + self.digitsLimit()) || event.charCode == 0;
            }

            self.GetDashboard = function (resultArray) {
                var dashboardArray = [];
                if (!resultArray) {
                    for (var i = 0; i < self.digitsLimit() ; i++) {
                        dashboardArray.push('X');
                    }
                }
                else {
                    for (var j = 0; j < self.digitsLimit() ; j++) {
                        if (resultArray[j].flag() == true) {
                            dashboardArray.push(resultArray[j].number);
                        }
                        else {
                            dashboardArray.push('X');
                        }
                    }
                }

                return dashboardArray;
            }

            self.Result = function (indexValue, numberValue, flagValue) {
                return {
                    index: ko.observable(indexValue),
                    number: ko.observable(numberValue),
                    flag: ko.observable(flagValue)
                };
            }

            self.Results = function (attemptValue, inputNumberValue, resultArrayValue) {
                return {
                    attempt: attemptValue,
                    number: inputNumberValue,
                    result: resultArrayValue
                };
            }

            self.GetResult = function (randomNumber, userInput) {
                var arrayOfRandomNumber = randomNumber.split('');
                var arrayOfUserInput = userInput.split('');

                var result = [];
                for (var index = 0; index < arrayOfRandomNumber.length; index++) {
                    var flag = arrayOfRandomNumber[index] == arrayOfUserInput[index];
                    var number = arrayOfRandomNumber[index];
                    result.push(new self.Result(index, number, flag));
                }

                return result;
            }

            self.RestartGame = function (gameLevel) {
                self.attemptsLimit(self.GetAttemptsLimit(gameLevel));
                self.digitsLimit(self.GetDigitsLimit(gameLevel));
                self.randomNumber = self.GenerateRandomNumber();
                self.inputNumber('');
                self.attempts(self.attemptsLimit());
                self.results([]);
                self.dashboard(self.GetDashboard(''));
            }

            self.OnEnterClick = function () {
                var resultArray = self.GetResult(
                    self.randomNumber, self.inputNumber());
                var digitsCorrectCount = 0;
                var resultArrayIndex = '';
                if (resultArray.length > 0) {
                    for (var i = 0; i < resultArray.length; i++) {
                        if (resultArray[i].flag() == true) {
                            var index = i + 1;
                            digitsCorrectCount++;
                            if (!resultArrayIndex)
                                resultArrayIndex = index;
                            else {
                                appendValue = ',' + index;
                                resultArrayIndex += appendValue;
                            }
                        }
                    }

                    if (resultArrayIndex.length == 0)
                        resultArrayIndex = 'none';

                    var newResults = new self.Results(
                            self.results().length + 1,
                            self.inputNumber(),
                            resultArrayIndex
                            );

                    self.results.push(newResults);

                    var attemptsRemaining = self.attempts() - 1;
                    self.inputNumber('');
                    self.attempts(attemptsRemaining);
                    self.dashboard(self.GetDashboard(resultArray));

                    if (digitsCorrectCount == self.digitsLimit()) {
                        alert('you guessed it correct... hurray!!!!');
                        self.RestartGame(self.selectedLevel());
                    }
                    else if (self.attempts() == 0 &&
                            digitsCorrectCount < self.digitsLimit()) {
                        alert('you missed it... Sorry... better luck next time...');
                        self.RestartGame(self.selectedLevel());
                    }
                }

                self.inputFocus(true);
            }

            self.levels = ko.observableArray([new Level(1, 'Level 1'), 
                                              new Level(2, 'Level 2'),
                                              new Level(3, 'Level 3')]);
            self.selectedLevel = ko.observable();
            self.attemptsLimit = ko.observable(0);
            self.digitsLimit = ko.observable(0);
            self.randomNumber = 0;
            self.dashboard = ko.observableArray(self.GetDashboard(''));
            self.inputNumber = ko.observable('');
            self.inputFocus = ko.observable(true);
            self.enableEnter = ko.computed(function () {
                return self.inputNumber().length == self.digitsLimit();
            }, self);
            self.attempts = ko.observable(self.attemptsLimit());
            self.results = ko.observableArray([]);

            self.selectedLevel.subscribe(function (newValue) {
                ko.utils.arrayForEach(self.levels(), function (item) {
                    if (item.id() === newValue) {
                        self.RestartGame(item.id());
                    }
                });
            });
        }

   $(function () {
    ko.applyBindings(new RandomNumberGameViewModel());
   });

HTML - View

 <p class="heading">Welcome to Random Number Guessing Game</p>
    <div class="bodycontainer">
        <h2><b>Level</b></h2>
        <div>
            <select title="Choose Level" name="level" data-bind="options: levels,  value: selectedLevel , optionsText: 'identifier', optionsValue: 'id'"></select>
        </div>
        <br />
        <h2><b>Instruction</b></h2>
        <p class="Entrypannelinputs">Computer generated a <span data-bind="text: digitsLimit"></span>digit random number. For each digit, the number is chosen between 1 - <span data-bind="text: digitsLimit"></span>. Numbers can repeat.</p>
        <h2><b>Dashboard</b></h2>
        <div class="Entrypannelinputs">
            <div data-bind="foreach: dashboard">
                <a class="randomNumber" data-bind="text: $data"></a>&nbsp;
            </div>
        </div>
        <p class="attempts">You have <b><span data-bind="text: attempts"></span></b>attempts remaining</p>
        <div class="entryPanel">
            <div class="Entrypannelinputs">
                <h2>Guess the <span data-bind="text: digitsLimit"></span>digit random number</h2>
                <input data-bind='value: inputNumber, valueUpdate: "afterkeydown", hasfocus: inputFocus, event: { keypress: checkInput }' />
                <input type="button" data-bind="click: OnEnterClick, enable: enableEnter" value="Enter" />
            </div>
        </div>
        <br />
        <div class="resultPanel" data-bind="visible: results().length > 0">
            <p class="result"><b>Result</b></p>
            <div data-bind="foreach: results.slice(0).reverse()">
                <p>
                    <span class="Valuedata">Attempt: </span><span class="Valuedataans" data-bind="text: $data.attempt"></span>
                    <br />
                    <span class="Valuedata">Your Guess: </span><span class="Valuedataans" data-bind="text: $data.number"></span>
                    <br />
                    <span class="Valuedata">Digit(s) in place  </span><span class="Valuedataans" data-bind="text: $data.result"></span>correct
                </p>
            </div>
        </div>

CSS - Styling

Thanks to Ajeesh (my colleague) for helping me out with the css part
body {
    font-family: segoe ui, verdana;
    margin: 0;
    padding: 0;
}

p.heading {
    color: #ffffff;
    margin: 0;
    padding: 10px;
    margin-bottom: 10px;
    background: rgb(59,103,158);
    background: rgba(59,103,158,1);
    text-align: center;
}

.randomNumber {
    border-radius: 2px;
    border: 1px solid #4D6B8B;
    display: inline-block;
    color: #ffffff;
    font-family: arial;
    font-size: 18px;
    padding: 3px 7px;
    text-decoration: none;
    background: rgba(59,103,158,1);
}

.bodycontainer {
    width: 95%;
    margin: 0 auto;
}

select {
    width: 100px;
    border-radius: 5px;
    border: 1px solid #4D6B8B;
    background: #CDDFF3;
    padding: 3px;
}

p {
    font-size: 14px;
    color: #094C94;
}

    p.attempts span {
        color: #F50000;
    }

    p.attempts {
        color: #094C94;
    }

.Entrypannelinputs {
    width: 95%;
    padding: 5px;
    background: #eeeeee;
    border: 1px solid #4D6B8B;
    font-size: 13px;
}

    .Entrypannelinputs input[type="text"] {
        width: 80%;
        border: 1px solid #357797;
        padding: 6px 5px;
    }

    .Entrypannelinputs input[type="button"] {
        background: #CDDFF3;
    }

h2 {
    margin: 0;
    padding: 0;
    font-size: 12px;
    font-weight: normal;
    margin-bottom: 5px;
    color: #094C94;
}

p span.Valuedata {
    width: 30%;
    float: left;
}

p span.Valuedataans {
    font-weight: bold;
}

p.result {
    margin: 0;
    border-bottom: 1px solid #cccccc;
    padding-bottom: 9px;
}

Points of Interest

We can write simple, readable and dynamic JavaScript using knockoutjs and MVVM pattern.

knockoutjs - 3 Key features

  • Data bind­ing – A sim­ple way to bind UI ele­ments to data model
  • Depen­dency track­ing– Auto­mat­i­cally track depen­den­cies and updates the UI when data model changes
  • Tem­plat­ing – Allows to cre­ate sophis­ti­cated nested UIs with data model

MVVM - 3 key parts

  • View — HTML part (UI ele­ments with data-bind attribute)
  • View­Model — JavaScript (con­tains observ­able items and and client side logic)
  • Model — JSon (data from the server)

History

Try this fiddle[^]

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

John-ph
Architect
India India


Comments and Discussions

 
Questionquestion regarding your code PinmemberTridip Bhattacharjee17-Jul-14 21:17 
AnswerRe: question regarding your code PinmemberJohn-ph17-Jul-14 21:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 18 Jul 2014
Article Copyright 2014 by John-ph
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid