特殊ジャッジ対策
少なくとも誤差ジャッジに対応する
特殊ジャッジには以下の3つの形態がありそうです。
- 小数の出力で誤差を許容
- 複数解のうち1つを出力すればOK
- インタラクティブ問題
1については問題文から許容誤差と絶対誤差・相対誤差のどちら(あるいは両方)を容認するかを抽出した上で、できれば出力形式も抽出して、小数表示のところは許容誤差内にあるかジャッジすることになると思います。 2, 3については判定スクリプトをユーザーに書いてもらって、そのスクリプトを走らせた結果で正解を判定することになると思います。2. の場合は判定スクリプト関数などでコードに埋め込んだ方が使いやすいかもです。その場合は、標準エラー出力かメイン関数の戻り値などで正解を判定することになります。 3. の判定スクリプトは場合によってはatcoderジャッジサーバーが用意したものを再現するのが困難(もしかすると元の問題を解くより難しくなるかも)なので、あまり需要がないかもです。
以上から、
- どの形態のジャッジなのか(通常、小数、複数解、インタラクティブ)
- 複数解、インタラクティブの場合はジャッジスクリプトの名前や仕様はどうするか?
を考える必要があります。ひとまず書いてみてもいいんですかね?
ひとまず、浮動小数対応バージョンを作りたいのですが、各問題のディレクトリに.configみたいなファイルを作成してそこにジャッジタイプ(通常、小数、複数解、インタラクティブ)のどれかを記述するという仕様でいかがでしょう?
ひとまず、浮動小数対応バージョンを作りたいのですが、各問題のディレクトリに.configみたいなファイルを作成してそこにジャッジタイプ(通常、小数、複数解、インタラクティブ)のどれかを記述するという仕様でいかがでしょう?
いいと思います。
現状、metadata.jsonというのが毎回ディレクトリに生成されて、submitコマンドとかtestで使ってるので、これを使い回すという手があります。中身はこんなかんじです。多分judge_typeとかいうフィールドを作るとよさそうです。
{
"code_filename": "main.cpp",
"lang": "cpp",
"problem": {
"alphabet": "A",
"contest": {
"contest_id": "jsc2019-qual"
},
"problem_id": "jsc2019_qual_a"
},
"sample_in_pattern": "in_*.txt",
"sample_out_pattern": "out_*.txt"
}
おぉーありましたか!気づかなかったです。あんまり意味ないですが、ジャッジタイプだけ抽出するっていうのやってみますかねー
意味は十分にあると思います。よろしくおねがいします。
https://github.com/kyuridenamida/atcoder-tools/blob/master/atcodertools/constprediction/constants_prediction.py
https://github.com/kyuridenamida/atcoder-tools/blob/master/atcodertools/constprediction/models/problem_constant_set.py
この辺のロジックに何か追加してみるとよいかなと思います。
はい!ちょうどそこを見ていました。
"judge_type":"normal" | "float" | "multiple" | "interactive" で"float"の場合は "error": "absolute" | "relative" "diff": "10^-??" みたいに記述されてる感じですね
はい!まさにそんなかんじで!ただerrorとdiffが実は意図を取り違えやすいので、それを阻止するために少し構造化してもらえますか?
"judge" : {
"type": "normal" | "float" | "multiple" | "interactive"
"error_type": "absolute" | "relative" | "absolute_or_relative" | null
"diff" : 小数 | null
}
diffはnumberのまま入れるのか気持ち悪いので何かしらの形で正規化するべきだとは思いますが、どうしたらいいんだろう...誤差とかこわいけどそこまでシビアになるのはアレなのでstringじゃなくて単純にnumberとして入れてもいいかもしれない。
UX的に人間が視覚的に見たときにわかりやすくすべきかという議論はあると思われますが、そもそもmetadata.jsonを見ることはあまり想定してないので、ジャッジ用のスクリプトが常にどんな値を誤差として使ったかを指数表記させれば良いという話はあります。
#164 で複数解・インタラクティブの実装が進んでいます。
@chaemon さんの発想でインタラクティブの実装が複数解にも再利用できることが分かったので、その方針でいきましょう。
複数解のジャッジとインタラクティブについて、ジャッジコードをユーザーに書いてもらってそれを使ってジャッジする方法を使っています。そこで、複数解・インタラクティブを検出したとき、あるいはatcoder-tools setで複数解・インタラクティブが選択されたときには以下のようなテンプレートも生成されるようにしたいと思っていますがいかがでしょうか?これはC++バージョンであり、他の言語のバージョンも追加していく予定です。
/////////start template
#include <bits/stdc++.h>
using namespace std;
void quitAC(){
cerr<<"Judge: AC"<<endl;
exit(0);
}
void quitWA(const string message = ""){
cerr<<"Judge: WA"<<" ( "<<message<<" )"<<endl;
exit(1);
}
/* for interactive */
const string header_prefix = "Input Output\n----------------------------------";
const string input_prefix = " ";
string input(){
string s;
getline(cin,s);
cerr<<input_prefix<<s<<endl;
return s;
}
void output(const string &s){
cerr<<s<<endl;
cout<<s<<endl;
}
/* end for interactive */
///////////////////end template
typedef long long Int;
int main(int argc, char *argv[]){
//////////////// start template
/* for interactive */
cerr<<header_prefix<<endl;
ifstream in_s_2(argv[1]);
while(in_s_2){
string s;
getline(in_s_2,s);
cout<<s<<endl;
cerr<<s<<endl;
}
/* end for interactive */
ifstream in_s(argv[1]), out_s(argv[2]);
//////////////// end template
}
良いと思います。
今考えている問題は
-
atcoder-tools genの時だけjudge.cpp生成すると、あとでそれが欲しくなったときに不便
- @chaemonさんが提案してくださった
atcoder-tools setみたいな発想が多分いちばん良い気がします。
- @chaemonさんが提案してくださった
-
どうユーザーにテンプレートに提供するか(別のファイルか単一のtemplateファイルか?)
- とりあえずjudgeを意識することはそんなに無いはずだし、かなり見た目が変わると思うので別ファイルでいいと思う。config fieldは
judge_template=...
- とりあえずjudgeを意識することはそんなに無いはずだし、かなり見た目が変わると思うので別ファイルでいいと思う。config fieldは
-
ディレクトリに複数実行ファイルが存在することになるが、実行ファイルの自動検出に失敗しないか?
- judgeプログラムの名前を予めconfigで指定できるようにした上で(
judge_program_name=judge.outとか?)、metadata.jsonに書きこんでおくのが良さそう。本当はmainプログラムもそうするべきなんですが、judgeプログラムだけを取り除けば十分かな?
- judgeプログラムの名前を予めconfigで指定できるようにした上で(
-
ジャッジに他の言語(例えばpython)を使いたい場合はどうしたらよいか?
-
judge_lang='cpp'みたいなconfig fieldを作るとよい?
-
-
ジャッジテンプレートに何を含むべきか?
- @chaemonさんが載せてくれたcppのたたき台をスタートに考えていきましょう
コード読んでて気づいたんですが、既存のコードジェネレーターのコードを使ってどうやってstdin以外に適用するか、ちょっと悩ましいですね。コードジェネレーター群の修正が必要そうです。
void exit_ac(){
cerr<<"Judge: AC"<<endl;
exit(0);
}
void exit_wa(const string message = ""){
cerr<<"Judge: WA"<<" ( "<<message<<" )"<<endl;
exit(1);
}
これは合っても良い気がしますが他はウーム、好みという感じがしますね。
他は主にインタラクティブ用です。インタラクティブのみで必要なところに/* for interactive */をつけました。入出力するときにstderrにログを載せるようにすることで、ログファイルを生成するようにしました。本当はログファイル生成もatcoder-toolsでできればきれいですが、それが難しい(仲介スクリプトの作り方がわからない)ので、こうなっています。
したがって、現状ではインタラクティブ問題ではinput, output関数を通して入出力してほしいです。
なるほど。inputとoutputをデフォとして使わせるのはちょっと場当たり的な対処な感じがして、公式ではサポートしたくないやり方ですね...(本当にデバッグ出力したい人は手動で出すくらいにしてほしい)
どっちにしてもjudgeコードのジャッジメッセージ(なぜ間違えたかなど)をどこかに残してそれを読むというのをやりたいのですが、標準出力しちゃうと./mainの方に渡っちゃうので、標準エラー出力か新たにログファイルを作るかまたは標準出力をそのまま./mainに渡さないで解析するとかしないといけないのです。。。
わざわざ公式が提供しなくてもユーザーが標準エラー出力を自分で書くとかで十分な気がしますね