Click here to Skip to main content
15,868,141 members
Articles / Web Development / HTML
Tip/Trick

Game Four in a Row with KnockoutJS

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
27 May 2014CPOL 20.8K   244   9   5
Sample game Four in a row with KnockoutJS

Image 1

Introduction

Game Four in a row using KnockoutJS. This tip shows how useful KnockoutJS is for binding data collections with DOM elements.

Background

You need to know JavaScript but not necessarily knockoutJS, you can use this code in order to learn KnockoutJS.

The sample focuses on basic declarative methods from KnockoutJS and in arrays binding as well. The AI of the game is separated in a JavaScript class, you can optionally check it.

Using the Code

The tip details the KnockoutJS code. To run the game, download the zip file.

HTML
    <div class="content">
        <table class="table table-bordered">
            <!--
                Throw buttons. Notice the biding to userThrow method (when click)
                and throwEnabled (full column)
            -->
            <thead>
               <tr>
                    <td><button data-bind="click: function(){userThrow(0);}, 
                    enable: throwEnabled(0)" type="button" />&#9660;</td>
                    <td><button data-bind="click: function(){userThrow(1);}, 
                    enable: throwEnabled(1)" type="button" />&#9660;</td>
                    <td><button data-bind="click: function(){userThrow(2);}, 
                    enable: throwEnabled(2)" type="button" />&#9660;</td>
                    <td><button data-bind="click: function(){userThrow(3);}, 
                    enable: throwEnabled(3)" type="button" />&#9660;</td>
                    <td><button data-bind="click: function(){userThrow(4);}, 
                    enable: throwEnabled(4)" type="button" />&#9660;</td>
                    <td><button data-bind="click: function(){userThrow(5);}, 
                    enable: throwEnabled(5)" type="button" />&#9660;</td>
                    <td><button data-bind="click: function(){userThrow(6);}, 
                    enable: throwEnabled(6)" type="button" />&#9660;</td>
                    <td><button data-bind="click: function(){userThrow(7);}, 
                    enable: throwEnabled(7)" type="button" />&#9660;</td>
                    <td><button data-bind="click: function(){userThrow(8);}, 
                    enable: throwEnabled(8)" type="button" />&#9660;</td>
                </tr>
            </thead>
             <!-- 
                foreach bind render an array from modelView 
                For more information, check knockout documentation: 
                http://knockoutjs.com/documentation/foreach-binding.html
                Notice the nested foreach. One for rows and the other one for columns
             -->
            <tbody data-bind="foreach: { data: board, as: 'row' }">
                <tr data-bind="foreach: cols">
                    <td><span data-bind="text: $data, attr:{'class': $data}" /></td>
                </tr>
            </tbody>
        </table>
        
        <!-- 
            Binding for play again button and winner text when game ends 
        -->
        <input type="button" data-bind="click: newGame, 
        visible: playAgainVisible" value="Play again" />
        <label class="endGame" data-bind="text:winner" />
   </div>

<!-- you must link knockout library and the viewmodel after declare DOM binding -->
<script src="knockout-2.1.0.js"></script>
<script src="fourInARowVM.js"></script>

The ViewModel is as follows:

JavaScript
/*
    Knockout ViewModel binding sample with array
    To check KnockOut documentation visit: http://knockoutjs.com/documentation/introduction.html
    Sample focuses on array binding,check documentation. 
    http://knockoutjs.com/documentation/foreach-binding.html
 */

//The model object. File fourInARow.js
//This class defines the structure (board array) and implements the logic (AI) of the game
var fourInARow = new FourInARow();

//The View Model class
function AppViewModel() {
    var self = this;

    //Define observable properties
    //The board: It's an array
    self.board = ko.observableArray(fourInARow.newBoard());
    //Label to winner text
    self.winner = ko.observable();
    //We use Ko.computed as a readonly property
    self.playAgainVisible = ko.computed(function () { return (self.winner()!=null); }, self);

    //self.board() it's a binding property
    //To get the array model we need to map it by using utils arrayMap
    self.getDashboard = function () {
        var dashboard = ko.utils.arrayMap(self.board(), function (item) {
            return ({
                index: item.index
                , cols: ko.utils.arrayMap(item.cols, function (item) {
                    return (item);
                })
            });
        });

        return (dashboard);
    };

    //Method new game. Clear board and starts new game
    self.newGame = function () {
        self.board(fourInARow.newBoard());
        self.winner(null);
    }
    
    //Method userThrow. Called when user click the button of column
    self.userThrow = function (column) {
        var row = self.throw(column, "O");
        if (fourInARow.checkThrowWinner(self.getDashboard(), column, row, "O")) {
            self.winner("YOU WIN!!");
            return;
        }

        if (!fourInARow.checkItemsFree(self.getDashboard())) {
            self.winner("DRAWN");
            return;
        }

        self.computerThrow();
    }
    
    //Method computerThrow. Called from userThrow
    self.computerThrow = function () {
        var computerThrow = fourInARow.nextComputerThrow(self.getDashboard());
        var row = self.throw(computerThrow, "X");
        if (fourInARow.checkThrowWinner(self.getDashboard(), computerThrow, row, "X")) {
            self.winner("PC WIN");
        }
        if (!fourInARow.checkItemsFree(self.getDashboard())) {
            self.winner("DRAWN");
            return;
        }
    };
    
    //Throw action. It sets the state of the dashboard, then KnockOut refresh DOM
    self.throw = function (column, ficha) {
        var dashboard = self.getDashboard();
        var row = fourInARow.getLastRowFree(dashboard, column);

        dashboard[row].cols[column] = ficha;
        self.board(dashboard);

        return (row);
    };

    //Throw enabled property to enable/disable column throw
    self.throwEnabled = function (column) {
        if (self.winner() != null) return (false);

        for (var i = 0; i <= (self.getDashboard().length - 1) ; i++) {
            if (self.getDashboard()[i].cols[column] == null) {
                return (true);
            }
        }

        return (false);
    };
}

//Apply binding to KnockOut
ko.applyBindings(new AppViewModel());

License

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


Written By
Software Developer (Senior)
Unknown
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralPlease slap me.... Pin
DaveAuld27-May-14 2:44
professionalDaveAuld27-May-14 2:44 
GeneralRe: Please slap me.... Pin
Vijay Gill27-May-14 2:59
professionalVijay Gill27-May-14 2:59 
GeneralRe: Please slap me.... Pin
Jaume González27-May-14 3:00
Jaume González27-May-14 3:00 
GeneralRe: Please slap me.... Pin
Karen Mitchelle27-May-14 22:28
professionalKaren Mitchelle27-May-14 22:28 
GeneralRe: Please slap me.... Pin
Alex DaSwagga Dresko28-May-14 7:35
Alex DaSwagga Dresko28-May-14 7:35 

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

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