Writing a chess microservice using Node.js and Seneca, Part 2

Handling new requirements without refactoring

A quick review:

  • Seneca services are identified by a pattern consisting of role and cmd properties. Additional properties can be added to the pattern as well.
this.add({
role: "movement",
cmd: "legalMoves" //, otherProp: value, ...
}, (msg, reply) => {...}
  • Services also have an implementation that takes a msg object and a reply callback. Themsg object contains the pattern properties in addition to all other data sent to the service.
  • Seneca.act()is used to indirectly invoke a service. The act method takes an object and a callback function. The object contains the role, cmd, and other properties that comprise the message to the service.
seneca.act({
role: "movement",
cmd: "legalMoves",
piece: p,
board: board
}, (err, msg) => {
  • When an action could be handled by more than one service that matches the pattern, the service with the most specific pattern match will be invoked.

Microservices and Technical Debt

Someday never comes

Back to the game in progress…

'role: movement;cmd: legalMoves'

Microservices are not a Panacea

module.exports = function diagonal(position, range = 7) {
var moves = [];
const cFile = position.file.charCodeAt()
const cRank = position.rank.charCodeAt();
for (var i = 1; i < range + 1; i++) {
moves.push({
file: String.fromCharCode(cFile - i),
rank: String.fromCharCode(cRank - i)
});
moves.push({
file: String.fromCharCode(cFile + i),
rank: String.fromCharCode(cRank + i)
});
moves.push({
file: String.fromCharCode(cFile - i),
rank: String.fromCharCode(cRank + i)
});
moves.push({
file: String.fromCharCode(cFile + i),
rank: String.fromCharCode(cRank - i)
});
}
return moves;
}
function diagonalMoves(position, range) {
var vectors = [[], [], [], []];
const cFile = position.file.charCodeAt()
const cRank = position.rank.charCodeAt();

for (var i = 1; i < range + 1; i++) {
vectors[0].push({
file: String.fromCharCode(cFile - i),
rank: String.fromCharCode(cRank - i)
});
vectors[1].push({
file: String.fromCharCode(cFile + i),
rank: String.fromCharCode(cRank + i)
});
vectors[2].push({
file: String.fromCharCode(cFile - i),
rank: String.fromCharCode(cRank + i)
});
vectors[3].push({
file: String.fromCharCode(cFile + i),
rank: String.fromCharCode(cRank - i)
});
}

const moves = Array.prototype.concat(...vectors)
return moves;
}

With Friends like These…

The extended service

this.add({
role: "movement",
cmd: "legalMoves"
}, (msg, reply) => {//returns unimpeded moves}
this.add('role:movement,cmd:legalMoves', function (msg, reply) {
this.prior(msg, function (err, moves) {
if (msg.board) {
const boardMoves = legalMovesWithBoard(msg, moves);
reply(err, boardMoves);
return;
}
reply(err, moves);
});
});
module.exports = function (msg, moves) {
if (!msg.board) return moves;
const blockers = moves.filter(m => {
return (msg.board.pieceAt(m))
})
var newMoves = [];
const pp = msg.piece.position;
const rangeChecks = {
B: diagonalChecks,
R: rankAndFileChecks,
K: panopticonChecks,
Q: panopticonChecks,
P: pawnChecks,
N: knightChecks
};
var rangeCheck = rangeChecks[msg.piece.piece];
// console.error(msg.piece.piece, rangeCheck.name)
newMoves = moves.filter(m => {
return rangeCheck(m, blockers, pp);
})
return newMoves;
}

--

--

We’ve moved to https://freecodecamp.org/news and publish tons of tutorials each week. See you there.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store