diff --git a/build/index.html b/build/index.html
index 00eb541..5dbe047 100644
--- a/build/index.html
+++ b/build/index.html
@@ -1,10 +1,56 @@
-
- Tic-Tac-Toe
-
-
-
-
-
+
+ Tic-Tac-Toe
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tic-Tac-Gradient
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/styles/index.css b/build/styles/index.css
new file mode 100644
index 0000000..c514986
--- /dev/null
+++ b/build/styles/index.css
@@ -0,0 +1,85 @@
+h1, h2, h3 {
+ font-family: 'Press Start 2P', cursive;
+ text-align: center;
+ color: grey;
+}
+
+h1 {
+ font-size: 4vw;
+ margin: 3vw;
+
+}
+
+h2, h3 {
+ font-size: 2vw;
+ margin-bottom: 0;
+}
+
+body {
+ margin: 10px 10%;
+ background-color: white;
+}
+
+#ttt-board, #played-games {
+ width: 60%;
+ margin: 0 auto;
+}
+
+li {
+ list-style: none;
+ border: .25vw solid white;
+ background-color: rgba(128, 128, 128, 0.4);
+ min-height: 11vw;
+ min-width: 11vw;
+ border-radius: .25rem;
+ padding: .25vw;
+}
+
+#players {
+ margin-bottom: 2vw;
+}
+
+#played-games > li {
+ padding: 4vw;
+ margin: 1vw;
+}
+
+#played-games > li:hover {
+ background-color: rgba(128, 128, 128, 1);
+ padding: 2vw;
+ padding-top: 3vw;
+}
+
+#played-games > li:hover h3::before {
+ font-family: FontAwesome;
+ content: '\f1f8';
+ color: white;
+ padding-left: 15vw;
+ font-size: 3vw;
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+/*X Gradient*/
+
+.X0 { background-color: #4A9E9D; }
+
+.X1 { background-color: #4C8E99; }
+
+.X2 { background-color: #4F8396; }
+
+.X3 { background-color: #536B90; }
+
+.X4 { background-color: #565D8E; }
+
+/*O Gradient*/
+
+.O0 { background-color: #FFC271; }
+
+.O1 { background-color: #FFB670; }
+
+.O2 { background-color: #FFA570; }
+
+.O3 { background-color: #FF936F; }
+
+.O4 { background-color: #FF8A6F; }
diff --git a/spec/board.spec.js b/spec/board.spec.js
new file mode 100644
index 0000000..04d1b7b
--- /dev/null
+++ b/spec/board.spec.js
@@ -0,0 +1,55 @@
+// Do not remove
+import Board from 'app/models/board';
+
+describe('Board', function() {
+
+ var testBoard = new Board();
+
+ describe('Board', function() {
+ it('should be defined', function() {
+ expect(testBoard).toBeDefined();
+ });
+
+ it('should have a grid', function() {
+ expect(testBoard.get('grid')).toBeDefined();
+ });
+
+ });
+
+ describe('grid', function() {
+
+ var grid = testBoard.get('grid');
+
+ it('should be an array', function() {
+ expect(Array.isArray(grid)).toBe(true);
+ });
+
+ it('should be 3 long', function() {
+ expect(grid.length).toEqual(3);
+ });
+
+ it('should be made of sub-arrays', function() {
+ grid.forEach(function(array) {
+ expect(Array.isArray(array)).toBe(true);
+ });
+ });
+
+ it('each sub-arrays should have a length of 3', function() {
+ grid.forEach(function(array) {
+ expect(array.length).toEqual(3);
+ });
+ });
+
+ it('each sub-arrays should default values of null', function() {
+ grid.forEach(function(array) {
+ array.forEach(function(element){
+ expect(element).toBeNull();
+
+ });
+ });
+ });
+
+
+
+ });
+});
diff --git a/spec/player.spec.js b/spec/player.spec.js
new file mode 100644
index 0000000..438d920
--- /dev/null
+++ b/spec/player.spec.js
@@ -0,0 +1,25 @@
+// Do not remove
+import Player from 'app/models/player';
+
+describe('Player', function() {
+
+ var testPlayerX = new Player({name: "Testy", marker: "X"});
+ var testPlayerO = new Player({name: "Crabby", marker: "O"});
+
+ describe('Player', function() {
+ it('should be defined', function() {
+ expect(testPlayerX).toBeDefined();
+ });
+
+ it('should have a name', function() {
+ expect(testPlayerX.get('name')).toEqual("Testy");
+ });
+
+ it('should be assigned a marker', function() {
+ expect(testPlayerX.get('marker')).toEqual("X");
+ expect(testPlayerO.get('marker')).toEqual("O");
+ });
+
+ });
+
+});
diff --git a/spec/tictactoe.spec.js b/spec/tictactoe.spec.js
new file mode 100644
index 0000000..e4e1350
--- /dev/null
+++ b/spec/tictactoe.spec.js
@@ -0,0 +1,286 @@
+// Do not remove
+import TicTacToe from 'app/models/tictactoe';
+
+jasmine.getEnv().topSuite().beforeEach({fn: function() {
+ //log in as admin
+}});
+
+describe('TicTacToe', function() {
+
+ var testTicTacToe;
+ beforeEach(function(){
+ testTicTacToe = new TicTacToe();
+ });
+
+ afterEach(function(){
+ testTicTacToe.destroy();
+ });
+
+
+ describe('TicTacToe', function() {
+ it('should be defined', function() {
+ expect(testTicTacToe).toBeDefined();
+ });
+
+ it('should have a board', function() {
+ expect(testTicTacToe.get('board')).toBeDefined();
+ });
+
+ it('should have two players', function() {
+ expect(testTicTacToe.get('player1')).toBeDefined();
+ expect(testTicTacToe.get('player2')).toBeDefined();
+ });
+
+ it('should have turns', function() {
+ expect(testTicTacToe.get('turns')).toBeDefined();
+ });
+
+ });
+
+ describe('playTurn and outputResult', function() {
+ var playTicTacToe = new TicTacToe();
+
+ var tieTicTacToe = new TicTacToe();
+ var winTicTacToe = new TicTacToe();
+
+
+ it('should return FALSE when the game has not ended', function() {
+ expect(playTicTacToe.playTurn([0,0])).toBeFalsy();
+ });
+
+ it('should output the correct message when the game is tied', function() {
+ tieTicTacToe.playTurn([0,0]);
+ tieTicTacToe.playTurn([0,1]);
+ tieTicTacToe.playTurn([0,2]);
+ tieTicTacToe.playTurn([1,0]);
+ tieTicTacToe.playTurn([1,2]);
+ tieTicTacToe.playTurn([1,1]);
+ tieTicTacToe.playTurn([2,0]);
+ tieTicTacToe.playTurn([2,2]);
+
+ expect(tieTicTacToe.playTurn([2,1])).toEqual("The Game is Over. You have tied.");
+ });
+
+ it('should output the correct message when someone wins', function() {
+ winTicTacToe.playTurn([0,0]);
+ winTicTacToe.playTurn([0,1]);
+ winTicTacToe.playTurn([1,1]);
+ winTicTacToe.playTurn([2,1]);
+
+ var winnerName = winTicTacToe.get('players')[winTicTacToe.currentPlayer].name;
+
+ expect(winTicTacToe.playTurn([2,2])).toEqual("The Game is Over. " + winnerName + " has won!" );
+ });
+
+ });
+
+ describe('isValidPlacement', function() {
+
+ it('should return false if the placement on the board does not exist', function() {
+ expect(testTicTacToe.isValidPlacement([0, 1000])).toBeFalsy();
+ });
+
+ it('should return false if the placement on the board is already occupied', function() {
+
+ var occupiedGrid = [
+ ['X', 'O', null],
+ [null, null, null],
+ [null, null, null]
+ ];
+
+ var occupiedTicTacToe = new TicTacToe();
+ occupiedTicTacToe.get('board').set('grid', occupiedGrid);
+
+ expect(occupiedTicTacToe.isValidPlacement([0,0])).toBeFalsy();
+
+ var test = new TicTacToe();
+ });
+
+ it('should return true if the placement on the board is not occupied', function() {
+ expect(testTicTacToe.isValidPlacement([0, 1])).toBeTruthy();
+ });
+
+ });
+
+ describe('updateBoard', function() {
+
+ it('should change the boards current value to the given marker', function() {
+ var boardPosition = [0,0];
+ var row = boardPosition[0];
+ var column = boardPosition[1];
+
+ var boardValue = testTicTacToe.get('board').get('grid')[row][column];
+
+ expect(boardValue).toBeNull();
+
+ testTicTacToe.updateBoard(boardPosition, "X");
+
+ var boardValueUpdate = testTicTacToe.get('board').get('grid')[row][column];
+
+ expect(boardValueUpdate).toEqual("X");
+
+ });
+
+ });
+
+ describe('endMove', function() {
+
+ it('should increment the games turns by 1 AND change the current player when turns are less than 5', function() {
+ var gameTurns = testTicTacToe.get('turns');
+ var originalPlayer = testTicTacToe.get('currentPlayer');
+
+ testTicTacToe.endMove();
+
+ expect(testTicTacToe.get('turns')).toEqual(gameTurns + 1);
+ expect(testTicTacToe.get('currentPlayer')).not.toEqual(originalPlayer);
+
+ });
+
+ it('should increment the games turns by 1 AND change the current player when turns are equal to or more than 5 and no one has won', function() {
+ var turnTicTacToe = new TicTacToe();
+
+ turnTicTacToe.set('turns', 5);
+ var gameTurns = turnTicTacToe.get('turns');
+ var originalPlayer = turnTicTacToe.get('currentPlayer');
+
+ turnTicTacToe.endMove();
+
+ expect(turnTicTacToe.get('turns')).toEqual(gameTurns + 1);
+ expect(turnTicTacToe.get('currentPlayer')).not.toEqual(originalPlayer);
+
+ // turns are now greater than 5 because of endMove increment
+ var gameTurns2 = turnTicTacToe.get('turns');
+ var originalPlayer2 = turnTicTacToe.get('currentPlayer');
+
+ turnTicTacToe.endMove();
+
+ expect(turnTicTacToe.get('turns')).toEqual(gameTurns2 + 1);
+ expect(turnTicTacToe.get('currentPlayer')).not.toEqual(originalPlayer2);
+
+ });
+
+ it('should increment the games turns by 1 AND NOT change the current player when turns are equal to or more than 5 AND someone has won', function() {
+ var horizontalGrid = [
+ ['X', 'X', 'X'],
+ ['O', null, 'O'],
+ [null, null, null]
+ ];
+ var wonTicTacToe = new TicTacToe();
+
+ wonTicTacToe.get('board').set('grid', horizontalGrid);
+ wonTicTacToe.set('turns', 5);
+
+
+ var gameTurns = wonTicTacToe.get('turns');
+ var originalPlayer = wonTicTacToe.get('currentPlayer');
+
+ wonTicTacToe.endMove();
+
+ expect(wonTicTacToe.get('turns')).toEqual(gameTurns + 1);
+ expect(wonTicTacToe.get('currentPlayer')).toEqual(originalPlayer);
+
+ });
+
+ });
+
+ describe('addTurn', function() {
+
+ it('should increment the games turns by 1', function() {
+ var gameTurns = testTicTacToe.get('turns');
+
+ testTicTacToe.addTurn();
+
+ expect(testTicTacToe.get('turns')).toEqual(gameTurns + 1);
+ });
+
+ });
+
+ describe('hasWon', function() {
+
+ it('should return FALSE if no one has won', function() {
+ // incomplete board (contains null)
+ expect(testTicTacToe.hasWon()).toBeFalsy();
+ // complete board (tie)
+ var tieGrid = [
+ ['X', 'O', 'X'],
+ ['O', 'X', 'O'],
+ ['O', 'X', 'O']
+ ];
+ var tieTicTacToe = new TicTacToe();
+
+ tieTicTacToe.get('board').set('grid', tieGrid);
+
+ expect(tieTicTacToe.hasWon()).toBeFalsy();
+ });
+
+ it('should not match nulls when evaluating markers for matches', function() {
+ expect(testTicTacToe.hasWon()).toBeFalsy();
+ });
+
+ it('should return TRUE if 3 markers match horizontally', function() {
+ var horizontalGrid = [
+ ['X', 'X', 'X'],
+ ['O', null, 'O'],
+ [null, null, null]
+ ];
+ var horizontalTicTacToe = new TicTacToe();
+
+ horizontalTicTacToe.get('board').set('grid', horizontalGrid);
+
+ expect(horizontalTicTacToe.hasWon()).toBeTruthy();
+ });
+
+ it('should return TRUE if 3 markers match vertically', function() {
+ var verticalGrid = [
+ ['X', 'O', 'X'],
+ ['X', 'O', 'O'],
+ ['X', null, null]
+ ];
+ var verticalTicTacToe = new TicTacToe();
+
+ verticalTicTacToe.get('board').set('grid', verticalGrid);
+
+ expect(verticalTicTacToe.hasWon()).toBeTruthy();
+ });
+
+ it('should return TRUE if 3 markers match diagonally', function() {
+ var diagonalBottomGrid = [
+ ['X', 'O', 'X'],
+ ['O', 'X', 'O'],
+ ['X', null, null]
+ ];
+ var diagonalBottomTicTacToe = new TicTacToe();
+
+ diagonalBottomTicTacToe.get('board').set('grid', diagonalBottomGrid);
+ expect(diagonalBottomTicTacToe.hasWon()).toBeTruthy();
+
+ var diagonalTopGrid = [
+ ['X', 'O', 'X'],
+ ['O', 'X', null],
+ ['O', null, 'X']
+ ];
+ var diagonalTopTicTacToe = new TicTacToe();
+
+ diagonalTopTicTacToe.get('board').set('grid', diagonalTopGrid);
+ expect(diagonalTopTicTacToe.hasWon()).toBeTruthy();
+ });
+
+ });
+
+ describe('changePlayers', function() {
+
+ it('change the current player to next player and back again', function() {
+ var originalPlayer = testTicTacToe.get('currentPlayer');
+
+ testTicTacToe.changePlayers();
+ expect(testTicTacToe.get('currentPlayer')).not.toEqual(originalPlayer);
+
+ testTicTacToe.changePlayers();
+ expect(testTicTacToe.get('currentPlayer')).toEqual(originalPlayer);
+
+
+ });
+
+ });
+
+});
diff --git a/src/app.js b/src/app.js
new file mode 100644
index 0000000..28bcc7a
--- /dev/null
+++ b/src/app.js
@@ -0,0 +1,18 @@
+import $ from 'jquery';
+
+import ApplicationView from 'app/views/application_view';
+import Games from 'app/collections/games';
+
+$(document).ready(function() {
+ var games = new Games({
+
+ });
+
+
+ var appView = new ApplicationView({
+ el: '#application',
+ model: games
+ });
+
+ appView.render();
+});
diff --git a/src/.keep b/src/app/collections/.keep
similarity index 100%
rename from src/.keep
rename to src/app/collections/.keep
diff --git a/src/app/collections/games.js b/src/app/collections/games.js
new file mode 100644
index 0000000..6509de4
--- /dev/null
+++ b/src/app/collections/games.js
@@ -0,0 +1,20 @@
+import Backbone from 'backbone';
+
+import TicTacToe from 'app/models/tictactoe';
+import Game from 'app/models/game';
+
+
+var Games = Backbone.Collection.extend({
+ model: Game,
+ game: new TicTacToe({}),
+ url: 'https://safe-mesa-21103.herokuapp.com/api/v1/games',
+ parse: function(data) {
+ return data;
+ }
+
+});
+
+module.exports = Games;
+
+// DO NOT REMOVE THIS
+export default Games;
diff --git a/src/app/models/board.js b/src/app/models/board.js
new file mode 100644
index 0000000..2de3a1d
--- /dev/null
+++ b/src/app/models/board.js
@@ -0,0 +1,26 @@
+import Backbone from 'backbone';
+
+const Board = Backbone.Model.extend({
+
+ defaults: {
+ grid : [
+ [null, null, null],
+ [null, null, null],
+ [null, null, null]
+ ]
+ },
+
+ initialize: function() {
+ this.set('grid', [
+ [null, null, null],
+ [null, null, null],
+ [null, null, null]
+ ]);
+ }
+
+});
+
+module.exports = Board;
+
+// DO NOT REMOVE THIS
+export default Board;
diff --git a/src/app/models/game.js b/src/app/models/game.js
new file mode 100644
index 0000000..d94c6b5
--- /dev/null
+++ b/src/app/models/game.js
@@ -0,0 +1,34 @@
+import Backbone from 'backbone';
+
+const Game = Backbone.Model.extend({
+// this model is a wrapper for the API that is formatted like the API is
+ initialize: function(options) {
+ this.board = options.board;
+ this.players = options.players;
+ this.marker = options.outcome;
+
+ switch (this.marker) {
+ case 'X':
+ this.outcome = 'Player1';
+ break;
+ case 'O':
+ this.outcome = 'Player2';
+ break;
+ case 'draw':
+ this.outcome = 'No one';
+ break;
+ default:
+ this.outcome = 'Unknown person';
+ break;
+ }
+
+ this.playet_at = options.played_at;
+
+ }
+
+});
+
+module.exports = Game;
+
+// DO NOT REMOVE THIS
+export default Game;
diff --git a/src/app/models/player.js b/src/app/models/player.js
new file mode 100644
index 0000000..cac3698
--- /dev/null
+++ b/src/app/models/player.js
@@ -0,0 +1,16 @@
+import Backbone from 'backbone';
+
+const Player = Backbone.Model.extend({
+
+ initialize: function(options) {
+ this.name = options.name;
+ this.marker = options.marker;
+
+ }
+
+});
+
+module.exports = Player;
+
+// DO NOT REMOVE THIS
+export default Player;
diff --git a/src/app/models/tictactoe.js b/src/app/models/tictactoe.js
new file mode 100644
index 0000000..2ad6f31
--- /dev/null
+++ b/src/app/models/tictactoe.js
@@ -0,0 +1,240 @@
+import Backbone from 'backbone';
+
+import Board from 'app/models/board';
+import Player from 'app/models/player';
+
+const TicTacToe = Backbone.Model.extend({
+ defaults: {
+ board: new Board(),
+ player1: new Player({ name: "Player1", marker: "X" }),
+ player2: new Player({ name: "Player2", marker: "O" }),
+ players: [],
+ turns: 0
+ },
+
+ initialize: function(options) {
+
+ var playersHash = [this.get('player1'), this.get('player2')];
+
+ this.set('players', playersHash);
+
+ this.set('board', new Board());
+
+ this.currentPlayer = 0;
+
+ var sample = function(array = [0,1]) {
+ var index = Math.floor ( Math.random() * array.length );
+ return array[index];
+ };
+
+ this.set('currentPlayer', sample());
+
+ // make an empty 'json object' for sending the completed game to the API
+ this.json = {};
+
+
+ },
+
+
+ playTurn: function(prompt) {
+ // A turn will:
+
+ // - know who the current player is
+
+ var player = this.get('players')[this.get('currentPlayer')];
+
+ // - prompt for placement
+ var placement = prompt;
+
+ // - check that the placement is valid
+ // - will return FALSE or valid placement position
+ if (this.isValidPlacement(placement) && !this.hasWon() ) {
+
+ // - update the board with a valid placement and players marker
+ this.updateBoard(placement, player.get('marker'));
+ } else {
+ // - if FALSE, reprompt/reclick
+ return false;
+ }
+
+ // - end the move
+ this.endMove();
+
+ // if outputResult is FALSE, the game continues. Otherwise, we return a string of the result of the game.
+ return this.outputResult(player);
+ },
+
+ outputResult: function(player) {
+
+ var playerName = player.name;
+ // - check if has won or if tie and report information
+
+ var result = "";
+ if (this.hasWon() || this.get('turns') == 9) {
+ result += "The Game is Over. ";
+ if(this.hasWon()) {
+ result += playerName + " has won!";
+ } else {
+ result += "You have tied.";
+ }
+ return result;
+ }
+
+ return false;
+ },
+
+ isValidPlacement: function(placement) {
+ // To be valid:
+ // - get the placement
+ // format of placement argument: [rowIndex, columnIndex]
+ // - check the board for valid placement
+ // - return FALSE if not valid
+ // - return the placement if valid
+
+ this.placement = placement;
+ this.row = this.placement[0];
+ this.column = this.placement[1];
+
+ var boardPosition = this.get('board').get('grid')[this.row][this.column];
+
+ if ( boardPosition === null ) {
+ return true;
+ } else {
+ return false;
+ }
+
+ },
+
+ updateBoard: function(boardPosition, marker) {
+ this.marker = marker;
+
+ this.boardPosition = boardPosition;
+ this.row = this.boardPosition[0];
+ this.column = this.boardPosition[1];
+
+ this.gridCopy = this.get('board').get('grid');
+
+ this.gridCopy[this.row][this.column] = this.marker;
+
+ this.get('board').set('grid', this.gridCopy);
+
+ return this.get('board');
+ },
+
+ endMove: function() {
+ // Ending the move will:
+ // - increment the turns counter by 1
+ // - check if the game has been won
+ // - switch current player
+
+ this.addTurn();
+ if (this.get('turns') >= 5 && !this.hasWon()) {
+ // only change players if hasWon is false after 5 turns
+ this.changePlayers();
+ } else if (this.get('turns') < 5) {
+ // for the first 4 turns, always changePlayers because there is no chance of victory
+ this.changePlayers();
+ }
+
+ },
+
+ addTurn: function() {
+ var endingValue = this.get('turns') + 1;
+ this.set('turns', endingValue);
+ },
+
+ hasWon: function() {
+ // grid[row][column]
+ var grid = this.get('board').get('grid');
+
+ // A horizontal match victory - all columns in same row are equal and none is null
+ for (var i = 0; i < 3; i++) {
+ if(grid[i][0] == grid[i][1] && grid[i][0] == grid[i][2] && grid[i][0] !== null){
+ return true;
+ }
+ }
+
+ // A vertical match victory - all rows in same column are equal and none is null
+ for (var j = 0; j < 3; j++) {
+ if(grid[0][j] == grid[1][j] && grid[0][j] == grid[2][j] && grid[0][j] !== null){
+ return true;
+ }
+ }
+
+ // A diagonal match victory - need to validate two cases:
+ // - starting in the bottom left
+ if(grid[2][0] == grid[1][1] && grid[2][0] == grid[0][2] && grid[2][0] !== null){
+ return true;
+ }
+ // - starting in the top left
+ if(grid[0][0] == grid[1][1] && grid[0][0] == grid[2][2] && grid[0][0] !== null){
+ return true;
+ }
+
+ return false;
+ },
+
+ changePlayers: function() {
+ this.set('currentPlayer', ((this.get('currentPlayer') === 0) ? 1 : 0));
+
+ },
+
+ getJson: function() {
+// {
+// "id": 1,
+// "board": [
+// "X",
+// " ",
+// "O",
+// "X",
+// "O",
+// " ",
+// "X",
+// " ",
+// " "
+// ],
+// "players": [
+// "X Player",
+// "O Player"
+// ],
+// "outcome": "X",
+// "played_at": "2016-11-20T22:59:10Z"
+// }
+
+ this.grid = this.get('board').get('grid');
+
+ this.jsonBoard = [].concat.apply([], this.grid);
+ // replace null
+ this.replaceNull(this.jsonBoard);
+
+ this.jsonPlayers = [
+ this.get('player1').get('name'), this.get('player2').get('name')
+ ];
+ this.jsonOutcome = (this.hasWon() ? this.get('players')[this.get('currentPlayer')].get('marker') : 'draw');
+ this.jsonPlayedAt = new Date(new Date().getTime());
+
+
+ this.json = {
+ "board": this.jsonBoard,
+ "players": this.jsonPlayers,
+ "outcome": this.jsonOutcome,
+ "played_at": this.jsonPlayedAt
+ };
+
+ return this.json;
+
+ },
+
+ replaceNull: function (arr) {
+ for (var i = 0, l = arr.length; i < l; i++) {
+ if (arr[i] === null) arr[i] = ' ';
+ }
+ return arr;
+ }
+
+});
+
+module.exports = TicTacToe;
+
+// DO NOT REMOVE THIS
+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..971c029
--- /dev/null
+++ b/src/app/views/application_view.js
@@ -0,0 +1,83 @@
+import Backbone from 'backbone';
+import BoardView from 'app/views/board_view';
+import PlayerView from 'app/views/player_view';
+import SquareView from 'app/views/square_view';
+import GamesView from 'app/views/games_view';
+
+// models
+import Board from 'app/models/board';
+import Player from 'app/models/player';
+import TicTacToe from 'app/models/tictactoe';
+
+const ApplicationView = Backbone.View.extend({
+ initialize: function() {
+
+ this.ticTacToe = this.model.game;
+
+ this.board = this.ticTacToe.get('board');
+ this.players = this.ticTacToe.get('players');
+
+ this.listenTo(this, 'change', this.render);
+
+ },
+
+ playTurn: function(marker) {
+ var isPlayable = this.ticTacToe.isValidPlacement(marker.position);
+ var lastTurn = this.ticTacToe.playTurn(marker.position);
+
+ if ( !isPlayable ) {
+ alert("Invalid move! Pls try again.");
+ } else if ( isPlayable && !lastTurn) {
+ this.trigger('change');
+ } else if ( lastTurn ) {
+ alert( lastTurn );
+ // send competeled game to the api
+ // create a new game
+
+ this.model.create(this.ticTacToe.getJson());
+ this.trigger('change');
+
+ }
+
+ this.trigger('change');
+ },
+
+ render: function() {
+ // we need to fetch up here for updates to show - idk why?
+ this.model.fetch();
+
+
+ const playerView = new PlayerView({
+ players: this.players,
+ el: this.$('#players'),
+ currentPlayer: this.ticTacToe.get('currentPlayer')
+ });
+
+ const boardView = new BoardView({
+ model: this.board,
+ el: this.$('main')
+ });
+
+ this.listenTo(boardView, 'squareSelected', this.playTurn);
+
+ playerView.render();
+ boardView.render();
+
+
+ const self = this;
+
+ this.model.fetch().done(function() {
+ const gamesView = new GamesView({
+ model: self.model,
+ el: self.$('body')
+ });
+
+ // gamesView.render();
+
+ });
+
+ 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..f51ead1
--- /dev/null
+++ b/src/app/views/board_view.js
@@ -0,0 +1,57 @@
+import _ from 'underscore';
+import Backbone from 'backbone';
+import SquareView from 'app/views/square_view';
+
+const BoardView = Backbone.View.extend({
+ initialize: function(options){
+ // we re-render the board when the model is updated (a square has been filled)
+ this.listenTo(this.model, 'change', this.render);
+ },
+
+ turn: function() {
+ // triggers an event
+ },
+
+ selectedSquare: function(marker) {
+ // we need to pass this up to the board which will update the model
+ this.trigger('squareSelected', marker);
+
+ // We return false to tell jQuery not to run any more event handlers.
+
+ return false;
+ },
+
+
+ render: function() {
+
+ const boardSquares = Backbone.$('#board-squares');
+ boardSquares.empty();
+ // loop within a loop - we need to have access to the larger this
+
+ const self = this;
+ this.grid = this.model.get('grid');
+
+ this.grid.forEach(function(row, index) {
+
+ var length = row.length;
+ for (var i = 0; i < length; i++) {
+ var column = i;
+ const square = new SquareView({
+ model: row[i],
+ position: [index, column],
+ tagName: 'li',
+ numberClass: index + i
+ });
+
+ self.listenTo(square, 'select', self.selectedSquare);
+ boardSquares.append(square.el).addClass('row small-up-3');
+ }
+
+ });
+
+ return this;
+ }
+
+});
+
+export default BoardView;
diff --git a/src/app/views/game_view.js b/src/app/views/game_view.js
new file mode 100644
index 0000000..defef4a
--- /dev/null
+++ b/src/app/views/game_view.js
@@ -0,0 +1,26 @@
+import _ from 'underscore';
+import Backbone from 'backbone';
+
+const GameView = Backbone.View.extend({
+ initialize: function(options){
+ },
+
+ events: {
+ 'click': 'onClick'
+ },
+
+ onClick: function(e) {
+ this.trigger('selectGame', this.model);
+
+ // We return false to tell jQuery not to run any more event handlers.
+
+ return false;
+ },
+
+ render: function() {
+ }
+
+
+});
+
+export default GameView;
diff --git a/src/app/views/games_view.js b/src/app/views/games_view.js
new file mode 100644
index 0000000..9bb9d80
--- /dev/null
+++ b/src/app/views/games_view.js
@@ -0,0 +1,45 @@
+import _ from 'underscore';
+import Backbone from 'backbone';
+
+import GameView from 'app/views/game_view';
+
+const GamesView = Backbone.View.extend({
+ initialize: function(options){
+ this.template = _.template(Backbone.$('#tmpl-results').html());
+ this.render();
+ },
+
+ selectedGame: function(game) {
+ console.log(game);
+ game.destroy();
+ this.render();
+ },
+
+ render: function() {
+ const gamesSquares = Backbone.$('#played-games');
+ gamesSquares.empty();
+
+
+ var games = this.model.models;
+ var length = games.length;
+
+ for (var i = 0; i < length; i++) {
+ var outcome = games[i].outcome;
+
+ const result = new GameView( {
+ outcome: outcome,
+ tagName: 'li',
+ model: games[i]
+ } );
+
+ this.listenTo(result, 'selectGame', this.selectedGame);
+
+ gamesSquares.prepend(result.$el.append(this.template({ results : outcome })));
+
+ }
+
+ }
+
+});
+
+export default GamesView;
diff --git a/src/app/views/player_view.js b/src/app/views/player_view.js
new file mode 100644
index 0000000..86f4f39
--- /dev/null
+++ b/src/app/views/player_view.js
@@ -0,0 +1,39 @@
+import _ from 'underscore';
+import Backbone from 'backbone';
+
+const PlayerView = Backbone.View.extend({
+ initialize: function(options){
+
+ this.players = options.players;
+
+ this.template = _.template(Backbone.$('#tmpl-player').html());
+
+ // helper function to wrap ugly random
+ // function sample(array) {
+ // var index = Math.floor ( Math.random() * array.length );
+ // return array[index];
+ // }
+
+ this.currentPlayerID = options.currentPlayer;
+
+ },
+
+
+
+ render: function() {
+
+ const playerSection = Backbone.$('#players');
+
+ this.currentPlayer = this.players[this.currentPlayerID];
+ this.currentPlayerTmpl = this.template(this.currentPlayer);
+
+ playerSection.html(this.currentPlayerTmpl);
+
+ this.currentPlayerID = ((this.currentPlayerID === 0) ? 1 : 0);
+
+ return this;
+ }
+
+});
+
+export default PlayerView;
diff --git a/src/app/views/square_view.js b/src/app/views/square_view.js
new file mode 100644
index 0000000..d89eeee
--- /dev/null
+++ b/src/app/views/square_view.js
@@ -0,0 +1,34 @@
+import _ from 'underscore';
+import Backbone from 'backbone';
+
+const SquareView = Backbone.View.extend({
+ initialize: function(options){
+
+ // clicks to tell the board to update itself
+ this.model = options.model;
+ this.position = options.position;
+ this.class = options.numberClass;
+
+ this.render();
+ },
+
+ events: {
+ 'click': 'onClick'
+ },
+
+ onClick: function(e) {
+ this.trigger('select', this);
+
+ // We return false to tell jQuery not to run any more event handlers.
+
+ return false;
+ },
+
+ render: function() {
+ this.$el.addClass('column').addClass(this.model + this.class);
+ return this;
+ }
+
+});
+
+export default SquareView;