以下の内容はhttps://rubikun.hatenablog.jp/entry/2025/12/23/032237より取得しました。


Topcoder SRM オフラインジャッジを作った

開発堕ち

まあ AI に聞いただけなんだけど

https://archive.topcoder.com/ProblemArchive

SRM フォルダを作って、中に solution.cpp (解法,これは本番通りにclassで書く) と tests.txt (各問題を開いて全てをコピペしたら良い) と judge.py (中身は後述) を置く

python3 judge.py をしたら、

=== testproblem | 3 Cases ===

Case 00: [ TLE ] (3.0s exceeded)
Case 01: [ TLE ] (3.0s exceeded)
Case 02: [ OK ] (0.0000s)

===================================
 最終結果まとめ
===================================
 OK: 1 | NG: 0 | TLE: 2 | RE: 0
 Max Time: 3.0000s
===================================

みたいなのが出てくる

judge.py の中身はこちら

import os
import subprocess
import re
import time

# ----------------------------------------------------------------
# 1. 判定ロジック(double誤差判定)
# ----------------------------------------------------------------
def check_double_error(actual_str, expected_str):
    try:
        a_vals = re.findall(r"-?\d+\.?\d*(?:e[-+]?\d+)?", actual_str)
        e_vals = re.findall(r"-?\d+\.?\d*(?:e[-+]?\d+)?", expected_str)
        if len(a_vals) != len(e_vals): return False
        for av, ev in zip(a_vals, e_vals):
            a, e = float(av), float(ev)
            if a == e: continue
            abs_err = abs(a - e)
            rel_err = abs_err / max(abs(e), 1e-18)
            if min(abs_err, rel_err) > 1e-9: return False
        return True
    except: return False

# ----------------------------------------------------------------
# 2. 問題文のパース
# ----------------------------------------------------------------
def parse_all_content(content):
    config = {}
    try:
        config['class'] = re.search(r'Class:\s*(\w+)', content).group(1)
        config['method'] = re.search(r'Method:\s*(\w+)', content).group(1)
        params_raw = re.search(r'Parameters:\s*(.*)', content).group(1)
        config['args_types'] = [p.strip() for p in params_raw.split(',')]
        config['returns'] = re.search(r'Returns:\s*([\w<> ]+)', content).group(1).strip()
        num_args = len(config['args_types'])
    except: return None, []
    
    examples_part = content.split("Examples", 1)[1] if "Examples" in content else content
    lines = [l.strip() for l in examples_part.split('\n') if l.strip()]
    cases = []
    for i, line in enumerate(lines):
        if "Returns:" in line:
            expected = line.replace("Returns:", "").strip()
            args = lines[i - num_args : i]
            if len(args) == num_args: cases.append({"args": args, "expected": expected})
    return config, cases

def format_cpp_value(raw_val, type_str):
    raw_val = raw_val.strip()
    if "vector" in type_str: return raw_val if "{" in raw_val else "{" + raw_val + "}"
    if "string" in type_str: return raw_val if raw_val.startswith('"') else f'"{raw_val}"'
    return raw_val

# ----------------------------------------------------------------
# 3. メイン処理
# ----------------------------------------------------------------
def run_judge():
    if not (os.path.exists("tests.txt") and os.path.exists("solution.cpp")):
        print("Error: tests.txt または solution.cpp が見つかりません。"); return

    with open("tests.txt", "r", encoding="utf-8") as f:
        content = f.read()

    config, cases = parse_all_content(content)
    if not config or not cases:
        print("Error: 問題文を解析できませんでした。"); return

    # C++ドライバー生成
    switch_cases = ""
    for i, case in enumerate(cases):
        cpp_args = [format_cpp_value(v, t) for v, t in zip(case['args'], config['args_types'])]
        switch_cases += f"""
        case {i}: {{
            auto start = chrono::high_resolution_clock::now();
            auto res = sol.{config['method']}({", ".join(cpp_args)});
            auto end = chrono::high_resolution_clock::now();
            chrono::duration<double> diff = end - start;
            print_val(res);
            cout << "\\nTIME: " << fixed << setprecision(6) << diff.count() << endl;
            break;
        }}"""

    driver_code = f"""
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
#include <chrono>
#include "solution.cpp"
using namespace std;
template<typename T> void print_val(const T& v) {{ cout << v; }}
template<> void print_val(const double& v) {{ cout << fixed << setprecision(15) << v; }}
template<typename T> void print_val(const vector<T>& v) {{
    cout << "{{";
    for(size_t i=0; i<v.size(); ++i) {{
        if(i>0) cout << ", ";
        print_val(v[i]);
    }}
    cout << "}}";
}}
int main(int argc, char** argv) {{
    if(argc < 2) return 1;
    int id = atoi(argv[1]);
    {config['class']} sol;
    switch(id) {{
        {switch_cases}
        default: break;
    }}
    return 0;
}}
"""
    with open("temp_driver.cpp", "w", encoding="utf-8") as f:
        f.write(driver_code)

    ext = ".exe" if os.name == 'nt' else ".out"
    cmd_ext = "./test" + ext if os.name != 'nt' else "test" + ext
    
    # コンパイル
    comp = subprocess.run(["g++", "-O3", "-std=c++20", "temp_driver.cpp", "-o", "test"+ext], capture_output=True)
    if comp.returncode != 0:
        print("=== Compile Error ==="); print(comp.stderr.decode()); return

    print(f"=== {config['class']} | {len(cases)} Cases ===\n")
    
    stats = {"OK": 0, "NG": 0, "TLE": 0, "RE": 0, "max_time": 0.0}
    is_double = "double" in config['returns'].lower()

    for i in range(len(cases)):
        try:
            # 3.0秒のタイムアウトを設定
            res = subprocess.run([cmd_ext, str(i)], capture_output=True, text=True, timeout=3.0)
            
            output_lines = res.stdout.strip().split('\n')
            actual = ""
            elapsed = 0.0
            for line in output_lines:
                if "TIME: " in line:
                    elapsed = float(line.replace("TIME: ", ""))
                else:
                    actual = line

            stats["max_time"] = max(stats["max_time"], elapsed)
            expected = cases[i]["expected"]
            success = check_double_error(actual, expected) if is_double else (actual.replace(" ","") == expected.replace(" ",""))
            
            if success:
                print(f"Case {i:02}: [ OK ] ({elapsed:.4f}s)")
                stats["OK"] += 1
            else:
                print(f"Case {i:02}: [ !! ] NG ({elapsed:.4f}s)")
                print(f"   期待: {expected} | 出力: {actual}")
                stats["NG"] += 1

        except subprocess.TimeoutExpired:
            print(f"Case {i:02}: [ TLE ] (3.0s exceeded)")
            stats["TLE"] += 1
            stats["max_time"] = max(stats["max_time"], 3.0)
        except:
            print(f"Case {i:02}: [ RE ] (Runtime Error)"); stats["RE"] += 1

    print(f"\n" + "="*35 + f"\n 最終結果まとめ\n" + "="*35)
    print(f" OK: {stats['OK']} | NG: {stats['NG']} | TLE: {stats['TLE']} | RE: {stats['RE']}")
    print(f" Max Time: {stats['max_time']:.4f}s")
    print("="*35)

if __name__ == "__main__":
    run_judge()

バグってるかは知りません、スペシャルジャッジは見なかったことにしよう
本当に問題文コピペだけでいけるのは偉いと思った、room移動で10分待ちなんてなかったんや


おまけ

javascript:(function(){var tables=document.getElementsByTagName('table');for(var i=0;i<tables.length;i++){var rows=tables[i].rows;if(rows.length>0){var lvlIdx=-1;var catIdx=-1;var headerCells=rows[0].cells;for(var j=0;j<headerCells.length;j++){var txt=headerCells[j].textContent.replace(/\s+/g,' ').trim();if(txt.includes('Div. 1 Level'))lvlIdx=j;if(txt.includes('Categories'))catIdx=j;}if(lvlIdx!==-1){for(var r=0;r<rows.length;r++){var row=rows[r];if(catIdx!==-1&&row.cells[catIdx])row.cells[catIdx].style.display='none';if(r>0){var levelCell=row.cells[lvlIdx];if(levelCell){var val=levelCell.textContent.trim();if(val!=='3')row.style.display='none';}}}}}}})();

で問題タグを消してDiv.1 hardだけ残せるようになった
chromeで、アーカイブをブックマークして編集でこれを打っておいて、アーカイブリンク開いてからブックマークを押すと動いた




以上の内容はhttps://rubikun.hatenablog.jp/entry/2025/12/23/032237より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14