| name | make-visualizer |
| description | AHCスタイルのヒューリスティックコンテスト用WASMビジュアライザを実装する。problem_description.txt と tools/src/ が揃っているときに使用する。フロントエンド(React/TypeScript)は原則変更しない。 |
make-visualizer
ヒューリスティックコンテスト用ビジュアライザを実装します。
以下の手順を順番に実行してください。次のステップに進む前に各ステップを完了させること。
ステップ1: 前提条件チェック
以下を確認し、問題があれば停止してユーザーに伝えてください:
problem_description.txt を読んで、プレースホルダーのまま(「コンテストの問題文をここに記載してください」のような内容)であれば停止して「problem_description.txt に問題文を記載してください」と伝える
tools/src/ が存在しなければ停止して「公式から配布されるテスターコードを tools/src/ に配置してください」と伝える
両方問題なければステップ2へ進む。
ステップ2: 問題を読んでビジュアライザ設計を提案する
以下の2ファイルを読む:
problem_description.txt — 入出力フォーマット・状態・スコア計算を確認
tools/src/lib.rs — 構造体定義と公開関数のシグネチャを把握(tools/src/bin/ 以下は読まない)
読んだら、実装せずに以下の内容をユーザーに提示してください:
提示する内容
【ビジュアライザ設計案】 として以下を箇条書きで示す:
- 描画する要素: フィールド・エージェント・目的地・障害物など、何をSVGに描くか
- ターンの定義:
calc_max_turn が返す値(出力の行数 or 操作数など)
- ターンごとの状態変化: スライダーを動かすと何が変わるか
- スコア表示:
visualize が返すスコアの計算方法
提示した後、「この設計で実装しますか?」とユーザーに確認を取り、承認を得てからステップ3へ進む。
ステップ3: wasm/src/impl_vis.rs を実装
ユーザーの承認を得たら実装に入る。
lib.rs の内容を impl_vis.rs の先頭に結合する
まず以下のシェルコマンドを実行して、tools/src/lib.rs の内容を wasm/src/impl_vis.rs のプレースホルダー関数の上に結合する:
macOS / Linux / Git Bash:
printf '%s' "$(cat tools/src/lib.rs wasm/src/impl_vis.rs)" > wasm/src/impl_vis.rs
Windows (PowerShell):
$lib = Get-Content tools/src/lib.rs -Raw
$impl = Get-Content wasm/src/impl_vis.rs -Raw
Set-Content wasm/src/impl_vis.rs "$lib$impl"
これにより impl_vis.rs は以下の構造になる:
tools/src/lib.rs の全内容(構造体・ロジック・ユーティリティ)
- 既存のプレースホルダー関数(
generate / calc_max_turn / visualize)
tools/src/bin/ 以下は読まない。lib.rs のみ結合すること。
新しく書くのは主にプレースホルダーを埋める SVG 描画部分(draw_svg など)。
WASM 非互換な箇所だけ修正する
eprintln! / println! → 削除するか web_sys::console::log_1 に変更
- ファイルI/O /
fn main() → 削除
proconio::input! はそのまま使える(OnceSource::from(str) 経由で)
#[wasm_bindgen] は付けない(lib.rs 側のみに付く)
gen 関数内の _ => panic!(...) ブランチ → WASM では panic がランタイムエラーになるため、デフォルト値を返すように置き換える
_ => { panic!("Unknown problem: {}", problem) }
_ => (0, rng.gen_range(1..=100) as f64, rng.gen_range(1..=20) as f64 * 0.01),
tools/src/lib.rs にはビジュアライザに不要なコードが含まれることがある。
スコア計算・状態遷移・パース関数はビジュアライザでも必要だが、外部プロセスを起動・制御するためのコード(exec 関数、read_line 関数、use std::process::ChildStdout などの import)はビジュアライザには不要なので、遠慮なく削除すること。
impl_vis.rs が公開する3つの関数
lib.rs から以下の関数名で呼び出されるため、必ずこのシグネチャで実装すること:
pub fn generate(seed: i32, problem_id: &str) -> String
pub fn calc_max_turn(input: &str, output: &str) -> usize
pub fn visualize(input: &str, output: &str, turn: usize) -> Result<(i64, String, String), String>
calc_max_turn の注意: 0 を返すとスライダーが動かない。出力が空でなければ必ず 1 以上を返すこと。
generate における problem_id の扱い
tools/src/lib.rs の gen 関数が問題カテゴリ(A/B/C など)を引数に取る場合でも、そのカテゴリが存在しない問題もある。
gen が問題カテゴリを引数に取らない(引数が seed だけ)場合: problem_id を無視してそのまま呼ぶ
gen が問題カテゴリを引数に取る場合: problem_id("A", "B", "C" など)を char に変換して渡す。ただし、problem_id が空文字列・未知の値のときはデフォルト値(最初の問題カテゴリ)にフォールバックする
pub fn generate(seed: i32, _problem_id: &str) -> String {
let input = gen(seed as u64);
format!("{}", input)
}
pub fn generate(seed: i32, problem_id: &str) -> String {
let problem = match problem_id.chars().next().unwrap_or('A') {
'B' => 'B',
'C' => 'C',
_ => 'A',
};
let input = gen(seed as u64, problem);
format!("{}", input)
}
visualize の実装パターン
pub fn visualize(input: &str, output: &str, turn: usize) -> Result<(i64, String, String), String> {
let svg = draw_svg().map_err(|e| e.to_string())?;
Ok((score, String::new(), svg))
}
SVG描画の基本パターン
use svg::Document;
use svg::node::element::{Rectangle, Circle, Line};
use svg::node::element::Text as SvgText;
fn draw_svg() -> Result<String, Box<dyn std::error::Error>> {
let size = 600;
let mut doc = Document::new()
.set("viewBox", format!("0 0 {size} {size}"))
.set("width", size).set("height", size);
doc = doc.add(Rectangle::new()
.set("x", x).set("y", y).set("width", w).set("height", h)
.set("fill", "#4488cc").set("stroke", "#000").set("stroke-width", 1));
doc = doc.add(Circle::new()
.set("cx", cx).set("cy", cy).set("r", r).set("fill", "#cc4444"));
doc = doc.add(Line::new()
.set("x1", x1).set("y1", y1).set("x2", x2).set("y2", y2)
.set("stroke", "#000").set("stroke-width", 2));
doc = doc.add(SvgText::new("ラベル")
.set("x", x).set("y", y)
.set("text-anchor", "middle").set("font-size", 12).set("fill", "#fff"));
Ok(doc.to_string())
}
注意: wasm/src/lib.rs の確認は通常不要。generate / calc_max_turn / visualize 以外の関数名を使った場合のみ修正すること。
ステップ4: ビルドと動作確認
まず cargo check でコンパイルエラーを確認し、通ったら wasm-pack build を実行する:
cd wasm && cargo check
エラーがなければ:
wasm-pack build --target web --out-dir ../public/wasm
cargo check でエラーが出たら原因を特定して修正してから wasm-pack build を実行する
- クレートのバージョン不一致が原因の場合のみ
wasm/Cargo.toml を修正する
ビルドが完了したらユーザーに yarn dev でサーバーを起動して動作確認するよう伝える:
- seed 入力 → 入力エリアに問題入力が表示される(
gen OK)
- 出力貼り付け → スライダーの上限が更新される(
get_max_turn OK)
- スライダーを動かす → SVG が描画される(
vis OK)
注意事項
proconio::input! は OnceSource::from(input.as_str()) と組み合わせて使う
getrandom は features = ["js"] が必要(すでに Cargo.toml に設定済みのはず)
impl はRustの予約語のためモジュール名に使えない(ファイル名は impl_vis.rs)