Skip to content

Commit c179833

Browse files
committed
Made code more testable
-rewrote a lot of the validation code so that it's easier to test. -added grunt-exec task to run the app after linting/testing -updated tests. still needs a lot of work :(
1 parent 9eeca8e commit c179833

File tree

7 files changed

+226
-190
lines changed

7 files changed

+226
-190
lines changed

Gruntfile.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ module.exports = function (grunt) {
2121
it: true,
2222
_: true,
2323
$: true,
24-
__dirname: true
24+
__dirname: true,
25+
pubsubz: true
2526
},
2627
force: true
2728
},
@@ -39,11 +40,17 @@ module.exports = function (grunt) {
3940
src: ['public/js/soc.js'],
4041
dest: 'public/bundle.js'
4142
}
43+
},
44+
exec: {
45+
run: {
46+
cmd: 'node app'
47+
}
4248
}
4349
});
4450

4551
grunt.loadNpmTasks('grunt-contrib-jshint');
4652
grunt.loadNpmTasks('grunt-mocha-test');
4753
grunt.loadNpmTasks('grunt-browserify');
48-
grunt.registerTask('default', ['jshint', 'mochaTest', 'browserify']);
54+
grunt.loadNpmTasks('grunt-exec');
55+
grunt.registerTask('default', ['jshint', 'mochaTest', 'browserify', 'exec']);
4956
};

app.js

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var handleConnection = function (socket) {
3838
socket.emit('joinboard', boardId);
3939
} else {
4040
// invalid board Id
41+
socket.emit('error', 'Invalid board id.');
4142
}
4243
});
4344

@@ -59,6 +60,7 @@ var handleConnection = function (socket) {
5960
boards[1] = board;
6061
board.addUser(player);
6162
socket.emit('joinboard', boardId);
63+
// generate url that lets others join this game.
6264
});
6365

6466
socket.on('disconnect', function () {

app/board.js

+103-75
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,11 @@ Board.prototype.nextTurn = function () {
231231
self.distributeResources(tileIds[i]);
232232
}
233233
} else {
234-
self.moveRobber(player);
234+
self.moveRobber(player)
235+
.then(function (tileId) {
236+
// steal resource from player who has a settlement
237+
// adjacent to tileId
238+
});
235239
}
236240
self.startTurn(player);
237241
socket.emit('start');
@@ -412,109 +416,115 @@ Board.prototype.yearOfPlentyCard = function (player, resourceType) {
412416
};
413417

414418
/**
415-
* placement methods must trigger propagate event.
419+
* returns true if player is done with initial setup.
420+
* @param {string} playerId
416421
*/
417-
Board.prototype.isValidSettlement = function (playerId, intersectionId) {
418-
// Check to see if this is a valid settlement location.
419-
var self = this,
420-
intersections = components.intersections,
421-
settlements = self.settlements,
422-
roads = self.roads.byPlayerId(playerId),
423-
intersection = utils.getTileIdsFromIntersectionId(intersectionId),
424-
isLegal;
425-
if (intersections.indexOf(intersectionId) < 0) {
426-
return;
427-
}
428-
429-
// check to see if location is legal
430-
// - location is not occupied
431-
// - at least 2 roads away from any other settlement.
432-
isLegal = settlements.every(function (settlement) {
422+
Board.prototype.isSetupComplete = function (playerId) {
423+
// to see if player has completed setup, check to see they have placed
424+
// at least 2 roads.
425+
return this.roads.byPlayerId(playerId).length >= 2;
426+
};
427+
428+
Board.prototype.hasConnectedRoad = function (playerId, intersectionId) {
429+
var roads = this.roads.byPlayerId(playerId);
430+
return roads.some(function (road) {
431+
var edge = road.edge;
432+
if (edge[0] === intersectionId || edge[1] === intersectionId) {
433+
return true;
434+
}
435+
});
436+
};
437+
438+
Board.prototype.isIntersection = function (intersectionId) {
439+
var intersections = components.intersections;
440+
return intersections.indexOf(intersectionId) >= 0;
441+
};
442+
443+
/**
444+
* returns true if there is not a settlement on the given intersection and
445+
* there are no settlements < 2 roads away.
446+
* @param {string} intersectionId
447+
*/
448+
Board.prototype.isIntersectionOccupied = function (intersectionId) {
449+
var settlements = this.settlements,
450+
intersection = utils.getTileIdsFromIntersectionId(intersectionId);
451+
return settlements.some(function (settlement) {
433452
var otherIntersection = utils.getTileIdsFromIntersectionId(settlement.intersectionId),
434453
count = 0;
435454
otherIntersection.forEach(function (point) {
436455
if (intersection.indexOf(point) >= 0) {
437456
count++;
438457
}
439458
});
440-
return count < 2;
441-
});
442-
if (!isLegal) {
443-
return;
444-
}
445-
if (settlements.byPlayerId(playerId).length < 2) {
446-
// if we're in the setup phase, don't have the requirement that settlement
447-
// must touch road.
448-
return true;
449-
}
450-
// - player has a road that is connected
451-
isLegal = roads.some(function (road) {
452-
var edge = road.edge;
453-
if (edge[0] === intersectionId || edge[1] === intersectionId) {
454-
return true;
455-
}
459+
return count >= 2;
456460
});
457-
if (!isLegal) {
458-
return;
459-
}
460-
return true;
461461
};
462462

463-
Board.prototype.isValidRoad = function (playerId, edge) {
464-
var intersections = components.intersections,
465-
roads = this.roads,
466-
startId = edge[0],
467-
endId = edge[1],
468-
startIntersection = utils.getTileIdsFromIntersectionId(startId),
469-
endIntersection = utils.getTileIdsFromIntersectionId(endId),
470-
isLegal;
463+
/**
464+
* checks to see if player can build a settlement at given intersection
465+
* @param {string} playerId
466+
* @param {string} intersectionId
467+
*/
468+
Board.prototype.isValidSettlement = function (playerId, intersectionId) {
469+
var self = this;
470+
return self.isIntersection(intersectionId) &&
471+
!self.isIntersectionOccupied(intersectionId) &&
472+
(self.isSetupComplete(playerId) ?
473+
self.hasConnectedRoad(playerId, intersectionId) : true);
474+
};
471475

472-
if (intersections.indexOf(startId) < 0 || intersections.indexOf(endId) < 0) {
473-
return;
474-
}
476+
Board.prototype.isEdge = function (edge) {
475477
// Make sure start and end positions are one edge length apart.
476-
var count = 2;
478+
if (!this.isIntersection(edge[0]) || !this.isIntersection(edge[1])) {
479+
return false;
480+
}
481+
var count = 2,
482+
startIntersection = utils.getTileIdsFromIntersectionId(edge[0]),
483+
endIntersection = utils.getTileIdsFromIntersectionId(edge[1]);
477484
startIntersection.forEach(function (tileId) {
478485
if (endIntersection.indexOf(tileId) >= 0) {
479486
count--;
480487
}
481488
});
482-
if (count) { return; }
489+
return !count;
490+
};
483491

484-
// - not already an existing road here.
485-
isLegal = roads.every(function (road) {
492+
Board.prototype.isEdgeOccupied = function (edge) {
493+
var roads = this.roads,
494+
startId = edge[0],
495+
endId = edge[1];
496+
497+
return roads.some(function (road) {
486498
var otherEdge = road.edge,
487499
intersectionId = otherEdge[0];
488500
if (startId === intersectionId || endId === intersectionId) {
489501
intersectionId = otherEdge[1];
490502
if (startId === intersectionId || endId === intersectionId) {
491-
return false;
503+
return true;
492504
}
493505
}
494-
return true;
495506
});
496-
if (!isLegal) { return; }
497-
498-
// check to see if location is legal
499-
// - connecting to another road / settlement
500-
roads = roads.byPlayerId(playerId);
501-
if (roads.length >= 2) {
502-
isLegal = roads.some(function (road) {
503-
var otherEdge = road.edge,
504-
intersectionId = otherEdge[0];
505-
if (intersectionId === startId || intersectionId === endId) {
506-
return true;
507-
}
508-
intersectionId = otherEdge[1];
509-
if (intersectionId === startId || intersectionId === endId) {
510-
return true;
511-
}
512-
});
513-
if (!isLegal) { return; }
514-
}
515-
return true;
516507
};
517508

509+
/**
510+
* check to see if player can build a road on the given edge.
511+
* @param {string} playerId
512+
* @param {array} edge
513+
*/
514+
Board.prototype.isValidRoad = function (playerId, edge) {
515+
var self = this;
516+
return self.isEdge(edge) &&
517+
!self.isEdgeOccupied(edge) &&
518+
(self.isSetupComplete(playerId) ?
519+
self.hasConnectedRoad(playerId, edge[0]) || self.hasConnectedRoad(playerId, edge[1]) :
520+
true);
521+
};
522+
523+
/**
524+
* check to see if player can build a city on given intersection
525+
* @param {string} playerId
526+
* @param {string} intersectionId
527+
*/
518528
Board.prototype.isValidCity = function (playerId, intersectionId) {
519529
var settlements = this.settlements.byPlayerId(playerId);
520530
return settlements.some(function (settlement) {
@@ -579,7 +589,25 @@ Board.prototype.initResourceValues = function (resourceMap) {
579589
return diceMap;
580590
};
581591

592+
/**
593+
* prompt player to choose robber location
594+
* @param {object} player
595+
*/
582596
Board.prototype.moveRobber = function (player) {
597+
// return a promise. when we receive client response, resolve promise
598+
var self = this,
599+
deferred = Q.defer();
600+
player.socket.emit('moverobber');
601+
player.socket.once('moverobber', function (tileId) {
602+
tileId = +tileId;
603+
if (tileId >= 0 && tileId <= 18) {
604+
self.robber = tileId;
605+
deferred.resolve(tileId);
606+
} else {
607+
deferred.reject(new Error('Invalid robber location'));
608+
}
609+
});
610+
return deferred.promise;
583611
};
584612

585613
Board.prototype.rollDice = function () {

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"grunt-contrib-jshint": "~0.6.4",
2020
"grunt-mocha-test": "~0.7.0",
2121
"browserify": "~2.33.1",
22-
"grunt-browserify": "~1.2.8"
22+
"grunt-browserify": "~1.2.8",
23+
"grunt-exec": "~0.4.2"
2324
},
2425
"bugs": {
2526
"url": "https://github.com/kevnchu/catan/issues"

0 commit comments

Comments
 (0)