Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

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

5.00/5 (4 votes)
18 Jul 2014CPOL1 min read 24.6K   368  
A simple JavaScript program Random Number Guessing Game using knockoutjs
Best viewed with Firefox / Chrome

Image 1

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

JavaScript
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

HTML
<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
CSS
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)