+
+
+
+
+
+
+ |
+ |
+ |
+
+
+ |
+ |
+ |
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spec/application.spec.js b/spec/application.spec.js
new file mode 100644
index 0000000..67f6579
--- /dev/null
+++ b/spec/application.spec.js
@@ -0,0 +1,135 @@
+import TicTacToe from 'app/models/tic_tac_toe';
+import Board from 'app/models/board';
+import Player from 'app/models/player';
+
+describe('TicTacToe', function() {
+ var testGame = new TicTacToe();
+
+ describe('ticTacToe', function() {
+ it('should start the turn count at 0', function() {
+ expect(testGame.totalTurns).toEqual(0);
+ });
+
+ it('should initialize a new game with a new board', function() {
+ expect(testGame.board instanceof Board).toEqual(true);
+ });
+
+ it('should start with an empty board', function() {
+ expect(testGame.board.playingField).toEqual([[' ',' ',' '],[' ',' ',' '],[' ',' ',' ']]);
+ });
+
+ it('should start with two players', function() {
+ expect(testGame.players.length).toEqual(2);
+ });
+
+ it('should have player objects', function() {
+ expect(testGame.players[0] instanceof Player).toEqual(true);
+ expect(testGame.players[1] instanceof Player).toEqual(true);
+ });
+ });
+
+ describe('placeMarker', function() {
+ it('should place appropriate player\'s marker on designated space', function() {
+ expect(testGame.board.emptySpace(0,1)).toEqual(true);
+ testGame.placeMarker(0,1,testGame.players[1]);
+ expect(testGame.board.emptySpace(0,1)).toEqual(false);
+ });
+ });
+
+ describe('turn', function(){
+ it('should toggle the player', function(){
+ var testToggle = new TicTacToe();
+ testToggle.turn(0, 0);
+ expect(testToggle.currentPlayer).toEqual(testToggle.players[1]);
+ testToggle.turn(0, 1);
+ expect(testToggle.currentPlayer).toEqual(testToggle.players[0]);
+ });
+
+ it('should increase the turn count at the end of the turn', function(){
+ testGame.turn(2, 2);
+ expect(testGame.turnCount).toEqual(1);
+ });
+
+ it('should check if player\'s choice is an available spot', function() {
+ expect(testGame.board.emptySpace(1, 1)).toEqual(true);
+ // console.log(testGame.board.playingField);
+ testGame.turn(1, 1);
+ // console.log("turn count:" + testGame.turnCount);
+ // console.log(testGame.checkWin());
+ // console.log(testGame.board.playingField);
+ expect(testGame.board.emptySpace(1, 1)).toEqual(false);
+ expect(function() {testGame.turn(1, 1);}).toThrow(new Error('Space is occupied. Pick an empty space.'));
+ });
+
+ it('should not let you play more than 9 turns', function(){
+ testGame.turnCount = 9;
+ expect(function() {testGame.turn(2, 1);}).toThrow(new Error('Game is over! Please clear your board for a new game.'));
+ });
+
+ it('should end game when someone wins', function() {
+ var winningGame = new TicTacToe();
+ winningGame.turn(0, 1);
+ winningGame.turn(1, 0);
+ winningGame.turn(0, 0);
+ winningGame.turn(1, 1);
+ // winningGame.turn(0, 2);
+ // console.log("check win returns: " + winningGame.checkWin());
+ expect(function() {winningGame.turn(0,2);}).toThrow(new Error('Player1 is the winner!'));
+ expect(function() {winningGame.turn(2,2);}).toThrow(new Error('Game is over! Please clear your board for a new game.'));
+ });
+
+ it('should return a tie error when there is no winner after 9 turns', function(){
+ var tiedGame = new TicTacToe();
+ tiedGame.turn(0,0);
+ tiedGame.turn(0,1);
+ tiedGame.turn(1,1);
+ tiedGame.turn(0,2);
+ tiedGame.turn(1,2);
+ tiedGame.turn(1,0);
+ tiedGame.turn(2,0);
+ tiedGame.turn(2,2);
+ expect(function() {tiedGame.turn(2,1);}).toThrow(new Error('It\'s a tie!'));
+ });
+
+ // it ('should get player\'s space input', function(){
+ // expect() ////@TODO wait until backbone
+ // });
+ });
+
+ describe('checkWin', function(){
+ it('should recognize a winner (or not) in a row', function(){
+ var testRowWin = new TicTacToe();
+ expect(testRowWin.checkWin()).toEqual(false);
+ testRowWin.board.playingField[1] = ['O','O','O'];
+ expect(testRowWin.checkWin()).toEqual(testRowWin.players[1]);
+ });
+ it('should recognize a winner (or not) in a column', function(){
+ var testColumnWin = new TicTacToe();
+ expect(testColumnWin.checkWin()).toEqual(false);
+ testColumnWin.board.playingField[0][2] = 'X';
+ testColumnWin.board.playingField[1][2] = 'X';
+ testColumnWin.board.playingField[2][2] = 'X';
+ expect(testColumnWin.checkWin()).toEqual(testColumnWin.players[0]);
+ });
+
+ it('should recognize a winner (or not) in a diagonal', function(){
+ var testDiagonalWin = new TicTacToe();
+ expect(testDiagonalWin.checkWin()).toEqual(false);
+ testDiagonalWin.board.playingField[0][2] = 'X';
+ testDiagonalWin.board.playingField[1][1] = 'X';
+ testDiagonalWin.board.playingField[2][0] = 'X';
+ expect(testDiagonalWin.checkWin()).toEqual(testDiagonalWin.players[0]);
+ });
+
+ it('should recognize no winner when total sum adds to win but not in correct order', function(){
+ var testWackyBoard = new TicTacToe();
+ expect(testWackyBoard.checkWin()).toEqual(false);
+ testWackyBoard.board.playingField[0][0] = 1;
+ testWackyBoard.board.playingField[1][0] = 5;
+ testWackyBoard.board.playingField[1][1] = 1;
+ testWackyBoard.board.playingField[2][0] = 1;
+ testWackyBoard.board.playingField[2][2] = 5;
+ expect(testWackyBoard.checkWin()).toEqual(false);
+ });
+ });
+});
diff --git a/spec/board.spec.js b/spec/board.spec.js
new file mode 100644
index 0000000..8d7d25b
--- /dev/null
+++ b/spec/board.spec.js
@@ -0,0 +1,41 @@
+import Board from 'app/models/board';
+
+describe('Board', function() {
+ var testBoard = new Board();
+
+ describe('board', function() {
+ it('should exist', function() {
+ expect(testBoard instanceof Board).toEqual(true);
+ });
+
+ it('should start with an empty board', function(){
+ expect(testBoard.playingField instanceof Array).toEqual(true);
+ });
+
+ it('should have 9 empty spaces with default value of " "', function(){
+ expect(testBoard.playingField).toEqual([[' ',' ',' '],[' ',' ',' '],[' ',' ',' ']]);
+ });
+ });
+
+ describe('emptySpace', function(){
+ //parameter passed is the coordinate: [][]
+ // console.log(testBoard.emptySpace(0, 1));
+ it('should return true if requested space is empty', function(){
+ expect(testBoard.emptySpace(0, 1)).toEqual(true);
+ });
+
+ it('should return false if requested space is occupied', function() {
+ testBoard.playingField[0][2] = 2;
+ expect(testBoard.emptySpace(0, 2)).toEqual(false);
+ });
+ });
+
+
+ // describe('board', function() {
+ // it('should exist', function() {
+ // expect(testBoard instanceof Board).toEqual(true);
+ // });
+ // });
+});
+
+// console.log(Object.getOwnPropertyNames(testBoard));
diff --git a/spec/player.spec.js b/spec/player.spec.js
new file mode 100644
index 0000000..b1a7e93
--- /dev/null
+++ b/spec/player.spec.js
@@ -0,0 +1,13 @@
+import Player from 'app/models/player';
+
+describe('Player', function() {
+ var testPlayer = new Player({
+ num: 1
+ });
+ // console.log(Object.getOwnPropertyNames(testPlayer));
+ describe('player', function() {
+ it('should have a marker', function() {
+ expect(testPlayer.marker).toEqual(1);
+ });
+ });
+});
diff --git a/src/app.js b/src/app.js
new file mode 100644
index 0000000..798edea
--- /dev/null
+++ b/src/app.js
@@ -0,0 +1,27 @@
+import $ from 'jquery';
+import TicTacToe from 'app/models/tic_tac_toe';
+// import TicTacToeList from 'app/collections/tic_tac_toe_list';
+// import TicTacToeListView from 'app/views/tic_tac_toe_list_view';
+import ApplicationView from 'app/views/application_view';
+
+$(document).ready(function() {
+ console.log("doing anything at all");
+
+ var game = new TicTacToe();
+
+ var appView = new ApplicationView({
+ el: '#application',
+ model: game
+ });
+
+ // var ticTacToeList = new TicTacToeList();
+ // ticTacToeList.fetch();
+ // // console.log(ticTacToeList);
+ //
+ // var options = {
+ // el: $('#past-games'),
+ // model: ticTacToeList
+ // };
+ // var gamesList = new TicTacToeListView(options);
+ // gamesList.render();
+});
diff --git a/src/app/collections/tic_tac_toe_list.js b/src/app/collections/tic_tac_toe_list.js
new file mode 100644
index 0000000..4c5c015
--- /dev/null
+++ b/src/app/collections/tic_tac_toe_list.js
@@ -0,0 +1,13 @@
+import Backbone from 'backbone';
+
+import TicTacToe from 'app/models/tic_tac_toe';
+
+var TicTacToeList = Backbone.Collection.extend({
+ model: TicTacToe,
+ url: 'http://localhost:3000/api/v1/games/',
+ parse: function(data){
+ return data;
+ }
+});
+
+export default TicTacToeList;
diff --git a/src/app/models/board.js b/src/app/models/board.js
new file mode 100644
index 0000000..4dbf68c
--- /dev/null
+++ b/src/app/models/board.js
@@ -0,0 +1,18 @@
+import Backbone from 'backbone';
+
+const Board = Backbone.Model.extend({
+ initialize: function() {
+ this.playingField = [[' ',' ',' '],[' ',' ',' '],[' ',' ',' ']];
+ this.markers = ['🐸', '👣'];
+ },
+
+ emptySpace: function(row, column) {
+ if (this.playingField[row][column] === ' ') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+});
+
+export default Board;
diff --git a/src/app/models/player.js b/src/app/models/player.js
new file mode 100644
index 0000000..a625818
--- /dev/null
+++ b/src/app/models/player.js
@@ -0,0 +1,10 @@
+import Backbone from 'backbone';
+
+const Player = Backbone.Model.extend({
+ initialize: function(options) {
+ this.marker = options.num;
+ }
+});
+
+
+export default Player;
diff --git a/src/app/models/tic_tac_toe.js b/src/app/models/tic_tac_toe.js
new file mode 100644
index 0000000..47f60c2
--- /dev/null
+++ b/src/app/models/tic_tac_toe.js
@@ -0,0 +1,115 @@
+import Backbone from 'backbone';
+import Board from 'app/models/board';
+import Player from 'app/models/player';
+
+const TicTacToe = Backbone.Model.extend({
+ url: 'http://localhost:3000/api/v1/games',
+
+ parse: function(response) {
+ return response;
+ },
+
+ toJSON: function(){
+ var merged = [].concat.apply([], this.board.playingField);
+
+ var object = {
+ board: merged,
+ players: ["X Player","O Player" ],
+ outcome: this.winner
+ };
+ return object;
+ },
+
+ initialize: function(){
+ console.log('model created');
+ this.MAX_TURNS = 9;
+ this.totalTurns = 0;
+ this.board = new Board();
+ this.players = [new Player({num: 'X'}), new Player({num: 'O'})];
+ this.winner = 'draw';
+ this.currentMarker = 'X';
+ this.currentPlayer = this.players[0];
+ this.turnCount = 0;
+ },
+
+ placeMarker: function(row, column, player) {
+ this.board.playingField[row][column] = player.marker;
+ },
+
+ checkWin: function() {
+ var leftDiagonalSum = 0;
+ var rightDiagonalSum = 0;
+ var playerValue = 0;
+
+ for (var i = 0; i< 3; i++) {
+ var rowSum = 0;
+ var columnSum = 0;
+
+ for (var x = 0; x < 3; x++){
+ if (this.board.playingField[i][x] == 'X') {
+ rowSum += 1;
+ } else if (this.board.playingField[i][x] == 'O') {
+ rowSum += 5;
+ }
+
+ if (this.board.playingField[x][i] == 'X') {
+ columnSum += 1;
+ } else if (this.board.playingField[x][i] == 'O') {
+ columnSum += 5;
+ }
+ }
+
+ if (this.board.playingField[i][i] == 'X') {
+ leftDiagonalSum += 1;
+ } else if (this.board.playingField[i][i] == 'O') {
+ leftDiagonalSum += 5;
+ }
+
+ if (this.board.playingField[i][2-i] == 'X') {
+ rightDiagonalSum += 1;
+ } else if (this.board.playingField[i][2-i] == 'O') {
+ rightDiagonalSum += 5;
+ }
+
+ if (rowSum === 3 || columnSum === 3|| leftDiagonalSum === 3 || rightDiagonalSum === 3) {
+ // toads
+ this.winner = 'X';
+ return this.players[0];
+ } else if (rowSum === 15 || columnSum === 15 || leftDiagonalSum === 15 || rightDiagonalSum === 15) {
+ // toes
+ this.winner = 'O';
+ return this.players[1];
+ }
+ }
+
+ return false;
+ },
+
+ turn: function(row, column) {
+ if (this.turnCount === 9 || this.checkWin() !== false) {
+ throw new Error('Game is over! Please clear your board for a new game.');
+ }
+
+ // if space is empty, placeMarker
+ // if not, throw error and start turn over
+ if (this.board.emptySpace(row, column) === true) {
+ this.placeMarker(row, column, this.currentPlayer);
+
+ // increase turn count
+ this.turnCount += 1;
+
+ // toggle the player
+ if (this.currentPlayer=== this.players[0]) {
+ this.currentPlayer = this.players[1];
+ } else {
+ this.currentPlayer = this.players[0];
+ }
+ } else {
+ alert('Space is occupied. Pick an empty space.');
+ throw new Error('Space is occupied. Pick an empty space.');
+ }
+
+ }
+});
+
+export default TicTacToe;
diff --git a/src/app/views/application_view.js b/src/app/views/application_view.js
new file mode 100644
index 0000000..8ef7047
--- /dev/null
+++ b/src/app/views/application_view.js
@@ -0,0 +1,87 @@
+import Backbone from 'backbone';
+import $ from 'jquery';
+import Board from 'app/models/board';
+import BoardView from 'app/views/board_view';
+import TicTacToe from 'app/models/tic_tac_toe';
+
+var ApplicationView = Backbone.View.extend({
+ initialize: function(){
+
+
+
+ console.log("ApplicationView created");
+ // var board = new Board();
+ // console.log(this.model.board);
+ this.boardView = new BoardView({
+ model: this.model.board,
+ el: this.$('#board'),
+ // application_view: this
+ });
+ $('.current-player').append(this.model.board.markers[0]);
+ this.listenTo(this.boardView, 'turn', this.takeTurn);
+ this.listenTo(this.boardView, "checkwinner", this.checkWinner);
+ this.render();
+ },
+
+ events: {
+ 'click .btn-new-game': 'newGame'
+ },
+
+ takeTurn: function(coordinates){
+ // console.log(this.model.winner);
+ this.player = 0;
+ if (this.model.currentPlayer == this.model.players[0]) {
+ this.player = 0;
+ this.currentMarker = 'X';
+ } else {
+ this.player = 1;
+ this.currentMarker = 'O';
+ }
+ // console.log(this.player);
+ // console.log('turncount: ' + this.model.turnCount);
+ this.model.turn(coordinates[0], coordinates[1]);
+ $('.current-player').empty();
+ $('.current-player').append(this.model.board.markers[(1 - this.player)]);
+ // this.render();
+ },
+
+ checkWinner: function(boardView) {
+ if (this.model.turnCount >= 5) {
+ // var winner = this.model.winner;
+ // console.log(winner);
+ if (this.model.checkWin() !== false){
+ // alert('someone won');
+ $('.winner').append(this.model.board.markers[this.player] + " won!");
+ $('.current-player').hide();
+ $('.current-player-header').hide();
+ this.model.save(this.model.toJSON);
+ } else if (this.model.turnCount == 9) {
+ $('.current-player').hide();
+ $('.current-player-header').hide();
+ $('.winner').append("It's a tie! Play again.");
+ this.model.save(this.model.toJSON);
+ }
+ }
+ },
+
+ newGame: function(){
+ console.log('new game clicked');
+ // console.log(this.model.board);
+ this.model = new TicTacToe();
+ this.boardView.model = this.model.board;
+ $('.current-player').show();
+ $('.current-player-header').show();
+ $('.current-player').append(this.model.board.markers[0]);
+ this.boardView.render();
+ this.render();
+ },
+
+ render: function(){
+ console.log("rendering within appView");
+ $('.current-player').empty().append(this.model.board.markers[0]);
+ $('.winner').empty();
+ return this;
+ }
+});
+
+export default ApplicationView;
diff --git a/src/app/views/board_view.js b/src/app/views/board_view.js
new file mode 100644
index 0000000..2a2baa7
--- /dev/null
+++ b/src/app/views/board_view.js
@@ -0,0 +1,57 @@
+import _ from 'underscore';
+import $ from 'jquery';
+import Backbone from 'backbone';
+import TicTacToe from 'app/models/tic_tac_toe';
+import Board from 'app/models/board';
+
+const BoardView = Backbone.View.extend({
+ initialize: function(options) {
+ console.log("BoardView created");
+ // console.log(this.model.playingField);
+
+ // this.application_view = this.options.application_view;
+
+ // this.template = _.template(Backbone.$('#tmpl-trip-card').html());
+ this.render();
+ },
+
+ events: {
+ 'click td': 'cellClick',
+ },
+
+ cellClick: function(e) {
+ var row = parseInt(e.currentTarget.id[0]),
+ column = parseInt(e.currentTarget.id[2]);
+
+ this.trigger('turn', [row, column]);
+
+ // Add class associated with player's number to determine marker color
+ if (this.model.playingField[row][column] === 'X') {
+ $(e.currentTarget).addClass('player-one').append(this.model.markers[0]);
+ } else if (this.model.playingField[row][column] === 'O') {
+ $(e.currentTarget).addClass('player-two').append(this.model.markers[1]);
+ }
+
+ // console.log(this.model.playingField);
+ // console.log(this.model);
+ this.trigger('checkwinner', [this.model]);
+
+ // this.render();
+
+ // this.trigger('select', this);
+
+ // We return false to tell jQuery not to run any more event handlers.
+ // Otherwise, it would run the 'click' event handler on RolodexView
+ // as well.
+ return false;
+ },
+
+ render: function() {
+ console.log('rendering in boardview');
+ $('td').removeClass();
+ $('td').empty();
+ return this;
+ }
+});
+
+export default BoardView;
diff --git a/src/app/views/space_view.js b/src/app/views/space_view.js
new file mode 100644
index 0000000..c964346
--- /dev/null
+++ b/src/app/views/space_view.js
@@ -0,0 +1,16 @@
+import _ from 'underscore';
+import Backbone from 'backbone';
+
+var SpaceView = Backbone.View.extend({
+ initialize: function(){
+ console.log("SpaceView created");
+ this.render();
+ },
+
+ render: function(){
+ console.log("inside space render");
+ return this;
+ }
+});
+
+export default SpaceView;
diff --git a/src/app/views/tic_tac_toe_list_view.js b/src/app/views/tic_tac_toe_list_view.js
new file mode 100644
index 0000000..d1f69e4
--- /dev/null
+++ b/src/app/views/tic_tac_toe_list_view.js
@@ -0,0 +1,74 @@
+import $ from 'jquery';
+import Backbone from 'backbone';
+
+import ApplicationView from 'app/views/application_view';
+
+var TicTacToeListView = Backbone.View.extend({
+ initialize: function(options) {
+ // Compile a template to be shared between the individual tasks
+ this.gameTemplate = _.template($('#game-template').html());
+
+ // Keep track of the
element
+ this.listElement = this.$('.game-list');
+
+ // We'll keep track of a list of game models and a list of game views.
+ this.gameList = [];
+
+ this.model.forEach(function(rawGame) {
+ // console.log('rawgame: ' + rawGame);
+ this.addGame(rawGame);
+ }, this); // bind `this` so it's available inside forEach
+
+ // // When a model is added to the collection, create
+ // // a card for that model and add it to our list of cards
+ // this.listenTo(this.model, 'add', this.addGame);
+ //
+ // // When a model is removed from the collection, remove
+ // // the card for that task from our list of cards
+ // this.listenTo(this.model, 'remove', this.removeGame);
+ //
+ // // When the model updates, re-render the list of cards
+ // this.listenTo(this.model, 'update', this.render);
+ },
+
+ render: function() {
+ // Make sure the list in the DOM is empty
+ // before we start appending items
+ this.listElement.empty();
+// console.log('gamelist ' + JSON.stringify(this.gameList));
+ // Loop through the data assigned to this view
+ this.gameList.forEach(function(game) {
+ // Cause the game to render
+ game.render();
+
+ // Add that HTML to our game list
+ this.listElement.append(game.$el);
+ }, this);
+
+ this.input = {
+ title: this.$('.new-task input[name="title"]'),
+ description: this.$('.new-task input[name="description"]')
+ };
+
+ return this; // enable chained calls
+ },
+
+ addGame: function(gameInfo) {
+ var game = new ApplicationView({
+ model: gameInfo
+ // ,
+ // template: this.gameTemplate
+ });
+ console.log(game);
+
+ // Add the card to our card list
+ this.gameList.push(game);
+ },
+
+ removeGame: function(task) {
+
+ }
+
+});
+
+export default TicTacToeListView;