ワンクリックで
make-visualizer
AHCスタイルのヒューリスティックコンテスト用WASMビジュアライザを実装する。problem_description.txt と tools/src/ が揃っているときに使用する。フロントエンド(React/TypeScript)は原則変更しない。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
AHCスタイルのヒューリスティックコンテスト用WASMビジュアライザを実装する。problem_description.txt と tools/src/ が揃っているときに使用する。フロントエンド(React/TypeScript)は原則変更しない。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | make-visualizer |
| description | AHCスタイルのヒューリスティックコンテスト用WASMビジュアライザを実装する。problem_description.txt と tools/src/ が揃っているときに使用する。フロントエンド(React/TypeScript)は原則変更しない。 |
ヒューリスティックコンテスト用ビジュアライザを実装します。 以下の手順を順番に実行してください。次のステップに進む前に各ステップを完了させること。
以下を確認し、問題があれば停止してユーザーに伝えてください:
problem_description.txt を読んで、プレースホルダーのまま(「コンテストの問題文をここに記載してください」のような内容)であれば停止して「problem_description.txt に問題文を記載してください」と伝えるtools/src/ が存在しなければ停止して「公式から配布されるテスターコードを tools/src/ に配置してください」と伝える両方問題なければステップ2へ進む。
以下の2ファイルを読む:
problem_description.txt — 入出力フォーマット・状態・スコア計算を確認tools/src/lib.rs — 構造体定義と公開関数のシグネチャを把握(tools/src/bin/ 以下は読まない)読んだら、実装せずに以下の内容をユーザーに提示してください:
【ビジュアライザ設計案】 として以下を箇条書きで示す:
calc_max_turn が返す値(出力の行数 or 操作数など)visualize が返すスコアの計算方法提示した後、「この設計で実装しますか?」とユーザーに確認を取り、承認を得てからステップ3へ進む。
ユーザーの承認を得たら実装に入る。
まず以下のシェルコマンドを実行して、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 など)。
eprintln! / println! → 削除するか web_sys::console::log_1 に変更fn main() → 削除proconio::input! はそのまま使える(OnceSource::from(str) 経由で)#[wasm_bindgen] は付けない(lib.rs 側のみに付く)gen 関数内の _ => panic!(...) ブランチ → WASM では panic がランタイムエラーになるため、デフォルト値を返すように置き換える
// 変更前
_ => { panic!("Unknown problem: {}", problem) }
// 変更後(問題Aのパラメータをデフォルトとして使う例)
_ => (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)はビジュアライザには不要なので、遠慮なく削除すること。
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>
// ^score ^err ^svg
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 が空文字列・未知の値のときはデフォルト値(最初の問題カテゴリ)にフォールバックする// problem_id が不要な場合の例
pub fn generate(seed: i32, _problem_id: &str) -> String {
let input = gen(seed as u64);
format!("{}", input)
}
// problem_id がある場合の例(A/B/C が存在するとき)
pub fn generate(seed: i32, problem_id: &str) -> String {
let problem = match problem_id.chars().next().unwrap_or('A') {
'B' => 'B',
'C' => 'C',
_ => 'A', // 未知の値は 'A' にフォールバック
};
let input = gen(seed as u64, problem);
format!("{}", input)
}
// 注意: gen 関数内の `_ => panic!(...)` も同様にデフォルト値返しに変更すること(上記のWASM非互換の項目を参照)
pub fn visualize(input: &str, output: &str, turn: usize) -> Result<(i64, String, String), String> {
// 1. 入力をパース(コピーしたパース関数を流用)
// 2. 出力をパースして turn 番目までの操作を取得
// 3. 状態を計算(コピーしたスコア計算関数を流用)
// 4. SVGを描画して返す
let svg = draw_svg(/* 状態 */).map_err(|e| e.to_string())?;
Ok((score, String::new(), svg)) // (score, err, 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));
// テキスト(svg 0.17: Text::new() は文字列を引数に取る)
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以外の関数名を使った場合のみ修正すること。
まず 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 でサーバーを起動して動作確認するよう伝える:
gen OK)get_max_turn OK)vis OK)proconio::input! は OnceSource::from(input.as_str()) と組み合わせて使うgetrandom は features = ["js"] が必要(すでに Cargo.toml に設定済みのはず)impl はRustの予約語のためモジュール名に使えない(ファイル名は impl_vis.rs)