Pawns and Tiles: concerning the pawns.

sapper_p2The pawn is central in the game play of Pawns And Tiles.
Every pawn of the game has his own specificity, but even a lot of common behavior.

At this moment you can choose among 6 pawns: Knight, Paladin, Pretorian, Sapper, Scout and Sniper.

All these pawns inherit from a generic class pawn.
I tried to be consistent and to apply the SRP so my class pawn has this public methods:

  • CheckStatus(status)
  • CheckStatusSetBy(status, pawnId)
  • CheckStatusRecurrencySetBy(status, pawnId)
  • CheckStatusRecurrence(status)
  • ClearStatus()
  • EndMove()
  • GetId()
  • GetClassName()
  • GetLastMove()
  • GetMovesDone()
  • GetMovesDoneIncludingStart()
  • GetName()
  • GetOriginalPosition()
  • GetPosition()
  • GetRemainingMoves()
  • GetSkills()
  • GetSkillById(id)
  • GetSkillByName(name)
  • GetStatistics()
  • GetStatus(status)
  • Heal(amount)
  • IsPlayable()
  • Move(coords, cost)
  • ReceiveDamage(damage)
  • RemoveStatus(status, numberOfTimes)
  • RemoveStatusSetBy(status, pawnId, numberOfTimes)
  • ResetMove()
  • SetStatus(status)
  • SetToMove()
  • UndoMove()

About Move:

What I tried to achieve was to have a pawn reusable with different set of rules so I decided that the pawn itself apply very soft control about moving and I gave to the MapService the responsibility to apply the game rules.
In this way I can use the same pawn object within different services that apply different game set.

It means that the only responsibility of the pawn about moving is to verify to receive  a Coordinates object, that the coordinates received are not the same coordinates where it is, to verify if it has remaining move for the turn and if the cost of the move is affordable.

Things like: there is a wall in the specific tile it wants to move, or there is another pawn in the tile, etc etc are all up to the MapService.

The Coordinates object is responsible to verify that x, y are both numbers and greater than 0.

About Statuses:

Every pawn can have a status set by default or can receive a status during the game. A status is not necessarily permanent. It’s possible for a pawn not to have a status.
The rules about a specific status are not applied by the Pawn: the pawn only stores a collection of its statuses.

The existing statuses are: onmove, defend, dead, moved, slowed, used, usedfree, dodging, parry.

About Dead:

Dead is a status very easy to have for a pawn. A pawn, like in the chess game, is powerful and vulnerable at the same time. When the hp of a pawn reaches 0 or less the pawn gains the status of “dead”. A “dead” pawn is removed from the board and all the statuses he applied to other pawns via skills are removed as well.

At the begin of every turn if a player has less than 5 pawns the game allows him to put on the board new pawns accordingly with the rules.

About Skills:

Every pawn has skills. Except the Pawn class. the Pawn class is supposed to be as an abstract class so is not playable.
A Pawn only carries the information of a skill, another service is used to apply the rules.

The existing skills are: combat, slash, parry, holy slash, heal, assault, block, firebomb, stab, rush, strike, dodge.
The move action is treated as a skill even if it has its own rules.

Every pawn has “move“, “combat” plus two.

About Game rules:

The pawn has no game logic and it doesn’t know about the game rules. Using its method directly is not the correct workflow to proceed: all the calls to a method should be performed via services that own the rule of the game.

Like for the move action, the pawn only verifies the minimum to be consistent, so, for instance, in case of “dead” status, the pawn is not able to determinate if the request to be “dead” is valid or not, the correct service will set the status if the conditions are fully respected.

About Test:

Obviously I wrote tests about the pawns, not so many honestly, at that time I was not so good in TDD.
At that time I was using QUnit.

module("A generic Pawn", {
	setup: function () {
		moves = [[5, 10], [4, 12], [2, 10], [1, 10], [3, 23]];
		pawn = new Pawn("fakeid");
		pawn.SetToMove();
		pawnFactory = new PawnsFactory();
	}
});
test("it should not be playable", function () {
	equal(pawn.IsPlayable(), false);
});

test("it should be possible set an Id in the constructor", function () {
	var newPawn = pawnFactory.CreateByClassName(PawnType.KNIGHT, "xxxx");
	equal(newPawn.GetId(), "xxxx");
});

test("it should be possible not to set an Id in the constructor", function () {
	var newPawn = pawnFactory.CreateByClassName(PawnType.KNIGHT);
	var guid = newPawn.GetId();

	notEqual(guid.search(/^tmp/), -1);
});

test("it should be possible get the name of a pawn", function () {
	var newPawn = new Pawn("fakeid");
	equal(newPawn.GetName(), "pawn");
});

test("it should have hit points and a number of move", function () {
	var statistics = pawn.GetStatistics();

	notEqual(typeof statistics.moves, "undefined");
	notEqual(typeof statistics.hp, "undefined");
});

module("about moving", {
	setup: function () {
		moves = [[5, 10], [4, 12], [2, 10], [1, 10], [3, 23]];
		pawn = new Pawn("fakeid");
		pawn.SetToMove();
		pawnFactory = new PawnsFactory();
	}
});

test("it should be possible to move only when the pawn is 'onmove' status", function () {
	var pawnLocal = new Pawn("fakeid");
	var coords = new Coords(5, 10);
	throws(function () { pawnLocal.Move(coords) }, "Pawn status is not move");

	pawnLocal.SetToMove();
	pawnLocal.Move(coords);
	var getCoords = pawnLocal.GetPosition();
	deepEqual(getCoords, coords);
});

test("it should be possible to setMove only if there's only one 'moved' status and none 'used' status", function () {
	var pawnLocal = pawnFactory.CreateByClassName("Pretorian");
	pawnLocal.SetToMove();
	pawnLocal.Move(new Coords(0, 0));
	pawnLocal.Move(new Coords(1, 1));

	equal(pawnLocal.GetRemainingMoves(), 3);

	pawnLocal.EndMove();

	pawnLocal.SetToMove();
	equal(pawnLocal.GetRemainingMoves(), 5);
	pawnLocal.Move(new Coords(2, 1));
	equal(pawnLocal.GetRemainingMoves(), 4);
	pawnLocal.EndMove();

	throws(function () { pawnLocal.SetToMove() }, "a throw");
});

test("it should be able to move", function () {
	var coords = new Coords(20, 10);
	pawn.Move(coords);
	var getcoords = pawn.GetPosition();
	equal(getcoords, coords);
});

test("it should decrease the remaining moves when moved", function () {
	var c = new Coords(2, 2);
	var remainingMoves = pawn.GetRemainingMoves();
	equal(remainingMoves, 5);
	pawn.Move(c);
	remainingMoves = pawn.GetRemainingMoves();
	equal(remainingMoves, 4);
});

test("it should not be possible to move without remaining moves, an exception will be thrown", function () {
	pawn.Move(new Coords(10, 10));
	pawn.Move(new Coords(10, 11));
	pawn.Move(new Coords(10, 12));
	pawn.Move(new Coords(10, 13));
	pawn.Move(new Coords(10, 14));
	throws(function () { pawn.Move(new Coords(10, 15)) }, "No more moves");
});

test("it should not be possible to move at the same coordinates it has, an exception will be thrown", function () {
	var c = new Coords(1, 1);
	pawn.Move(c);
	throws(function () { pawn.Move(c) }, "Invalid move");
});

test("it should know all moves done", function () {
	for (var i = 0; i < moves.length; i++) {
		pawn.Move(new Coords(moves[i][0], moves[i][1]));
	}

	var movesDone = pawn.GetMovesDone();
	for (var i = 0; i < movesDone.length; i++) {
		equal(movesDone[i].x, moves[i][0]);
		equal(movesDone[i].y, moves[i][1]);
	}
});

test("it should be possible undo a move", function () {
	for (var i = 0; i < moves.length; i++) {
		pawn.Move(new Coords(moves[i][0], moves[i][1]));
	}
	pawn.UndoMove();
	var coords = pawn.GetPosition();
	equal(coords.x, 1);
	equal(coords.y, 10);
});

test("it should be possible reset the moves", function () {
	for (var i = 0; i < moves.length; i++) {
		pawn.Move(new Coords(moves[i][0], moves[i][1]));
	}
	pawn.ResetMove();

	var coords = pawn.GetPosition();
	equal(coords.x, 0);
	equal(coords.y, 0);
});

test("it should be possible to know how many moves are remaining", function () {
	var remainingMoves = pawn.GetRemainingMoves();

	ok(remainingMoves > -1);
	equal(typeof (remainingMoves), "number");
});

module("about health", {
	setup: function () {
		moves = [[5, 10], [4, 12], [2, 10], [1, 10], [3, 23]];
		pawn = new Pawn("fakeid");
		pawn.SetToMove();
		pawnFactory = new PawnsFactory();
	}
});
test("it should get damage", function () {
	var hp = pawn.GetStatistics().hp
		, startHP = hp;
	pawn.ReceiveDamage();
	hp = pawn.GetStatistics().hp;

	equal(hp, startHP - 1);
});

test("it should get heal", function () {
	var hp = pawn.GetStatistics().hp
		, startHP = hp;
	pawn.ReceiveDamage();
	hp = pawn.GetStatistics().hp;

	equal(hp, startHP - 1);
	pawn.Heal(1);
	equal(pawn.GetStatistics().hp, startHP);
	pawn.Heal(1);
	equal(pawn.GetStatistics().hp, startHP);
});

test("it should have status 'dead' if it reaches <= 0 hp", function () {
	pawn.ReceiveDamage(3);
	var status = pawn.GetStatus(Status.DEAD);
	deepEqual(status, [{ name: Status.DEAD, by: "" }], JSON.stringify(status));
});

test("it should be possible to be notified when a pawn is in 'dead' status", function () {
	var notifier = Backbone.Model.extend();

	var mine = new notifier();
	var idDeadPawn = "";
	mine.observer.on("pawn:dead", function (e) { idDeadPawn = e._id; });

	pawn.ReceiveDamage(3);

	equal(idDeadPawn, pawn._id);
});

module("about skills and status", {
	setup: function () {
		moves = [[5, 10], [4, 12], [2, 10], [1, 10], [3, 23]];
		pawn = new Pawn("fakeid");
		pawn.SetToMove();
		pawnFactory = new PawnsFactory();
	}
});
test("it should have a 'combat' skill", function () {
	var skills = pawn.GetSkills();
	var skillFound = false;

	for (var i = 0; i < skills.length; i++) {
		if (skills[i].GetName() === "combat") {
			skillFound = true;
			break;
		}
	}

	ok(skillFound);
});

test("it should be possible get a skill by Name", function () {
	var skill = pawn.GetSkillByName("combat");

	equal(skill.GetName(), "combat");
});

test("it should be possible get a skill by Id", function () {
	var skill = pawn.GetSkillById(1);

	equal(skill.GetName(), "combat");
});

test("it should receive a 'null' object if a skill get by Id or Name doesn't have a match", function () {
	var skill = pawn.GetSkillById(2);

	equal(skill, null);

	skill = pawn.GetSkillByName("pippo");

	equal(skill, null);
});

test("it should be possible to know the status of a pawn", function () {
	deepEqual(pawn.GetStatus(Status.ONMOVE), [{ name: Status.ONMOVE, by: "self" }]);
});

test("it should be possible to set and find a specific status per pawn", function () {
	pawn.SetStatus({
		name:
		"probe", by:
		"self"
	});
	var isProbe = pawn.CheckStatus("probe");
	var isStunned = pawn.CheckStatus("stunned");

	ok(isProbe);
	equal(isStunned, false);
});

test("it should be possible to set and find a specific status in a object type", function () {
	pawn.SetStatus({ "name": "parry", "by": "8" });
	pawn.SetStatus({
		name:
		"probe", by:
		"self"
	});
	var isProbe = pawn.CheckStatus("probe");
	var isParry = pawn.CheckStatus("parry");

	ok(isProbe);
	ok(isParry);
});

test("it should be possible to get number of a specific status in a object or string type", function () {
	pawn.SetStatus({ "name": "parry", "by": "8" });
	pawn.SetStatus({ "name": "parry", "by": "2" });
	pawn.SetStatus({ "name": "dazed", "by": "2" });
	pawn.SetStatus({ name: "probe", by: "self" });
	pawn.SetStatus({ name: "probe", by: "self" });
	var numberOfProbe = pawn.CheckStatusRecurrence("probe");
	var numberOfParry = pawn.CheckStatusRecurrence("parry");
	var numberOfDazed = pawn.CheckStatusRecurrence("dazed");

	equal(numberOfProbe, 2);
	equal(numberOfParry, 2);
	equal(numberOfDazed, 1);
});

test("it should be possible to remove 'x' recurrence of a specific status in a object or string type", function () {
	pawn.SetStatus({ "name": "parry", "by": "8" });
	pawn.SetStatus({ "name": "parry", "by": "2" });
	pawn.SetStatus({ "name": "dazed", "by": "2" });
	pawn.SetStatus({ name: "probe", by: "self" });
	pawn.SetStatus({ name: "probe", by: "self" });

	pawn.RemoveStatus("parry", 1);
	pawn.RemoveStatus("probe", 1);
	pawn.RemoveStatus("dazed", 1);

	var numberOfProbe = pawn.CheckStatusRecurrence("probe");
	var numberOfParry = pawn.CheckStatusRecurrence("parry");
	var numberOfDazed = pawn.CheckStatusRecurrence("dazed");

	equal(numberOfProbe, 1);
	equal(numberOfParry, 1);
	equal(numberOfDazed, 0);
});

test("it should be possible to remove 'x' recurrence of a specific status assigned by a specific pawn", function () {
	pawn.SetStatus({ "name": "parry", "by": "8" });
	pawn.SetStatus({ "name": "parry", "by": "2" });
	pawn.SetStatus({ "name": "dazed", "by": "2" });

	pawn.RemoveStatusSetBy("parry", "2", 1);
	pawn.RemoveStatusSetBy("dazed", "8", 1);

	var numberOfParry = pawn.CheckStatusRecurrence("parry");
	var numberOfDazed = pawn.CheckStatusRecurrence("dazed");

	equal(numberOfParry, 1);
	equal(numberOfDazed, 1);
});

test("it should be possible to check for a specific status set by a pawn", function () {
	pawn.SetStatus({ "name": "parry", "by": "8" });
	pawn.SetStatus({ "name": "parry", "by": "2" });
	pawn.SetStatus({ "name": "dazed", "by": "2" });

	var isParry = pawn.CheckStatusSetBy("parry", "2");
	var isDazed = pawn.CheckStatusSetBy("dazed", "8");

	equal(isParry, true);
	equal(isDazed, false);
});

test("it should be possible to get the recurrency for a specific status set by a pawn", function () {
	pawn.SetStatus({ "name": "parry", "by": "8" });
	pawn.SetStatus({ "name": "parry", "by": "2" });
	pawn.SetStatus({ "name": "parry", "by": "2" });
	pawn.SetStatus({ "name": "dazed", "by": "2" });

	var numberOfParry = pawn.CheckStatusRecurrencySetBy("parry", "2");
	var numberOfDazed = pawn.CheckStatusRecurrencySetBy("dazed", "8");

	equal(numberOfParry, 2);
	equal(numberOfDazed, 0);
});

test("it should be possible to get a status", function () {
	pawn.SetStatus({ "name": "parry", "by": "8" });
	pawn.SetStatus({ "name": "parry", "by": "2" });
	pawn.SetStatus({ "name": "parry", "by": "2" });
	pawn.SetStatus({ "name": "dazed", "by": "2" });

	var parryStatus = pawn.GetStatus({ "name": "parry", "by": "8" });
	var parryStatus2 = pawn.GetStatus("parry");

	deepEqual(parryStatus, [{ "name": "parry", "by": "8" }]);
	deepEqual(parryStatus2, [{ "name": "parry", "by": "8" }, { "name": "parry", "by": "2" }, { "name": "parry", "by": "2" }]);

});

//Knight Tests
var knight;
module("Knight Pawn", {
	setup: function () {

		knight = new Knight("fakeid");
	}
});

test("it should be playable", function () {
	ok(knight.IsPlayable());
});

test("it should be possible get the name of the pawn", function () {
	equal(knight.GetName(), "knight");
});

test("it should have 3 hp and 4 moves and id 1", function () {
	var statistics = knight.GetStatistics();

	equal(statistics.hp, 3);
	equal(statistics.moves, 4);
	equal(knight._id, 1);
});

test("it should have a 'combat', 'slash' and  'parry' skills", function () {
	var skills = knight.GetSkills();
	var skillFound = 0;

	for (var i = 0; i < skills.length; i++) {
		if (skills[i].GetName() === "slash") {
			skillFound++;
		}
		if (skills[i].GetName() === "parry") {
			skillFound++;
		}

		if (skills[i].GetName() === "combat") {
			skillFound++;
		}
	}

	equal(skillFound, 3);
});

test("it should be possible to be notified when a pawn is in 'dead' status", function () {
	var notifier = Backbone.Model.extend();

	var mine = new notifier();
	var idDeadPawn;
	mine.observer.on("pawn:dead", function (e) { idDeadPawn = e._id; });

	knight.ReceiveDamage(3);

	equal(idDeadPawn, knight._id);
});

test("it should be possible to set initial values for hp, maxhp, status, coords", function () {
	var pawnDTO = new PawnDTO({ hp: 1, maxhp: 3, x: 3, y: 0, status: [Status.MOVED] });
	var pawnFactory = new PawnsFactory();
	var newKnight = pawnFactory.CreateByClassName(PawnType.KNIGHT, "fakeid", pawnDTO);

	deepEqual(newKnight.GetPosition(), new Coords(3, 0));
	deepEqual(newKnight.GetStatistics(), new Statistics(1, 3, 4));
	ok(newKnight.CheckStatus(Status.MOVED));
});

// Paladin Tests
var paladin;
module("Paladin Pawn", {
	setup: function () {

		paladin = new Paladin("fakeid");
	}
});

test("it should be playable", function () {
	ok(paladin.IsPlayable());
});

test("it should have 3 hp and 4 moves and id 2", function () {
	var statistics = paladin.GetStatistics();

	equal(statistics.hp, 3);
	equal(statistics.moves, 4);
	equal(paladin._id, 2);
});

test("it should have a 'combat', 'holy slash' and  'Heal' skills", function () {
	var skills = paladin.GetSkills();
	var skillFound = 0;

	for (var i = 0; i < skills.length; i++) {
		if (skills[i].GetName() === "holyslash") {
			skillFound++;
		}
		if (skills[i].GetName() === "heal") {
			skillFound++;
		}

		if (skills[i].GetName() === "combat") {
			skillFound++;
		}
	}

	equal(skillFound, 3);
});

test("it should be possible to be notified when a pawn is in 'dead' status", function () {
	var notifier = Backbone.Model.extend();

	var mine = new notifier();
	var idDeadPawn;
	mine.observer.on("pawn:dead", function (e) { idDeadPawn = e._id; });

	paladin.ReceiveDamage(3);

	equal(idDeadPawn, paladin._id);
});

test("it should be possible to set initial values for hp, maxhp, status, coords", function () {
	var pawnDTO = new PawnDTO({ hp: 1, maxhp: 3, x: 3, y: 0, status: [Status.MOVED, Status.SLOWED] });
	var pawnFactory = new PawnsFactory();
	var newKnight = pawnFactory.CreateByClassName(PawnType.PALADIN, "fakeid", pawnDTO);

	deepEqual(newKnight.GetPosition(), new Coords(3, 0));
	deepEqual(newKnight.GetStatistics(), new Statistics(1, 3, 4));
	ok(newKnight.CheckStatus(Status.MOVED));
	ok(newKnight.CheckStatus(Status.SLOWED));
});

// Pretorian Tests
var pretorian;
module("Pretorian Pawn", {
	setup: function () {

		pretorian = new Pretorian("fakeid");
	}
});

test("it should be playable", function () {
	ok(pretorian.IsPlayable());
});

test("it should have 2 hp and 5 moves and id 3", function () {
	var statistics = pretorian.GetStatistics();

	equal(statistics.hp, 2);
	equal(statistics.moves, 5);
	equal(pretorian._id, 3);
});

test("it should have a 'combat', 'assault' and  'block' skills", function () {
	var skills = pretorian.GetSkills();
	var skillFound = 0;

	for (var i = 0; i < skills.length; i++) {
		if (skills[i].GetName() === "assault") {
			skillFound++;
		}
		if (skills[i].GetName() === "block") {
			skillFound++;
		}

		if (skills[i].GetName() === "combat") {
			skillFound++;
		}
	}

	equal(skillFound, 3);
});

test("it should be possible to be notified when a pawn is in 'dead' status", function () {
	var notifier = Backbone.Model.extend();

	var mine = new notifier();
	var idDeadPawn;
	mine.observer.on("pawn:dead", function (e) { idDeadPawn = e._id; });

	pretorian.ReceiveDamage(3);

	equal(idDeadPawn, pretorian._id);
});

test("it should be possible to set initial values for hp, maxhp, status, coords", function () {
	var pawnDTO = new PawnDTO({ hp: 1, maxhp: 2, x: 3, y: 0, status: [Status.MOVED, Status.SLOWED] });
	var pawnFactory = new PawnsFactory();
	var newKnight = pawnFactory.CreateByClassName(PawnType.PRETORIAN, "fakeid", pawnDTO);

	deepEqual(newKnight.GetPosition(), new Coords(3, 0));
	deepEqual(newKnight.GetStatistics(), new Statistics(1, 2, 5));
	ok(newKnight.CheckStatus(Status.MOVED));
	ok(newKnight.CheckStatus(Status.SLOWED));
	equal(newKnight.CheckStatus(Status.DEFEND), false);
});

// RuneMaster Tests
var runemaster;
module("RuneMaster Pawn", {
	setup: function () {

		runemaster = new RuneMaster("fakeid");
	}
});

test("it should be playable", function () {
	ok(runemaster.IsPlayable());
});

test("it should have 2 hp and 5 moves and id 4", function () {
	var statistics = runemaster.GetStatistics();

	equal(statistics.hp, 2);
	equal(statistics.moves, 5);
	equal(runemaster._id, 4);
});

test("it should have a 'combat', 'rune' and  'rune move' skills", function () {
	var skills = runemaster.GetSkills();
	var skillFound = 0;

	for (var i = 0; i < skills.length; i++) {
		if (skills[i].GetName() === "rune") {
			skillFound++;
		}
		if (skills[i].GetName() === "runemove") {
			skillFound++;
		}

		if (skills[i].GetName() === "combat") {
			skillFound++;
		}
	}

	equal(skillFound, 3);
});

test("it should be possible to be notified when a pawn is in 'dead' status", function () {
	var notifier = Backbone.Model.extend();

	var mine = new notifier();
	var idDeadPawn;
	mine.observer.on("pawn:dead", function (e) { idDeadPawn = e._id; });

	runemaster.ReceiveDamage(3);

	equal(idDeadPawn, runemaster._id);
});

test("it should be possible to set initial values for hp, maxhp, status, coords", function () {
	var pawnDTO = new PawnDTO({ hp: 1, maxhp: 2, x: 3, y: 0, status: [Status.MOVED, Status.SLOWED] });
	var pawnFactory = new PawnsFactory();
	var newKnight = pawnFactory.CreateByClassName(PawnType.RUNEMASTER, "fakeid", pawnDTO);

	deepEqual(newKnight.GetPosition(), new Coords(3, 0));
	deepEqual(newKnight.GetStatistics(), new Statistics(1, 2, 5));
	ok(newKnight.CheckStatus(Status.MOVED));
	ok(newKnight.CheckStatus(Status.SLOWED));
	equal(newKnight.CheckStatus(Status.DEFEND), false);
});

// Sapper Tests
var sapper;
module("Sapper Pawn", {
	setup: function () {

		sapper = new Sapper("fakeid");
	}
});

test("it should be playable", function () {
	ok(sapper.IsPlayable());
});

test("it should have 2 hp and 5 moves and id 5", function () {
	var statistics = sapper.GetStatistics();

	equal(statistics.hp, 2);
	equal(statistics.moves, 5);
	equal(sapper._id, 5);
});

test("it should have a 'combat', 'fire bomb' and  'ooze bomb' skills", function () {
	var skills = sapper.GetSkills();
	var skillFound = 0;

	for (var i = 0; i < skills.length; i++) {
		if (skills[i].GetName() === "firebomb") {
			skillFound++;
		}
		if (skills[i].GetName() === "dodge") {
			skillFound++;
		}

		if (skills[i].GetName() === "combat") {
			skillFound++;
		}
	}

	equal(skillFound, 3);
});

test("it should be possible to be notified when a pawn is in 'dead' status", function () {
	var notifier = Backbone.Model.extend();

	var mine = new notifier();
	var idDeadPawn;
	mine.observer.on("pawn:dead", function (e) { idDeadPawn = e._id; });

	sapper.ReceiveDamage(3);

	equal(idDeadPawn, sapper._id);
});

test("it should be possible to set initial values for hp, maxhp, status, coords", function () {
	var pawnDTO = new PawnDTO({ hp: 1, maxhp: 2, x: 3, y: 0, status: [Status.MOVED, Status.SLOWED] });
	var pawnFactory = new PawnsFactory();
	var newKnight = pawnFactory.CreateByClassName(PawnType.SAPPER, "fakeid", pawnDTO);

	deepEqual(newKnight.GetPosition(), new Coords(3, 0));
	deepEqual(newKnight.GetStatistics(), new Statistics(1, 2, 5));
	ok(newKnight.CheckStatus(Status.MOVED));
	ok(newKnight.CheckStatus(Status.SLOWED));
	equal(newKnight.CheckStatus(Status.DEFEND), false);
});

// Scout Tests
var scout;
module("Scout Pawn", {
	setup: function () {

		scout = new Scout("fakeid");
	}
});

test("it should be playable", function () {
	ok(scout.IsPlayable());
});

test("it should have 2 hp and 5 moves and id 6", function () {
	var statistics = scout.GetStatistics();

	equal(statistics.hp, 2);
	equal(statistics.moves, 5);
	equal(scout._id, 6);
});

test("it should have a 'combat', 'stab' and  'rush' skills", function () {
	var skills = scout.GetSkills();
	var skillFound = 0;

	for (var i = 0; i < skills.length; i++) {
		if (skills[i].GetName() === "stab") {
			skillFound++;
		}
		if (skills[i].GetName() === "rush") {
			skillFound++;
		}

		if (skills[i].GetName() === "combat") {
			skillFound++;
		}
	}

	equal(skillFound, 3);
});

test("it should be possible to be notified when a pawn is in 'dead' status", function () {
	var notifier = Backbone.Model.extend();

	var mine = new notifier();
	var idDeadPawn;
	mine.observer.on("pawn:dead", function (e) { idDeadPawn = e._id; });

	scout.ReceiveDamage(3);

	equal(idDeadPawn, scout._id);
});

test("it should be possible to set initial values for hp, maxhp, status, coords", function () {
	var pawnDTO = new PawnDTO({ hp: 1, maxhp: 2, x: 3, y: 0, status: [Status.MOVED, Status.SLOWED] });
	var pawnFactory = new PawnsFactory();
	var newKnight = pawnFactory.CreateByClassName(PawnType.SCOUT, "fakeid", pawnDTO);

	deepEqual(newKnight.GetPosition(), new Coords(3, 0));
	deepEqual(newKnight.GetStatistics(), new Statistics(1, 2, 5));
	ok(newKnight.CheckStatus(Status.MOVED));
	ok(newKnight.CheckStatus(Status.SLOWED));
	equal(newKnight.CheckStatus(Status.DEFEND), false);
});

// Sniper Tests
var sniper;
module("Sniper Pawn", {
	setup: function () {

		sniper = new Sniper("fakeid");
	}
});

test("it should be playable", function () {
	ok(sniper.IsPlayable());
});

test("it should have 2 hp and 5 moves and id 7", function () {
	var statistics = sniper.GetStatistics();

	equal(statistics.hp, 2);
	equal(statistics.moves, 5);
	equal(sniper._id, 7);
});

test("it should have a 'combat', 'strike' and  'dodge' skills", function () {
	var skills = sniper.GetSkills();
	var skillFound = 0;

	for (var i = 0; i < skills.length; i++) {
		if (skills[i].GetName() === "strike") {
			skillFound++;
		}
		if (skills[i].GetName() === "dodge") {
			skillFound++;
		}

		if (skills[i].GetName() === "combat") {
			skillFound++;
		}
	}

	equal(skillFound, 3);
});

test("it should be possible to be notified when a pawn is in 'dead' status", function () {
	var notifier = Backbone.Model.extend();

	var mine = new notifier();
	var idDeadPawn;
	mine.observer.on("pawn:dead", function (e) { idDeadPawn = e._id; });

	sniper.ReceiveDamage(3);

	equal(idDeadPawn, sniper._id);
});

test("it should be possible to set initial values for hp, maxhp, status, coords", function () {
	var pawnDTO = new PawnDTO({ hp: 1, maxhp: 2, y: 0, status: [Status.MOVED, Status.SLOWED] });
	var pawnFactory = new PawnsFactory();
	var newKnight = pawnFactory.CreateByClassName(PawnType.SNIPER, "fakeid", pawnDTO);

	deepEqual(newKnight.GetPosition(), new Coords(0, 0));
	deepEqual(newKnight.GetStatistics(), new Statistics(1, 2, 5));
	ok(newKnight.CheckStatus(Status.MOVED));
	ok(newKnight.CheckStatus(Status.SLOWED));
	equal(newKnight.CheckStatus(Status.DEFEND), false);
});

About AngularJs

What about the future and my refactoring intentions? Well, I think things can be improved, I’d like to have a Pawn as an Angular Directive.
What I want to have is something similar to

<pawn type="pawn.type"
   statistics="pawn.statistics"
   position="pawn.position"
   status="pawn.status" />

Even better, I want something similar to

<roster id="player.id"
   faction="player.faction"
   pawns="player.pawns" />

One thought on “Pawns and Tiles: concerning the pawns.

  1. Pingback: Pawns and Tiles: concerning the Tiles | Agile. Angular/Js. Asp.NET & TDD

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s