Закончив серию из трех частей на написании двигателя правил с микросервисами Seneca.
Части 1 & 2 этой серии покрыты:
- Модуль Microvies The Seneca Node.js
- Как написать услугу, как определить его по образцу и как это позвонить
- Как строить сервисные звонки вместе
- Как расширить существующую услугу
По пути я размышлял о том, что должна возвращаться услугу. Я пришел к выводу, что возвращая объект данных (JSON в этом случае) был наиболее гибким. Это позволяет услугами Украсьте вывод без влияния на существующих клиентов службы.
Украсьте? Этим я имею в виду промежуточные результаты могут поддерживаться как средство отслеживания информации, которая может быть полезной позже, на службу еще не написано. В настоящем деле у меня была Rawphoves Сервис, который вернул список ходов. Это было немедленно достаточным для клиентов, которые у меня было. Служба рассчитана перемещается вдоль векторов движения и объединила их в одномерное массив.
Позже я обнаружил, что эти векторы движения будут пригодны, когда Юридические комолы Сервис, написанный позже, необходимый для учета дружественных произведений, которые блокируют движение. Векторы сделали бы эти расчеты проще и эффективнее, но они были «выброшены» Rawphoves услуга.
Чтобы вернуться и добавить векторы (в дополнение к списку перемещения) означали изменение клиентов оригинальной службы для приема объекта, а не массив. Правда, я мог бы сделать оригинальный сервис состоятельными, но это был бы излишек. У меня был выбор: рефактору службы и его клиенты, или Иметь дело с этим ™ ️ . В части 2 я выбрал последний.
Тем не менее, в этом рассрошении время пришло в рефактору. Rawphoves Теперь возвращается {движется, двигатели} И восходящие клиенты службы могут выбрать, что обратить внимание. Требуется забота, хотя, что движется и Movevectors в синхронизации всегда.
Посмотрим, что такое преимущество. В исходном коде, нахождение Юридические комолы был вовлеченный процесс, если дан только кусок, перемещение списка, и дружественные части в другом месте на доске ( Пример) Отказ Сравните этот код к одному, которое использует 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)
}
}Много, намного проще … и более эффективно. Функция упаковки экспортируется и используется Юридические комолы услуга .
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);
//...Вернуться к игре
Обзор услуг
Все запросы на движение обрабатываются юристы Сервис, который опирается на несколько других услуг и методов помощника:
- Позвоните в
Rawphovesуслуга Это вернет все движения одинокого произведения на виртуальной шахматной доске 15×15 (называемые маской перемещения ). Объясняется в части 1 - Позвоните в базу
Юридические комолыуслуга Это закреплено Маска движения На краю «реальной» доски 8×8, с надлежащим Алгебраические координаты Отказ Объяснено в части 2 - Позвоните в переопределение
Юридические комолыСервис Если есть доска в составе входящей части входящего сообщения (шаблон обслуживания), то для учета наличия удобренных и противоположных произведений, потому что это повлияет на движение. Объяснено в этой части (часть 3).
Итак, Часть 2 Позаботились о дружественных частях, блокирующих другие дружеские кусочки, но теперь есть эти раздражающие части вражеских произведений. Как дружественные части, части врага могут блокировать движение, но они также могут быть захвачены. При некоторых условиях вражеские части могут даже увеличить наши варианты движения.
Тогда есть замка: единственный ход, где две части могут сдвинуть свое положение одновременно. Особые соображения применяются, некоторые из которых вовлекают вражеские произведения.
Королева, ладья и епископ
Новые правила, связанные с частями врага, распространяются или изменили оригинал Юридические комолы Сервис в части 2, которые имели дело только с дружелюбными частями. Новое расширение микросервиса необходимо знать, если блокирующая часть друга или врага. Если друг, то движение заблокировано на площади раньше. Если враги, то движение заблокировано квадратом противоположной части (захватом). В списке юридических ходов возвращаются кусочком, мы обозначим захваты, установив получил Флаг, наряду с типом вражеского куска для захвата.
Vectorchecks Способ помощника, показанный в предыдущем списке Gist, обрабатывает все векторные движения для королевы, ладьи и епископа.
рыцарь
Рыцари прыгают вокруг доски, поэтому заблокированы только дружеские кусочки, которые находятся на одном из его потенциальных посадочных квадратов. Част врага не блокирует, но будет захвачен, если бы на него приземлился рыцарь. Метод, используемый Юридические комолы Сервис легко написать.
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]
};
}Пешка
Пешки сначала кажутся довольно простыми случаем. Пешка заблокирована, если какая-либо стоит ли друг или враг перед ним. Но это может сдвинуть один квадратный по диагонали вперед, чтобы захватить врага, который сидит на этом квадрате.
Есть также RU Passant Правило, где пешка может захватить соседнюю пешку противника, что просто Переместил два квадрата на предыдущем повороте:
И тогда есть проблема обязательного продвижения после того, как пешка достигла 8-го ранга. Смущает, это относится к восьмую званию перед пешкой, который был бы первым званием координат Совета, если играет черный.
Все эти соображения делают довольно вовлеченный набор правил для определения вариантов движения пешки. Это может быть найден в сопровождении Исходный код в Github.
король
Пешка была немного работой, но король еще больше. Есть несколько условий:
- Потенциальная площадь движения, контролируемого вражеским произведением? Устранить этот вариант.
- Является ли король в чеке? Если это так, это Должен Переместить этот поворот * Если это в чеке, и не может выйти из чека, игра закончиться! Шах и мат! * Если это не в чеке, но нет никаких других юридических ходов любым дружелюбным произведением на доске, гласит!
- Может ли король замок (королева или король сторона)? * King находится в чеке: Нет. * Король ранее переехал: Нет. * Rook ранее переехал: Нет. * Промежущиеся квадраты между K и R заняты: № * Промежущиеся квадраты пустые, но управляемые вражеским произведением: Нет. * В противном случае: Да.
Эта услуга я сломаю в деталях. Как вы можете вспомнить, Юридические комолы Сервис разбивается на две части. Одна часть относится к штуку, как будто она только на доске. Другая часть имеет дело с дружелюбными и противоположными частями. Давайте посмотрим на список:
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);
}
});
});Для каждого куска, но король, мы просто называем базовый сервис (через метод Seneca Framework’s Prior () ), а затем метод помощника Юридические мозгиWithboard () , части которых были перечислены в предыдущих циклах этого поста.
Если кусок – король, дополнительный помощник методом ЮридическиеОгревывиткинг () называется. Параметры вызова – Это Ссылка, A Msg Объект, содержащий доску и кусок перемещения (король), Результат2 который был пришел с базы Юридические комолы Сервисный звонок (это содержит информацию о движении), а Ответить Перезвоните.
Пройдите немного кода, поэтому я буду ссылаться на разделы по номеру строки:
module.exports = function (boardAndPiece, candidateMoves, reply) {
const opposingColor = boardAndPiece.piece.color === 'W' ? 'black' : 'white';
//temporarily remove the K to avoid cycles
boardAndPiece.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 in
boardAndPiece.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 needed
Object.assign(candidateMoves, additional);
console.log(candidateMoves)
reply(null, candidateMoves)
});
};Пусть начать с середины, в строке 30. Сервис под названием Squarescontrolledby импортируется в рамки от Squarecontrol.js Отказ Он собирает все юридические ходы противоборствующей стороны и призывает этих контролируемых квадратов. Нам нужна эта информация, потому что король не может перейти на квадрат «контролироваться» врагом. Король не может переехать в проверку.
Есть сложный бит к этому, и это потому, что Squarescontrolledby Сервис зависит от Юридические комолы услуга. Что может произойти, это то, что:
Юридические комолыСервис вызывается для дружеской части- Если дружелюбный кусок – король,
Squarescontrolledbyвызывается для противоположной стороны SquarescontrolledbyЗапросыЮридические комолыДля всех противоборствующих сторон кусочки- Если
Юридические комолыЗапрошено для противоборствующего короля, он позвонит услугуSquarescontrolledbyдля его противоположная сторона (наша сторона). - Мы пришли полный круг, а раунд и раунд мы идем …
Эти циклы являются одним из Gotchas микросервисов и должны быть тщательно учитываются. Я не буду входить в различные стратегии для решения этого, но Seneca предоставляет варианты трассировки для действий (- SENECA.PRINT.TREE) и сервисные вызовы (- SENECA.LOG.ALL) Это может быть полезно в отладке.
Трюк, который я использовал для того, чтобы избежать бесконечного велосипеда, должен был временно удалить дружественный король из доски (строка 5) и позже добавьте его обратно (линия 46). Я бы сказал, что лучшая практика будет не изменять данные о действиях входящих услуг. Есть потенциальные трудностей побочные эффекты. В целях завершения этой серии в разумных временных рамках я буду упускать из виду немного обременения.
Мы нажимаем дополнительную информацию ( Incheck , варианты замка [строки 7-28], CheckMate ) к Ответить Сохраняя его в локальной структуре данных, а затем используя Объект.assign () объединить его в кандидатемовы ... состав. кандидатемовы … Теперь объект будет длиться длиной с новыми свойствами, предоставленными дополнительным объектом (строки 54-73).
Это обертывает это! Помните, что если вы нашли эту серию полезными и участвующими, пожалуйста, не забудьте порекомендовать его (нажмите на это маленькое значок сердца). Обратная связь всегда приветствуется.
Полный источник (включая тесты) для этой части 3 серии можно найти здесь Отказ
Оригинал: “https://www.freecodecamp.org/news/writing-a-chess-microservice-using-node-js-and-seneca-part-3-ab38b8ef9b0a/”