java通用象棋游戏

The Universal Chess Interface (UCI) has been around a long time and used by many chess engines. What does GraphQL bring to the mix?

通用国际象棋界面(UCI)已经存在了很长时间,并且被许多国际象棋引擎使用。 GraphQL带来了什么?

I had some email exchanges with an owner of a chess website recently, and he asked me what I knew about UCI and websockets. That got me looking closer at UCI and thinking about how and why one would wrap a GraphQL schema around it.

最近,我与一个国际象棋网站的所有者进行了一些电子邮件往来,他问我对UCI和Websockets的了解。 这使我更加了解UCI,并思考了如何以及为什么将GraphQL模式包装在它周围。

通用国际象棋界面 (The Universal Chess Interface)

The UCI has been around for over a decade, and is based on standard I/O messaging between a chess engine and its client (usually a graphical UI). The client submits a message to the chess engine, and the engine may send back a response. I say may because UCI doesn’t require a response for some incoming messages to the chess engine.

UCI已经存在了十多年 ,它基于国际象棋引擎与其客户端(通常是图形UI)之间的标准I / O消息传递。 客户向国际象棋引擎提交一条消息,该引擎可能 发回回应。 我说的可能是因为UCI不需要某些传入邮件的国际象棋引擎的响应。

The engine also might send back more than one response. During game analysis, the engine will be sending back info packets detailing it’s thinking. The client says what the start position is, tells it “Go”, and the engine keeps going until it arrives at a best move. During the process, the engine streams back messages about what it’s thinking.

引擎也可能 发送回多个响应。 在游戏分析过程中,引擎将发回详细说明其思想的信息包。 客户说出起始位置是什么,告诉它“开始”,发动机一直运转直到到达最佳移动位置。 在此过程中,引擎会流回有关其想法的消息。

The UCI specification is short, and you don’t need a deep understanding of it to see the basics of how it works — and it has worked well so far, so why monkey with it?

UCI规范很短,您不需要深入了解它即可了解其工作原理,而且到目前为止效果很好,那么为什么要继续使用它呢?

If the engine resides on a remote server, then just open a websocket and do as you would normally do. That works, of course, but it doesn’t hurt to look at the pros and cons of doing things slightly differently.

如果引擎位于远程服务器上,则只需打开一个websocket并按照通常的方式进行即可。 当然,这是可行的,但是以稍微不同的方式看待事情的利弊也不会受到损害。

国际象棋引擎与国际象棋服务器 (Chess Engine versus Chess Server)

I want to start by mocking up a UCI service. A next step will be to build a functional prototype and, as always seems to be the case, its not hard to find a Node.js package that helps with most of the work.

我想从模拟UCI服务开始。 下一步将是构建功能性的原型,并且似乎总是如此,找到有助于大多数工作的Node.js包并不难。

One of the more popular engines is called Stockfish. Someone has taken the trouble of transpiling it from C++ to JavaScript so that the engine can be run wholly in Node.js.

一种最受欢迎​​的引擎称为Stockfish 。 有人将其从C ++转换为JavaScript的麻烦使引擎可以完全在Node.js中运行。

It’s this simple:

就这么简单:

  • create and cd into a folder
    创建并CD进入文件夹
  • npm init

    npm init

  • npm install stockfish

    npm install stockfish

  • node node_modules/stockfish/src/stockfish.js

    node node_modules/stockfish/src/stockfish.js

And now you are in a Stockfish command shell, and can start typing in commands.

现在,您处于Stockfish命令外壳中,并且可以开始输入命令。

The first thing to do is to fire up the UCI interface by typing uci. You should get a response like this:

首先要做的是通过键入uci来启动UCI接口。 您应该得到这样的响应:

id name Stockfish.js 8
id author T. Romstad, M. Costalba, J. Kiiski, G. Linscottoption name Contempt type spin default 0 min -100 max 100
option name Threads type spin default 1 min 1 max 1
option name Hash type spin default 16 min 1 max 2048
option name Clear Hash type button
option name Ponder type check default false
option name MultiPV type spin default 1 min 1 max 500
option name Skill Level type spin default 20 min 0 max 20
option name Move Overhead type spin default 30 min 0 max 5000
option name Minimum Thinking Time type spin default 20 min 0 max 5000
option name Slow Mover type spin default 89 min 10 max 1000
option name nodestime type spin default 0 min 0 max 10000
option name UCI_Chess960 type check default false
option name UCI_Variant type combo default chess var chess var giveaway var atomic var crazyhouse var horde var kingofthehill var racingkings var relay var threecheck
option name Skill Level Maximum Error type spin default 200 min 0 max 5000
option name Skill Level Probability type spin default 128 min 1 max 1000
uciok

It shows what the option settings are set to and then returns uciok, meaning the interface is ready. The next step is to set options and then call isready, and when the engine responds readyok, it can start analyzing a chess position.

它显示了选项设置所设置的内容,然后返回uciok ,这意味着界面已准备就绪。 下一步是设置选项,然后调用isready ,当引擎响应readyok ,它可以开始分析棋的位置。

I won’t actually be using this engine for my mock implementation, but it does come in handy if I want to examine what a command does using a real engine.

我实际上不会在模拟实现中使用此引擎,但是如果我想检查使用真实引擎的命令的功能,它确实会派上用场。

In a real server implementation, I would be firing up one engine per client (or perhaps more). GraphQL helps me define an API that would support multiple clients running multiple engines.

在真实的服务器实现中,我将为每个客户端启动一个引擎(或可能更多)。 GraphQL帮助我定义了一个API,它将支持运行多个引擎的多个客户端。

GraphQL (GraphQL)

For this mock, I’ve divide up the UCI component into HTTP requests of a call/response nature, and websocket subscriptions for handling streaming responses. This means that a socket is only open if the user wants to subscribe to detailed information about what the engine is thinking. Furthermore, I can refine the number and types of info messages I receive on the client, so that socket traffic is minimized.

对于此模拟,我将UCI组件分为具有呼叫/响应性质的HTTP请求和用于处理流响应的websocket订阅。 这意味着只有在用户希望订阅有关引擎在想什么的详细信息时,套接字才会打开。 此外,我可以优化在客户端上收到的信息消息的数量和类型,以使套接字流量最小化。

每个命令都会得到一个响应 (Each command gets a response)

Because client-server interaction is happening (in most cases) over unreliable HTTP, it’s important that the client (running on the browser) knows that its message got through to the server. The UCI command setoption, for instance, doesn’t send a response back according to the specification.

由于客户端-服务器交互(大多数情况下)是通过不可靠的HTTP发生的,因此(在浏览器上运行的)客户端知道其消息已到达服务器很重要。 例如,UCI命令setoption不会根据规范发送回响应。

That’s fine for an interface based on reliable sockets, not so good for HTTP requests. GraphQL ensures that there is a response sent back to every received request, if only to acknowledge that the request was received.

对于基于可靠套接字的接口而言,这很好,而对于HTTP请求而言则不太好。 GraphQL确保仅在确认已接收到请求的情况下,才将响应发送回每个收到的请求。

每个命令及其参数都是类型安全的 (Each command and its arguments are type-safe)

GraphQL interfaces are schema-based, UCI interfaces are not (they’re based on descriptive text in the specification). If a client to sends an invalid command, the chess engine server should never have to deal with it. By defining UCI in terms of types in GraphQL, I can waylay an errant command at the API level — in GraphQL — before it gets to the engine.

GraphQL接口是基于架构的,而UCI接口不是(它们基于规范中的描述性文本)。 如果客户端发送无效命令,则象棋引擎服务器永远不必处理该命令。 通过根据GraphQL中的类型定义UCI,我可以在GraphQL中在API级别处放置错误命令,然后再到达引擎。

GraphQL解析器可以将响应分解为JSON结构 (GraphQL resolvers can decompose responses into JSON structures)

JavaScript is the language of the internet, and GraphQL returns JSON responses. By having the GraphQL resolvers take a UCI response and break it down in a fine-grained and structured way, the client is alleviated of the UCI response parsing task.

JavaScript是互联网的语言,而GraphQL返回JSON响应。 通过让GraphQL解析器获取UCI响应并以细粒度和结构化的方式将其分解,可以减轻客户端的UCI响应解析任务。

我可以使用Apollo GraphQL工具轻松模拟我的API (I can easily mock my API using Apollo GraphQL Tools)

After designing an API, but before heading off to implementation land, it’s useful to first check the API look and feel of using mocks. The graphql-tools package makes this easy and painless. You can even mix mocks with real resolvers, giving you the option of iterative implementation of your API.

在设计了API之后,但在进行实施之前,先检查一下使用模拟的API外观是很有用的。 graphql-tools软件包使此操作变得简单而轻松 。 您甚至可以将模拟与真实的解析器混合使用,从而可以选择迭代实现API。

我可以通过GraphiQL服务与API进行交互 (I can interact with the API through the GraphiQL service)

GraphiQL is the interactive service that can be run atop a GraphQL server. This is convenient for doing ad hoc testing of the API, based on either a mock or implementation.

Graph i QL是可以在GraphQL服务器之上运行的交互式服务。 这对于基于模拟或实现对API进行临时测试非常方便。

继续模拟! (On to the Mocking!)

Let’s take a look at the dependencies first:

让我们先来看一下依赖关系:

"dependencies": {"apollo-server-express": "^1.3.2","babel-cli": "^6.26.0","babel-preset-env": "^1.6.1","express": "^4.16.2","graphql": "^0.12.3","graphql-subscriptions": "^0.5.7","graphql-tag": "^2.7.3","graphql-tools": "^2.21.0","stockfish": "^8.0.0","subscriptions-transport-ws": "^0.9.5"},"devDependencies": {"casual": "^1.5.19","randexp": "^0.4.8"},

I’m calling this server chessQ, and the server itself will be based on apollo-server-express, the Apollo Group’s GraphQL server implementation. The stockfish.js package, mentioned earlier, is included as an embedded engine. Though this mock doesn’t use it, it’s there for reference. In a real implementation, one would probably access an externally running engine.

我将此服务器称为ChessQ ,服务器本身将基于Apollo Group的GraphQL服务器实现apollo-server-express 。 前面提到的stockfish.js软件包是作为嵌入式引擎提供的。 尽管此模拟程序未使用它,但仍可供参考。 在实际的实现中,可能会访问外部运行的引擎 。

Included is casual and randexp for helping with the mocks. Finally, graphql-subscriptions and subscriptions-transport-ws will handle the streaming messages coming back from our mock while it is pretending to analyze.

其中包括casualrandexp ,可帮助您进行randexp 。 最后, graphql-subscriptionssubscriptions-transport-ws将在假装分析时处理从我们的模拟返回的流消息。

ChessQ模式 (The chessQ schema)

Let me first say that I haven’t spent time polishing up the schema, so consider it a first draft. It’s functional, but it will probably change as I continue to develop it. At the end of this article, I’ll link to a stable branch that corresponds to what is described here. I won’t be going into painstaking detail on the code, but will link to relevant source in GitHub where appropriate. Watch for those.

首先让我说我没有花时间完善模式,因此将其视为初稿。 它具有功能,但是随着我继续开发它可能会改变。 在本文的结尾,我将链接到一个稳定的分支,该分支与此处描述的相对应。 我不会详细介绍代码,但会在适当的地方链接到GitHub中的相关源代码。 注意那些。

First thing is to define the top-level queries. These are the entry points for the client:

首先是定义顶级查询 。 这些是客户端的入口点:

type Query {createEngine: EngineResponseuci(engineId: String!): UciResponse!register(engineId: String!, name: String, code: String): StringregisterLater(engineId: String!): StringsetSpinOption(engineId: String!, name: String!, value: Int!): String!setButtonOption(engineId: String!, name: String!): String!setCheckOption(engineId: String!, name: String!, value: Boolean!): String!setComboOption(engineId: String!, name: String!, value: String!): String!quit(engineId: String!): String!isready(engineId: String!): String!}

The createEngine request will return an EngineResponse, inside of which is an engine instance identifier that is used for subsequent requests:

createEngine请求将返回EngineResponse ,其中包含用于后续请求的引擎实例标识符:

{"data": {"createEngine": {"engineId": "46d89031-03c3-4851-ae97-34e4b5d1d7c6"}}
}

The uci request will return a UciResponse detailing the current option settings. In the GraphQL schema, each type of option (spin, check, button, and combo) has its own specific fields:

uci 请求将返回详细说明当前选项设置的UciResponse 。 在GraphQL模式中,每种类型的选项(旋转,选中,按钮和组合)都有其自己的特定字段:

interface Option {name: String!type: String!}type SpinOption implements Option {name: String!type: String!value: Int!min: Int!max: Int!}type ButtonOption implements Option {name: String!type: String!}type CheckOption implements Option {name: String!type: String!value: Boolean!}type ComboOption implements Option {name: String!type: String!value: String!options: [String!]!}

A mock uci query might be:

模拟的uci query可能是:

query uci {uci(engineId: "46d89031-03c3-4851-ae97-34e4b5d1d7c6") {uciokayoptions {nametype... on SpinOption {valueminmax}}}
}

and the response:

和响应:

{"data": {"uci": {"uciokay": true,"options": [{"name": "Porro tempora minus","type": "check"},{"name": "Id ducimus","type": "combo"},{"name": "Aliquam voluptates","type": "button"},{"name": "Voluptatibus illo ullam","type": "spin","value": 109,"min": 0,"max": 126},{"name": "Temporibus et nisi","type": "check"}]}}
}

Technically, some of these commands could be thought of as Mutations, not Queries, since they change the state of the engine. But Mutations in GraphQL are primarily about sequential order-of-execution and that does not apply in this case: any option can be set in any order.

从技术上讲,由于其中某些命令会更改引擎的状态,因此可以将它们视为“变异”而不是“查询”。 但是GraphQL中的突变主要与顺序执行顺序有关,在这种情况下不适用:可以按任何顺序设置任何选项。

Ultimately, each engine instance will need to maintain some indication of its state (not implemented in this mock). These might be:

最终,每个引擎实例将需要维护其状态的某种指示(此模拟中未实现)。 这些可能是:

enum eEngineState {CREATEDINITIALIZEDREADYRUNNINGSTOPPED}

If for instance, a go command is sent before the engine state is READY, then that would be an error.

例如,如果go 在引擎状态为READY之前发送命令,这将是一个错误。

就绪架构 (The Ready Schema)

When the engine is READY, three new commands are possible:

当引擎为READY时,可以使用三个新命令:

  • ucinewgame: tell the engine a new game has started

    ucinewgame :告诉引擎新游戏已经开始

  • position: tell the engine what the starting position is (along with any moves from that position)

    position :告诉引擎起始位置是什么(以及从该位置开始的任何移动)

  • go: start the engine!

    go :启动引擎!

Before issuing the go command, the client has the option to subscribe to any info messages streaming in through the websocket (otherwise, there will be just a BestMove HTTP response when the engine is finished).

在发出go命令之前,客户端可以选择通过websocket订阅流式传输的任何信息消息 (否则,只有BestMove 引擎完成时的HTTP响应)。

Details on how to set up a subscription service using graphql-subscriptions can be found elsewhere, so here I will focus on the schema and resolver implementation.

有关如何使用graphql-subscriptions设置订阅服务的详细信息,可以在其他地方找到,因此这里我将重点介绍模式和解析程序的实现。

The schema defines the types of Subscriptions available. For this mock, there is just one:

该模式定义了可用的Subscriptions类型。 对于此模拟,只有一个:

type Subscription {info: Info}

The Info type, like the Option type, is a union of several specific info structures:

Info类型与Option类型一样,是几个特定信息结构的联合:

type Score {cp: Int!depth: Int!nodes: Int!time: Int!pv: [Move!]!}type Depth {depth: Int!seldepth: Int!nodes: Int}type Nps {value: Int!}type BestMove {value: Move!,ponder: Move}union Info = Score | Depth | Nps | BestMove

The precise meaning of these Info messages is irrelevant to this discussion. The important thing is to know that they come in any order, except for the BestMove message, which is last.

这些Info消息的确切含义与此讨论无关。 重要的是要知道它们以任何顺序出现,除了最后移动的BestMove消息。

The client subscribes to info messages using a subscription request like the following:

客户端使用如下subscription请求来subscription信息消息:

subscription sub {info {... on Score {pv}... on BestMove {value}}
}

There’s a resolver to handle the Subscription request, which uses methods in the graphql-subscriptions package:

有一个解析器可以处理Subscription请求,它使用graphql-subscriptions包中的方法:

import {PubSub, withFilter} from 'graphql-subscriptions';
...resolvers:
...
Subscription: {info: {subscribe: withFilter(() => pubsub.asyncIterator(TOPIC), (payload, variables) => {return true})}}...

In this subscription resolver, the the function passed to withFilter passes every message back. But a real subscribe resolver could be more discriminating based on parameters passed in by the client.

在此订阅解析器中,传递给withFilter的函数将每条消息传回。 但是,真正的订阅解析器可能会根据客户端传递的参数来进行区分。

看到它在行动 (Seeing it in action)

You can query, mutate, and subscribe in GraphiQL, so there’s no need to write a client for testing purposes. The one gotcha is that GraphiQL will enter “subscription” mode once a subscription is requested, and won’t happily respond to further commands.

您可以在GraphiQL中进行查询,变异和订阅,因此无需编写用于测试目的的客户端。 一个陷阱是,一旦请求订阅,GraphiQL将进入“订阅”模式,并且不会高兴地响应其他命令。

The solution is to have two GraphiQL tabs open in your browser, one for issuing queries and mutations, and the other for listening to subscribed messages.

解决方案是在浏览器中打开两个GraphiQL选项卡,一个选项卡用于发出查询和变异,另一个选项卡用于侦听订阅的消息。

Download the chessQ package, run npm install and then npm run dev . The chessQ mock application should now be running.

下载chessQ软件包,运行npm install ,然后npm run dev 。 现在,chessQ模拟应用程序应该正在运行。

Open two tabs to http://localhost:3001/graphiql.

打开两个选项卡以http:// localhost:3001 / graphiql 。

In one tab, enter:

在一个标签中,输入:

subscription sub {info {__typename... on Score {pv}... on BestMove {value}}
}

You’ll see a message that says:

您会看到一条消息,内容为:

"Your subscription data will appear here after server publication!"

To generate messages, there is a go resolver (shown below) that iterates through a static set of info messages and publishes them one by one. Because the subscriber pane will only show one message at a time, there’s a simple sleep implementation that slows down the messaging so that you can see them fly by:

要生成消息, go 解析器(如下所示)迭代一组静态的信息消息并逐一发布。 由于订户窗格一次只会显示一条消息 ,因此有一个简单的sleep实现可以减慢消息传递的速度,以便您可以看到它们飞过:

function sleep(ms) {return new Promise(resolve => {setTimeout(resolve, ms)})
}
...resolvers: {...Mutation: {go: async () => {let info;for (info of InfoGenerator()) {pubsub.publish(TOPIC, {info})await sleep(1000)}return info;}},...

Finally, in the non-subscription tab, start the analysis with go:

最后,在“非订阅”标签中,使用go开始分析:

mutation go {go {__typenamevalue}
}

While this tab is awaiting the go response showing the BestMove, the subscription tab will be catching info messages and displaying them one-by-one.

虽然这片正在等待go展示响应BestMovesubscription选项卡将被捕获信息,然后显示他们一个接一个。

进一步的想法 (Further Thoughts)

Before rolling forth from mock to implementation, a couple of notes:

从模拟过渡到实现之前,请注意以下几点:

The simple pub/sub mechanism used in this example is neither robust or scalable. That’s okay, because there are Redis and RabbitMQ implementations of graphql-subscription that are. A more refined subscription specification could also be defined and give fine-grained control to the subscriber as to which messages are received.

本示例中使用的简单发布/订阅机制既不健壮也不具有可伸缩性。 没关系,因为这里有graphql-subscription的Redis和RabbitMQ实现。 还可以定义更精细的订阅规范,并向订户提供有关接收到哪些消息的细粒度控制。

Not a lot of thought was given to managing websocket lifetime in this mock, which is something that needs to be considered if serving a large number of users.

在此模拟中,对于管理websocket的生存期没有太多考虑,如果服务于大量用户,则需要考虑这一点。

All source code for this article can be found here.

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

翻译自: https://www.freecodecamp.org/news/mocking-a-graphql-wrapper-around-the-universal-chess-interface-1c5bb1acd821/

java通用象棋游戏

java通用象棋游戏_在通用国际象棋界面周围模拟GraphQL包装器相关推荐

  1. Java网络象棋游戏(功能版)

    Java 网络象棋游戏(功能版) 网络象棋目录 客户端 走棋 悔棋 认输 保存棋谱 演示棋谱(自动.手动) 服务器端 与客户端相同 网络象棋目录 客户端 走棋 悔棋 认输 保存棋谱 演示棋谱(自动.手 ...

  2. js 象棋游戏 _ 支持双方在线对战

    说明:实在对不住诸位,的确是我弄错了,views/index.ejs里的对 socket.io.js的引用使用了我本地的绝对路径,需要修改为<script src="/socket.i ...

  3. 【笔记】JAVA 中国象棋游戏 部分源码

    /**  * 演示棋谱类  *  * @author cnlht  */ public class Demon extends JPanel implements ActionListener, Ru ...

  4. java面向对象跑马游戏_面向“对象”和“过程”

    在学习软工视频的时候总是想不明白,面向对象和面向过程,这两个东西字面上肯定能分得清,这个是没有问题的,但是具体到怎样算是面向对象,怎样又是面向过程?这样一问,大概模糊的想法还是有的,毕竟是模糊的. 面 ...

  5. c# 基于layui的通用后台管理系统_简单通用的Java后台管理系统

    前言 这套Base Admin是一套简单通用的后台管理系统,主要功能有:权限管理.菜单管理.用户管理,系统设置.实时日志,实时监控,API加密,以及登录用户修改密码.配置个性菜单等 技术栈 前端:la ...

  6. java dao修改语句_一个通用的DAO模型实现增删改查

    首先三个架包: mysql-connector-java-jar commons-dbcp-1.4jar commons-pool-1.5.5jar 导进去: (从上往下一次调用,实现功能) ---- ...

  7. java field 获得值_反射通用获取字段值

    像之前回答的那样,您应该使用: Object value = field.get(objectInstance); 有时更喜欢的另一种方法是动态调用getter.示例代码: public static ...

  8. gdpr通用数据保护条例_关于通用数据保护法规(GDPR),您需要了解的15件事

    gdpr通用数据保护条例 The General Data Protection Regulation (GDPR) comes into force on 25th May 2018. Design ...

  9. java吃豆游戏_利用java编写的精灵吃豆的游戏

    学完线程的时候做了一个精灵吃豆的小游戏. 窗体类: package www.csdn.net.zuoye; //用窗体实现精灵吃豆子的小游戏 import java.awt.Color; import ...

最新文章

  1. GPU、FPGA芯片成为增强机器学习能力的“左膀右臂”
  2. SpringBoot+MyBatisPlus+ElementUI一步一步搭建前后端分离的项目(附代码下载)
  3. DHCP和DHCP中继功能与配置
  4. mysql隔离级别和mvcc_数据库MVCC和隔离级别的关系是什么?
  5. web文件服务器 开源,10个开源的基于WEB的文件管理器
  6. Linux Signal及Golang中的信号处理
  7. Sentinel服务熔断只配置fallback_客户自定义限流处理_削峰填谷_流量控制_速率控制_服务熔断_服务降级---微服务升级_SpringCloud Alibaba工作笔记0050
  8. html字颜色代码,css 字体颜色(css color)
  9. 存储图片到数据库中的一个异常信息
  10. 毕设题目:Matlab数字信号去噪
  11. 利用GPU加速的软件
  12. 洗衣机测试点 思维导图
  13. 高性能消息中间件 NSQ 解析-应用实践
  14. Appium移动端自动化测试--搭建模拟器和真机环境一
  15. 求解线性同余方程--扩展欧几里得
  16. Centos7 Python3.6+Qt5.12.9+ PyQt5.12+Sip v5+QScintilla-2.10+Eric6
  17. 爬取京东图书商品信息
  18. Cocos2d-x 屏幕适配
  19. hdu 2094 “产生冠军“
  20. OUC暑期培训(深度学习)——第四周学习记录:MobileNetV1,V2,V3

热门文章

  1. 【Elastic Stack(一)】Elastic Stack简介
  2. ant 修改组件默认样式属性
  3. 【微信小程序】异步请求,权重,自适应宽度并折行,颜色渐变,绝对定位
  4. OC 消息转发实现多继承
  5. Java基础学习总结(1)——equals方法
  6. JavaScript 事件冒泡简介及应用(转)
  7. win7系统下载 ghost win7 Sp1 32位纯净3月版
  8. log4j日志记录级别是如何工作?
  9. JAVA面向对象-----final关键字
  10. Visual C++ 2012/2013的内存溢出检測工具