LDUOJ For example, goto - > github
LDUOJ Platform Developer spj development blog
The spj usage rules of different platforms may be different, but there are not too many changes to be made
first:
Parameter correspondence
args[1] corresponds to the input of data
args[2] is the answer to the corresponding data, that is, an allowable condition or an impossible condition (which will be discussed below)
args[3] corresponds to the output of the user's results, which is where we need to focus
Return value
0 means no problem, i.e. AC
1 represents a problem, i.e. WA
For some systems
42 represents AC and 43 represents WA. Different systems may have different return values
Code submission
Similar to the Niuke platform, the class name of the Main class of the submitted Java code must be Main, otherwise a compilation error will occur
For example:
public class Main{ public static void main(){ /** your code **/ } }
Several spj
The first: a simple special judgment
For example, the following simple spj program:
#include <stdio.h> #include <math.h> #include <cstring> const double eps = 1e-6; int main(int argc,char *args[]) { FILE * f_in=fopen(args[1],"r"); FILE * f_out=fopen(args[2],"r"); FILE * f_user=fopen(args[3],"r"); fclose(f_in); fclose(f_out); fclose(f_user); return ret; }
In the case of file pointers, we can directly use fscanf for input and output
The format is as follows:
fscanf(pnt,"%d",&x);
Where pnt is the file pointer where you want to read from. For example, to get the user's output, replace pnt with f_user
Second: special judgment of multiple groups of inputs
Example: UVA10886
The corresponding spj program should be:
#include <stdio.h> #include <math.h> const double eps = 1e-4; int main(int argc,char *args[])///Main function { FILE * f_in=fopen(args[1],"r");///Test input FILE * f_out=fopen(args[2],"r");///Test output FILE * f_user=fopen(args[3],"r");///User output int ret=0;///Return value int T; double a,x; char cas[100],num[100]; fscanf(f_in,"%d",&T);///Number of data groups read from input T while(T--) { fscanf(f_out,"%s %s %lf",&cas,&num,&a); fscanf(f_user,"%s %s %lf",&cas,&num,&x); if(fabs(a-x)>eps) ret = 1;///Wrong Answer } fclose(f_in); fclose(f_out); fclose(f_user); return ret; }
The third kind: need to judge the special situation [impossible]
Then the corresponding spj should be:
#include <stdio.h> #include <math.h> #include <cstring> const double eps = 1e-6; int main(int argc,char *args[]) { FILE * f_in=fopen(args[1],"r"); FILE * f_out=fopen(args[2],"r"); FILE * f_user=fopen(args[3],"r"); int ret = 0; double a,x; char std[100],usr[100]; while(fscanf(f_out,"%s",std) == 1 && fscanf(f_user,"%s",usr) == 1){ if(strcmp(std,"IMPOSSIBLE") && !strcmp(usr,"IMPOSSIBLE")) { ret = 1; return ret; } if(strcmp(usr,"IMPOSSIBLE") && !strcmp(std,"IMPOSSIBLE")) { ret = 1; return ret; } double sstd = atof(std); double uusr = atof(usr); if(fabs(sstd - uusr) > eps) { ret = 1; return ret; } } fclose(f_in); fclose(f_out); fclose(f_user); return ret; }
Fourth: spj with [testlib.h]
In general, the spj of this topic is relatively standardized, and testlib.h It is open source on Github
The inventor of the header file should be Mike mirzayanov, the administrator of Codeforces
You only need to match args [] in the above, so there is no big problem
The fifth: GCPC [German College Programming Contest] class spj
Case of a single file
This situation is relatively simple
Take GCPC2019 Keeping the Dogs Out as an example:
Open to see:
#include <fstream> #include <iostream> #include <limits> #include <sstream> #include <string> #include <vector> #include <cassert> typedef long long ll; using std::cin; using std::endl; using std::ifstream; using std::ofstream; using std::string; using std::vector; constexpr int CORRECT = 42;///Modification constexpr int INCORRECT = 43;///Modification int main(int argc, char* argv[]) { assert(argc == 4); ifstream input(argv[1]); ifstream answer(argv[2]); ofstream debug_output(argv[3] + string("/judgemessage.txt"));///Modification string s1, s2; answer >> s1; cin >> s2; if (s1 != s2 && (s1 == "impossible" || s2 == "impossible")) { debug_output << "Expected: " << s1 << ", got: " << s2 << endl;///Forced corresponding modification return INCORRECT; } if (s1 != "impossible") { ll x, y; std::stringstream first_token(s2); if (!(first_token >> x) || !(cin >> y)) { debug_output << "Too little output" << endl;///Forced corresponding modification return INCORRECT; } if (!cin || x <= 0 || y <= 0) { debug_output << "Presentation Error" << endl;///Forced corresponding modification return INCORRECT; } if (std::numeric_limits<ll>::max() / x < y) { debug_output << "Area too large." << endl;///Forced corresponding modification return INCORRECT; } ll n; input >> n; vector<ll> cnt(n + 1); for (ll& i: cnt) input >> i; ll current_area = 0; for (int k = n; k >= 0; --k) { ll border_length = 1ll << k; current_area += border_length * border_length * cnt[k]; ll a = (x / border_length) * border_length; ll b = (y / border_length) * border_length; if (a * b < current_area) { debug_output << "Incorrect dimensions, cannot fit all squares of size " << border_length << " and larger." << endl;///Forced corresponding modification return INCORRECT; } } if (current_area != x * y) { debug_output << "Area is " << x * y << ", should be " << current_area << "." << endl;///Forced corresponding modification return INCORRECT; } } char c; if (cin >> c) { debug_output << "Too much output." << endl;///Forced corresponding modification return INCORRECT; } return CORRECT; }
The modified spj should look like this:
It is worth mentioning that the input here is not through the file pointer, but through the stream to read and write the file
Moreover, argc[3] of GCPC competition platform should be a parameter to feed back the results to the platform
#include <fstream> #include <iostream> #include <limits> #include <sstream> #include <string> #include <vector> #include <cassert> typedef long long ll; using std::cin; using std::endl; using std::ifstream; using std::ofstream; using std::string; using std::vector; constexpr int CORRECT = 0; constexpr int INCORRECT = 1; int main(int argc, char* argv[]) { // assert(argc == 4); ifstream input(argv[1]); ifstream answer(argv[2]); ifstream user(argv[3]); // ofstream debug_output(argv[3] + string("/judgemessage.txt")); string s1, s2; answer >> s1; user >> s2; if (s1 != s2 && (s1 == "impossible" || s2 == "impossible")) { return INCORRECT; } if (s1 != "impossible") { ll x, y; std::stringstream first_token(s2); if (!(first_token >> x) || !(user >> y)) { return INCORRECT; } if (!user || x <= 0 || y <= 0) { return INCORRECT; } if (std::numeric_limits<ll>::max() / x < y) { return INCORRECT; } ll n; input >> n; vector<ll> cnt(n + 1); for (ll& i: cnt) input >> i; ll current_area = 0; for (int k = n; k >= 0; --k) { ll border_length = 1ll << k; current_area += border_length * border_length * cnt[k]; ll a = (x / border_length) * border_length; ll b = (y / border_length) * border_length; if (a * b < current_area) { return INCORRECT; } } if (current_area != x * y) { return INCORRECT; } } char c; if (user >> c) { return INCORRECT; } return CORRECT; }
*.h *.cpp situation
Take GCPC 2019 historical math as an example:
We just need to merge the two files together h file on top The cpp file is placed below
If a structure or a common or private function in a class is lifeless, 80% plus using namespace std can be solved
Taking this title as an example, the spj after merging these files is:
#include <bits/stdc++.h> /* Utility functions for writing output validators for the Kattis * problem format. * * The primary functions and variables available are the following. * In many cases, the only functions needed are "init_io", * "wrong_answer", and "accept". * * - init_io(argc, argv): * initialization * * - judge_in, judge_ans, author_out: * std::istream objects for judge input file, judge answer * file, and submission output file. * * - accept(): * exit and give Accepted! * * - accept_with_score(double score): * exit with Accepted and give a score (for scoring problems) * * - judge_message(std::string msg, ...): * printf-style function for emitting a judge message (a * message that gets displayed to a privileged user with access * to secret data etc). * * - wrong_answer(std::string msg, ...): * printf-style function for exitting and giving Wrong Answer, * and emitting a judge message (which would typically explain * the cause of the Wrong Answer) * * - judge_error(std::string msg, ...): * printf-style function for exitting and giving Judge Error, * and emitting a judge message (which would typically explain * the cause of the Judge Error) * * - author_message(std::string msg, ...): * printf-style function for emitting an author message (a * message that gets displayed to the author of the * submission). (Use with caution, and be careful not to let * it leak information!) * */ #include <sys/stat.h> #include <cassert> #include <cstdarg> #include <cstdlib> #include <iostream> #include <fstream> #include <sstream> typedef void (*feedback_function)(const std::string &, ...); const int EXITCODE_AC = 42;///Comments / modifications const int EXITCODE_WA = 43;///Comments / modifications const std::string FILENAME_AUTHOR_MESSAGE = "teammessage.txt"; const std::string FILENAME_JUDGE_MESSAGE = "judgemessage.txt"; const std::string FILENAME_JUDGE_ERROR = "judgeerror.txt"; const std::string FILENAME_SCORE = "score.txt"; #define USAGE "%s: judge_in judge_ans feedback_dir < author_out\n" std::ifstream judge_in, judge_ans; std::istream author_out(std::cin.rdbuf()); char *feedbackdir = NULL; void vreport_feedback(const std::string &category, const std::string &msg, va_list pvar) { std::ostringstream fname; if (feedbackdir) fname << feedbackdir << '/'; fname << category; FILE *f = fopen(fname.str().c_str(), "a"); assert(f); vfprintf(f, msg.c_str(), pvar); fclose(f); } void report_feedback(const std::string &category, const std::string &msg, ...) { va_list pvar; va_start(pvar, msg); vreport_feedback(category, msg, pvar); } void author_message(const std::string &msg, ...) { va_list pvar; va_start(pvar, msg); vreport_feedback(FILENAME_AUTHOR_MESSAGE, msg, pvar); } void judge_message(const std::string &msg, ...) { va_list pvar; va_start(pvar, msg); vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar); } void wrong_answer(const std::string &msg, ...) { va_list pvar;///Comments / modifications va_start(pvar, msg); vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar); exit(EXITCODE_WA); } void judge_error(const std::string &msg, ...) { va_list pvar;///Comments / modifications va_start(pvar, msg); vreport_feedback(FILENAME_JUDGE_ERROR, msg, pvar); assert(0); } void accept() { exit(EXITCODE_AC); } void accept_with_score(double scorevalue) { report_feedback(FILENAME_SCORE, "%.9le", scorevalue); exit(EXITCODE_AC); } bool is_directory(const char *path) { struct stat entry; return stat(path, &entry) == 0 && S_ISDIR(entry.st_mode); } void init_io(int argc, char **argv) { if(argc < 4) {///Comments / modifications fprintf(stderr, USAGE, argv[0]);///Comments / modifications judge_error("Usage: %s judgein judgeans feedbackdir [opts] < userout", argv[0]);///Comments / modifications }///Comments / modifications ///Comments / modifications // Set up feedbackdir first, as that allows us to produce feedback / / / comments / modifications // files for errors in the other parameters./// Comments / modifications if (!is_directory(argv[3])) {///Comments / modifications judge_error("%s: %s is not a directory\n", argv[0], argv[3]);///Comments / modifications }///Comments / modifications feedbackdir = argv[3];///Comments / modifications judge_in.open(argv[1], std::ios_base::in); if (judge_in.fail()) { judge_error("%s: failed to open %s\n", argv[0], argv[1]); } judge_ans.open(argv[2], std::ios_base::in); if (judge_ans.fail()) { judge_error("%s: failed to open %s\n", argv[0], argv[2]); } author_out.rdbuf(std::cin.rdbuf());///Comments / modifications } using namespace std; using ll = long long; using vl = vector<ll>; #define sz(c) ll((c).size()) #define FOR(i,a,b) for(ll i = (a); i < (b); i++) #define FORD(i,a,b) for(ll i = ll(b) - 1; i >= (a); i--) const ll MAX_BASE = (2LL << 60) + 1; ll check_and_parse_author(const string& to_parse) { //first pass; check for invalid character if(sz(to_parse) == 0) wrong_answer("Submission provided empty string to parse.\n"); if(to_parse[0] == '+' && sz(to_parse) == 1) wrong_answer("Answer is not a number.\n"); if(to_parse[0] != '+' && (to_parse[0] < '0' || '9' < to_parse[0])) wrong_answer("Answer contains invalid character.\n"); FOR(i,1,sz(to_parse)) if(to_parse[i] < '0' || '9' < to_parse[i]) wrong_answer("Answer contains invalid character.\n"); //second pass; calculate answer base ll base = 0; if(to_parse[0] != '+') base = to_parse[0] - '0'; FOR(i,1,sz(to_parse)) { // avoid overflow if(MAX_BASE / 10 < base) return MAX_BASE; base = base*10 + (to_parse[i] - '0'); } return base; } void multiply(vl &a, vl &b, vl &res, ll base) { res.assign(sz(res), 0); FOR(i,0,sz(a)) { FOR(j,0,sz(b)) { res[i + j] += a[i] * b[j]; res[i + j + 1] += res[i + j] / base; res[i + j] %= base; } } FOR(i,0,sz(res) - 1) { res[i + 1] += res[i] / base; res[i] %= base; } } ll compare(vl &a, vl &b) { if(sz(a) < sz(b)) { FOR(i,sz(a),sz(b)) if(b[i] != 0) return -1; } if(sz(a) > sz(b)) { FOR(i,sz(b), sz(a)) if(a[i] != 0) return 1; } FORD(i,0,min(sz(a),sz(b))) { if(a[i] - b[i] != 0) return a[i] - b[i]; } return 0; } int main(int argc, char **argv) { init_io(argc,argv);///Comment / modify this function string a_ans, j_ans; char foo; judge_ans >> j_ans; if(!( author_out >> a_ans)) wrong_answer("Less output than expected.\n"); if(author_out >> foo) wrong_answer("More output than expected.\n"); transform(a_ans.begin(), a_ans.end(), a_ans.begin(), ::tolower); //quick accept if(a_ans == j_ans) accept(); if(a_ans == "impossible") wrong_answer("Submission claims impossible, judge has answer.\n"); ll base = check_and_parse_author(a_ans); if(base < 2) wrong_answer("Invalid base.\n"); bool differs = false; if(j_ans == "impossible") differs = true; ll tmp, maxdigit = 1; judge_in >> tmp; vl a(tmp); FORD(i, 0, tmp) {judge_in >> a[i]; maxdigit = max(maxdigit, a[i]);} judge_in >> tmp; vl b(tmp); FORD(i, 0, tmp){ judge_in >> b[i]; maxdigit = max(maxdigit, b[i]);} judge_in >> tmp; vl prod(tmp); FORD(i, 0, tmp){ judge_in >> prod[i]; maxdigit = max(maxdigit, prod[i]);} vl res(sz(a) + sz(b) + 1); if(base < maxdigit + 1) wrong_answer("Base not greater than all occuring digits.\n"); multiply(a,b,res,base); ll cmp = compare(prod, res); if(cmp == 0) { if(differs) judge_error("Judge answer is 'impossible' but submission gave valid answer.\n"); accept(); } wrong_answer("Invalid base.\n"); }
Due to different platforms, there will be problems when the function contains a line of code to write to the file. Therefore, to comment it out, change the returned status code to 0 1 instead of 42 43
Note that some header files cannot be used on Windows platform and will report compilation errors, but they can be compiled on Linux platform. After submitting spj, they will be compiled successfully
Comments have been added to the above code where modifications are needed. After modification, it should be:
#include <bits/stdc++.h> #include <sys/stat.h> #include <cassert> #include <cstdarg> #include <cstdlib> #include <iostream> #include <fstream> #include <sstream> typedef void (*feedback_function)(const std::string &, ...); const int EXITCODE_AC = 0; const int EXITCODE_WA = 1; const std::string FILENAME_AUTHOR_MESSAGE = "teammessage.txt"; const std::string FILENAME_JUDGE_MESSAGE = "judgemessage.txt"; const std::string FILENAME_JUDGE_ERROR = "judgeerror.txt"; const std::string FILENAME_SCORE = "score.txt"; #define USAGE "%s: judge_in judge_ans feedback_dir < author_out\n" std::ifstream judge_in, judge_ans; std::ifstream author_out; char *feedbackdir = NULL; void vreport_feedback(const std::string &category, const std::string &msg, va_list pvar) { std::ostringstream fname; if (feedbackdir) fname << feedbackdir << '/'; fname << category; FILE *f = fopen(fname.str().c_str(), "a"); assert(f); vfprintf(f, msg.c_str(), pvar); fclose(f); } void report_feedback(const std::string &category, const std::string &msg, ...) { va_list pvar; va_start(pvar, msg); vreport_feedback(category, msg, pvar); } void author_message(const std::string &msg, ...) { va_list pvar; va_start(pvar, msg); vreport_feedback(FILENAME_AUTHOR_MESSAGE, msg, pvar); } void judge_message(const std::string &msg, ...) { va_list pvar; va_start(pvar, msg); vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar); } void wrong_answer(const std::string &msg, ...) { // va_list pvar; // va_start(pvar, msg); // vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar); exit(EXITCODE_WA); } void judge_error(const std::string &msg, ...) { // va_list pvar; // va_start(pvar, msg); // vreport_feedback(FILENAME_JUDGE_ERROR, msg, pvar); assert(0); } void accept() { exit(EXITCODE_AC); } void accept_with_score(double scorevalue) { // report_feedback(FILENAME_SCORE, "%.9le", scorevalue); exit(EXITCODE_AC); } bool is_directory(const char *path) { struct stat entry; return stat(path, &entry) == 0 && S_ISDIR(entry.st_mode); } void init_io(int argc, char **argv) { // if(argc < 4) { // fprintf(stderr, USAGE, argv[0]); // judge_error("Usage: %s judgein judgeans feedbackdir [opts] < userout", argv[0]); // } // // // Set up feedbackdir first, as that allows us to produce feedback // // files for errors in the other parameters. // if (!is_directory(argv[3])) { // judge_error("%s: %s is not a directory\n", argv[0], argv[3]); // } // feedbackdir = argv[3]; judge_in.open(argv[1], std::ios_base::in); if (judge_in.fail()) { judge_error("%s: failed to open %s\n", argv[0], argv[1]); } judge_ans.open(argv[2], std::ios_base::in); if (judge_ans.fail()) { judge_error("%s: failed to open %s\n", argv[0], argv[2]); } author_out.open(argv[3], std::ios_base::in); if (author_out.fail()) { judge_error("%s: failed to open %s\n", argv[0], argv[3]); } } using namespace std; using ll = long long; using vl = vector<ll>; #define sz(c) ll((c).size()) #define FOR(i,a,b) for(ll i = (a); i < (b); i++) #define FORD(i,a,b) for(ll i = ll(b) - 1; i >= (a); i--) const ll MAX_BASE = (2LL << 60) + 1; ll check_and_parse_author(const string& to_parse) { //first pass; check for invalid character if(sz(to_parse) == 0) wrong_answer("Submission provided empty string to parse.\n"); if(to_parse[0] == '+' && sz(to_parse) == 1) wrong_answer("Answer is not a number.\n"); if(to_parse[0] != '+' && (to_parse[0] < '0' || '9' < to_parse[0])) wrong_answer("Answer contains invalid character.\n"); FOR(i,1,sz(to_parse)) if(to_parse[i] < '0' || '9' < to_parse[i]) wrong_answer("Answer contains invalid character.\n"); //second pass; calculate answer base ll base = 0; if(to_parse[0] != '+') base = to_parse[0] - '0'; FOR(i,1,sz(to_parse)) { // avoid overflow if(MAX_BASE / 10 < base) return MAX_BASE; base = base*10 + (to_parse[i] - '0'); } return base; } void multiply(vl &a, vl &b, vl &res, ll base) { res.assign(sz(res), 0); FOR(i,0,sz(a)) { FOR(j,0,sz(b)) { res[i + j] += a[i] * b[j]; res[i + j + 1] += res[i + j] / base; res[i + j] %= base; } } FOR(i,0,sz(res) - 1) { res[i + 1] += res[i] / base; res[i] %= base; } } ll compare(vl &a, vl &b) { if(sz(a) < sz(b)) { FOR(i,sz(a),sz(b)) if(b[i] != 0) return -1; } if(sz(a) > sz(b)) { FOR(i,sz(b), sz(a)) if(a[i] != 0) return 1; } FORD(i,0,min(sz(a),sz(b))) { if(a[i] - b[i] != 0) return a[i] - b[i]; } return 0; } int main(int argc, char **argv) { init_io(argc,argv); string a_ans, j_ans; char foo; judge_ans >> j_ans; if(!( author_out >> a_ans)) wrong_answer("Less output than expected.\n"); if(author_out >> foo) wrong_answer("More output than expected.\n"); transform(a_ans.begin(), a_ans.end(), a_ans.begin(), ::tolower); //quick accept if(a_ans == j_ans) accept(); if(a_ans == "impossible") wrong_answer("Submission claims impossible, judge has answer.\n"); ll base = check_and_parse_author(a_ans); if(base < 2) wrong_answer("Invalid base.\n"); bool differs = false; if(j_ans == "impossible") differs = true; ll tmp, maxdigit = 1; judge_in >> tmp; vl a(tmp); FORD(i, 0, tmp) { judge_in >> a[i]; maxdigit = max(maxdigit, a[i]); } judge_in >> tmp; vl b(tmp); FORD(i, 0, tmp) { judge_in >> b[i]; maxdigit = max(maxdigit, b[i]); } judge_in >> tmp; vl prod(tmp); FORD(i, 0, tmp) { judge_in >> prod[i]; maxdigit = max(maxdigit, prod[i]); } vl res(sz(a) + sz(b) + 1); if(base < maxdigit + 1) wrong_answer("Base not greater than all occuring digits.\n"); multiply(a,b,res,base); ll cmp = compare(prod, res); if(cmp == 0) { if(differs) judge_error("Judge answer is 'impossible' but submission gave valid answer.\n"); accept(); } wrong_answer("Invalid base.\n"); }
Sixth: spj of interactive questions
The platform does not support interactive questions temporarily, so the interactive questions in the question bank have not been processed
Can refer to Interactive question spj of Luogu Correspondence writing