node seneca

Finishing up a three-part series on writing a rules engine with Seneca microservices.

完成有关使用Seneca微服务编写规则引擎的三部分系列文章。

Parts 1 & 2 of this series covered:

本系列的第1部分和第2部分涉及:

  • The Seneca microservices Node.js moduleSeneca微服务Node.js模块
  • How to write a service, how to identify it by pattern and how to call it如何编写服务,如何通过模式识别服务以及如何调用服务
  • How to string service calls together如何将服务电话串在一起
  • How to enhance an existing service如何增强现有服务

Along the way, I pondered what a service should return. I came to the conclusion that returning a data object (JSON in this case) was the most flexible. It allows services to embellish the output without affecting existing clients of the service.

在此过程中,我考虑了服务应返回的内容。 我得出的结论是,返回数据对象(在这种情况下为JSON)是最灵活的。 它可以修饰服务 输出而不会影响该服务的现有客户。

Embellish? By that I mean intermediate results can be maintained as a means of tracking information that might be useful later, to a service not yet written. In the present case, I had a rawMoves service that returned a list of moves. That was immediately sufficient for the clients I had. The service calculated moves along movement vectors, and combined them into a 1-dimensional array.

润? 我的意思是说,中间结果可以作为一种跟踪信息的方式来维护,该信息以后可能对尚未编写的服务有用。 在目前的情况下,我有一个rawMoves 服务返回了动作列表。 对于我所拥有的客户而言,这立即就足够了。 计算出的服务沿着运动矢量移动,并将它们组合成一维数组。

Later though, I discovered that those movement vectors would have come in handy when a legalMoves service written later needed to take into account friendly pieces that were blocking movement. Vectors would have made those calculations simpler and more efficient, but they were “tossed out” by the rawMoves service.

不过,后来我发现,当legalMoves时,这些运动向量会派上用场 稍后编写的服务需要考虑阻碍移动的友好部分。 向量本可以使这些计算更简单,更有效,但是rawMoves它们“抛弃”了 服务。

To go back and add the vectors (in addition to the move list) meant changing the clients of the original service to accept an object, not an array. True, I could have made the original service stateful, but that would have been overkill. I had a choice: refactor the service and its clients, or Deal with It™️. In Part 2, I chose the latter.

返回并添加矢量(除了移动列表之外)意味着更改原始服务的客户端以接受对象,而不是数组。 没错,我本可以使原始服务具有状态,但是那会太过分了。 我有一个选择:重构服务及其客户,或者处理It™️ 。 在第2部分中,我选择了后者。

Yet in this installment, time has come to refactor. rawMoves now returns {moves, moveVectors}, and the upstream clients of the service can choose what to pay attention to. Care has to be taken, though, that moves and moveVectors are in sync at all times.

然而在本期中,时间已经到了重构的时候了。 rawMoves现在返回{moves, moveVectors} ,该服务的上游客户端可以选择要注意的内容。 小心,必须考虑,虽然, movesmoveVectors是同步的时刻。

Let’s see what the advantage is. In the original code, finding legalMoves was an involved process if given just a piece, move list, and friendly pieces elsewhere on the board (example). Compare that code to one that uses moveVectors:

让我们看看有什么好处。 在原始代码中,如果只给出一块,移动列表和板上其他位置的友好块,则找到legalMoves是一个涉及过程( 示例) 。 将代码与使用moveVectors代码进行比较:

module.exports = function (boardAndPiece, candidateMoves) {if (!boardAndPiece.board) return candidateMoves;const rangeChecks = {B: vectorChecks,R: vectorChecks,K: vectorChecks,Q: vectorChecks,P: pawnChecks,N: knightChecks};var rangeCheck = rangeChecks[boardAndPiece.piece.piece];return rangeCheck(boardAndPiece, candidateMoves)
}//...function vectorChecks(boardAndPiece, candidateMoves) {for (const [j, v] of candidateMoves.moveVectors.entries()) {for (const [i, m] of v.entries()) {const p = boardAndPiece.board.pieceAt(m);if (p) {if (p.color === boardAndPiece.piece.color) {candidateMoves.moveVectors[j] = v.slice(0, i);break;} else {candidateMoves.moveVectors[j] = v.slice(0, i + 1);Object.assign(candidateMoves.moveVectors[j].slice(-1)[0], {hasCaptured: p})break;}}}}return {moveVectors: candidateMoves.moveVectors,moves: Array.prototype.concat(...candidateMoves.moveVectors)}
}

Much, much simpler…and more efficient. The wrapping function is exported and used by the legalMoves service.

更简单,更高效。 包装功能由legalMoves 服务导出并使用。

const legalMovesWithBoard = require("./helpers/legalMovesWithBoard")
//...this.add('role:movement,cmd:legalMoves', function (msg, reply) {this.prior(msg, function (err, result) {if (msg.board) {const result2 = legalMovesWithBoard(msg, result);//...

回到游戏 (Back to the Game)

服务总览 (Service Overview)

All movement requests are handled by the legalMoves service, which relies on several other services and helper methods:

所有移动请求均由legalMoves处理 服务,它依赖于其他几种服务和辅助方法:

  • Call the rawMoves service

    调用rawMoves 服务

    This will return all moves of a lone piece on a virtual 15x15 chessboard (referred to as the

    这将返回虚拟15x15棋盘上的单个棋子的所有移动(称为

    movement mask). Explained in Part 1

    运动面具 )。 在第1部分中解释

  • Call the base legalMoves service

    叫基地legalMoves 服务

    This will clip the

    这将剪辑

    movement mask at the edge of the “real” 8x8 board, with proper algebraic coordinates. Explained in Part 2

    位于“真实” 8x8电路板边缘的运动遮罩 ,具有适当的代数坐标 。 在第2部分中解释

  • Call the overriding legalMoves service

    呼叫最重要的legalMoves 服务

    If there is a board as part of the incoming message (the service pattern), then a series of checks is done to account for the presence of friendly and opposing pieces, because these will affect movement. Explained in this part (Part 3).

    如果传入消息(服务模式)中有一块板,则要进行一系列检查以检查是否存在友好的相对的部件,因为它们会影响运动。 在本部分(第3部分)中进行解释。

So Part 2 took care of friendly pieces blocking other friendly pieces, but now there are those annoying enemy pieces to deal with. Like friendly pieces, enemy pieces can block movement, but they can also be captured. Under some conditions, enemy pieces may even increase our movement options.

因此, 第2部分照顾了友军,阻止了其他友军,但现在有那些烦人的敌军要处理。 像友军碎片一样,敌方碎片可以阻止移动,但也可以将其捕获。 在某些情况下,敌方部队甚至可能增加我们的行动选择。

Then there’s castling: the only move where two pieces can shift their position at once. Special considerations apply, some of which involve enemy pieces.

然后就是cast声:唯一的动作,两个棋子可以一次移动其位置。 需要特别考虑,其中一些涉及敌人。

女王,白嘴鸦和主教 (Queen, Rook, & Bishop)

The new rules involving enemy pieces extend or modify the original legalMoves service in Part 2 that dealt with friendly pieces only. The new microservice extension will need to know if the blocking piece is friend or foe. If friend, then movement is blocked at the square before. If foe, then movement is blocked by the square of the opposing piece (by capture). In the list of legal moves returned by a piece, we will denote captures by setting a hasCaptured flag, along with the type of enemy piece to be captured.

涉及敌方碎片的新规则扩展或修改了第2部分中仅处理友好碎片的原始legalMoves服务。 新的微服务扩展将需要知道阻塞块是敌还是友。 如果是朋友,则运动被阻止在广场之前。 如果是敌人,则移动被对方棋子的方块阻止(通过捕获)。 在棋子返回的合法动作列表中,我们将通过设置hasCaptured标志以及要捕捉的敌军棋子的类型来表示捕捉。

The vectorChecks helper method shown in the previous gist listing handles all vector-based movement for Queen, Rook, and Bishop.

前面要点清单中显示的vectorChecks帮助器方法可以处理Queen,Rook和Bishop的所有基于矢量的移动。

骑士 (Knight)

Knights jump around the board, so are only blocked by friendly pieces that are on one of its potential landing squares. An enemy piece does not block, but would be captured if a Knight landed on it. The method used by the legalMoves service is easy to write.

骑士在棋盘上跳来跳去,因此只被其潜在着陆广场之一上的友军所阻挡。 敌人的碎片不会阻挡,但如果有骑士降落,它将被捕获。 legalMoves使用的方法 服务很容易写。

function knightChecks(boardAndPiece, candidateMoves) {const newMoves = [];for (const m of candidateMoves.moves) {const p = boardAndPiece.board.pieceAt(m)if (!p) {newMoves.push(m)} else if (p.color !== boardAndPiece.piece.color) {m.hasCaptured = p;newMoves.push(m)}}return {moves: newMoves,moveVectors: [newMoves]};
}

典当 (Pawn)

Pawns at first seem like a pretty simple case. The pawn is blocked if any piece whether friend or enemy stands in front of it. But it can to move one square diagonally forward to capture an enemy that sits in that square.

最初的典当似乎很简单。 如果任何棋子(无论是朋友还是敌人)站在它的前面,它都会被阻止。 但是它可以对角线向前移动一个正方形,以俘获一个坐在那个正方形中的敌人。

There is also the en passant rule, where a pawn can capture an adjacent enemy pawn that just moved two squares on the previous turn:

也有顺便规则,其中的棋子可以捕获相邻的敌人棋子, 只是移动上一转两个格:

And then there’s the issue of mandatory promotion once a pawn reaches the 8th rank. Confusingly, this refers to the eighth rank in front of the pawn, which would be the first rank of the board coordinates if playing Black.

当棋子达到第8位时,就会出现强制升级的问题。 令人困惑的是,这指的是棋子前面的第8位,如果玩Black,那将是棋盘坐标的第1位。

All these considerations make for a rather involved set of rules to determine the pawn’s movement options. These can be found in the accompanying source code at GitHub.

所有这些考虑因素都构成了一套相当复杂的规则来确定棋子的移动选项。 这些可以发现在所附的源代码在GitHub上。

国王 (King)

The Pawn was a bit of work, but the king even more so. There are several conditions:

Pawn有点工作,但国王更是如此。 有几个条件:

  • Is a potential move square controlled by an enemy piece?

    潜在的移动方块是否受到敌方控制?

    Eliminate that option.

    消除该选项。

  • Is the king in check?

    国王在检查吗?

    If so, it

    如果是这样

    must move this turn

    必须转弯

    * If it is in check, and can’t move out of check, game over! Checkmate!

    *如果在检查中,并且不能移出检查,则游戏结束! 将死!

    * If it is not in check, but there are no other legal moves by any friendly piece on the board, stalemate!

    *如果不在检查范围之内,但董事会上任何友好的行动都没有其他合法行动,就此成为僵局!

  • Can the King castle (queen side or king side)?

    国王城堡(女王侧还是国王侧)可以吗?

    * King is in check: No.

    *国王在检查:不。

    * King has previously moved: No.

    * King之前已搬家:否。

    * Rook has previously moved: No.

    * Rook之前已搬家:否。

    * Intervening squares between K and R occupied: No.

    *占据K和R之间的中间方格:否。

    * Intervening squares empty, but controlled by enemy piece: No.

    *中间方块为空,但由敌方控制:否

    * Otherwise: Yes.

    *否则:是。

This service I will break down into detail. As you may recall, the legalMoves service is broken into two parts. One part treats a piece as if it is alone on the board. The other part deals with friendly and opposing pieces. Let’s look at the listing:

我将详细介绍这项服务。 您可能还记得, legalMoves服务分为两部分。 一部分将棋子视为单独在板上。 另一部分涉及友好和对立的部分。 让我们看一下清单:

this.add('role:movement,cmd:legalMoves', function (msg, reply) {this.prior(msg, function (err, result) {if (msg.board) {const result2 = legalMovesWithBoard(msg, result);if (msg.piece.piece === 'K') {legalMovesWithKing.call(this, msg, result2, reply)} else {reply(err, result2);}} else {reply(err, result);}});});

For every piece but the King, we simply call the base service (via the Seneca framework’s prior() method) followed by the helper method legalMovesWithBoard(), parts of which were listed in the previous gists of this post.

对于每一块,但王,我们只需调用基本服务(通过塞内卡框架的prior()方法),其次是辅助方法legalMovesWithBoard()这部分在这篇文章的前学家上市。

If the piece is a King, the additional helper method legalMovesWithKing() is called. The calling parameters are the this reference, a msg object containing board and the piece being moved (the King), the result2 which was came from the base legalMoves service call (this contains movement info), and the reply callback.

如果作品是国王,则调用附加的辅助方法legalMovesWithKing() 。 调用参数是this引用,一个包含木板和正在移动的棋子(国王)的msg对象, result2来自基础legalMoves 服务呼叫(其中包含移动信息)和reply回调。

There’s a bit of code to slog through, so I will refer to sections by line number:

有一些代码可以通过,所以我将按行号引用各节:

module.exports = function (boardAndPiece, candidateMoves, reply) {const opposingColor = boardAndPiece.piece.color === 'W' ? 'black' : 'white';//temporarily remove the K to avoid cyclesboardAndPiece.board.removePiece(boardAndPiece.piece);function canCastle(king, rook, intervening, opposing) {// console.log("canCastle", arguments)const opposingControlled = [...opposing.controlled]const board = boardAndPiece.board;const canCastle = !candidateMoves.inCheck &&!king.hasMoved &&rook &&rook.color === king.color &&!rook.hasMoved;if (!canCastle) return false;const pieceInTheWay = !!intervening.find(sq => board.pieceAt(sq));if (pieceInTheWay) return false;const passThruCheck = !!intervening.find(sq =>opposingControlled.find(opp => (opp.rank === sq.rank && opp.file == sq.file)))if (passThruCheck) return false;return true;}this.use(require('../SquareControl'))this.act({role: "board",cmd: "squaresControlledBy",board: boardAndPiece.board,color: opposingColor,}, (err, opposing) => {if (err) {reply(err);return;}const king = boardAndPiece.piece;// console.log(opposing.controlled)// add the removed K back inboardAndPiece.board.addPiece(king);const filteredMoves = candidateMoves.moves.filter(m =>!!!opposing.controlled.find(o => o.rank === m.rank && o.file === m.file))const kingSq = king.position;const inCheck = !!opposing.controlled.find(o => o.rank === kingSq.rank && o.file === kingSq.file)const additional = {}additional.inCheck = inCheck;additional.checkMated = (inCheck && filteredMoves.length === 0)const rank = additional.color === 'W' ? 1 : 8;let rook = boardAndPiece.board.pieceAt(`a${rank}`);let intervening = [`b${rank}`, `c${rank}`, `d${rank}`]additional.canQSideCastle = canCastle(king, rook, intervening, opposing)rook = boardAndPiece.board.pieceAt(`h${rank}`);intervening = [`f${rank}`, `g${rank}`]additional.canKSideCastle = canCastle(king, rook, intervening, opposing)candidateMoves.moves = filteredMoves;delete candidateMoves.moveVectors; // no longer valid, and no longer neededObject.assign(candidateMoves, additional);console.log(candidateMoves)reply(null, candidateMoves)});
};

Let start from the middle, at line 30. A service called squaresControlledBy is imported into the framework from SquareControl.js. It gathers all legal moves of the opposing side and calls those the controlled squares. We need this information because the King cannot move into a square ‘controlled’ by the enemy. The King cannot move into check.

让我们从中间的第30行开始。一个名为squaresControlledBy的服务 从SquareControl.js导入到框架中。 它收集了对方的所有合法举动,并称这些为受控方。 我们需要这些信息,因为国王无法进入被敌人“控制”的广场。 国王无法阻止。

There’s a tricky bit to this, and that is because the squaresControlledBy service relies on the legalMoves service. What can happen is that:

这有一个棘手的问题,这是因为squaresControlledBy 服务依赖legalMoves 服务。 可能发生的情况是:

  • legalMoves service is called for friendly piece

    legalMoves服务被称为友好作品

  • if the friendly piece is a King, squaresControlledBy is called for opposing side

    如果友善的棋子是国王, squaresControlledBy称为对面

  • squaresControlledBy requests legalMoves for all opposing sides pieces

    squaresControlledBy要求为所有相对的边块legalMoves

  • if legalMoves is requested for the opposing King, it will call service squaresControlledBy for its opposing side (our side).

    如果legalMoves要求为反对国王,它会调用服务squaresControlledBy 相反侧(我方)。

  • we’ve come full circle, and round and round we go…我们走了整整一个圈,然后又走了……

These cycles are one of the gotchas of microservices, and have to be carefully accounted for. I won’t go into the various strategies for dealing with this, but Seneca provides trace options for actions ( — seneca.print.tree)and service invocations ( — seneca.log.all) that can be helpful in debugging.

这些周期是微服务的陷阱之一,必须仔细考虑。 我不会讨论用于解决此问题的各种策略,但是Seneca提供了对操作( — seneca.print.tree)和服务调用( — seneca.log.all)跟踪选项,这些选项可能有助于调试。

The trick I used to avoid endless cycling was to temporarily remove the friendly king from the board (line 5) and later add it back in (line 46). I would say that best practice would be to not modify incoming service action data. There are potential hard-to-track side-effects. For purposes of finishing this series in a reasonable time frame, though, I will overlook a bit of fudging.

我用来避免无休止循环的诀窍是从板上暂时删除友好的国王(第5行),然后将其重新添加到第46行。 我会说,最佳实践是不修改传入的服务操作数据。 存在潜在的难以追踪的副作用。 但是,为了在合理的时间范围内完成本系列文章,我会忽略一些麻烦。

We push additional information (inCheck, castle options [lines 7–28], checkmate) to the reply by storing it in a local data structure and then using Object.assign() to merge it into the candidateMoves structure. The candidateMoves object will now have moves long with new properties provided by the additional object (lines 54–73).

通过将其他信息存储在本地数据结构中,然后使用Object.assign()将其合并到candidateMoves结构中,我们将其他信息( inCheck ,城堡选项[7–28行, checkmate )推送到reply 。 现在,带有附加对象提供的新属性的candidateMoves移动对象将移动很长时间(第54-73行)。

That wraps it up! Remember, if you found this series useful and engaging, please don’t forget to recommend it (click that little heart icon). Feedback always welcome.

结束了! 请记住,如果您发现本系列有用且引人入胜,请不要忘记推荐它(单击该小心脏图标)。 反馈随时欢迎。

Full source (including tests) for this Part 3 of the series can be found here.

该系列第3部分的完整资源(包括测试)可以在这里找到。

翻译自: https://www.freecodecamp.org/news/writing-a-chess-microservice-using-node-js-and-seneca-part-3-ab38b8ef9b0a/

node seneca

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

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

    node seneca (This is Part 1 of a three-part series [Part 2, Part 3]) (这是一个由三部分组成的系列文章的第1部分[ 第2 部分 , ...

  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. [深入理解Android卷一全文-第十章]深入理解MediaScanner
  3. 南通工学院计算机系97顾月,南通大学电气工程学院
  4. linux管道原子性写入,write(2)/ read(2)linux中进程之间的原子性
  5. 多目标粒子群优化算法_基于粒子群优化的投资组合优化研究
  6. AutoMake文档
  7. SQL存储过程的导入导出
  8. mysql binlog-row-image=minimal_十一:参数binlog_row_image(笔记)
  9. 有哪些将英文文献翻译为中文的网站或软件?
  10. coolie PK webpack 之三:模块构建
  11. 高等数学:第一章 函数与极限(6)极限存在准则、两个重要极限
  12. WPF QQ群发助手
  13. 双注入法/开路短路法
  14. scrapy安装时遇到问题怎么解决
  15. 小米应用开发者文档(标注需要注意的地方)
  16. java同时访问多个接口
  17. 【转】网站流量UV是什么意思?什么是流量UV?
  18. 弗里德里克·弗朗索瓦·肖邦
  19. Transformer在细粒度分类上的应用
  20. 大数据实训基地建设方案分享

热门文章

  1. 阿里P8亲自教你!熬夜整理华为最新Java笔试题
  2. java 多重属性_最全面的44个Java 性能调优细节
  3. 餐厅点餐系统:测试与部署
  4. 微信公众号接入开发者模式,服务器配置Token验证
  5. C#中实现对象的深拷贝
  6. 管理Sass项目文件结构
  7. 剑指Offer_52_正则表达式匹配
  8. 通过path绘制点击区域
  9. Android RecyclerView (一) 使用完全解析
  10. Bootstrap页面布局14 - BS按钮群组