<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ seneca - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ seneca - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 27 Jul 2026 04:23:00 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/seneca/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Writing a chess microservice using Node.js and Seneca, Part 1 ]]>
                </title>
                <description>
                    <![CDATA[ By Jeff M Lowery (This is Part 1 of a three-part series [Part 2, Part 3]) I’ve begun wrapping my head around microservices. Up to this time I regarded it as a scalability pattern and overlooked the functional programming principles behind it. The rul... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/follow-the-rules-with-seneca-b3cf3d08fe5d/</link>
                <guid isPermaLink="false">66d45f6d51f567b42d9f8469</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microservices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ seneca ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 29 May 2017 23:47:32 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*QVKnJrXn5COBq3KjJ1OJvQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Jeff M Lowery</p>
<p>(This is Part 1 of a three-part series [<a target="_blank" href="https://medium.com/@jefflowery/follow-the-rules-with-seneca-ii-c22074debac">Part 2</a>, <a target="_blank" href="https://medium.com/@jefflowery/writing-a-chess-microservice-using-node-js-and-seneca-part-3-ab38b8ef9b0a">Part 3</a>])</p>
<p>I’ve begun wrapping my head around microservices. Up to this time I regarded it as a scalability pattern and overlooked the functional programming principles behind it.</p>
<p>The <a target="_blank" href="https://www.chess.com/learn-how-to-play-chess">rules of chess</a> can be decomposed easily into microservices. They are neither random nor ambiguous, which is perfect for writing small, stateless services that deal with movements of various pieces.</p>
<p>In this post, I’ll walk through several services I created that determine what the legal moves are for lone pieces on an empty chessboard. We’ll use the <a target="_blank" href="http://senecajs.org/">Seneca framework</a>, a microservices toolkit for Node.js, because it’s intuitive and well documented.</p>
<h3 id="heading-setting-up-seneca">Setting up Seneca</h3>
<p><a target="_blank" href="http://senecajs.org/getting-started/">Seneca</a> is a Node.js module that is installed using npm:</p>
<p><code>npm install seneca</code></p>
<p>Also, we’ll rely on globally installed <a target="_blank" href="http://chaijs.com/api/bdd/">mocha/chai</a> modules for the tests that will illustrate functionality.</p>
<h3 id="heading-find-all-the-legal-moves">Find all the legal moves</h3>
<p>It’s actually not necessary to maintain an in-memory representation of a chessboard, just the pieces and their location on an 8x8 coordinate grid. <a target="_blank" href="https://en.wikipedia.org/wiki/Algebraic_notation_(chess)">Algebraic notation</a> is commonly used to describe the coordinates on a chessboard, where the files are denoted by letters and the ranks by numbers:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*CIAIhpn7wBT1n15aOw0pkA.gif" alt="Image" width="372" height="372" loading="lazy">
<em>View from White side of board</em></p>
<p>For the player who is White, the rightmost bottom corner is h1; for Black it is a8. A rook on b2, moving to square f2, would be denoted as Rb2-f2.</p>
<h3 id="heading-raw-moves">Raw Moves</h3>
<p>I am defining <strong>raw moves</strong> as the moves a piece would make if unimpeded by other pieces <em>or the edge of the board</em>. That last bit may seem odd, but it allows me to construct a 15x15 movement mask, which is then truncated to fit the 8x8 board. A fellow named <a target="_blank" href="https://en.wikipedia.org/wiki/Procrustes">Procrustes</a> came up with a similar idea ages ago.</p>
<p>Kings, Queens, Bishops and Rooks move along diagonals and/or files, so I will use one service for the movements of those four pieces. Pawns have unique movement characteristics, so a special service will be used for them. The same goes for Knights, since they can jump over pieces and don’t move along files or ranks.</p>
<p>For example, a rook can move 7 squares along any rank or file on an 15x15 board in which the rook is centered. Similar rules apply to bishop and queen. The king is limited to a one-square range in any direction(the exception is castling, which I will deal with in a future post).</p>
<p>I will use a <code>ChessPiece</code> class to hold information about the type and location of each chess piece. It won’t play too important a role for now, but it will later when I expand the scope of the rules covered by the services.</p>
<h3 id="heading-first-service-rook-bishop-queen-and-king-moves">First service: Rook, Bishop, Queen and King moves</h3>
<p>In Seneca, services are invoked via <code>role</code> and <code>cmd</code>. The <code>role</code> is akin to a category, and <code>cmd</code> names a specific service. As we’ll see later, a service can be further specified by additional parameters.</p>
<p>Services are added using <code>seneca.add()</code>, and invoked via <code>seneca.act()</code>. Let’s look at the service, first (from Movement.js):</p>
<pre><code class="lang-js"> <span class="hljs-built_in">this</span>.add({
        <span class="hljs-attr">role</span>: <span class="hljs-string">"movement"</span>,
        <span class="hljs-attr">cmd</span>: <span class="hljs-string">"rawMoves"</span>,
    }, <span class="hljs-function">(<span class="hljs-params">msg, reply</span>) =&gt;</span> {
        <span class="hljs-keyword">var</span> err = <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">var</span> rawMoves = [];

        <span class="hljs-keyword">var</span> pos = msg.piece.position;

        <span class="hljs-keyword">switch</span> (msg.piece.piece) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">'R'</span>:
            rawMoves = rankAndFile(pos);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'B'</span>:
            rawMoves = diagonal(pos);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'Q'</span>:
            rawMoves = rankAndFile(pos)
                .concat(diagonal(pos));
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'K'</span>:
            rawMoves = rankAndFile(pos, <span class="hljs-number">1</span>)
                .concat(diagonal(pos, <span class="hljs-number">1</span>))
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">default</span>:
            err = <span class="hljs-string">"unhandled "</span> + msg.piece;
            <span class="hljs-keyword">break</span>;
        };

        reply(err, rawMoves);
    });
</code></pre>
<p>Now let’s see how the test invokes the service (movesTest.js):</p>
<pre><code class="lang-js"> <span class="hljs-keyword">var</span> Ba1 = <span class="hljs-keyword">new</span> ChessPiece(<span class="hljs-string">'Ba1'</span>);
        seneca.act({
            <span class="hljs-attr">role</span>: <span class="hljs-string">"movement"</span>,
            <span class="hljs-attr">cmd</span>: <span class="hljs-string">"rawMoves"</span>,
            <span class="hljs-attr">piece</span>: Ba1
        }, <span class="hljs-function">(<span class="hljs-params">err, msg</span>) =&gt;</span> {...});
</code></pre>
<p>Note that in addition to <code>role</code> and <code>cmd</code>, there is a <code>piece</code> argument. This, along with the <code>role</code> and <code>cmd</code>, are properties of the <code>msg</code> argument received by the service. Before you can invoke the service, though, you must tell Seneca which services to use:</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> movement = <span class="hljs-built_in">require</span>(‘../services/Movement’)
<span class="hljs-keyword">const</span> seneca = <span class="hljs-built_in">require</span>(<span class="hljs-string">'seneca'</span>)({
        <span class="hljs-attr">log</span>: <span class="hljs-string">'silent'</span>
    })

 .use(movement);
</code></pre>
<p>The raw moves for a bishop at square a1 are in the <code>msg</code> received <em>back</em> from the service:</p>
<p>[ { file: ‘<code>’, rank: ‘0’ },  
 { file: ‘b’, rank: ‘2’ },  
 { file: ‘</code>’, rank: ‘2’ },<br> { file: ‘b’, rank: ‘0’ },<br> { file: ‘<em>’, rank: ‘/’ },<br> { file: ‘c’, rank: ‘3’ },<br> { file: ‘</em>’, rank: ‘3’ },<br> { file: ‘c’, rank: ‘/’ },<br> { file: ‘^’, rank: ‘.’ },<br> { file: ‘d’, rank: ‘4’ },<br> { file: ‘^’, rank: ‘4’ },<br> { file: ‘d’, rank: ‘.’ },<br> { file: ‘]’, rank: ‘-’ },<br> { file: ‘e’, rank: ‘5’ },<br> { file: ‘]’, rank: ‘5’ },<br> { file: ‘e’, rank: ‘-’ },<br> { file: ‘\’, rank: ‘,’ },<br> { file: ‘f’, rank: ‘6’ },<br> { file: ‘\’, rank: ‘6’ },<br> { file: ‘f’, rank: ‘,’ },<br> { file: ‘[‘, rank: ‘+’ },<br> { file: ‘g’, rank: ‘7’ },<br> { file: ‘[‘, rank: ‘7’ },<br> { file: ‘g’, rank: ‘+’ },<br> { file: ‘Z’, rank: ‘<em>’ },<br> { file: ‘h’, rank: ‘8’ },<br> { file: ‘Z’, rank: ‘8’ },<br> { file: ‘h’, rank: ‘</em>’ } ]</p>
<p>Note that there are some weird squares listed! These are the positions that “fall off” the 8x8 board and will be eliminated later by another service.</p>
<h4 id="heading-what-just-happened">What just happened?</h4>
<p>A service was defined with <code>role=”movement”</code> and <code>cmd=”rawMoves”</code>. When <code>act()</code> is later invoked, the parameters of the act request are matched against a service that handles those parameters (this is called the service’s <strong>pattern</strong>). As mentioned previously and as will be shown in the next example, <code>role</code> and <code>cmd</code> are not necessarily the only parameters that determine the service invoked.</p>
<h3 id="heading-next-services-pawns-and-knights"><strong>Next services: Pawns and Knights</strong></h3>
<p>Pawns move one square forward unless they are on their original square, in which case they can move one or two squares forward. There are other moves a pawn can make when it is not the lone piece on an empty board, but that’s for future consideration. Pawns alway start on the second rank, and can never move backwards.</p>
<p>Knights move in an L-shape pattern. In our imaginary 15x15 board with the knight centered, there will always be eight possible moves.</p>
<p>I’ll write two services (one for pawns, the other for knights) and place both in one module (SpecialMovements.js):</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">specialMovement</span>(<span class="hljs-params">options</span>) </span>{
  <span class="hljs-comment">//...</span>
      <span class="hljs-built_in">this</span>.add({
        <span class="hljs-attr">role</span>: <span class="hljs-string">"movement"</span>,
        <span class="hljs-attr">cmd</span>: <span class="hljs-string">"rawMoves"</span>,
        <span class="hljs-attr">isPawn</span>: <span class="hljs-literal">true</span>
    }, <span class="hljs-function">(<span class="hljs-params">msg, reply</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (msg.piece.piece !== <span class="hljs-string">'P'</span>) {
            <span class="hljs-keyword">return</span> (<span class="hljs-string">"piece was not a pawn"</span>)
        }

        <span class="hljs-keyword">var</span> pos = msg.piece.position;

        <span class="hljs-keyword">const</span> rawMoves = pawnMoves(pos);
        reply(<span class="hljs-literal">null</span>, rawMoves);
    });

    <span class="hljs-built_in">this</span>.add({
        <span class="hljs-attr">role</span>: <span class="hljs-string">"movement"</span>,
        <span class="hljs-attr">cmd</span>: <span class="hljs-string">"rawMoves"</span>,
        <span class="hljs-attr">isKnight</span>: <span class="hljs-literal">true</span>
    }, <span class="hljs-function">(<span class="hljs-params">msg, reply</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (msg.piece.piece !== <span class="hljs-string">'N'</span>) {
            <span class="hljs-keyword">return</span> (<span class="hljs-string">"piece was not a knight"</span>)
        }

        <span class="hljs-keyword">var</span> rawMoves = [];
        <span class="hljs-keyword">var</span> pos = msg.piece.position;

        rawMoves = knightMoves(pos);
        reply(<span class="hljs-literal">null</span>, rawMoves);
    });
}
</code></pre>
<p>See the <code>isPawn</code> and <code>isKnight</code> parameters in the services? The first object passed to Seneca <code>add()</code> is called the <strong>service pattern</strong>. What happens is that Seneca will invoke the service with the <em>most specific</em> pattern match. In order to invoke the right service, I need to add <code>isPawn:true</code> or <code>isKnight:true</code> to the act request:</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> movement = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../services/Movement'</span>)
<span class="hljs-keyword">var</span> specialMovement = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../services/SpecialMovement'</span>)

<span class="hljs-keyword">const</span> seneca = <span class="hljs-built_in">require</span>(<span class="hljs-string">'seneca'</span>)({
        <span class="hljs-attr">log</span>: <span class="hljs-string">'silent'</span>
    })
    .use(specialMovement)

...

var p = <span class="hljs-keyword">new</span> ChessPiece(<span class="hljs-string">'Pe2'</span>);
        seneca.act({
            <span class="hljs-attr">role</span>: <span class="hljs-string">"movement"</span>,
            <span class="hljs-attr">cmd</span>: <span class="hljs-string">"rawMoves"</span>,
            <span class="hljs-attr">piece</span>: p,
...

isPawn: <span class="hljs-literal">true</span>
        }, <span class="hljs-function">(<span class="hljs-params">err, msg</span>) =&gt;</span> {...}

...
 var p = <span class="hljs-keyword">new</span> ChessPiece(<span class="hljs-string">'Nd4'</span>);
        seneca.act({
            <span class="hljs-attr">role</span>: <span class="hljs-string">"movement"</span>,
            <span class="hljs-attr">cmd</span>: <span class="hljs-string">"rawMoves"</span>,
            <span class="hljs-attr">piece</span>: p,

<span class="hljs-attr">isKnight</span>: <span class="hljs-literal">true</span>
        }, <span class="hljs-function">(<span class="hljs-params">err, msg</span>) =&gt;</span> {
</code></pre>
<h3 id="heading-legal-moves">Legal Moves</h3>
<p>Our rudimentary legal move service will just filter out all the square positions that are not on files a-h or ranks 1–8. The legal move service will be called directly with a <code>ChessPiece</code> instance as part of the service payload. The legal move service will then invoke the raw move service to get the movement mask. The mask will be truncated to the edges of the board, and the result will be the square positions that can legally be played.</p>
<pre><code class="lang-js">    <span class="hljs-built_in">this</span>.add({
        <span class="hljs-attr">role</span>: <span class="hljs-string">"movement"</span>,
        <span class="hljs-attr">cmd</span>: <span class="hljs-string">"legalSquares"</span>,
    }, <span class="hljs-function">(<span class="hljs-params">msg, reply</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> isPawn = msg.piece.piece === <span class="hljs-string">'P'</span>;
        <span class="hljs-keyword">const</span> isKnight = msg.piece.piece === <span class="hljs-string">'N'</span>;

        <span class="hljs-built_in">this</span>.act({
            <span class="hljs-attr">role</span>: <span class="hljs-string">"movement"</span>,
            <span class="hljs-attr">cmd</span>: <span class="hljs-string">"rawMoves"</span>,
            <span class="hljs-attr">piece</span>: msg.piece,
            <span class="hljs-attr">isPawn</span>: isPawn,
            <span class="hljs-attr">isKnight</span>: isKnight
        }, <span class="hljs-function">(<span class="hljs-params">err, msg</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> squared = [];

            msg.forEach(<span class="hljs-function">(<span class="hljs-params">move</span>) =&gt;</span> {
                <span class="hljs-keyword">if</span> (move.file &gt;= <span class="hljs-string">'a'</span> &amp;&amp; move.file &lt;= <span class="hljs-string">'h'</span>) {
                    <span class="hljs-keyword">if</span> (move.rank &gt;= <span class="hljs-number">1</span> &amp;&amp; move.rank &lt;= <span class="hljs-number">8</span>) {
                        squared.push(move)
                    }
                }
            })

            reply(<span class="hljs-literal">null</span>, squared);
        });
    })
</code></pre>
<p>The <code>legalSquares</code> service first invokes the <code>rawMoves</code> service. This gets us the 15x15 movement mask for whatever piece is passed via the <code>msg</code> parameter. It is important, though, that the right service is invoked by setting the <code>isKnight</code> or <code>isPawn</code> pattern field to true for either of those two pieces… if both are false, then the “regular” <code>rawMoves</code> service for K,Q,B,R will be invoked.</p>
<p>Once the raw moves are retrieved, then the <code>legalSquares</code> service removes the invalid positions and returns what is left. So if I invoke the service with the piece at Na1, I get:</p>
<pre><code class="lang-js">[ { <span class="hljs-attr">file</span>: ‘c’, <span class="hljs-attr">rank</span>: ‘<span class="hljs-number">2</span>’ }, { <span class="hljs-attr">file</span>: ‘b’, <span class="hljs-attr">rank</span>: ‘<span class="hljs-number">3</span>’ } ]
</code></pre>
<p>If instead I pass in Rd4, legalSquares returns:<br>[ { file: ‘c’, rank: ‘4’ },<br> { file: ‘d’, rank: ‘5’ },<br> { file: ‘e’, rank: ‘4’ },<br> { file: ‘d’, rank: ‘3’ },<br> { file: ‘b’, rank: ‘4’ },<br> { file: ‘d’, rank: ‘6’ },<br> { file: ‘f’, rank: ‘4’ },<br> { file: ‘d’, rank: ‘2’ },<br> { file: ‘a’, rank: ‘4’ },<br> { file: ‘d’, rank: ‘7’ },<br> { file: ‘g’, rank: ‘4’ },<br> { file: ‘d’, rank: ‘1’ },<br> { file: ‘d’, rank: ‘8’ },<br> { file: ‘h’, rank: ‘4’ } ]</p>
<p>which is a little harder to decipher, but contains all files along the 4th rank and all ranks along the d-file (trust me!).</p>
<p>That’s it for now! In a future post I’ll go over services that deal with friendly pieces impeding movement, then dealing with the potential capture of hostile pieces. Further services will handle rules for castling, <em>en passant,</em> check, checkmate, and stalemate.</p>
<p>All source code can be found <a target="_blank" href="https://github.com/JeffML/ms-chess">here</a>.</p>
<p>Continue to <a target="_blank" href="https://medium.com/@jefflowery/follow-the-rules-with-seneca-ii-c22074debac">Part 2 of this series</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
