サーバーレス将棋 AI - WebSocket 編

Web ブラウザからクラウド上の将棋 AI を呼ぶアプリケーションを作りました。
https://d3iczbzr06u2at.cloudfront.net/

<a href="https://d3iczbzr06u2at.cloudfront.net/" target="blank"><img width="1129" alt="Serverless_Shogi_DEMO.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15173/dc70e6af-9b6f-0204-12a2-63f1649def7d.png"></a>

将棋 AI が [伝説の ▲52 銀](https://book.mynavi.jp/shogi/detail/id=88815) を発見する瞬間を、ブラウザで体験できます！

# 背景

三年前、[サーバーレス将棋 AI ☖](https://qiita.com/na-o-ys/items/b04ce27732c82b6beb9b) という記事を書きましたが、あれからサーバーレスを取り巻く技術的な環境は大きく変わりました。

AWS Lambda の実行時間・メモリ上限は増え、[API Gateway が WebSocket に対応](https://aws.amazon.com/jp/blogs/news/announcing-websocket-apis-in-amazon-api-gateway/) しました。また [AWS Amplify](https://aws-amplify.github.io/docs/js/start) の登場で、バックエンド (Lambda と種々のデータソース) とフロントエンドを一元的に、開発から本番デプロイまでほとんど全てのフェーズで具体的なインフラを意識せずに実現できるようになりました。

現在のサーバーレス技術で、あらためて将棋 AI を利用してどんなアプリケーションが作れるのか、実証のためにこのデモを作りました。

# アーキテクチャ

![sshogi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15173/3642643e-d322-dd97-5f22-f21e301b040e.png)

ブラウザアプリケーション、API Gateway、AWS Lambda の 3 つのコンポーネントで構成されています。
AWS Lambda 上で将棋 AI を走らせます。2019 年現在最も強い AI のひとつであるやねうら王 (探索部) と illqha4 (評価関数) をデプロイしています。
API Gateway はブラウザと Lambda の間に立つ WebSocket サーバーとして振る舞います。クライアント (ブラウザ) からのリクエストに応じて Lambda を起動し、Lambda からのメッセージをクライアントに WebSocket で非同期送信します。

# 実装

全てのソースコードは [こちらのリポジトリ](https://github.com/na-o-ys/shogi-api/tree/master) にまとまっています。

### Serverless Framework (AWS Lambda + API Gateway)

本当は Amplify でやりたかったんですが、2019/12 現在 API Gateway Websocket は [非対応](https://github.com/aws-amplify/amplify-js/issues/3591) の模様。
今回は Serverless Framework で実装しました。AWS Lambda と API Gateway の実装・定義とデプロイを cli で完結することができます。大きくこのような流れです。

1. 将棋 AI を Lambda 用にビルド
2. Lambda 関数の実装と API Gateway (Websocket イベント) の定義
3. デプロイ

ビルドについては [こちらの記事 (やねうら王を AWS Lambda で動かす)](https://qiita.com/na-o-ys/items/e4f32ade2927f400b11b) をご覧ください。
Lambda 関数では、API Gateway のリクエストを受けて将棋 AI プロセスを起動し、AI の標準出力を改行で分割し Websocket に送信しています。

### React & Amplify

フロントは React を使っています。UI コンポーネントは [chakra](https://chakra-ui.com/) を使いました。 Box を並べるだけでそれっぽくなります。デフォルトカラーテーマが良い感じです。

あと SFEN (将棋 AI のメッセージフォーマット) の parse が地味に大変で、いつも頑張って自前実装しているんですが、今回は [PEG.js](https://pegjs.org/) という Parser Generator を使ってみました。構文定義ファイルから parser を生成してくれるもので、コーナーケースに気をつけながら集中して自前実装するより 100 倍ラクでした。

Amplify は静的コンテンツのホスティング機能のみ利用しています。`create-react-app` で開発開始して、`amplify publish` でいきなり SSL のデプロイ済み URL が降ってくるのは便利です。

# 注意点

API Gateway と Lambda の組み合わせで WebSocket を利用する場合にいくつか注意点があります。

### 1. 双方向通信ではない

WebSocket を利用すればブラウザアプリケーションと Lambda プロセスが双方向通信できるような気がしていたのですが、API Gateway + Lambda の組み合わせを使う限りこれは難しいようです。
具体的には、Lambda からブラウザに対しては非同期にメッセージを push できる一方で、ブラウザから Lambda に対しては "Lambda 起動リクエスト" しか送ることができません。そのため、将棋 AI のように単一プロセスがインメモリで状態を持ち対話的に処理を行う CLI アプリケーションについては、工夫が必要です。最初のリクエストで全ての情報を渡して、Lambda から逐次レスポンスを受け取る形になります。

### 2. WebSocket が到達順序を保証しない

保証してくれません。かなりズレます。将棋 AI のように最新の結果のみに興味がある場合、何らかの対応を行う必要があります。
今回はメッセージに Sequence ID を振り、クライアントサイドで受信メッセージの再ソートを行いました。

# まとめと今後の展望

サーバーレス関連技術すごいですね。今回作ったアプリケーションについても、一連のバックエンド機能を実装するのにひと昔前だったらどれくらい手間がかかっただろうと考えると、気が遠くなります。
また、WebSocket ✕ 将棋 AI の相性の良さを感じました。従来の Web API だと 30 秒後に結果のみがポンと返ってきます。WebSocket にすることでリアルタイムに思考過程を表示でき、ユーザーが受け取れる情報量が格段に増えました。
今後は Windows ユーザーの恵まれた将棋検討環境 (将棋所など) を Mac やスマホで実現するウェブアプリケーションを作りたいです。ずっと言ってますが。