Skip to main content
Notice removed Draw attention by EasilyBaffled
Bounty Ended with konijn's answer chosen by EasilyBaffled
deleted 8 characters in body
Source Link
    table, th, td {
    border: 3px solid #ccc;
    border-collapse:collapse
}
svg {
    display: block;
}
td {
    box-sizing: border-box;
    width: 33px;
    height: 33px;
    padding: 0px;
}
.arrow_to_place_container {
    display: inline-block;
    border: 3px solid #aaa;
}
.win_position {
    background-color: blue;
}
.player_1 {
    background-color: green;
}
.player_2 {
    background-color: purple;
}
.player_3 {
    background-color: yellow;
}
    var UP = 0;
var RIGHT = 45;
var LEFT = 315;
function print(stuff) {
    console.log(stuff);
}
//arrow should technically be a part of Display, but it's just too cumbersome to put in with the rest of the code.
function arrow(direction) {
    return "<span class='" + direction + "'><svg 'version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='30' height='30' viewBox='0 0 32 32' ><g transform='rotate(" + direction + " 15 15)'>   <path d='M27.414 12.586l-10-10c-0.781-0.781-2.047-0.781-2.828 0l-10 10c-0.781 0.781-0.781 2.047 0 2.828 0.781 0.781 2.047 0.781 2.828 0l6.586-6.586v19.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-19.172l6.586 6.586c0.39 0.39 0.902 0.586 1.414 0.586s1.024-0.195 1.414-0.586c0.781-0.781 0.781-2.047 0-2.828z' fill='#000000' / ></g></svg></span>";
}

function Cell(row, column) {
    this.row = row;
    this.column = column;
    this.direction = null;
    this.get_row = function () {
        return this.row;
    };
    this.get_column = function () {
        return this.column;
    };
    this.get_direction = function () {
        return this.direction;
    };
    this.set_direction = function (new_direction) {
        this.direction = new_direction;
    };
}

function Board(number_of_rows, number_of_column) {
    this.board = [];
    this.number_of_rows = number_of_rows;
    this.number_of_column = number_of_column;
    this.win_position = {
        'row': 0,
            'column': Math.floor((Math.random() * number_of_column))
    };
    this.next_direction = UP;
    for (var row_index = 0; row_index < number_of_rows; row_index++) {
        var row = [];
        for (var column_index = 0; column_index < number_of_column; column_index++) {
            row.push(new Cell(row_index, column_index));
        }
        this.board.push(row);
    }
    this.get_board = function () {
        return this.board;
    };
    this.get_cell = function (row, column) {
        return this.board[row][column];
    };
    this.get_win_position = function () {
        return this.win_position;
    };
    this.is_this_the_win_position = function (row, column) {
        return (row == this.win_position['row']) && (column == this.win_position['column']);
    };
    this.get_next_direction = function () {
        return this.next_direction;
    };
    this.set_next_direction = function (new_direction) {
        this.next_direction = new_direction;
    };
    this.get_cell_direction = function (row, column) {
        var cell = this.board[row][column];
        return cell.get_direction();
    };
    this.set_cell_direction = function (row, column, direction) {
        var cell = this.board[row][column];
        return cell.set_direction(direction);
    };
    this.rotate_next_arrow = function(){
        var new_direction;
        if(this.next_direction == UP){
            this.next_direction = LEFT;
        } else if (this.next_direction == LEFT){
            this.next_direction = RIGHT;
        } else {
            this.next_direction = UP;
        }
        return this.next_direction;
    };
    this.calculate_next_cell_location = function (row, column, direction) {
        row--;
        if (direction == 315 && column > 0) {
            column--;
        } else if (direction == 45 && column < number_of_column - 1) {
            column++;
        }
        if (row < 0) {
            return null;
        } else {
            return { "row": row, "column": column };
        }

    };
}

function Model(number_of_rows, number_of_column, number_of_players) {
    this.board = new Board(number_of_rows, number_of_column);
    this.start = {
        'row': number_of_rows - 1,
            'column': Math.floor(number_of_column / 2)
    };
    this.player_position = {
        'row': number_of_rows - 1,
            'column': Math.floor(number_of_column / 2)
    };
    this.get_start_position = function () {
        return this.start;
    };
    this.get_player_position = function () {
        return this.player_position;
    };
    //this.path = new Path(number_of_rows, number_of_column);
    this.get_board = function(){
        return this.board;
    };
    this.rotate_next_arrow = function(){
        return this.board.rotate_next_arrow();

    };
    this.take_turn = function (row, column, new_direction) {
        var removed = [];
        var added = [];
        var current_row = row;
        var current_column = column;
        var old_direction = this.board.get_cell_direction(current_row, current_column);
        var current_next_cell_location = this.board.calculate_next_cell_location(row, column, old_direction);
        var new_next_cell_location = this.board.calculate_next_cell_location(row, column, new_direction);
        var current_next_cell_direction = null;
        this.board.set_cell_direction(row, column, new_direction);
        removed.push([current_row, current_column]);
        added.push({'row': current_row, 'column': current_column, 'direction': new_direction});
        if(current_next_cell_location){
            current_next_cell_direction = this.board.get_cell_direction(current_next_cell_location['row'], current_next_cell_location['column']);
        }
        var next_step = new_next_cell_location;
        while(new_next_cell_location && current_next_cell_location && current_next_cell_direction !== null){
            print("+++");
            current_row = current_next_cell_location['row'];
            current_column = current_next_cell_location['column'];
            var direction = current_next_cell_direction;
            this.board.set_cell_direction(current_row, current_column, null);
            removed.push([current_row, current_column]);

            current_next_cell_location = this.board.calculate_next_cell_location(current_row, current_column, direction);
            if(current_next_cell_location){
                current_next_cell_direction = this.board.get_cell_direction(current_next_cell_location['row'], current_next_cell_location['column']);
            } else{
                current_next_cell_direction = null;
            }

            var new_row = new_next_cell_location['row'];
            var new_column = new_next_cell_location['column'];
            this.board.set_cell_direction(new_row, new_column, direction);
            added.push({'row': new_row, 'column': new_column, 'direction': direction});
            new_next_cell_location = this.board.calculate_next_cell_location(new_row, new_column, direction);

            current_row = new_row;
            current_column = new_column;
            if(this.board.is_this_the_win_position(current_row, current_column)){
                return 0;
            }
        }
        if(this.board.is_this_the_win_position(current_row, current_column)){
            return 0;
        }
        return {'removed' : removed, 'added': added, 'next_cell_location': next_step};
    };
}

function Display(number_of_rows, number_of_column, number_of_players) {
    $('body').append($("<table />"));
    for (var row_index = 0; row_index < number_of_rows; row_index++) {
        var html_row = $("<tr />", {
            id: row_index
        });
        for (var column_index = 0; column_index < number_of_column; column_index++) {
            html_cell = $("<td />", {
                class: column_index
            });
            html_row.append(html_cell);
        }
        $('table').append(html_row);
    }
    var arrow_to_place_container = $('<div />', {
        class: "arrow_to_place_container"
    });
    arrow_to_place_container.append(arrow(UP));
    $('body').append(arrow_to_place_container);

    this.position_to_jquery_id = function (row, column) {
        return $("#" + row + " > ." + column);
    };
    this.set_win_position = function (row, column) {
        var id = this.position_to_jquery_id(row, column);
        id.toggleClass("win_position");
    };
    this.set_player_position = function (row, column, old_player_number, new_player_number) {
        $(".player_" + old_player_number).toggleClass("player_" + old_player_number);
        var id = this.position_to_jquery_id(row, column);
        id.toggleClass("player_" + new_player_number);
    };
    this.set_arrow = function (row, column, direction) {
        var id = this.position_to_jquery_id(row, column);
        id.empty();
        id.append(arrow(direction));
    };
    this.empty_cell = function (row, column) {
        var id = this.position_to_jquery_id(row, column);
        id.empty();
    };
    this.game_over = function (current_player) {
        $('table').remove();
        $('body').append("<h1>Player " + current_player + " Wins!!</h1>");
    };
    this.rotate_next_arrow = function(new_direction){
        $(".arrow_to_place_container").empty();
        $(".arrow_to_place_container").append(arrow(new_direction));
    };
    this.take_turn = function(current_player, next_player, object_of_board_changes, start_location){
        cells_to_remove = object_of_board_changes['removed'];
        for(var cell_index in cells_to_remove){
            var cell_location = cells_to_remove[cell_index];
            cell_row = cell_location[0];
            cell_column = cell_location[1];
            this.empty_cell(cell_row, cell_column);
        }
        cells_to_add = object_of_board_changes['added'];
        for(cell_index in cells_to_add){
            var cell_information = cells_to_add[cell_index];
            cell_row = cell_information['row'];
            cell_column = cell_information['column'];
            cell_direction = cell_information['direction'];
            this.set_arrow(cell_row, cell_column, cell_direction);
        }

        var next_cell_location = object_of_board_changes['next_cell_location'];
        if(!next_cell_location){
            next_cell_location = start_location;
        }
        this.set_player_position(next_cell_location['row'], next_cell_location['column'], current_player, next_player);
    };
}

function Game_Controller(number_of_rows, number_of_column, number_of_players) {
    this.model = new Model(number_of_rows, number_of_column, number_of_players);
    this.display = new Display(number_of_rows, number_of_column, number_of_players);
    var player_position = this.model.get_player_position();
    this.display.set_player_position(player_position['row'], player_position['column'], 1, 1);
    var win_position = this.model.board.get_win_position();
    this.display.set_win_position(win_position['row'], win_position['column']);
    this.current_player = 1;
    var controller = this;
    $("td").click(function () {
        if($(this).hasClass("player_"+controller.current_player)){
            var row = parseInt(this.parentNode.id, 10);
            var column = parseInt(this.className, 10);
            var direction = controller.model.get_board().get_next_direction();
            var model_changes = controller.model.take_turn(row, column, direction);
            if(model_changes){
                var next_player;
                if(controller.current_player < 3){
                    next_player = controller.current_player + 1;
                } else{
                    next_player = 1;
                }
                var game_status = controller.display.take_turn(controller.current_player, next_player, model_changes, controller.model.get_start_position());
                controller.current_player = next_player;
            } else {
                controller.display.game_over(controller.current_player);
            }
        }
    });
    $(document).keydown(function (keyboard_event) {
        if (keyboard_event.keyCode == 82) {
            var new_direction = controller.model.rotate_next_arrow();
            controller.display.rotate_next_arrow(new_direction);
    }
});
}

var game = new Game_Controller(9, 9, 1);
    table, th, td {
    border: 3px solid #ccc;
    border-collapse:collapse
}
svg {
    display: block;
}
td {
    box-sizing: border-box;
    width: 33px;
    height: 33px;
    padding: 0px;
}
.arrow_to_place_container {
    display: inline-block;
    border: 3px solid #aaa;
}
.win_position {
    background-color: blue;
}
.player_1 {
    background-color: green;
}
.player_2 {
    background-color: purple;
}
.player_3 {
    background-color: yellow;
}
    var UP = 0;
var RIGHT = 45;
var LEFT = 315;
function print(stuff) {
    console.log(stuff);
}
//arrow should technically be a part of Display, but it's just too cumbersome to put in with the rest of the code.
function arrow(direction) {
    return "<span class='" + direction + "'><svg 'version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='30' height='30' viewBox='0 0 32 32' ><g transform='rotate(" + direction + " 15 15)'>   <path d='M27.414 12.586l-10-10c-0.781-0.781-2.047-0.781-2.828 0l-10 10c-0.781 0.781-0.781 2.047 0 2.828 0.781 0.781 2.047 0.781 2.828 0l6.586-6.586v19.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-19.172l6.586 6.586c0.39 0.39 0.902 0.586 1.414 0.586s1.024-0.195 1.414-0.586c0.781-0.781 0.781-2.047 0-2.828z' fill='#000000' / ></g></svg></span>";
}

function Cell(row, column) {
    this.row = row;
    this.column = column;
    this.direction = null;
    this.get_row = function () {
        return this.row;
    };
    this.get_column = function () {
        return this.column;
    };
    this.get_direction = function () {
        return this.direction;
    };
    this.set_direction = function (new_direction) {
        this.direction = new_direction;
    };
}

function Board(number_of_rows, number_of_column) {
    this.board = [];
    this.number_of_rows = number_of_rows;
    this.number_of_column = number_of_column;
    this.win_position = {
        'row': 0,
            'column': Math.floor((Math.random() * number_of_column))
    };
    this.next_direction = UP;
    for (var row_index = 0; row_index < number_of_rows; row_index++) {
        var row = [];
        for (var column_index = 0; column_index < number_of_column; column_index++) {
            row.push(new Cell(row_index, column_index));
        }
        this.board.push(row);
    }
    this.get_board = function () {
        return this.board;
    };
    this.get_cell = function (row, column) {
        return this.board[row][column];
    };
    this.get_win_position = function () {
        return this.win_position;
    };
    this.is_this_the_win_position = function (row, column) {
        return (row == this.win_position['row']) && (column == this.win_position['column']);
    };
    this.get_next_direction = function () {
        return this.next_direction;
    };
    this.set_next_direction = function (new_direction) {
        this.next_direction = new_direction;
    };
    this.get_cell_direction = function (row, column) {
        var cell = this.board[row][column];
        return cell.get_direction();
    };
    this.set_cell_direction = function (row, column, direction) {
        var cell = this.board[row][column];
        return cell.set_direction(direction);
    };
    this.rotate_next_arrow = function(){
        var new_direction;
        if(this.next_direction == UP){
            this.next_direction = LEFT;
        } else if (this.next_direction == LEFT){
            this.next_direction = RIGHT;
        } else {
            this.next_direction = UP;
        }
        return this.next_direction;
    };
    this.calculate_next_cell_location = function (row, column, direction) {
        row--;
        if (direction == 315 && column > 0) {
            column--;
        } else if (direction == 45 && column < number_of_column - 1) {
            column++;
        }
        if (row < 0) {
            return null;
        } else {
            return { "row": row, "column": column };
        }

    };
}

function Model(number_of_rows, number_of_column, number_of_players) {
    this.board = new Board(number_of_rows, number_of_column);
    this.start = {
        'row': number_of_rows - 1,
            'column': Math.floor(number_of_column / 2)
    };
    this.player_position = {
        'row': number_of_rows - 1,
            'column': Math.floor(number_of_column / 2)
    };
    this.get_start_position = function () {
        return this.start;
    };
    this.get_player_position = function () {
        return this.player_position;
    };
    //this.path = new Path(number_of_rows, number_of_column);
    this.get_board = function(){
        return this.board;
    };
    this.rotate_next_arrow = function(){
        return this.board.rotate_next_arrow();

    };
    this.take_turn = function (row, column, new_direction) {
        var removed = [];
        var added = [];
        var current_row = row;
        var current_column = column;
        var old_direction = this.board.get_cell_direction(current_row, current_column);
        var current_next_cell_location = this.board.calculate_next_cell_location(row, column, old_direction);
        var new_next_cell_location = this.board.calculate_next_cell_location(row, column, new_direction);
        var current_next_cell_direction = null;
        this.board.set_cell_direction(row, column, new_direction);
        removed.push([current_row, current_column]);
        added.push({'row': current_row, 'column': current_column, 'direction': new_direction});
        if(current_next_cell_location){
            current_next_cell_direction = this.board.get_cell_direction(current_next_cell_location['row'], current_next_cell_location['column']);
        }
        var next_step = new_next_cell_location;
        while(new_next_cell_location && current_next_cell_location && current_next_cell_direction !== null){
            print("+++");
            current_row = current_next_cell_location['row'];
            current_column = current_next_cell_location['column'];
            var direction = current_next_cell_direction;
            this.board.set_cell_direction(current_row, current_column, null);
            removed.push([current_row, current_column]);

            current_next_cell_location = this.board.calculate_next_cell_location(current_row, current_column, direction);
            if(current_next_cell_location){
                current_next_cell_direction = this.board.get_cell_direction(current_next_cell_location['row'], current_next_cell_location['column']);
            } else{
                current_next_cell_direction = null;
            }

            var new_row = new_next_cell_location['row'];
            var new_column = new_next_cell_location['column'];
            this.board.set_cell_direction(new_row, new_column, direction);
            added.push({'row': new_row, 'column': new_column, 'direction': direction});
            new_next_cell_location = this.board.calculate_next_cell_location(new_row, new_column, direction);

            current_row = new_row;
            current_column = new_column;
            if(this.board.is_this_the_win_position(current_row, current_column)){
                return 0;
            }
        }
        if(this.board.is_this_the_win_position(current_row, current_column)){
            return 0;
        }
        return {'removed' : removed, 'added': added, 'next_cell_location': next_step};
    };
}

function Display(number_of_rows, number_of_column, number_of_players) {
    $('body').append($("<table />"));
    for (var row_index = 0; row_index < number_of_rows; row_index++) {
        var html_row = $("<tr />", {
            id: row_index
        });
        for (var column_index = 0; column_index < number_of_column; column_index++) {
            html_cell = $("<td />", {
                class: column_index
            });
            html_row.append(html_cell);
        }
        $('table').append(html_row);
    }
    var arrow_to_place_container = $('<div />', {
        class: "arrow_to_place_container"
    });
    arrow_to_place_container.append(arrow(UP));
    $('body').append(arrow_to_place_container);

    this.position_to_jquery_id = function (row, column) {
        return $("#" + row + " > ." + column);
    };
    this.set_win_position = function (row, column) {
        var id = this.position_to_jquery_id(row, column);
        id.toggleClass("win_position");
    };
    this.set_player_position = function (row, column, old_player_number, new_player_number) {
        $(".player_" + old_player_number).toggleClass("player_" + old_player_number);
        var id = this.position_to_jquery_id(row, column);
        id.toggleClass("player_" + new_player_number);
    };
    this.set_arrow = function (row, column, direction) {
        var id = this.position_to_jquery_id(row, column);
        id.empty();
        id.append(arrow(direction));
    };
    this.empty_cell = function (row, column) {
        var id = this.position_to_jquery_id(row, column);
        id.empty();
    };
    this.game_over = function (current_player) {
        $('table').remove();
        $('body').append("<h1>Player " + current_player + " Wins!!</h1>");
    };
    this.rotate_next_arrow = function(new_direction){
        $(".arrow_to_place_container").empty();
        $(".arrow_to_place_container").append(arrow(new_direction));
    };
    this.take_turn = function(current_player, next_player, object_of_board_changes, start_location){
        cells_to_remove = object_of_board_changes['removed'];
        for(var cell_index in cells_to_remove){
            var cell_location = cells_to_remove[cell_index];
            cell_row = cell_location[0];
            cell_column = cell_location[1];
            this.empty_cell(cell_row, cell_column);
        }
        cells_to_add = object_of_board_changes['added'];
        for(cell_index in cells_to_add){
            var cell_information = cells_to_add[cell_index];
            cell_row = cell_information['row'];
            cell_column = cell_information['column'];
            cell_direction = cell_information['direction'];
            this.set_arrow(cell_row, cell_column, cell_direction);
        }

        var next_cell_location = object_of_board_changes['next_cell_location'];
        if(!next_cell_location){
            next_cell_location = start_location;
        }
        this.set_player_position(next_cell_location['row'], next_cell_location['column'], current_player, next_player);
    };
}

function Game_Controller(number_of_rows, number_of_column, number_of_players) {
    this.model = new Model(number_of_rows, number_of_column, number_of_players);
    this.display = new Display(number_of_rows, number_of_column, number_of_players);
    var player_position = this.model.get_player_position();
    this.display.set_player_position(player_position['row'], player_position['column'], 1, 1);
    var win_position = this.model.board.get_win_position();
    this.display.set_win_position(win_position['row'], win_position['column']);
    this.current_player = 1;
    var controller = this;
    $("td").click(function () {
        if($(this).hasClass("player_"+controller.current_player)){
            var row = parseInt(this.parentNode.id, 10);
            var column = parseInt(this.className, 10);
            var direction = controller.model.get_board().get_next_direction();
            var model_changes = controller.model.take_turn(row, column, direction);
            if(model_changes){
                var next_player;
                if(controller.current_player < 3){
                    next_player = controller.current_player + 1;
                } else{
                    next_player = 1;
                }
                var game_status = controller.display.take_turn(controller.current_player, next_player, model_changes, controller.model.get_start_position());
                controller.current_player = next_player;
            } else {
                controller.display.game_over(controller.current_player);
            }
        }
    });
    $(document).keydown(function (keyboard_event) {
        if (keyboard_event.keyCode == 82) {
            var new_direction = controller.model.rotate_next_arrow();
            controller.display.rotate_next_arrow(new_direction);
    }
});
}

var game = new Game_Controller(9, 9, 1);
table, th, td {
    border: 3px solid #ccc;
    border-collapse:collapse
}
svg {
    display: block;
}
td {
    box-sizing: border-box;
    width: 33px;
    height: 33px;
    padding: 0px;
}
.arrow_to_place_container {
    display: inline-block;
    border: 3px solid #aaa;
}
.win_position {
    background-color: blue;
}
.player_1 {
    background-color: green;
}
.player_2 {
    background-color: purple;
}
.player_3 {
    background-color: yellow;
}
var UP = 0;
var RIGHT = 45;
var LEFT = 315;
function print(stuff) {
    console.log(stuff);
}
//arrow should technically be a part of Display, but it's just too cumbersome to put in with the rest of the code.
function arrow(direction) {
    return "<span class='" + direction + "'><svg 'version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='30' height='30' viewBox='0 0 32 32' ><g transform='rotate(" + direction + " 15 15)'>   <path d='M27.414 12.586l-10-10c-0.781-0.781-2.047-0.781-2.828 0l-10 10c-0.781 0.781-0.781 2.047 0 2.828 0.781 0.781 2.047 0.781 2.828 0l6.586-6.586v19.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-19.172l6.586 6.586c0.39 0.39 0.902 0.586 1.414 0.586s1.024-0.195 1.414-0.586c0.781-0.781 0.781-2.047 0-2.828z' fill='#000000' / ></g></svg></span>";
}

function Cell(row, column) {
    this.row = row;
    this.column = column;
    this.direction = null;
    this.get_row = function () {
        return this.row;
    };
    this.get_column = function () {
        return this.column;
    };
    this.get_direction = function () {
        return this.direction;
    };
    this.set_direction = function (new_direction) {
        this.direction = new_direction;
    };
}

function Board(number_of_rows, number_of_column) {
    this.board = [];
    this.number_of_rows = number_of_rows;
    this.number_of_column = number_of_column;
    this.win_position = {
        'row': 0,
            'column': Math.floor((Math.random() * number_of_column))
    };
    this.next_direction = UP;
    for (var row_index = 0; row_index < number_of_rows; row_index++) {
        var row = [];
        for (var column_index = 0; column_index < number_of_column; column_index++) {
            row.push(new Cell(row_index, column_index));
        }
        this.board.push(row);
    }
    this.get_board = function () {
        return this.board;
    };
    this.get_cell = function (row, column) {
        return this.board[row][column];
    };
    this.get_win_position = function () {
        return this.win_position;
    };
    this.is_this_the_win_position = function (row, column) {
        return (row == this.win_position['row']) && (column == this.win_position['column']);
    };
    this.get_next_direction = function () {
        return this.next_direction;
    };
    this.set_next_direction = function (new_direction) {
        this.next_direction = new_direction;
    };
    this.get_cell_direction = function (row, column) {
        var cell = this.board[row][column];
        return cell.get_direction();
    };
    this.set_cell_direction = function (row, column, direction) {
        var cell = this.board[row][column];
        return cell.set_direction(direction);
    };
    this.rotate_next_arrow = function(){
        var new_direction;
        if(this.next_direction == UP){
            this.next_direction = LEFT;
        } else if (this.next_direction == LEFT){
            this.next_direction = RIGHT;
        } else {
            this.next_direction = UP;
        }
        return this.next_direction;
    };
    this.calculate_next_cell_location = function (row, column, direction) {
        row--;
        if (direction == 315 && column > 0) {
            column--;
        } else if (direction == 45 && column < number_of_column - 1) {
            column++;
        }
        if (row < 0) {
            return null;
        } else {
            return { "row": row, "column": column };
        }

    };
}

function Model(number_of_rows, number_of_column, number_of_players) {
    this.board = new Board(number_of_rows, number_of_column);
    this.start = {
        'row': number_of_rows - 1,
            'column': Math.floor(number_of_column / 2)
    };
    this.player_position = {
        'row': number_of_rows - 1,
            'column': Math.floor(number_of_column / 2)
    };
    this.get_start_position = function () {
        return this.start;
    };
    this.get_player_position = function () {
        return this.player_position;
    };
    //this.path = new Path(number_of_rows, number_of_column);
    this.get_board = function(){
        return this.board;
    };
    this.rotate_next_arrow = function(){
        return this.board.rotate_next_arrow();

    };
    this.take_turn = function (row, column, new_direction) {
        var removed = [];
        var added = [];
        var current_row = row;
        var current_column = column;
        var old_direction = this.board.get_cell_direction(current_row, current_column);
        var current_next_cell_location = this.board.calculate_next_cell_location(row, column, old_direction);
        var new_next_cell_location = this.board.calculate_next_cell_location(row, column, new_direction);
        var current_next_cell_direction = null;
        this.board.set_cell_direction(row, column, new_direction);
        removed.push([current_row, current_column]);
        added.push({'row': current_row, 'column': current_column, 'direction': new_direction});
        if(current_next_cell_location){
            current_next_cell_direction = this.board.get_cell_direction(current_next_cell_location['row'], current_next_cell_location['column']);
        }
        var next_step = new_next_cell_location;
        while(new_next_cell_location && current_next_cell_location && current_next_cell_direction !== null){
            print("+++");
            current_row = current_next_cell_location['row'];
            current_column = current_next_cell_location['column'];
            var direction = current_next_cell_direction;
            this.board.set_cell_direction(current_row, current_column, null);
            removed.push([current_row, current_column]);

            current_next_cell_location = this.board.calculate_next_cell_location(current_row, current_column, direction);
            if(current_next_cell_location){
                current_next_cell_direction = this.board.get_cell_direction(current_next_cell_location['row'], current_next_cell_location['column']);
            } else{
                current_next_cell_direction = null;
            }

            var new_row = new_next_cell_location['row'];
            var new_column = new_next_cell_location['column'];
            this.board.set_cell_direction(new_row, new_column, direction);
            added.push({'row': new_row, 'column': new_column, 'direction': direction});
            new_next_cell_location = this.board.calculate_next_cell_location(new_row, new_column, direction);

            current_row = new_row;
            current_column = new_column;
            if(this.board.is_this_the_win_position(current_row, current_column)){
                return 0;
            }
        }
        if(this.board.is_this_the_win_position(current_row, current_column)){
            return 0;
        }
        return {'removed' : removed, 'added': added, 'next_cell_location': next_step};
    };
}

function Display(number_of_rows, number_of_column, number_of_players) {
    $('body').append($("<table />"));
    for (var row_index = 0; row_index < number_of_rows; row_index++) {
        var html_row = $("<tr />", {
            id: row_index
        });
        for (var column_index = 0; column_index < number_of_column; column_index++) {
            html_cell = $("<td />", {
                class: column_index
            });
            html_row.append(html_cell);
        }
        $('table').append(html_row);
    }
    var arrow_to_place_container = $('<div />', {
        class: "arrow_to_place_container"
    });
    arrow_to_place_container.append(arrow(UP));
    $('body').append(arrow_to_place_container);

    this.position_to_jquery_id = function (row, column) {
        return $("#" + row + " > ." + column);
    };
    this.set_win_position = function (row, column) {
        var id = this.position_to_jquery_id(row, column);
        id.toggleClass("win_position");
    };
    this.set_player_position = function (row, column, old_player_number, new_player_number) {
        $(".player_" + old_player_number).toggleClass("player_" + old_player_number);
        var id = this.position_to_jquery_id(row, column);
        id.toggleClass("player_" + new_player_number);
    };
    this.set_arrow = function (row, column, direction) {
        var id = this.position_to_jquery_id(row, column);
        id.empty();
        id.append(arrow(direction));
    };
    this.empty_cell = function (row, column) {
        var id = this.position_to_jquery_id(row, column);
        id.empty();
    };
    this.game_over = function (current_player) {
        $('table').remove();
        $('body').append("<h1>Player " + current_player + " Wins!!</h1>");
    };
    this.rotate_next_arrow = function(new_direction){
        $(".arrow_to_place_container").empty();
        $(".arrow_to_place_container").append(arrow(new_direction));
    };
    this.take_turn = function(current_player, next_player, object_of_board_changes, start_location){
        cells_to_remove = object_of_board_changes['removed'];
        for(var cell_index in cells_to_remove){
            var cell_location = cells_to_remove[cell_index];
            cell_row = cell_location[0];
            cell_column = cell_location[1];
            this.empty_cell(cell_row, cell_column);
        }
        cells_to_add = object_of_board_changes['added'];
        for(cell_index in cells_to_add){
            var cell_information = cells_to_add[cell_index];
            cell_row = cell_information['row'];
            cell_column = cell_information['column'];
            cell_direction = cell_information['direction'];
            this.set_arrow(cell_row, cell_column, cell_direction);
        }

        var next_cell_location = object_of_board_changes['next_cell_location'];
        if(!next_cell_location){
            next_cell_location = start_location;
        }
        this.set_player_position(next_cell_location['row'], next_cell_location['column'], current_player, next_player);
    };
}

function Game_Controller(number_of_rows, number_of_column, number_of_players) {
    this.model = new Model(number_of_rows, number_of_column, number_of_players);
    this.display = new Display(number_of_rows, number_of_column, number_of_players);
    var player_position = this.model.get_player_position();
    this.display.set_player_position(player_position['row'], player_position['column'], 1, 1);
    var win_position = this.model.board.get_win_position();
    this.display.set_win_position(win_position['row'], win_position['column']);
    this.current_player = 1;
    var controller = this;
    $("td").click(function () {
        if($(this).hasClass("player_"+controller.current_player)){
            var row = parseInt(this.parentNode.id, 10);
            var column = parseInt(this.className, 10);
            var direction = controller.model.get_board().get_next_direction();
            var model_changes = controller.model.take_turn(row, column, direction);
            if(model_changes){
                var next_player;
                if(controller.current_player < 3){
                    next_player = controller.current_player + 1;
                } else{
                    next_player = 1;
                }
                var game_status = controller.display.take_turn(controller.current_player, next_player, model_changes, controller.model.get_start_position());
                controller.current_player = next_player;
            } else {
                controller.display.game_over(controller.current_player);
            }
        }
    });
    $(document).keydown(function (keyboard_event) {
        if (keyboard_event.keyCode == 82) {
            var new_direction = controller.model.rotate_next_arrow();
            controller.display.rotate_next_arrow(new_direction);
    }
});
}

var game = new Game_Controller(9, 9, 1);
added 10 characters in body
Source Link

I am trying to makehave made a small game in Javascript, while simultaneously learn about the ever so popular model view controller. The game is this, there are 3 players(green, purple, yellow) and they have to try and build a path of arrows to the blue square. The first player to make the path go to the blue square wins. When the path reaches the top row, the players go back to the start, and redirect the old path.

I am trying to make a small game, while simultaneously learn about the ever so popular model view controller. The game is this, there are 3 players(green, purple, yellow) and they have to try and build a path of arrows to the blue square. The first player to make the path go to the blue square wins. When the path reaches the top row, players go back to the start, and redirect the old path.

I have made a small game in Javascript, while simultaneously learn about the ever so popular model view controller. The game is this, there are 3 players(green, purple, yellow) and they have to try and build a path of arrows to the blue square. The first player to make the path go to the blue square wins. When the path reaches the top row, the players go back to the start, and redirect the old path.

Tweeted twitter.com/#!/StackCodeReview/status/470785864365133824
Notice added Draw attention by EasilyBaffled
Bounty Started worth 50 reputation by EasilyBaffled
added 5140 characters in body
Source Link

MV* OrganizationPattern for a board game

I have done this with just plain javascript, jQuery, and a little CSS. Ideally I was hoping to use this as a project to learn React.js, and Backbone.js, but I should get it into basic MV* form first. How should I adjust my code to better fit MV*?

JSFiddleEDIT: The previous version of this, was asking how to rewrite my code to fit an MV* pattern. I have done a massive refactorization of the code. It's in an MVC, or at least what I think is an MVC pattern. Is this the best way to arrange the code, or is there another way to better fit the game?

I still indent to bring in React.js and Backbone.js so any additional tips for that would be nice.

JSFiddle for a playable version

    table, th, td {
    border: 3px solid #ccc;
    border-collapse:collapse
}
svg {
    display: block;
}
td {
    box-sizing: border-box;
    width: 33px;
    height: 33px;
    padding: 0px;
}
.arrow_to_place_container {
    display: inline-block;
    border: 3px solid #aaa;
}
.win_position {
    background-color: blue;
}
.player_1 {
    background-color: green;
}
.player_2 {
    background-color: purple;
}
.player_3 {
    background-color: yellow;
}
var board_height = 9;
var board_width = 9;
var path = {};
var step_in_path = 1;
var current_player = 1;
var board = [];
var win_position = {
    'row': 0,
        'col': Math.floor((Math.random() * board_width))
};
function arrow(direction) {
    return "<span class='" + direction + "'><svg 'version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='30' height='30' viewBox='0 0 32 32' ><g transform='rotate(" + direction + " 15 15)'>   <path d='M27.414 12.586l-10-10c-0.781-0.781-2.047-0.781-2.828 0l-10 10c-0.781 0.781-0.781 2.047 0 2.828 0.781 0.781 2.047 0.781 2.828 0l6.586-6.586v19.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-19.172l6.586 6.586c0.39 0.39 0.902 0.586 1.414 0.586s1.024-0.195 1.414-0.586c0.781-0.781 0.781-2.047 0-2.828z' fill='#000000' / ></g></svg></span>";
}
var arrow_up = new Arrow(arrow(0));
var arrow_ru = new Arrow(arrow(45));
var arrow_lu = new Arrow(arrow(315));
print(active_cell);
$('body').append($("<table />"));
for (var row_index = 0; row_index < board_height; row_index++) {
    var html_row = $("<tr />", {
        id: row_index
    });
    for (var col_index = 0; col_index < board_width; col_index++) {
        html_cell = $("<td />", {
            class: col_index
        });
        html_row.append(html_cell);
    }
    $('table').append(html_row);
}
var arrow_to_place_container = $('<div />', {
    class: "arrow_to_place_container"
});

for (var row_index = 0; row_index < board_height; row_index++) {
    var row = [];
    for (var col_indexUP = 0; col_index < board_width; col_index++) {
        row.push(new Cell(row_index, col_index));
    }
    board.push(row);
}
var next_arrow_container = new Next_Arrow_Container();
var winning_cellRIGHT = board[win_position['row']][win_position['col']];45;
var active_cellLEFT = board[board_height - 1][Math.floor(board_width / 2)];
active_cell.toggle_active();
arrow_to_place_container.append(next_arrow_container.get_new_arrow().get_arrow_html());
$("#" + win_position['row'] + ' > .' + win_position['col']).toggleClass("win_position");
$('body').append(arrow_to_place_container);

315;
function print(stuff) {
    console.log(stuff);
}
//arrow should technically be a part of Display, but it's just too cumbersome to put in with the rest of the code.
function arrow(direction) {
    return "<span class='" + direction + "'><svg 'version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='30' height='30' viewBox='0 0 32 32' ><g transform='rotate(" + direction + " 15 15)'>   <path d='M27.414 12.586l-10-10c-0.781-0.781-2.047-0.781-2.828 0l-10 10c-0.781 0.781-0.781 2.047 0 2.828 0.781 0.781 2.047 0.781 2.828 0l6.586-6.586v19.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-19.172l6.586 6.586c0.39 0.39 0.902 0.586 1.414 0.586s1.024-0.195 1.414-0.586c0.781-0.781 0.781-2.047 0-2.828z' fill='#000000' / ></g></svg></span>";
}

function ArrowCell(elementrow, column) {
    this.html_elementrow = element;row;
    this.column = column;
    this.direction = $null;
    this.get_row = function (element) {
        return this.attrrow;
    };
    this.get_column = function ("class") {
        return this.column;
    };
    this.get_direction = function () {
        return parseInt(this.direction);direction;
    };
    this.get_arrow_htmlset_direction = function (new_direction) {
        return this.html_element;direction = new_direction;
    };
}

function Next_Arrow_ContainerBoard(number_of_rows, number_of_column) {
    this.arrowboard = arrow_up;[];
    this.get_new_arrownumber_of_rows = functionnumber_of_rows;
 ()   this.number_of_column = number_of_column;
    this.win_position = {
        return'row': new0,
 Arrow(this           'column': Math.arrowfloor((Math.get_arrow_htmlrandom() * number_of_column);)
    };
    this.rotate_arrownext_direction = function () {UP;
       for (var directionrow_index = this.arrow.get_direction();
     0; row_index < switchnumber_of_rows; (directionrow_index++) {
            case 315:
               var this.arrowrow = arrow_up;
                break;
            case 0:[];
              for (var this.arrowcolumn_index = arrow_ru;
                break;
        0; column_index < number_of_column; casecolumn_index++) 45:{
                thisrow.arrow = arrow_lu;
              push(new Cell(row_index, break;column_index));
        }
        this.board.push(row);
    }
    this.get_board = function () {
        return this.board;
    };
}

function Cell(row, column) {
    this.active = false;
    this.locationget_cell = {
        "row":function (row,
   column) {
        "col":return columnthis.board[row][column];
    };
    this.jquery_object = $("#" + row + " > ." + column);
    this.arrow = null;
    this.get_rowget_win_position = function () {
        return this.location["row"];win_position;
    };
    this.get_colis_this_the_win_position = function (row, column) {
        return (row == this.location["col"];win_position['row']) && (column == this.win_position['column']);
    };
    this.get_jquery_versionget_next_direction = function () {
        return this.jquery_object;next_direction;
    };
    this.get_arrowset_next_direction = function (new_direction) {
        return this.arrow;next_direction = new_direction;
    };
    this.toggle_activeget_cell_direction = function (row, column) {
        this.activevar cell = !this.active;board[row][column];
        this.jquery_object.toggleClass("player_" + current_player);
       return this.jquery_objectcell.toggleClassget_direction("active_cell");
    };
    this.is_activeset_cell_direction = function (row, column, direction) {
        returnvar cell = this.active;board[row][column];
        return cell.set_direction(direction);
    };
    this.add_arrowrotate_next_arrow = function (arrow){
        var new_direction;
        if(this.next_direction == UP){
            this.next_direction = LEFT;
        } else if (this.arrownext_direction == LEFT){
            this.next_direction = RIGHT;
        } else {
            this.remove_arrow();next_direction = UP;
        }
        this.arrow = arrow;
       return this.jquery_object.append(arrow.get_arrow_html());next_direction;
    };
    this.remove_arrowcalculate_next_cell_location = function (row, column, direction) {
        row--;
        if (this.arrowdirection == 315 && column > 0) {
            this.arrowcolumn--;
 = null;      } else if (direction == 45 && column < number_of_column - 1) {
            this.jquery_object.emptycolumn++;
        }
        if (row < 0) {
            return null;
        } else {
            return { "row": row, "column": column };
        }
 
    };
}

$function Model(documentnumber_of_rows, number_of_column, number_of_players) {
    this.keydown(functionboard = new Board(enumber_of_rows, number_of_column);
    this.start = {
    if (e   'row': number_of_rows - 1,
            'column': Math.keyCodefloor(number_of_column ==/ 822)
    };
    this.player_position = {
        next_arrow_container'row': number_of_rows - 1,
            'column': Math.rotate_arrowfloor(number_of_column / 2);
    };
    $("this.arrow_to_place_container"get_start_position = function () {
        return this.emptystart;
    };
    this.get_player_position = function (); {
        $("return this.arrow_to_place_container")player_position;
    };
    //this.appendpath = new Path(next_arrow_containernumber_of_rows, number_of_column);
    this.get_new_arrowget_board = function(){
        return this.get_arrow_htmlboard;
    };
    this.rotate_next_arrow = function(){
        return this.board.rotate_next_arrow();

    };
    this.take_turn = function (row, column, new_direction) {
        var removed = [];
        var added = [];
        var current_row = row;
        var current_column = column;
        var old_direction = this.board.get_cell_direction(current_row, current_column);
        var current_next_cell_location = this.board.calculate_next_cell_location(row, column, old_direction);
        var new_next_cell_location = this.board.calculate_next_cell_location(row, column, new_direction);
        var current_next_cell_direction = null;
        this.board.set_cell_direction(row, column, new_direction);
        removed.push([current_row, current_column]);
        added.push({'row': current_row, 'column': current_column, 'direction': new_direction});
        if(current_next_cell_location){
            current_next_cell_direction = this.board.get_cell_direction(current_next_cell_location['row'], current_next_cell_location['column']);
        }
        var next_step = new_next_cell_location;
        while(new_next_cell_location && current_next_cell_location && current_next_cell_direction !== null){
            print("+++");
            current_row = current_next_cell_location['row'];
            current_column = current_next_cell_location['column'];
            var direction = current_next_cell_direction;
            this.board.set_cell_direction(current_row, current_column, null);
            removed.push([current_row, current_column]);

function get_next_cell           current_next_cell_location = this.board.calculate_next_cell_location(current_row, current_cellcurrent_column, direction);
            if(current_next_cell_location){
    var location           current_next_cell_direction = get_next_cell_locationthis.board.get_cell_direction(current_row, current_cellcurrent_next_cell_location['row'], directioncurrent_next_cell_location['column']);
    return board[location['row']][location['col']];       } else{
                current_next_cell_direction = null;
            }

function get_next_cell_location(current_row, current_cell, direction) {
    current_row = parseInt(current_row);
   var current_cellnew_row = parseInt(current_cell);new_next_cell_location['row'];
    var next_row = current_row;
     var next_colnew_column = current_cell;new_next_cell_location['column'];
    switch (direction) {
       this.board.set_cell_direction(new_row, casenew_column, 315:direction);
            next_rowadded.push({'row': =new_row, current_row'column': -new_column, 1;'direction': direction});
            next_colnew_next_cell_location = current_cell - 1;
          this.board.calculate_next_cell_location(new_row, new_column, break;direction);
        case 0:
            next_row = current_row -= 1;new_row;
            break;
       current_column case= 45:new_column;
            next_row = if(this.board.is_this_the_win_position(current_row -, 1;current_column)){
            next_col = current_cell + 1;
           return break;0;
    }
    if (next_row < 0) {}
        next_row = board_height - 1;}
    }
    if (next_col > board_width -this.board.is_this_the_win_position(current_row, 1current_column) ){
        next_col = board_width - 1;
    } else if (next_col < 0)return {0;
        next_col = 0;}
    }
    return {
       'removed' "row": next_rowremoved,
          'added': added, "col"'next_cell_location': next_colnext_step};
    };
}

function align_pathDisplay(number_of_rows, number_of_column, number_of_players) {
    $('body').append($("<table />"));
    for (var steprow_index = step_in_path;0; steprow_index < Object.keys(path).length;number_of_rows; step++row_index++) {
        var current_cellhtml_row = path[step];
$("<tr />", {
      var current_arrow = current_cell.get_arrow();
   id: row_index
    var current_direction = current_arrow.get_direction( });
        var current_col =for current_cell.get_col();
var column_index = 0; column_index < number_of_column; column_index++) var{
 current_row = current_cell.get_row();
        var new_next_cellhtml_cell = get_next_cell$(current_row,"<td current_col/>", current_direction);{
        var old_next_cell = path[step + 1];   class: column_index
        var old_next_arrow = old_next_cell.get_arrow( });
        old_next_cell    html_row.remove_arrowappend(html_cell);
        new_next_cell.add_arrow(old_next_arrow);}
        path[step + 1] = new_next_cell;$('table').append(html_row);
    }
    var arrow_to_place_container = $('<div />', {
        class: "arrow_to_place_container"
    });
    arrow_to_place_container.append(arrow(UP));
    $('body').append(arrow_to_place_container);

$("td")    this.click(position_to_jquery_id = function (row, column) {
    if (   return $(this"#" + row + " > ." + column);
    };
    this.hasClassset_win_position = function ("active_cell")row, column) {
        active_cellvar id = this.remove_arrowposition_to_jquery_id(row, column);
        varid.toggleClass("win_position");
 new_arrow   };
    this.set_player_position = next_arrow_containerfunction (row, column, old_player_number, new_player_number) {
        $(".get_new_arrowplayer_" + old_player_number).toggleClass("player_" + old_player_number);
        var directionid = new_arrowthis.get_directionposition_to_jquery_id(row, column);
        active_cellid.add_arrowtoggleClass(new_arrow"player_" + new_player_number);
    };
    path[step_in_path]this.set_arrow = active_cell;function (row, column, direction) {
        var current_colid = active_cellthis.get_colposition_to_jquery_id(row, column);
        id.empty();
        id.append(arrow(direction));
    };
    this.empty_cell = function (row, column) {
        var current_rowid = active_cellthis.get_rowposition_to_jquery_id(row, column);
        align_pathid.empty();
    };
    step_in_path++;this.game_over = function (current_player) {
        active_cell$('table').toggle_activeremove();
        if $(current_row'body').append("<h1>Player ==" 0+ &&current_player + " Wins!!</h1>");
    };
    this.rotate_next_arrow = function(directionnew_direction){
 == 315 || direction == 0 || direction$(".arrow_to_place_container").empty();
 == 45      $(".arrow_to_place_container").append(arrow(new_direction));
 {   };
    this.take_turn = function(current_player, next_player, object_of_board_changes, start_location){
        step_in_pathcells_to_remove = 1;object_of_board_changes['removed'];
        for(var cell_index in cells_to_remove){
            active_cellvar cell_location = path[step_in_path];cells_to_remove[cell_index];
        } else {  cell_row = cell_location[0];
            active_cellcell_column = get_next_cell(current_row,cell_location[1];
 current_col           this.empty_cell(cell_row, directioncell_column);
        }
        cells_to_add = object_of_board_changes['added'];
        for(cell_index in cells_to_add){
            var path_keyscell_information = Objectcells_to_add[cell_index];
            cell_row = cell_information['row'];
            cell_column = cell_information['column'];
            cell_direction = cell_information['direction'];
            this.keysset_arrow(pathcell_row, cell_column, cell_direction);
        }

        var last_keynext_cell_location = Objectobject_of_board_changes['next_cell_location'];
        if(!next_cell_location){
            next_cell_location = start_location;
        }
        this.keysset_player_position(pathnext_cell_location['row'], next_cell_location['column'], current_player, next_player)[Object;
    };
}

function Game_Controller(number_of_rows, number_of_column, number_of_players) {
    this.keysmodel = new Model(pathnumber_of_rows, number_of_column, number_of_players);
    this.lengthdisplay -= 1];new Display(number_of_rows, number_of_column, number_of_players);
    var player_position = this.model.get_player_position();
 if   this.display.set_player_position(path[last_key]player_position['row'], ==player_position['column'], winning_cell1, 1);
 {   var win_position = this.model.board.get_win_position();
    this.display.set_win_position(win_position['row'], win_position['column']);
    this.current_player = 1;
    var controller = this;
    $('table'"td").removeclick(function () {
        if($(this).hasClass("player_"+controller.current_player)){
            var row = parseInt(this.parentNode.id, 10);
            $var column = parseInt('body'this.className, 10);
            var direction = controller.model.get_board().appendget_next_direction("<h1>Player);
 " + current_player + " Wins!!</h1>"      var model_changes = controller.model.take_turn(row, column, direction);
        } else   if(model_changes){
            current_player++;    var next_player;
            if    if(controller.current_player ==< 43) {
                    next_player = controller.current_player + 1;
                } else{
                    next_player = 1;
                }
                var game_status = controller.display.take_turn(controller.current_player, next_player, model_changes, controller.model.get_start_position());
                controller.current_player = next_player;
            } else {
                controller.display.game_over(controller.current_player);
            }
            active_cell.toggle_active();
        }
    });
    $(document).keydown(function (keyboard_event) {
        if (keyboard_event.keyCode == 82) {
            var new_direction = controller.model.rotate_next_arrow();
            controller.display.rotate_next_arrow(new_direction);
    }
});
}

var game = new Game_Controller(9, 9, 1);

MV* Organization for a board game

I have done this with just plain javascript, jQuery, and a little CSS. Ideally I was hoping to use this as a project to learn React.js, and Backbone.js, but I should get it into basic MV* form first. How should I adjust my code to better fit MV*?

JSFiddle for a playable version

table, th, td {
    border: 3px solid #ccc;
    border-collapse:collapse
}
svg {
    display: block;
}
td {
    box-sizing: border-box;
    width: 33px;
    height: 33px;
    padding: 0px;
}
.arrow_to_place_container {
    display: inline-block;
    border: 3px solid #aaa;
}
.win_position {
    background-color: blue;
}
.player_1 {
    background-color: green;
}
.player_2 {
    background-color: purple;
}
.player_3 {
    background-color: yellow;
}
var board_height = 9;
var board_width = 9;
var path = {};
var step_in_path = 1;
var current_player = 1;
var board = [];
var win_position = {
    'row': 0,
        'col': Math.floor((Math.random() * board_width))
};
function arrow(direction) {
    return "<span class='" + direction + "'><svg 'version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='30' height='30' viewBox='0 0 32 32' ><g transform='rotate(" + direction + " 15 15)'>   <path d='M27.414 12.586l-10-10c-0.781-0.781-2.047-0.781-2.828 0l-10 10c-0.781 0.781-0.781 2.047 0 2.828 0.781 0.781 2.047 0.781 2.828 0l6.586-6.586v19.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-19.172l6.586 6.586c0.39 0.39 0.902 0.586 1.414 0.586s1.024-0.195 1.414-0.586c0.781-0.781 0.781-2.047 0-2.828z' fill='#000000' / ></g></svg></span>";
}
var arrow_up = new Arrow(arrow(0));
var arrow_ru = new Arrow(arrow(45));
var arrow_lu = new Arrow(arrow(315));
print(active_cell);
$('body').append($("<table />"));
for (var row_index = 0; row_index < board_height; row_index++) {
    var html_row = $("<tr />", {
        id: row_index
    });
    for (var col_index = 0; col_index < board_width; col_index++) {
        html_cell = $("<td />", {
            class: col_index
        });
        html_row.append(html_cell);
    }
    $('table').append(html_row);
}
var arrow_to_place_container = $('<div />', {
    class: "arrow_to_place_container"
});

for (var row_index = 0; row_index < board_height; row_index++) {
    var row = [];
    for (var col_index = 0; col_index < board_width; col_index++) {
        row.push(new Cell(row_index, col_index));
    }
    board.push(row);
}
var next_arrow_container = new Next_Arrow_Container();
var winning_cell = board[win_position['row']][win_position['col']];
var active_cell = board[board_height - 1][Math.floor(board_width / 2)];
active_cell.toggle_active();
arrow_to_place_container.append(next_arrow_container.get_new_arrow().get_arrow_html());
$("#" + win_position['row'] + ' > .' + win_position['col']).toggleClass("win_position");
$('body').append(arrow_to_place_container);


function print(stuff) {
    console.log(stuff);
}

function Arrow(element) {
    this.html_element = element;
    this.direction = $(element).attr("class");
    this.get_direction = function () {
        return parseInt(this.direction);
    };
    this.get_arrow_html = function () {
        return this.html_element;
    };
}

function Next_Arrow_Container() {
    this.arrow = arrow_up;
    this.get_new_arrow = function () {
        return new Arrow(this.arrow.get_arrow_html());
    };
    this.rotate_arrow = function () {
        var direction = this.arrow.get_direction();
        switch (direction) {
            case 315:
                this.arrow = arrow_up;
                break;
            case 0:
                this.arrow = arrow_ru;
                break;
            case 45:
                this.arrow = arrow_lu;
                break;
        }
    };
}

function Cell(row, column) {
    this.active = false;
    this.location = {
        "row": row,
            "col": column
    };
    this.jquery_object = $("#" + row + " > ." + column);
    this.arrow = null;
    this.get_row = function () {
        return this.location["row"];
    };
    this.get_col = function () {
        return this.location["col"];
    };
    this.get_jquery_version = function () {
        return this.jquery_object;
    };
    this.get_arrow = function () {
        return this.arrow;
    };
    this.toggle_active = function () {
        this.active = !this.active;
        this.jquery_object.toggleClass("player_" + current_player);
        this.jquery_object.toggleClass("active_cell");
    };
    this.is_active = function () {
        return this.active;
    };
    this.add_arrow = function (arrow) {
        if (this.arrow) {
            this.remove_arrow();
        }
        this.arrow = arrow;
        this.jquery_object.append(arrow.get_arrow_html());
    };
    this.remove_arrow = function () {
        if (this.arrow) {
            this.arrow = null;
            this.jquery_object.empty();
        }
    };
}

$(document).keydown(function (e) {
    if (e.keyCode == 82) {
        next_arrow_container.rotate_arrow();
        $(".arrow_to_place_container").empty();
        $(".arrow_to_place_container").append(next_arrow_container.get_new_arrow().get_arrow_html());

    }
});

function get_next_cell(current_row, current_cell, direction) {
    var location = get_next_cell_location(current_row, current_cell, direction);
    return board[location['row']][location['col']];
}

function get_next_cell_location(current_row, current_cell, direction) {
    current_row = parseInt(current_row);
    current_cell = parseInt(current_cell);
    var next_row = current_row;
    var next_col = current_cell;
    switch (direction) {
        case 315:
            next_row = current_row - 1;
            next_col = current_cell - 1;
            break;
        case 0:
            next_row = current_row - 1;
            break;
        case 45:
            next_row = current_row - 1;
            next_col = current_cell + 1;
            break;
    }
    if (next_row < 0) {
        next_row = board_height - 1;
    }
    if (next_col > board_width - 1) {
        next_col = board_width - 1;
    } else if (next_col < 0) {
        next_col = 0;
    }
    return {
        "row": next_row,
            "col": next_col
    };
}

function align_path() {
    for (var step = step_in_path; step < Object.keys(path).length; step++) {
        var current_cell = path[step];
        var current_arrow = current_cell.get_arrow();
        var current_direction = current_arrow.get_direction();
        var current_col = current_cell.get_col();
        var current_row = current_cell.get_row();
        var new_next_cell = get_next_cell(current_row, current_col, current_direction);
        var old_next_cell = path[step + 1];
        var old_next_arrow = old_next_cell.get_arrow();
        old_next_cell.remove_arrow();
        new_next_cell.add_arrow(old_next_arrow);
        path[step + 1] = new_next_cell;
    }
}

$("td").click(function () {
    if ($(this).hasClass("active_cell")) {
        active_cell.remove_arrow();
        var new_arrow = next_arrow_container.get_new_arrow();
        var direction = new_arrow.get_direction();
        active_cell.add_arrow(new_arrow);
        path[step_in_path] = active_cell;
        var current_col = active_cell.get_col();
        var current_row = active_cell.get_row();
        align_path();
        step_in_path++;
        active_cell.toggle_active();
        if (current_row == 0 && (direction == 315 || direction == 0 || direction == 45)) {
            step_in_path = 1;
            active_cell = path[step_in_path];
        } else {
            active_cell = get_next_cell(current_row, current_col, direction);
        }
        var path_keys = Object.keys(path);
        var last_key = Object.keys(path)[Object.keys(path).length - 1];
        if (path[last_key] == winning_cell) {
            $('table').remove();
            $('body').append("<h1>Player " + current_player + " Wins!!</h1>");
        } else {
            current_player++;
            if (current_player == 4) {
                current_player = 1;
            }
            active_cell.toggle_active();
        }
    }
});

MV* Pattern for a board game

I have done this with just plain javascript, jQuery, and a little CSS. Ideally I was hoping to use this as a project to learn React.js, and Backbone.js, but I should get it into basic MV* form first.

EDIT: The previous version of this, was asking how to rewrite my code to fit an MV* pattern. I have done a massive refactorization of the code. It's in an MVC, or at least what I think is an MVC pattern. Is this the best way to arrange the code, or is there another way to better fit the game?

I still indent to bring in React.js and Backbone.js so any additional tips for that would be nice.

JSFiddle for a playable version

    table, th, td {
    border: 3px solid #ccc;
    border-collapse:collapse
}
svg {
    display: block;
}
td {
    box-sizing: border-box;
    width: 33px;
    height: 33px;
    padding: 0px;
}
.arrow_to_place_container {
    display: inline-block;
    border: 3px solid #aaa;
}
.win_position {
    background-color: blue;
}
.player_1 {
    background-color: green;
}
.player_2 {
    background-color: purple;
}
.player_3 {
    background-color: yellow;
}
    var UP = 0;
var RIGHT = 45;
var LEFT = 315;
function print(stuff) {
    console.log(stuff);
}
//arrow should technically be a part of Display, but it's just too cumbersome to put in with the rest of the code.
function arrow(direction) {
    return "<span class='" + direction + "'><svg 'version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='30' height='30' viewBox='0 0 32 32' ><g transform='rotate(" + direction + " 15 15)'>   <path d='M27.414 12.586l-10-10c-0.781-0.781-2.047-0.781-2.828 0l-10 10c-0.781 0.781-0.781 2.047 0 2.828 0.781 0.781 2.047 0.781 2.828 0l6.586-6.586v19.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-19.172l6.586 6.586c0.39 0.39 0.902 0.586 1.414 0.586s1.024-0.195 1.414-0.586c0.781-0.781 0.781-2.047 0-2.828z' fill='#000000' / ></g></svg></span>";
}

function Cell(row, column) {
    this.row = row;
    this.column = column;
    this.direction = null;
    this.get_row = function () {
        return this.row;
    };
    this.get_column = function () {
        return this.column;
    };
    this.get_direction = function () {
        return this.direction;
    };
    this.set_direction = function (new_direction) {
        this.direction = new_direction;
    };
}

function Board(number_of_rows, number_of_column) {
    this.board = [];
    this.number_of_rows = number_of_rows;
    this.number_of_column = number_of_column;
    this.win_position = {
        'row': 0,
            'column': Math.floor((Math.random() * number_of_column))
    };
    this.next_direction = UP;
    for (var row_index = 0; row_index < number_of_rows; row_index++) {
        var row = [];
        for (var column_index = 0; column_index < number_of_column; column_index++) {
            row.push(new Cell(row_index, column_index));
        }
        this.board.push(row);
    }
    this.get_board = function () {
        return this.board;
    };
    this.get_cell = function (row, column) {
        return this.board[row][column];
    };
    this.get_win_position = function () {
        return this.win_position;
    };
    this.is_this_the_win_position = function (row, column) {
        return (row == this.win_position['row']) && (column == this.win_position['column']);
    };
    this.get_next_direction = function () {
        return this.next_direction;
    };
    this.set_next_direction = function (new_direction) {
        this.next_direction = new_direction;
    };
    this.get_cell_direction = function (row, column) {
        var cell = this.board[row][column];
        return cell.get_direction();
    };
    this.set_cell_direction = function (row, column, direction) {
        var cell = this.board[row][column];
        return cell.set_direction(direction);
    };
    this.rotate_next_arrow = function(){
        var new_direction;
        if(this.next_direction == UP){
            this.next_direction = LEFT;
        } else if (this.next_direction == LEFT){
            this.next_direction = RIGHT;
        } else {
            this.next_direction = UP;
        }
        return this.next_direction;
    };
    this.calculate_next_cell_location = function (row, column, direction) {
        row--;
        if (direction == 315 && column > 0) {
            column--;
        } else if (direction == 45 && column < number_of_column - 1) {
            column++;
        }
        if (row < 0) {
            return null;
        } else {
            return { "row": row, "column": column };
        }
 
    };
}

function Model(number_of_rows, number_of_column, number_of_players) {
    this.board = new Board(number_of_rows, number_of_column);
    this.start = {
        'row': number_of_rows - 1,
            'column': Math.floor(number_of_column / 2)
    };
    this.player_position = {
        'row': number_of_rows - 1,
            'column': Math.floor(number_of_column / 2)
    };
    this.get_start_position = function () {
        return this.start;
    };
    this.get_player_position = function () {
        return this.player_position;
    };
    //this.path = new Path(number_of_rows, number_of_column);
    this.get_board = function(){
        return this.board;
    };
    this.rotate_next_arrow = function(){
        return this.board.rotate_next_arrow();

    };
    this.take_turn = function (row, column, new_direction) {
        var removed = [];
        var added = [];
        var current_row = row;
        var current_column = column;
        var old_direction = this.board.get_cell_direction(current_row, current_column);
        var current_next_cell_location = this.board.calculate_next_cell_location(row, column, old_direction);
        var new_next_cell_location = this.board.calculate_next_cell_location(row, column, new_direction);
        var current_next_cell_direction = null;
        this.board.set_cell_direction(row, column, new_direction);
        removed.push([current_row, current_column]);
        added.push({'row': current_row, 'column': current_column, 'direction': new_direction});
        if(current_next_cell_location){
            current_next_cell_direction = this.board.get_cell_direction(current_next_cell_location['row'], current_next_cell_location['column']);
        }
        var next_step = new_next_cell_location;
        while(new_next_cell_location && current_next_cell_location && current_next_cell_direction !== null){
            print("+++");
            current_row = current_next_cell_location['row'];
            current_column = current_next_cell_location['column'];
            var direction = current_next_cell_direction;
            this.board.set_cell_direction(current_row, current_column, null);
            removed.push([current_row, current_column]);

            current_next_cell_location = this.board.calculate_next_cell_location(current_row, current_column, direction);
            if(current_next_cell_location){
                current_next_cell_direction = this.board.get_cell_direction(current_next_cell_location['row'], current_next_cell_location['column']);
            } else{
                current_next_cell_direction = null;
            }

            var new_row = new_next_cell_location['row'];
            var new_column = new_next_cell_location['column'];
            this.board.set_cell_direction(new_row, new_column, direction);
            added.push({'row': new_row, 'column': new_column, 'direction': direction});
            new_next_cell_location = this.board.calculate_next_cell_location(new_row, new_column, direction);

            current_row = new_row;
            current_column = new_column;
            if(this.board.is_this_the_win_position(current_row, current_column)){
                return 0;
            }
        }
        if(this.board.is_this_the_win_position(current_row, current_column)){
            return 0;
        }
        return {'removed' : removed, 'added': added, 'next_cell_location': next_step};
    };
}

function Display(number_of_rows, number_of_column, number_of_players) {
    $('body').append($("<table />"));
    for (var row_index = 0; row_index < number_of_rows; row_index++) {
        var html_row = $("<tr />", {
            id: row_index
        });
        for (var column_index = 0; column_index < number_of_column; column_index++) {
            html_cell = $("<td />", {
                class: column_index
            });
            html_row.append(html_cell);
        }
        $('table').append(html_row);
    }
    var arrow_to_place_container = $('<div />', {
        class: "arrow_to_place_container"
    });
    arrow_to_place_container.append(arrow(UP));
    $('body').append(arrow_to_place_container);

    this.position_to_jquery_id = function (row, column) {
        return $("#" + row + " > ." + column);
    };
    this.set_win_position = function (row, column) {
        var id = this.position_to_jquery_id(row, column);
        id.toggleClass("win_position");
    };
    this.set_player_position = function (row, column, old_player_number, new_player_number) {
        $(".player_" + old_player_number).toggleClass("player_" + old_player_number);
        var id = this.position_to_jquery_id(row, column);
        id.toggleClass("player_" + new_player_number);
    };
    this.set_arrow = function (row, column, direction) {
        var id = this.position_to_jquery_id(row, column);
        id.empty();
        id.append(arrow(direction));
    };
    this.empty_cell = function (row, column) {
        var id = this.position_to_jquery_id(row, column);
        id.empty();
    };
    this.game_over = function (current_player) {
        $('table').remove();
        $('body').append("<h1>Player " + current_player + " Wins!!</h1>");
    };
    this.rotate_next_arrow = function(new_direction){
        $(".arrow_to_place_container").empty();
        $(".arrow_to_place_container").append(arrow(new_direction));
    };
    this.take_turn = function(current_player, next_player, object_of_board_changes, start_location){
        cells_to_remove = object_of_board_changes['removed'];
        for(var cell_index in cells_to_remove){
            var cell_location = cells_to_remove[cell_index];
            cell_row = cell_location[0];
            cell_column = cell_location[1];
            this.empty_cell(cell_row, cell_column);
        }
        cells_to_add = object_of_board_changes['added'];
        for(cell_index in cells_to_add){
            var cell_information = cells_to_add[cell_index];
            cell_row = cell_information['row'];
            cell_column = cell_information['column'];
            cell_direction = cell_information['direction'];
            this.set_arrow(cell_row, cell_column, cell_direction);
        }

        var next_cell_location = object_of_board_changes['next_cell_location'];
        if(!next_cell_location){
            next_cell_location = start_location;
        }
        this.set_player_position(next_cell_location['row'], next_cell_location['column'], current_player, next_player);
    };
}

function Game_Controller(number_of_rows, number_of_column, number_of_players) {
    this.model = new Model(number_of_rows, number_of_column, number_of_players);
    this.display = new Display(number_of_rows, number_of_column, number_of_players);
    var player_position = this.model.get_player_position();
    this.display.set_player_position(player_position['row'], player_position['column'], 1, 1);
    var win_position = this.model.board.get_win_position();
    this.display.set_win_position(win_position['row'], win_position['column']);
    this.current_player = 1;
    var controller = this;
    $("td").click(function () {
        if($(this).hasClass("player_"+controller.current_player)){
            var row = parseInt(this.parentNode.id, 10);
            var column = parseInt(this.className, 10);
            var direction = controller.model.get_board().get_next_direction();
            var model_changes = controller.model.take_turn(row, column, direction);
            if(model_changes){
                var next_player;
                if(controller.current_player < 3){
                    next_player = controller.current_player + 1;
                } else{
                    next_player = 1;
                }
                var game_status = controller.display.take_turn(controller.current_player, next_player, model_changes, controller.model.get_start_position());
                controller.current_player = next_player;
            } else {
                controller.display.game_over(controller.current_player);
            }
        }
    });
    $(document).keydown(function (keyboard_event) {
        if (keyboard_event.keyCode == 82) {
            var new_direction = controller.model.rotate_next_arrow();
            controller.display.rotate_next_arrow(new_direction);
    }
});
}

var game = new Game_Controller(9, 9, 1);
Source Link
Loading