generate-judge-problems
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGenerate judge v2 problems
生成 Judge v2 题目
Read these references before authoring any file:
- references/problem-format.md — judge v2 directory layout and frontmatter schema.
problem.md - references/problem-authoring.md — statement writing rules, difficulty levels, and machine-grading constraints.
- references/example-problem.md — a complete minimal v2 example problem.
Generate v2 problems only: the statement file is exactly inside the problem directory. Never generate the legacy naming.
problem.md<id>.problem.md-
Confirm the inputs: learning objectives or the source material, target course, target programming language(s), number of problems, and difficulty range. Place each problem under; never create a repository-global problem or reference another course's problem. Derive problem drafts (id, name, overview, objective, difficulty) from the inputs; when generating multiple problems, order them from easy to hard per the difficulty levels in references/problem-authoring.md.
<courseId>/problems/<problemId>/ -
Make the problem files reproducible from the repository. Preserve existing mise configuration and compatible pinned versions. If none of,
mise.toml, or.mise.tomlexists, create.tool-versions; otherwise keep the existing format. Add exact versions of every runtime, package manager, and command-line tool required to execute the tracked model answers, templates, judges, and debuggers. Declare their imported third-party libraries in each language's standard project manifest with exact direct versions when supported, and update its standard lockfile. Run.tool-versionsfor a mise TOML config, runmise trust --yes, and then use the installed commands normally. Do not rely on global installations, ad hoc downloads, or temporary dependency environments, and do not add tools used only to author or validate the problem.mise install -
For each problem, write the primary model answer FIRST under(e.g.
model_answers/<languageId>/). Solving the problem yourself before writing the statement exposes ambiguities early. Use only concepts the learner has already studied; when no studied-concept list is given, derive reasonable assumptions from the learning objectives and state them in the final report.python/main.py -
Write(frontmatter + Japanese statement) per references/problem-format.md, following the statement rules in references/problem-authoring.md. Do NOT create
problem.mdorjudge.tsfor a standard stdin/stdout problem — the judge server auto-generates both, and committed default-content copies are rejected by the validator. Write a customdebug.ts(with a matchingjudge.ts) only when the problem needs special judging (GUI, LLM, command-based, etc.); a genuinely customdebug.tsmay also accompany a standard stdio problem on its own when the problem needs customized debugging.debug.ts -
Create test inputs under: at least 1
test_cases/case (shown to learners and mirrored as 入力例/出力例 in the statement; for an input-only problem the 出力例 shows the judged output file, see references/problem-format.md) and at least 1 hidden case, at least 4 in total. Make inputs diverse and include edge cases (minimum/maximum constraint values, boundary conditions such as 0 or negative numbers where allowed). All inputs must be distinct. A program that reads files instead of stdin takes them fromexample_*; a program that writes files is judged with<id>.fin/(see references/problem-format.md).<id>.fout/ -
Produce everyfile and every file under
.outby RUNNING the model answer locally — never hand-write expected outputs. For a plain stdin/stdout problem:.fout/bashcd <problemDir> && for f in test_cases/*.in; do python3 model_answers/python/main.py < "$f" > "${f%.in}.out"; doneWhen any case uses,.fin/or_shared.fin/, or omits.fout/, run each case in a scratch directory inside the problem directory (so the repository's pinned tools still apply), feeding.inwhen it exists, capturing stdout, and replacing<id>.inwith what the program wrote. Save the following as<id>.fout/(an indented heredoc would not terminate when pasted) and run<problemDir>/.tmp/expected.sh:bash -euo pipefail <problemDir>/.tmp/expected.sh [<outputFile> ...]bash# The produced file paths are the arguments; stdout-only problems pass none. cd <problemDir> root=$PWD # A scratch directory inside the problem keeps the pinned tools; it is removed on success and kept on failure. mkdir -p .tmp && scratch=$(mktemp -d .tmp/expected.XXXXXX) trap 'st=$?; if [ $st -eq 0 ]; then rm -rf "$scratch"; else echo "kept $scratch for inspection" >&2; fi; exit $st' EXIT # Every test case id (the shared name of its entries), excluding the shared input directory. for id in $(ls -1 test_cases | sed -E -n 's/\.(in|out|fin|fout)$//p' | sort -u | grep -vx _shared); do # Stage the case's working directory: shared inputs first, then the case's own inputs. w=$scratch/$id && mkdir -p "$w" if [ -d test_cases/_shared.fin ]; then cp -R test_cases/_shared.fin/. "$w"/; fi if [ -d "test_cases/$id.fin" ]; then cp -R "test_cases/$id.fin/." "$w"/; fi # Run the model answer with the case's stdin (if any) and capture its stdout. in=/dev/null && if [ -f "test_cases/$id.in" ]; then in="$root/test_cases/$id.in"; fi (cd "$w" && python3 "$root/model_answers/python/main.py" < "$in" > "$root/$scratch/$id.stdout") if [ -d "test_cases/$id.fout" ]; then # A file case: the produced file paths (the script arguments) are required and every file must exist # before .fout/ is refilled, which protects committed contents; .out is refreshed only when the case has one. [ $# -gt 0 ] || { echo "usage: expected.sh <outputFile> ... (test_cases/$id.fout/ needs the produced file paths)" >&2; exit 2; } for f in "$@"; do test -f "$w/$f"; done find "test_cases/$id.fout" -mindepth 1 -delete for f in "$@"; do mkdir -p "test_cases/$id.fout/$(dirname "$f")" && cp "$w/$f" "test_cases/$id.fout/$f"; done if [ -f "test_cases/$id.out" ]; then mv "$scratch/$id.stdout" "test_cases/$id.out"; fi else # A stdout case: the captured stdout is its expectation. mv "$scratch/$id.stdout" "test_cases/$id.out" fi donePass every relative path the program writes as the script arguments; a stdout-only problem passes none (the loop refuses to replace awithout arguments and verifies each file exists first, and.fout/stops at the first failure whatever the interactive shell is); the recipe assumes every file case writes the same paths, so adapt it per case when they differ. Createbash -euo pipefailbeforehand for every case judged by files: the loop refills existing directories, writestest_cases/<id>.fout/for every other case, and refreshes an existing.outof a file case (create an empty.outbeforehand when a file case's stdout is compared as well). Adapt the commands to the model answer's language. For a customtest_cases/<id>.out, follow its own contract instead: keep thejudge.ts,.inand.fin/entries it reads, and do not create_shared.fin/when it does not compare stdout (e.g. it compares a file listed in.out) — never print a marker such as the output file name just to have stdout.requiredOutputFilePaths -
Cross-check with a second model answer: write an independent model answer in another target language under(for a single-language problem, write the second implementation in any language under
model_answers/, judge it with the same command, and delete it afterwards instead of committing it), then judge every model answer with the<problemDir>/.tmp/cross_check/CLI from the problem directory (the CLI ships withexercode-problem, which must be a declared repository dependency — see setup-exercode-course-repository):@exercode/problem-utilsbashcd <problemDir> && bunx exercode-problem judge model_answers/<languageId>Pass required harness parameters as the JSON second argument (e.g.for an LLM judge). The run passes only when the command exits 0, prints at least one'{ "model": "<modelId>" }'line, and every printed line has the accepted decision codeTEST_CASE_RESULT(the comparison rule is described in references/problem-format.md). On any failure, diagnose the decision code and stderr and fix that cause — an output mismatch means the statement is ambiguous or an answer is wrong; regenerate the2000files only when the expected outputs change..out -
If you provide starter code under, ensure it does NOT solve the problem: judge each template directory from the problem directory with
templates/(judgebunx exercode-problem judge templates/<languageId>astemplates/_default, and files placed directly underbunx exercode-problem judge templates/_defaultastemplates/; in both cases pass the target language asbunx exercode-problem judge templates), confirm at least one test case is not accepted by an actually executed run — a'{ "language": "<languageId>" }'result (decision codemain file not found) means the language param matched no file in the template directory, so rerun with the language of the files it contains — and confirm no template directory contains, unchanged, every file a model answer is judged by (its source files for a standard problem, every file for a custom judge; shared helper modules may be identical).1201 -
If the statement constrains syntax (see the 制約 rules in references/problem-authoring.md), mirror the constraints in frontmatter/
requiredRegExpsInCode/forbiddenRegExpsInCode. Required patterns must match every model answer; forbidden patterns must match none, and must never match input-parsing helpers (forbiddenTextsInCode,Scanner,map,int, regexes). The patterns apply to submissions in every target language, so only use language-agnostic patterns; keep language-specific constraints (e.g. Python'ssplit) in the statement prose instead.range -
Validate and fix until clean:bash
bunx exercode-problem validate-problem <problemDir>...Fix every reported error and rerun. Repeat until the command reports success. Address warnings too unless they are intentional; if you keep a warning, tell the user why in the final report. -
Report the created problem directories with their difficulties, and any open decisions (e.g. constraint values you chose, intentionally kept warnings).
在编写任何文件前,请阅读以下参考资料:
- references/problem-format.md — Judge v2 目录布局和 frontmatter schema。
problem.md - references/problem-authoring.md — 题面编写规则、难度级别和机器评分约束。
- references/example-problem.md — 一个完整的最小 v2 示例题目。
仅生成 v2 题目:题面文件必须恰好是题目目录下的 。绝不生成旧版的 命名。
problem.md<id>.problem.md-
确认输入项:学习目标或源材料、目标课程、目标编程语言、题目数量和难度范围。将每道题放在下;绝不创建仓库全局题目,也不要引用其他课程的题目。根据输入生成题目草稿(id、name、overview、objective、difficulty);生成多道题时,按照 references/problem-authoring.md 中的难度级别从易到难排序。
<courseId>/problems/<problemId>/ -
确保题目文件可依据仓库复现。保留现有的 mise 配置和兼容的固定版本。如果、
mise.toml或.mise.toml均不存在,则创建.tool-versions;否则保持现有格式。添加执行受版本控制的 model answer、模板、judge 和 debugger 所需的每种运行时、包管理器及命令行工具的精确版本。在每种语言的标准项目清单中声明其导入的第三方库;在支持的情况下使用精确的直接依赖版本,并更新该语言的标准 lockfile。对于 mise TOML 配置,运行.tool-versions,然后运行mise trust --yes,之后正常使用已安装的命令。不要依赖全局安装、临时下载或临时依赖环境,也不要添加仅用于编写或验证题目的工具。mise install -
对于每道题,首先在下编写主要 model answer(例如
model_answers/<languageId>/)。先自行解题再编写题面,可以尽早发现歧义。仅使用学习者已经学过的概念;如果没有提供已学概念列表,则根据学习目标作出合理假设,并在最终报告中说明这些假设。python/main.py -
按照 references/problem-format.md 编写(frontmatter + 日语题面),并遵循 references/problem-authoring.md 中的题面规则。对于标准 stdin/stdout 题目,不要创建
problem.md或judge.ts——judge server 会自动生成二者,且验证器会拒绝提交默认内容的副本。仅当题目需要特殊评测时,才编写自定义debug.ts(并提供匹配的judge.ts),例如 GUI、LLM 或基于命令行的评测。如果标准 stdio 题目需要自定义调试,也可以单独提供真正自定义的debug.ts。debug.ts -
在下创建测试输入:至少 1 个
test_cases/用例(向学习者展示,并以example_*的形式镜像到题面中;对于仅输入型题目,入力例/出力例展示被评测的输出文件,参见 references/problem-format.md),以及至少 1 个隐藏用例,总数至少为 4。测试输入应多样化,并包含边界情况(约束值的最小值和最大值,以及在允许时包含 0 或负数等边界条件)。所有输入必须互不相同。读取文件而非 stdin 的程序从出力例获取输入;写出文件的程序使用<id>.fin/进行评测(参见 references/problem-format.md)。<id>.fout/ -
必须通过在本地运行 model answer 来生成每个文件和
.out下的每个文件——绝不手写预期输出。对于普通 stdin/stdout 题目:.fout/bashcd <problemDir> && for f in test_cases/*.in; do python3 model_answers/python/main.py < "$f" > "${f%.in}.out"; done当任何用例使用、.fin/或_shared.fin/,或者省略.fout/时,请在题目目录内运行每个用例的临时工作目录(以便仓库的固定工具仍然生效),在存在.in时将其作为 stdin 输入,捕获 stdout,并用程序写出的内容替换<id>.in。将以下内容保存为<id>.fout/(缩进的 heredoc 在粘贴时不会终止),并运行<problemDir>/.tmp/expected.sh:bash -euo pipefail <problemDir>/.tmp/expected.sh [<outputFile> ...]bash# The produced file paths are the arguments; stdout-only problems pass none. cd <problemDir> root=$PWD # A scratch directory inside the problem keeps the pinned tools; it is removed on success and kept on failure. mkdir -p .tmp && scratch=$(mktemp -d .tmp/expected.XXXXXX) trap 'st=$?; if [ $st -eq 0 ]; then rm -rf "$scratch"; else echo "kept $scratch for inspection" >&2; fi; exit $st' EXIT # Every test case id (the shared name of its entries), excluding the shared input directory. for id in $(ls -1 test_cases | sed -E -n 's/\.(in|out|fin|fout)$//p' | sort -u | grep -vx _shared); do # Stage the case's working directory: shared inputs first, then the case's own inputs. w=$scratch/$id && mkdir -p "$w" if [ -d test_cases/_shared.fin ]; then cp -R test_cases/_shared.fin/. "$w"/; fi if [ -d test_cases/$id.fin ]; then cp -R test_cases/$id.fin/. "$w"/; fi # Run the model answer with the case's stdin (if any) and capture its stdout. in=/dev/null && if [ -f "test_cases/$id.in" ]; then in="$root/test_cases/$id.in"; fi (cd "$w" && python3 "$root/model_answers/python/main.py" < "$in" > "$root/$scratch/$id.stdout") if [ -d "test_cases/$id.fout" ]; then # A file case: the produced file paths (the script arguments) are required and every file must exist # before .fout/ is refilled, which protects committed contents; .out is refreshed only when the case has one. [ $# -gt 0 ] || { echo "usage: expected.sh <outputFile> ... (test_cases/$id.fout/ needs the produced file paths)" >&2; exit 2; } for f in "$@"; do test -f "$w/$f"; done find "test_cases/$id.fout" -mindepth 1 -delete for f in "$@"; do mkdir -p "test_cases/$id.fout/$(dirname "$f")" && cp "$w/$f" "test_cases/$id.fout/$f"; done if [ -f "test_cases/$id.out" ]; then mv "$scratch/$id.stdout" "test_cases/$id.out"; fi else # A stdout case: the captured stdout is its expectation. mv "$scratch/$id.stdout" "test_cases/$id.out" fi done将程序写入的每个相对路径作为脚本参数传入;仅使用 stdout 的题目不传任何参数(循环会拒绝替换没有参数的,并在替换前验证每个文件都存在,而.fout/无论交互 shell 如何,都会在首次失败时停止);该方案假设每个文件用例写入相同的路径,因此当各用例路径不同时,需按用例调整命令。预先为每个按文件评测的用例创建bash -euo pipefail:循环会重新填充已有目录,为其他每个用例写入test_cases/<id>.fout/,并刷新文件用例已有的.out(如果文件用例的 stdout 也要比较,请预先创建一个空的.out)。根据 model answer 的语言调整命令。对于自定义test_cases/<id>.out,改为遵循其自身约定:保留它读取的judge.ts、.in和.fin/条目;如果它不比较 stdout(例如比较_shared.fin/中列出的文件),则不要创建requiredOutputFilePaths——绝不要仅仅为了产生 stdout 而打印输出文件名之类的标记。.out -
使用第二个 model answer 进行交叉核验:在下用另一个目标语言编写独立的 model answer(对于单语言题目,在
model_answers/下用任意语言编写第二个实现,使用相同命令进行评测,之后删除它而不提交),然后从题目目录运行<problemDir>/.tmp/cross_check/CLI,评测每个 model answer(该 CLI 随exercode-problem提供,后者必须声明为仓库依赖——参见 setup-exercode-course-repository):@exercode/problem-utilsbashcd <problemDir> && bunx exercode-problem judge model_answers/<languageId>将所需的 harness 参数作为第二个 JSON 参数传入(例如,LLM judge 使用)。只有当命令以 0 退出、至少打印一行'{ "model": "<modelId>" }',且打印的每一行都包含可接受的判定码TEST_CASE_RESULT时,运行才通过(比较规则见 references/problem-format.md)。任何失败都应诊断判定码和 stderr 并修复根本原因——输出不匹配意味着题面存在歧义或答案有误;仅当预期输出发生变化时才重新生成2000文件。.out -
如果提供了下的起始代码,请确保它不能解决题目:从题目目录分别评测每个模板目录(评测
templates/的命令为templates/_default,直接放在bunx exercode-problem judge templates/_default下的文件则使用templates/;在这两种情况下,都将目标语言作为bunx exercode-problem judge templates传入)。确认在实际执行的运行中至少有一个测试用例未被接受——出现'{ "language": "<languageId>" }'结果(判定码main file not found)意味着 language 参数没有匹配模板目录中的任何文件,因此应改用模板目录内文件所用的语言重新运行。并确认任何模板目录都没有原样包含 model answer 评测所依据的每个文件(标准题目为其源文件,自定义 judge 为所有文件;共享辅助模块可以相同)。1201 -
如果题面约束语法(参见 references/problem-authoring.md 中的约束规则),则在 frontmatter/
requiredRegExpsInCode/forbiddenRegExpsInCode中镜像这些约束。必需模式必须与每个 model answer 匹配;禁止模式必须与任何内容都不匹配,并且绝不能匹配输入解析辅助代码(forbiddenTextsInCode、Scanner、map、int、regexes)。这些模式适用于所有目标语言的提交,因此只能使用与语言无关的模式;语言特定的约束(例如 Python 的split)应改为保留在题面正文中。range -
验证并修复,直至通过:bash
bunx exercode-problem validate-problem <problemDir>...修复所有报告的错误并重新运行。重复操作,直到命令报告成功。警告也应处理,除非它们是有意保留的;如果保留警告,请在最终报告中说明原因。 -
报告已创建的题目目录及其难度,以及任何待决事项(例如你选择的约束值、有意保留的警告)。