node seneca

(This is Part 1 of a three-part series [Part 2, Part 3])

(这是一个由三部分组成的系列文章的第1部分[ 第2 部分 , 第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 rules of chess 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.

国际象棋的规则可以轻松地分解为微服务。 它们既不是随机的,也不是模棱两可的,非常适合编写处理各种动作的小型无状态服务。

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 Seneca framework, a microservices toolkit for Node.js, because it’s intuitive and well documented.

在本文中,我将逐步介绍我创建的几个服务,这些服务将确定空白棋盘上的单个棋子的合法移动方式。 我们将使用Seneca框架 ,这是用于Node.js的微服务工具包,因为它直观且文档齐全。

设置塞内卡 (Setting up Seneca)

Seneca is a Node.js module that is installed using npm:

Seneca是使用npm安装的Node.js模块:

npm install seneca

npm install seneca

Also, we’ll rely on globally installed mocha/chai modules for the tests that will illustrate functionality.

此外,我们将使用全球安装的mocha / chai模块进行测试,以说明功能。

查找所有合法举动 (Find all the legal moves)

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. Algebraic notation is commonly used to describe the coordinates on a chessboard, where the files are denoted by letters and the ranks by numbers:

实际上,不必维护棋盘的内存表示形式,只需保持棋子及其在8x8坐标网格上的位置即可。 代数符号通常用于描述棋盘上的坐标,其中文件用字母表示,等级用数字表示:

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.

对于怀特的玩家,最右下角是h1; 对于Black,它是a8。 b2上的小车移动到平方f2,将表示为Rb2-f2。

原始动作 (Raw Moves)

I am defining raw moves as the moves a piece would make if unimpeded by other pieces or the edge of the board. 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 Procrustes came up with a similar idea ages ago.

我将原始动作定义为一块棋子在不受其他棋子或棋盘边缘阻碍时会做出的动作。 最后一点可能看起来很奇怪,但是它允许我构造一个15x15的移动蒙版,然后将其截短以适合8x8的电路板。 一个叫Procrustes的家伙很久以前也提出了类似的想法。

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.

国王,皇后,主教和白嘴鸦沿着对角线和/或锉刀移动,因此我将使用一种服务来移动这四件作品。 典当具有独特的移动特性,因此将为其使用特殊服务。 骑士也是如此,因为他们可以跳过棋子并且不会沿着文件或等级移动。

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).

例如,车子可以在车子居中放置的15x15板上沿任何等级或文件移动7个正方形。 类似的规则适用于主教和皇后。 国王在任何方向上的范围都限于一个正方形(例外是cast,我将在以后的文章中讨论)。

I will use a ChessPiece 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.

我将使用ChessPiece类来保存有关每个棋子的类型和位置的信息。 目前,它的作用并不重要,但是稍后,当我扩展服务所涵盖的规则的范围时,它将起到作用。

第一次服务:白嘴鸦,主教,女王和国王的举动 (First service: Rook, Bishop, Queen and King moves)

In Seneca, services are invoked via role and cmd. The role is akin to a category, and cmd names a specific service. As we’ll see later, a service can be further specified by additional parameters.

在Seneca中,通过role调用服务 和cmd 。 该role类似于类别, cmd命名特定的服务。 稍后我们将看到,可以通过其他参数进一步指定服务。

Services are added using seneca.add(), and invoked via seneca.act(). Let’s look at the service, first (from Movement.js):

服务使用seneca.add()添加,并通过seneca.act()调用。 首先,让我们看一下服务(来自Movement.js):

this.add({role: "movement",cmd: "rawMoves",}, (msg, reply) => {var err = null;var rawMoves = [];var pos = msg.piece.position;switch (msg.piece.piece) {case 'R':rawMoves = rankAndFile(pos);break;case 'B':rawMoves = diagonal(pos);break;case 'Q':rawMoves = rankAndFile(pos).concat(diagonal(pos));break;case 'K':rawMoves = rankAndFile(pos, 1).concat(diagonal(pos, 1))break;default:err = "unhandled " + msg.piece;break;};reply(err, rawMoves);});

Now let’s see how the test invokes the service (movesTest.js):

现在,让我们看看测试如何调用服务(movesTest.js):

var Ba1 = new ChessPiece('Ba1');seneca.act({role: "movement",cmd: "rawMoves",piece: Ba1}, (err, msg) => {...});

Note that in addition to role and cmd, there is a piece argument. This, along with the role and cmd, are properties of the msg argument received by the service. Before you can invoke the service, though, you must tell Seneca which services to use:

注意除了rolecmd ,有piece 论据。 这以及rolecmd都是msg属性 服务收到的参数。 但是,在调用服务之前,您必须告诉Seneca使用哪些服务:

var movement = require(‘../services/Movement’)
const seneca = require('seneca')({log: 'silent'}).use(movement);

The raw moves for a bishop at square a1 are in the msg received back from the service:

主教在a1广场的原始举动在msg 从服务中收到的回馈

[ { file: ‘`’, rank: ‘0’ }, { file: ‘b’, rank: ‘2’ }, { file: ‘`’, rank: ‘2’ }, { file: ‘b’, rank: ‘0’ }, { file: ‘_’, rank: ‘/’ }, { file: ‘c’, rank: ‘3’ }, { file: ‘_’, rank: ‘3’ }, { file: ‘c’, rank: ‘/’ }, { file: ‘^’, rank: ‘.’ }, { file: ‘d’, rank: ‘4’ }, { file: ‘^’, rank: ‘4’ }, { file: ‘d’, rank: ‘.’ }, { file: ‘]’, rank: ‘-’ }, { file: ‘e’, rank: ‘5’ }, { file: ‘]’, rank: ‘5’ }, { file: ‘e’, rank: ‘-’ }, { file: ‘\\’, rank: ‘,’ }, { file: ‘f’, rank: ‘6’ }, { file: ‘\\’, rank: ‘6’ }, { file: ‘f’, rank: ‘,’ }, { file: ‘[‘, rank: ‘+’ }, { file: ‘g’, rank: ‘7’ }, { file: ‘[‘, rank: ‘7’ }, { file: ‘g’, rank: ‘+’ }, { file: ‘Z’, rank: ‘*’ }, { file: ‘h’, rank: ‘8’ }, { file: ‘Z’, rank: ‘8’ }, { file: ‘h’, rank: ‘*’ } ]

[{文件:'`',等级:'0'},{文件:'b',等级:'2'},{文件:'`',等级:'2'},{文件:'b',等级:'0'},{文件:'_',等级:'/'},{文件:'c',等级:'3'},{文件:'_',等级:'3'},{文件:“ c”,等级:“ /”},{文件:“ ^”,等级:“。” },{文件:'d',等级:'4'},{文件:'^',等级:'4'},{文件:'d',等级:'。 },{文件:']',等级:'-'},{文件:'e',等级:'5'},{文件:']',等级:'5'},{文件:'e' ,等级:'-'},{文件:'\\',等级:','},{文件:'f',等级:'6'},{文件:'\\',等级:'6' },{文件:'f',等级:','},{文件:'[',等级:'+'},{文件:'g',等级:'7'},{文件:'[' ,等级:“ 7”},{文件:“ g”,等级:“ +”},{文件:“ Z”,等级:“ *”},{文件:“ h”,等级:“ 8”}, {文件:'Z',等级:'8'},{文件:'h',等级:'*'}]

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.

请注意,其中列出了一些奇怪的方块! 这些是“跌落” 8x8电路板的位置,以后将由另一服务消除。

刚才发生了什么? (What just happened?)

A service was defined with role=”movement” and cmd=”rawMoves”. When act() is later invoked, the parameters of the act request are matched against a service that handles those parameters (this is called the service’s pattern). As mentioned previously and as will be shown in the next example, role and cmd are not necessarily the only parameters that determine the service invoked.

定义了一个服务,其中的role=”movement”role=”movement”cmd=”rawMoves” 。 稍后调用act() ,会将act请求的参数与处理这些参数的服务进行匹配(这称为服务的模式 )。 如前所述,将在下一个示例中显示, rolecmd 不一定是确定调用服务的唯一参数。

下一个服务:典当骑士 (Next services: Pawns and Knights)

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.

典当除非向前移动一个正方形,否则它们会向前移动一个正方形,在这种情况下,它们可以向前或向后移动两个正方形。 当典当不是一块空板上的一块棋子时,它还可以采取其他措施,但是这有待将来考虑。 典当始终从第二等级开始,并且永远不会向后移动。

Knights move in an L-shape pattern. In our imaginary 15x15 board with the knight centered, there will always be eight possible moves.

骑士以L形移动。 在我们想象中的以骑士为中心的15x15棋盘中,总是会有八种可能的动作。

I’ll write two services (one for pawns, the other for knights) and place both in one module (SpecialMovements.js):

我将编写两个服务(一个用于典当,另一个用于骑士),并将两者都放在一个模块中(SpecialMovements.js):

module.exports = function specialMovement(options) {//...this.add({role: "movement",cmd: "rawMoves",isPawn: true}, (msg, reply) => {if (msg.piece.piece !== 'P') {return ("piece was not a pawn")}var pos = msg.piece.position;const rawMoves = pawnMoves(pos);reply(null, rawMoves);});this.add({role: "movement",cmd: "rawMoves",isKnight: true}, (msg, reply) => {if (msg.piece.piece !== 'N') {return ("piece was not a knight")}var rawMoves = [];var pos = msg.piece.position;rawMoves = knightMoves(pos);reply(null, rawMoves);});
}

See the isPawn and isKnight parameters in the services? The first object passed to Seneca add() is called the service pattern. What happens is that Seneca will invoke the service with the most specific pattern match. In order to invoke the right service, I need to add isPawn:true or isKnight:true to the act request:

isPawnisKnight 服务中的参数? 传递给Seneca add()的第一个对象称为服务模式 。 发生的情况是,Seneca将使用最特定的模式匹配来调用服务。 为了调用正确的服务,我需要添加 行为请求的isPawn:trueisKnight:true

var movement = require('../services/Movement')
var specialMovement = require('../services/SpecialMovement')const seneca = require('seneca')({log: 'silent'}).use(specialMovement)...var p = new ChessPiece('Pe2');seneca.act({role: "movement",cmd: "rawMoves",piece: p,
...isPawn: true}, (err, msg) => {...}...var p = new ChessPiece('Nd4');seneca.act({role: "movement",cmd: "rawMoves",piece: p,isKnight: true}, (err, msg) => {

法律行动 (Legal Moves)

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 ChessPiece 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.

我们基本的法律搬迁服务只会过滤掉不在文件ah或1–8中的所有方形位置。 合法移动服务将直接用ChessPiece实例作为服务有效负载的一部分进行调用。 然后,合法移动服务将调用原始移动服务以获取移动掩码。 遮罩将被截断到棋盘的边缘,结果是可以合法播放的正方形位置。

this.add({role: "movement",cmd: "legalSquares",}, (msg, reply) => {const isPawn = msg.piece.piece === 'P';const isKnight = msg.piece.piece === 'N';this.act({role: "movement",cmd: "rawMoves",piece: msg.piece,isPawn: isPawn,isKnight: isKnight}, (err, msg) => {const squared = [];msg.forEach((move) => {if (move.file >= 'a' && move.file <= 'h') {if (move.rank >= 1 && move.rank <= 8) {squared.push(move)}}})reply(null, squared);});})

The legalSquares service first invokes the rawMoves service. This gets us the 15x15 movement mask for whatever piece is passed via the msg parameter. It is important, though, that the right service is invoked by setting the isKnight or isPawn pattern field to true for either of those two pieces… if both are false, then the “regular” rawMoves service for K,Q,B,R will be invoked.

legalSquares 服务首先调用rawMoves 服务。 对于通过msg参数传递的任何片段,这都会为我们提供15x15的移动蒙版。 但是重要的是,通过设置isKnight来调用正确的服务 或isPawn 对于这两个部分中的任何一个,pattern字段都为true…如果两者均为false,则“常规” rawMoves K,Q,B,R的服务将被调用。

Once the raw moves are retrieved, then the legalSquares service removes the invalid positions and returns what is left. So if I invoke the service with the piece at Na1, I get:

一旦检索到原始移动,则legalSquares 服务会删除无效的职位并返回剩余的职位。 因此,如果我在Na1处调用该服务,则会得到:

[ { file: ‘c’, rank: ‘2’ }, { file: ‘b’, rank: ‘3’ } ]

If instead I pass in Rd4, legalSquares returns:[ { file: ‘c’, rank: ‘4’ }, { file: ‘d’, rank: ‘5’ }, { file: ‘e’, rank: ‘4’ }, { file: ‘d’, rank: ‘3’ }, { file: ‘b’, rank: ‘4’ }, { file: ‘d’, rank: ‘6’ }, { file: ‘f’, rank: ‘4’ }, { file: ‘d’, rank: ‘2’ }, { file: ‘a’, rank: ‘4’ }, { file: ‘d’, rank: ‘7’ }, { file: ‘g’, rank: ‘4’ }, { file: ‘d’, rank: ‘1’ }, { file: ‘d’, rank: ‘8’ }, { file: ‘h’, rank: ‘4’ } ]

相反,如果我通过Rd4,则legalSquares返回:[{文件:'c',等级:'4'},{文件:'d',等级:'5'},{文件:'e',等级:'4 '},{文件:'d',等级:'3'},{文件:'b',等级:'4'},{文件:'d',等级:'6'},{文件:'f ',等级:'4'},{文件:'d',等级:'2'},{文件:'a',等级:'4'},{文件:'d',等级:'7'} ,{文件:'g',等级:'4'},{文件:'d',等级:'1'},{文件:'d',等级:'8'},{文件:'h',等级:“ 4”}]

which is a little harder to decipher, but contains all files along the 4th rank and all ranks along the d-file (trust me!).

有点难以理解,但包含第4级的所有文件和d文件的所有级(相信我!)。

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, en passant, check, checkmate, and stalemate.

现在就这样! 在以后的文章中,我将介绍一些服务,这些服务处理阻碍移动的友善物品,然后处理可能捕获敌对物品的问题。 进一步的服务将处理有关转换, 传递,检查,将死和僵持的规则。

All source code can be found here.

所有源代码都可以在这里找到。

Continue to Part 2 of this series.

继续本系列的第2部分 。

翻译自: https://www.freecodecamp.org/news/follow-the-rules-with-seneca-b3cf3d08fe5d/

node seneca

node seneca_使用Node.js和Seneca编写国际象棋微服务,第1部分相关推荐

  1. node seneca_使用Node.js和Seneca编写国际象棋微服务,第3部分

    node seneca Finishing up a three-part series on writing a rules engine with Seneca microservices. 完成 ...

  2. node seneca_使用Node.js和Seneca编写国际象棋微服务,第2部分

    node seneca 处理新需求而无需重构 (Handling new requirements without refactoring) Part 1 of this series talked ...

  3. Seneca :NodeJS 微服务框架入门指南

    Seneca :NodeJS 微服务框架入门指南 原文:http://onmr.com/press/getting-started-seneca.html Seneca 是一个能让您快速构建基于消息的 ...

  4. Seneca:NodeJS 微服务框架入门(一)

    Seneca是什么? (1)官网是这样介绍的: Seneca is a microservices toolkit for Node.js. It helps you write clean, org ...

  5. 高质量 Node.js 微服务的编写和部署

    前几天在微信群做的一次分享,整理出来分享给大家,相关代码请戳 https://github.com/Carrotzpc/docker_web_app 微服务架构是一种构造应用程序的替代性方法.应用程序 ...

  6. js word 预览_Node.js微服务实践(二)

    基于Seneca 和 PM2构建 本章主要分为三个小节: 选择Nodejs的理由:将证明选择Node.js来构建的正确性.介绍使用Node.js时设计的软件栈. 微服务架构Seneca:关于Senec ...

  7. Node.js微服务 2 :基于Seneca和PM2构建Node.js微服务

    2.1 选择Node.js的理由 如今,Node.js已经成为国际上许多科技公司的首选方案.特别的,对于在服务器端需要非阻塞特性(例如Web Sockets)的场景,Node.js俨然成了最好的选择. ...

  8. 小程序 timestamp_通过构建Timestamp微服务应用程序来学习Node.js

    小程序 timestamp by Ayo Isaiah 通过Ayo Isaiah 通过构建Timestamp微服务应用程序来学习Node.js (Learn Node.js by building a ...

  9. Node微服务之Seneca的使用

    Seneca在Node微服务中的运用 1.seneca基本用法介绍 准备工作: npm i -S seneca npm i -S seneca-basic #匹配模式 /** demo.js **/ ...

最新文章

  1. 在服务器托管中对流量和带宽进行限制
  2. springMVC(一) --前端控制器(DispatcherServlet)的作用
  3. Java 图片处理——如何生成高清晰度而占有磁盘小的缩略图
  4. iphone各机型参数对比_我们对比新旧两代iPhone,发现iPhone 12最值得买
  5. .net core 不启用 https_.NET 应用如何优雅的做功能开关(Feature Flag)
  6. SAP CRM Fiori busy dialog的工作原理
  7. 用字节流查看txt文件
  8. Error: GlobalConfigUtils setMetaData Fail
  9. JAVA-5NIO之Selector
  10. thinkphp 二级域名绑定模块,导致设置的路由被多域名共用的问题解决方案
  11. CF1062F Upgrading Cities
  12. mpush环境部署测试问题:安卓APP闪退
  13. JS日历控件 (兼容IE firefox) 可选择时间
  14. Android2017Google IO
  15. 4-渔夫打鱼晒网问题
  16. Gradle's dependency cache may be corrupt (this sometimes occurs after a net错误解决
  17. Sphinx使用说明
  18. 软件开发程序员的“九阳神功”——设计模式
  19. 深度学习中常见的损失函数(L1Loss、L2loss)
  20. vscode配置c语言环境

热门文章

  1. HashSet中的add()方法( 四 )(详尽版)
  2. iOS专题1-蓝牙扫描、连接、读写
  3. react native 常用学习或查资料网址
  4. iOS使用支付宝支付步骤
  5. 终止forEach的循环
  6. uniapp H5 JSSDK封装使用
  7. iOS访问系统日历 添加提醒事件
  8. 图解c/c++多级指针与“多维”数组
  9. [LeetCode] Longest Substring with At Most K Distinct Characters 最多有K个不同字符的最长子串...
  10. mysql的越过用户权限表登录