AHC019 の Web 版ビジュアライザを手元で動かして実行結果を自動で反映したい

AHC の Web 版ビジュアライザはとても便利ですが、毎度出力を入力欄にコピペする必要があるのが難点です。そこでこの記事では、Web 版ビジュアライザを手元で動かし、seed 値の入力欄を変更すると即座に直近の手元での実行結果が表示されるようにしたときの手順をつらつらと書きます。

【追記】ssaattoo さんが公開しているユーザースクリプトの方が万倍便利なのでこの記事は読まずにそちらを使いましょう。

github.com


動作イメージは下のような感じです。

ローカルサーバを立てる

手元でサーバを立てるため Vite で適当に最低限構成のプロジェクトを作成します。

$ npm create vite@4.2.0 local_visualizer -- --template vanilla

Scaffolding project in /Users/iwashi/procon/ahc019/local_visualizer...

Done. Now run:

  cd local_visualizer
  npm install
  npm run dev

Now run: に続く 3 つのコマンドをそのまま実行してブラウザで http://localhost:5173/ にアクセスし Hello Vite! と表示されれば OK です。

諸々をダウンロード

Web 版ビジュアライザを Chrome で開き、適当なところを右クリックして「別名で保存」を選択すると、こういうものがダウンロードできます。(これは保存時のファイル名に Visualizer を指定した場合です)

先ほど作成した local_visualizer ディレクトリ配下にある index.html の中身をこのダウンロードしてきた Visualizer.html で置き換えます。

$ pwd
/Users/iwashi/procon/ahc019/local_visualizer
$ mv ~/Downloads/Visualizer.html index.html

Visualizer_files の方はディレクトリごと public 配下に持ってきます。

$ pwd
/Users/iwashi/procon/ahc019/local_visualizer
$ mv ~/Downloads/Visualizer_files public/

さてページの方はどうかな?

./b36525d8.js がないぞと怒られていますね。 b36525d8.js をダウンロードしてきて local_visualizer 配下( index.html と同じ階層)に設置しましょう。

さてどうかな?

three が import できないと怒られていますね。本来なら importmap のあたりで解決されてるような気がするのですが、よく分からないので npm で three をインストールしてしまいましょう。

$ npm install three

さてどうかな?

コンパイルエラーは出なくなりましたが、seed 値を入れても input が自動入力されません。

ブラウザの開発者ツールのコンソールを見ると b36525d8_bg.wasm の取得に失敗したよと言われています。なるほど wasm ってそういう感じなんですね。 b36525d8_bg.wasm をダウンロードしてきて public 配下に設置します。

さてどうかな?

コンソールのエラーは消え、入力も自動生成されるようになりました。ただ肝心のビジュアライズ部分が真っ白ですね。

よく見たらすごい下の方にありました。

むむむ、なぜか canvas 要素が 4 つもありますね。

よく分からないですが index.html に初めから記述されている canvas 要素を消すとなんかちゃんと動いてそうな感じがします。まぁこれで良しとしましょう。

動いた!

追記:冷静に考えたらこの canvas は js で動的に追加されるもので、動的に追加された後の html をダウンロードしてきたからダブってたんですね。なので index.html 内の canvas を消すは結果的に正しい対応でした。

出力結果の読み込み

僕は普段手元での実行結果を {project_root}/tools/versions/latest/out/0000.txt みたいなところに保存しています。ビジュアライザでこれを読み込んで seed 値を変えた時に自動で表示するようにします。

index.html 内の script タグ内の先頭にこういうのを書きます。

import output_0 from "../tools/versions/latest/out/0000.txt?raw";
import output_1 from "../tools/versions/latest/out/0001.txt?raw";
import output_2 from "../tools/versions/latest/out/0002.txt?raw";
import output_3 from "../tools/versions/latest/out/0003.txt?raw";
...

すると、それぞれの txt ファイル内のテキストが output_** という変数に格納されます。これを表示したい seed 分列挙します。えっ!?... 本来なら出力ファイルを public 配下に置いて Ajax で都度取ってくるべきなんでしょうが、1 分でも多く解法考えることに時間を使いたかったので今回はコレでいきました。vim のマクロなりを使えば 1000 シード分くらいでも一瞬で書けますね。

さて、index.html 内の js を読むと seed の値を変えた時には generate という関数が実行されるようです。ここに先ほど import した出力をフィールドに差し込む処理を追加します。

    function generate() {
      const seed = document.getElementById("seed").value;
      var D = 0;
      if (document.getElementById("fix_D").checked) {
        document.getElementById("D").disabled = false;
        D = document.getElementById("D").value;
      } else {
        document.getElementById("D").disabled = true;
      }
      const input = gen(seed, D);
      document.getElementById("input").value = input;
+     document.getElementById("output").value = eval("output_" + seed);
      updateOutput();
    }

さてどうかな?

ハイ、動きました。おつかれさま!