いよいよWebアプリ版の形四アプリを作っていきます。
基本設計メモ
- 仕様はすでにある C#版アプリ(KCSharp) と同等とする。
- 画面表示、UI、ファイル入力の処理は JavaScript で記述する。
- 局面データ、思考エンジン等の内部ロジックは Rust で記述して WebAssembly にコンパイルする。
- すでにあるC#版のソースを JavaScript と Rust に移植する。
- UIの見映えを良くするため、フレームワークに Bootstrap を利用する。
- PC等の横画面とスマホ等の縦画面でレイアウトを切り替える。
(横画面なら左に盤、右にボタン類。縦画面なら上に盤、下にボタン類。)
詳細設計メモ (1) UIのデザイン
- HTML, CSS で記述する。(index.html と webkc.css に記述する)
- Bootstrap を利用してボタン等を見映えの良いスタイルにする。
- スマホでも適切に表示されるように下記を head に記述する。
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
- 画面を盤面部とフォーム部(ボタンやセレクト類)に分け、各々をdisplay: flex なdiv要素とする。
- 横長画面であれば、盤面部とフォーム部は flex-direction: row (横並び) とする。
- 縦長画面であれば、盤面部とフォーム部は flex-direction: column (縦並び) とする。
- 盤面部とフォーム部が画面内に収まり、かつPCの画面で盤が大きすぎないように盤の大きさを調整する。
- これらのレイアウト制御は、後述のJavaScriptにて、document の DOMContentLoaded イベント、および window の resize イベントの際に実行する。
詳細設計メモ (2) UIのロジック
- JavaScriptで記述する。
- C#版の FormMain.cs と Kifu.cs から移植する。
- FormMain.cs (フォームのクラス) から UIの処理を webkc.js に移植する。
- Kifu.cs (棋譜クラス, 初期局面クラス) を kifu.js に移植する。
- C#版では、思考エンジンは Task.Run( ) を用いて別スレッドで実行している。
- JavaScriptは基本的にはシングルスレッドだが、Web Workerを用いれば別スレッドで実行できる。
- ワーカースレッド側の処理は work.js に記述する。
- メインスレッドとワーカースレッド間のやりとりは postMessage( ) でおこなう。
- Rustの構造体は下記のようにして取り込んで使用する。
import init, {Position, Move, Board, DaiPunch} from "./pkg/webkc_rust.js";
- Rustの構造体は、init() の完了を待ってから使用すること。(new することも含む)
- Rustの構造体はpostMessage( )で送ることができない。
- Rust側でシリアライズ / デシリアライズ するのがエレガントぽいが、
今回は通常のオブジェクトに値を積み替えて送ることにする。 - Rustの構造体のコピーには、Rust側で定義した clone_js() を用いる。(後述)
- 盤面の canvas のイベントは、マウスとタッチに両対応するため、pointerdown イベントを受ける。
- C# の MessageBox の移植には、alert() は見栄えがあまり良くないので、Bootstrap の div class="modal fade" を使用する。
- div class="modal fade" は index.html の body の末尾あたりに記述し、JavaScript で show() する。
詳細設計メモ (3) 内部ロジック
- Rustで記述してWebAssembly (WASM) にコンパイルする。
- JavaScript側から呼び出すためのラッパ ./pkg/webkc_rust.js が生成される。
- Board.cs (位置構造体, 着手構造体, 盤面構造体, 大パンチ判定クラス) を board.rs に移植する。
- Engine.cs, Engine_AB_SKR.cs (思考エンジンクラス) をengine.rs に移植する。
- JavaScript側に公開する構造体には #[wasm_bindgen] を指定する。
- 位置構造体, 着手構造体には、Clone, Copy, PartialEq, Eq をderiveする。
- 盤面構造体には、Clone, Copy をderiveする。
- clone() はJavaScript側には公開されないので、clone_js() でラップして公開する。
- JavaScriptからオブジェクトを引数で渡すときは参照渡しとする。
- JavaScript側には公開できないが Rust側では pub にしたいメソッドは、implブロックを分ける。
- Rustの構造体の関連定数 (pub const で定義される、クラス定数のようなもの) は、
JavaScriptに対応する概念がないので、JavaScript側に公開する構造体には持たせられない。 - 必要であれば、Rust側で定数として定義して、関数を経由してJavaScript側に取得させる。
- C#版では、再帰関数内でListを生成するコストを避けるため、静的に確保した配列をSpanで切り出して渡している。
- Rustで同様の記述をすると、自己借用 (self の二重可変借用) のためコンパイルエラーとなる。
- ひとまず、構造を大きく変えずに対応するため、ローカルで配列を定義する形に変更する。
- C#版では、EngineクラスをEngine_AB_SKRクラスが継承している。
- Rustでは、ひとまず一体物のEngine構造体として実装する。(Rustには継承がないので。)
- C#版では着手の変数名を move としているが、Rust では move は予約語であることに注意。
Rustの開発環境について
Rust / WebAssembly の開発環境については下記の記事を参照。
wasm-packのインストール
cargo install wasm-pack
プロジェクトの作成
cargo new --lib プロジェクト名
コンパイル
wasm-pack build --target web
クレートの追加
例えば乱数発生器のクレート rand をプロジェクトに追加するには、
cargo add rand
これにより Cargo.toml の [dependencies] に記述が追加される。