Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emily's tic-tac-toe #20

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b185413
imports objects from game
RedSquirrelious Dec 12, 2016
ce0f9f5
export objects, set Game and Player method names
RedSquirrelious Dec 12, 2016
7c9d8d5
import objects for real
RedSquirrelious Dec 12, 2016
fc139fa
did first few tests for Game class
eabrash Dec 13, 2016
756080e
added error test for setCurrentPlayer
RedSquirrelious Dec 13, 2016
ac98f7a
added error for setCurrentPlayer, created method setCurrentPlayer
RedSquirrelious Dec 13, 2016
6083e6b
split objects into separate files, this one now just game
RedSquirrelious Dec 13, 2016
8817e26
importing objects from correct files
RedSquirrelious Dec 13, 2016
833b6dd
player has own file
RedSquirrelious Dec 13, 2016
02ef894
board has own file
RedSquirrelious Dec 13, 2016
2780d3a
scoreboard has own file
RedSquirrelious Dec 13, 2016
b73dcfa
made Board proper function
RedSquirrelious Dec 13, 2016
ca8aa05
Built out most of Player tests
eabrash Dec 13, 2016
385eb43
added Board specs
RedSquirrelious Dec 13, 2016
7e7b514
added setMarkAtPosition method, clarified board attribute
RedSquirrelious Dec 13, 2016
027c46f
Added calculation of winning logic - mostly
eabrash Dec 13, 2016
ebe6841
Added small tests for winning function - not complete
eabrash Dec 13, 2016
9c25614
added row and column win checks
RedSquirrelious Dec 13, 2016
88c35b6
added tests for checkWinStatus
RedSquirrelious Dec 13, 2016
986da73
added attribute playCounter
RedSquirrelious Dec 13, 2016
11a374a
import Board and Player, uncommented board attribute, added win and d…
RedSquirrelious Dec 13, 2016
b944d69
tests for Player chooseSquare method
RedSquirrelious Dec 13, 2016
9deba5a
added chooseSquare method
RedSquirrelious Dec 13, 2016
f248ff9
working on toggling turns
RedSquirrelious Dec 14, 2016
3f89755
added semicolon that was breaking everything
RedSquirrelious Dec 14, 2016
8ffc81b
Merge pull request #1 from RedSquirrelious/ea-sk-test-game-players
RedSquirrelious Dec 14, 2016
ad2090e
Added a Game model and edited views to use a GameView view
eabrash Dec 19, 2016
67e073d
Basic gameplay functionality working - draw not called early, games n…
eabrash Dec 19, 2016
7e006fb
Showing recently played games
eabrash Dec 20, 2016
6fa51e0
Basically working with collections
eabrash Dec 21, 2016
f86c5b4
Pretty much working, but lacks tests - scoreboard now functional
eabrash Dec 21, 2016
eb8402a
Added tests for Game model
eabrash Dec 21, 2016
772bf25
Pretty much finished using scoreboard - but does not meet collection-…
eabrash Dec 22, 2016
7f317ab
Added previous games list and delete feature
eabrash Dec 23, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions build/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,80 @@
<head>
<title>Tic-Tac-Toe</title>
<meta charset="utf-8">
<link rel="stylesheet" href="css/foundation.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>

<div id="application">

<header class ="row">
<h1 class="column">Tic-Tac-Toe: Ultimate Tournament</h1>
</header>

<section class="row">

<div class="column new-game-form small-6">
<h3>Who's playing?</h3>
<label>
Player 1
<input type="text" name="player1">
</label>
<label>
Player 2
<input type="text" name="player2">
</label>
<button class="button start-game-button">Start New Game</button>
<p id="error"></p>
</div>

<div class="column scoreboard small-6">
<h3>Top scorers</h3>
<div class="top-scorers-div">
<ol id="top-scorers">
</ol>
</div>
</div>

</section>

<section class="row hidden" id="board">
<div class="column">
<h3 id="player-prompt"></h3>

<table id="board">
<tr>
<td id="0"><h3></h3></td>
<td id="1"><h3></h3></td>
<td id="2"><h3></h3></td>
</tr>
<tr>
<td id="3"><h3></h3></td>
<td id="4"><h3></h3></td>
<td id="5"><h3></h3></td>
</tr>
<tr>
<td id="6"><h3></h3></td>
<td id="7"><h3></h3></td>
<td id="8"><h3></h3></td>
</tr>
</table>
</div>
</section>

</div>

<section class="row">
<div class="column previous-games small-12">
<h3>Previous games</h3>
<div class="past-games-div">
<ol id="past-games">
</ol>
</div>
</div>
</section>


<script src="/app.bundle.js" charset="utf-8"></script>
</body>
</html>
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"babel-register": "^6.18.0",
"backbone": "^1.3.3",
"jasmine": "^2.5.2",
"jasmine-ajax": "^3.3.1",
"jasmine-expect": "^3.0.1",
"jquery": "^3.1.1",
"webpack": "^1.13.3",
Expand Down
184 changes: 184 additions & 0 deletions spec/game.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import Game from 'app/models/game';

describe("Game", function() {

var game;
beforeEach(function() {
game = new Game();
});

describe('initialize', function(){

it('a new game not given parameters should have the appropriate default values', function(){
expect(game.get("players")).toEqual(["Player1", "Player2"]);
expect(game.get("board")).toEqual([" ", " ", " ", " ", " ", " ", " ", " ", " "]);
expect(game.get("outcome")).toEqual("in progress");
});

it('a new game given player names should contain the correct names', function(){
game = new Game({"players": ["Satine", "Lottie"]});
expect(game.get("players")).toEqual(["Satine", "Lottie"]);
});

});

describe('pickStartingPlayer', function(){

it('pickStartingPlayer sets players to an array of length 2', function(){
game.pickStartingPlayer();
expect(game.get("players")).toBeArray();
expect(game.get("players").length).toEqual(2);
});

it('both players are still present after pickStartingPlayer is called', function(){
game = new Game({"players": ["Satine", "Lottie"]});
game.pickStartingPlayer();
expect(game.get("players")).toContain("Satine");
expect(game.get("players")).toContain("Lottie");
});

});

describe('currentPlayer', function(){

it('currentPlayer returns player 1 (first in players array) for a brand-new game', function(){
game.pickStartingPlayer();
expect(game.currentPlayer()).toEqual(game.get("players")[0]);
});

it('currentPlayer alternates as marks are placed on the board', function(){
game.pickStartingPlayer();
expect(game.currentPlayer()).toEqual(game.get("players")[0]);
game.setSquare(0,0);
expect(game.currentPlayer()).toEqual(game.get("players")[1]);
game.setSquare(0,1);
expect(game.currentPlayer()).toEqual(game.get("players")[0]);
game.setSquare(0,2);
expect(game.currentPlayer()).toEqual(game.get("players")[1]);
});

});

describe('setSquare', function(){

it('sets a square and returns true if the square is empty and valid', function(){
var result = game.setSquare(0,0);
expect(result).toEqual(true);
expect(game.get("board")).toEqual(["X", " ", " ", " ", " ", " ", " ", " ", " "]);
result = game.setSquare(2,2);
expect(result).toEqual(true);
expect(game.get("board")).toEqual(["X", " ", " ", " ", " ", " ", " ", " ", "O"]);
});

it('returns false if attempting to set a square that is already filled', function(){
game.setSquare(0,0);
var result = game.setSquare(0,0);
expect(result).toEqual(false);
});

it('returns false if attempting to set a square that is not on the board', function(){
var result = game.setSquare(-1,-1);
expect(result).toEqual(false);
var result = game.setSquare(0,4);
expect(result).toEqual(false);
});

});

describe('hasBeenWon', function(){

// Will not call wins before all marks have been placed, even if a win is inevitable (e.g., only one space is left on the
// board and X will win by filling that space)

it('a blank game has not been won yet', function(){
expect(game.hasBeenWon()).toEqual(false);
expect(game.get("outcome")).toEqual("in progress");
});

it('a game with six plays in a non-winning configuration has not been won yet', function(){
game.set("board", ["X", "O", " ", "O", "X", " ", "X", "O", " "]);
expect(game.hasBeenWon()).toEqual(false);
expect(game.get("outcome")).toEqual("in progress");
});

it('a game with three identical marks in a column has been won (by the mark type that is three-in-a-column)', function(){
game.set("board", ["X", "O", " ", "X", "O", " ", "X", " ", " "]);
expect(game.hasBeenWon()).toEqual("X");
expect(game.get("outcome")).toEqual("X");
game.set("board", ["X", "O", "X", " ", "O", " ", "X", "O", " "]);
expect(game.hasBeenWon()).toEqual("O");
expect(game.get("outcome")).toEqual("O");
game.set("board", [" ", "O", "X", " ", "O", "X", " ", " ", "X"]);
expect(game.hasBeenWon()).toEqual("X");
expect(game.get("outcome")).toEqual("X");
});

it('a game with three identical marks in a row has been won (by the mark type that is three-in-a-row)', function(){
game.set("board", ["X", "X", "X", "O", "O", " ", " ", " ", " "]);
expect(game.hasBeenWon()).toEqual("X");
expect(game.get("outcome")).toEqual("X");
game.set("board", [" ", "X", "X", "O", "O", "O", "X", " ", " "]);
expect(game.hasBeenWon()).toEqual("O");
expect(game.get("outcome")).toEqual("O");
game.set("board", [" ", " ", " ", "O", "O", " ", "X", "X", "X"]);
expect(game.hasBeenWon()).toEqual("X");
expect(game.get("outcome")).toEqual("X");
});

it('a game with three identical marks on the diagonal has been won (by the mark type that occupies the diagonal)', function(){
game.set("board", ["X", "O", " ", "O", "X", " ", " ", " ", "X"]);
expect(game.hasBeenWon()).toEqual("X");
expect(game.get("outcome")).toEqual("X");
game.set("board", ["X", " ", "O", " ", "O", "X", "O", "X", " "]);
expect(game.hasBeenWon()).toEqual("O");
expect(game.get("outcome")).toEqual("O");
});

it('a game with a full board in a non-winning configuration has not been won', function(){
game.set("board", ["O", "O", "X", "X", "X", "O", "O", "X", "X"]);
expect(game.hasBeenWon()).toEqual(false);
expect(game.get("outcome")).toEqual("in progress");
});

it('a game with a full board in a winning configuration has been won', function(){
game.set("board", ["X", "X", "X", "O", "X", "O", "X", "O", "O"]);
expect(game.hasBeenWon()).toEqual("X");
expect(game.get("outcome")).toEqual("X");
})

});

describe('isADraw', function(){

// Will not call a draw until the board is completely full, even if there is no way for either
// player to win give the remaining turns and spaces

it('a blank game is not a draw', function(){
expect(game.isADraw()).toEqual(false);
expect(game.get("outcome")).toEqual("in progress");
});

it('a game with eight plays in a non-winning configuration is not a draw', function(){
game.set("board", ["X", "O", "X ",
"O", "X", "X",
" ", "O", "O"]);
expect(game.hasBeenWon()).toEqual(false);
expect(game.get("outcome")).toEqual("in progress");
});

it('a game with nine plays in a non-winning configuration is a draw', function(){
game.set("board", ["X", "O", "X", "X", "O", "O", "O", "X", "X"]);
expect(game.isADraw()).toEqual(true);
expect(game.get("outcome")).toEqual("draw");
});

it('a game that has been won is not a draw, even if the board is full)', function(){
game.set("board", ["X", "X", "X", "O", "X", "O", "X", "O", "O"]);
expect(game.hasBeenWon()).toEqual("X");
expect(game.isADraw()).toEqual(false);
expect(game.get("outcome")).toEqual("X");
});

});

});
37 changes: 37 additions & 0 deletions spec/gameslist.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// *** NOT WORKING - approach would be to use Sinon to mock responses, but haven't
// figured out details. Making direct calls to the API does not work from here in
// the testing section. This seems slightly out of scope since we did not cover API
// testing with Backbone in class. Instructions to get Sinon working at:
// https://tinnedfruit.com/2011/03/03/testing-backbone-apps-with-jasmine-sinon.html

// import Game from 'app/models/game';
// import GamesList from 'app/collections/games';
// import $ from 'jquery';
//
// describe("GamesList", function() {
//
// var gamesList = new GamesList();
// gamesList.fetch();
//
// describe('initialize/defaults', function(){
//
// it('the gamesList contains Game objects', function(){
// gamesList.each(function(model){
// expect(typeOf(model)).toEqual("Game");
// });
// });
//
// it('the gamesList does not contain incomplete games', function(){
// gamesList.each(function(model){
// expect(model.outcome == "in progress").toBeFalsy();
// });
// });
//
// // it('...', function(){
// //
// // });
//
// });


});
5 changes: 5 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ApplicationView from 'app/views/application_view';

var appView = new ApplicationView({
el: 'body'
});
81 changes: 81 additions & 0 deletions src/app/collections/games.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import Backbone from 'backbone';
import Game from 'app/models/game'

const GamesList = Backbone.Collection.extend({
model: Game,
url: 'http://localhost:3000/api/v1/games',
upToTopN: function(n){
var playersAndTheirWins = {}

// If one player wins, they get +1 point, and the losing player gets -1 point.
// If two players tie, they both get 0 points (nothing happens).
// This section of code creates a big hashtable where each player's name is a key and
// their score (computed from all their games) is the corresponding value.

this.each(function(model){
if (model.get("outcome") == "X"){
if (playersAndTheirWins[model.get("players")[0]] == undefined){
playersAndTheirWins[model.get("players")[0]] = 1;
} else {
playersAndTheirWins[model.get("players")[0]] += 1;
}
if (playersAndTheirWins[model.get("players")[1]] == undefined){
playersAndTheirWins[model.get("players")[1]] = -1;
} else {
playersAndTheirWins[model.get("players")[1]] -= 1;
}
} else if (model.get("outcome") == "O") {
if (playersAndTheirWins[model.get("players")[1]] == undefined){
playersAndTheirWins[model.get("players")[1]] = 1;
} else {
playersAndTheirWins[model.get("players")[1]] += 1;
}
if (playersAndTheirWins[model.get("players")[0]] == undefined){
playersAndTheirWins[model.get("players")[0]] = -1;
} else {
playersAndTheirWins[model.get("players")[0]] -= 1;
}
} else if (model.get("outcome") == "draw") {
if (playersAndTheirWins[model.get("players")[1]] == undefined){
playersAndTheirWins[model.get("players")[1]] = 0;
}
if (playersAndTheirWins[model.get("players")[0]] == undefined){
playersAndTheirWins[model.get("players")[0]] = 0;
}
}
});

// Next, we take the hashtable and convert it into an array of small objects, each of which
// stores a name and a score. This conversion facilitates sorting of the players by score.

var arrayOfScoreObjects = [];

// Documentation of getOwnPropertyNames:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames

for (var i = 0; i < Object.getOwnPropertyNames(playersAndTheirWins).length; i++){
var miniObject = {};
console.log(Object.getOwnPropertyNames(playersAndTheirWins)[i]);
miniObject["name"] = Object.getOwnPropertyNames(playersAndTheirWins)[i];
miniObject["score"] = playersAndTheirWins[Object.getOwnPropertyNames(playersAndTheirWins)[i]];
arrayOfScoreObjects.push(miniObject);
}

// Sort the players by score in descending order
// Sort based on: http://stackoverflow.com/questions/979256/sorting-an-array-of-javascript-objects

var sorted = arrayOfScoreObjects.sort(function(a, b) {
return parseFloat(b.score) - parseFloat(a.score);
})

var max = Math.min(n, sorted.length);

console.log("Sorted, sliced array of objects:");
console.log(sorted.slice(0, max));

return sorted.slice(0, max);

}
});

export default GamesList;
Loading