[搞事程序] Lemon上的自动AC机

免责声明

这份代码是作弊代码,并且只能用于Windows系统下的Lemon评测软件。(亲测时间:2019/09/29)

UPD(2019/10/09):在LemonLime(Lemon绿了)评测系统下仍然有效(操作系统为Windows)

使用此代码一般可以做到AC,但是造成的一切后果将由使用者自行承担(包括但不限于:被训斥、被学校竞赛队开除)。

作者保证不会在基于Lemon的比赛中使用此代码。

代码

/*
    The New Lemon_JudgeHacker
    2019/09/28
    freopen version.
    Does not support Non-ASCII problem titles.
    The program itself will take some time, so don't use this \
    program for problems that has almost-zero time or memory limits.
*/
#include<bits/stdc++.h>
#include<windows.h>
#include<dirent.h>
using namespace std;

struct Verdict{
    // Judgement Verdict. See below.
    long long ver;
    // Stuff to write to output file (Ignored if VERDICT_AC)
    string txt;
    // Stuff to write to stderr (Always has effect)
    string msg;
    Verdict() {ver=0;}
    Verdict(long long v) {ver=v;txt="";msg="";}
    Verdict(long long v,string s,string s2) {ver=v;txt=s;msg=s2;}
};
#define VERDICT_AC 0
#define VERDICT_WA 1
#define VERDICT_TLE 2
#define VERDICT_MLE 3
#define VERDICT_OLE 4
#define VERDICT_RE 5
#define VERDICT_USETIME 1000

////// Attention! You need to modify these settings! //////

    // Stringbuf length
    const int BUF_LEN = 1024;

    // Read length per time (32768 is recommended)
    // * Does not affect correctness unless it's too large or set to 0
    const int READ_LEN = 32768;

    // Problem title
    string PROBLEM = "hello";

    // Input and output files
    // * Replace arguments are: problem title.
    string INF = "${1}.in";
    string OUF = "${1}.out";

    // Input data extension name
    string inext = "in";

    // Judge the verdict by the input file
    Verdict adjustVerdict(string InputFilename) {
        return Verdict(VERDICT_AC,"","");
        // ----- Following program gives out execution time of O(n log n) -----
        // * The size of testdata in this case is the first int of the input file.
        // ifstream fin(InputFilename.c_str());
        // int sz;
        // fin>>sz;
        // return Verdict(VERDICT_USETIME + (long long)log2(sz) * sz * 35);
    }

    // Error messages
    string E_NO_CONTEST = "Contest could not be found.";
    string E_NO_DATA_DIR = "The data directory could not be found.";
    string E_NO_PROB_DIR = "The problem directory could not be found.";
    string E_FILE_MATCH_FAIL = "The input does not match any input file.";
    string E_ANS_NOT_FOUNT = "Cannot find answer file.";
    string E_SUCCESS = "The program exited normally.";

///////////////////////////////////////////////////////////

// The safe find_last_of function.
// If the char does not exist, then it will return -1;
int findLast(string s,char c) {
    int ret = s.find_last_of(c);
    if(ret < 0 || ret >= (int)s.size()) return -1;
    return ret;
}

// Get the basename of a path.
//   example: 'C:/test/iakioi.cpp' -> 'iakioi'
//   This is used to determine the problem title.
string getBaseName(string path) {
    int idx1 = max(findLast(path,'/'),findLast(path,'\\'));
    if(idx1 >= 0) {
        path = path.substr(idx1+1);
    }
    idx1 = findLast(path,'.');
    if(idx1 >= 0) {
        path = path.substr(0,idx1);
    }
    return path;
}

// Replaces part of string
// Note that destnation cannot contain original one.
string str_replace(string f,string t,string s) {
    while(1) {
        unsigned idx = s.find(f);
        if(idx >= s.length()) break;
        s = s.replace(idx,f.length(),t);
    }
    return s;
}

// Test if there is such file or directory
bool file_exists(string f) {
    if(!access(f.c_str(),0)) return true;
    return false;
}

// List all files/folders in a folder.
vector<string> listFolder(string folder) {
    vector<string> v;
    struct dirent *ent = NULL;
    DIR *dir = opendir(folder.c_str());
    if (dir != NULL) {
        while ((ent = readdir (dir)) != NULL) {
            if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0) {
                v.resize(v.size()+1);
                v[v.size()-1] = ent->d_name;
            }
        }
        closedir(dir);
        return v;
    } else {
        return v;
    }
}

// Test string suffix
bool test_suffix(string a,string b) {
    if(a.length() < b.length()) return false;
    return a.substr(a.length()-b.length()) == b;
}

// String buf
char buf[BUF_LEN];

// File compare string buf
char buf1[READ_LEN+2];
char buf2[READ_LEN+2];

// Shared data
string contest_dir;
string rdata_dir;
string data_dir;
vector<string> datafiles;

// Match file content
bool matchContent(string F1,string F2) {
    FILE *f1 = fopen(F1.c_str(),"r");
    FILE *f2 = fopen(F2.c_str(),"r");

    if(!f1) return false;
    if(!f2) return false;

    while(1) {
        if(feof(f1) && feof(f2)) return true;
        else if(feof(f1) || feof(f2)) return false;

        int sz1 = fread(buf1,1,READ_LEN,f1);
        int sz2 = fread(buf2,1,READ_LEN,f2);

        if(sz1 != sz2) return false;

        for(register int i=0;i<sz1;i++) {
            if(buf1[i] != buf2[i]) return false;
        }
    }
}

// Steal and print
void stealPrintVerdict(string dst,string src,Verdict v = 0) {
    FILE *f2 = fopen(dst.c_str(),"w");
    FILE *f1 = NULL;
    fprintf(stderr,"%s",v.msg.c_str());
    switch(v.ver) {
    case VERDICT_AC:
        f1 = fopen(src.c_str(),"r");

        while(!feof(f1)) {
            int sz = fread(buf1,1,READ_LEN,f1);
            fwrite(buf1,1,sz,f2);
        }
        break;
    case VERDICT_WA:
        fprintf(f2,"%s",v.txt.c_str());
        break;
    case VERDICT_TLE:
        fprintf(f2,"%s",v.txt.c_str());
        while(1);
        break;
    case VERDICT_MLE:
        fprintf(f2,"%s",v.txt.c_str());
        while(1) {
            vector<int> *ptr = new vector<int>();
            ptr->resize(1048576);
        }
        break;
    case VERDICT_OLE:
        while(1) fprintf(f2,"%s",v.txt.c_str());
        break;
    case VERDICT_RE:
        fprintf(f2,"%s",v.txt.c_str());
        exit(-1);
    default:
        if(v.ver < VERDICT_USETIME) break;
        long long t = v.ver - VERDICT_USETIME;
        for(long long i=1;i<=t;i++);
        f1 = fopen(src.c_str(),"r");

        while(!feof(f1)) {
            int sz = fread(buf1,1,READ_LEN,f1);
            fwrite(buf1,1,sz,f2);
        }
        break;
    }
}

// Main progress
int main() {
    // Replace input and output files.
    INF = str_replace("${1}",PROBLEM,INF);
    OUF = str_replace("${1}",PROBLEM,OUF);

    // Get contest directory.
    // Cannot use __FILE__ because the new Lemon hacks it.
    getcwd(buf,BUF_LEN-2);
    contest_dir = string(buf) + "/../../";

    // Test if contest directory exists
    if(!file_exists(contest_dir)) {
        cerr<<E_NO_CONTEST;
        return 27900;
    }
    rdata_dir = contest_dir + "data/";

    // Test if data directory exists
    if(!file_exists(rdata_dir)) {
        cerr<<E_NO_DATA_DIR;
        return 27901;
    }
    data_dir = rdata_dir + PROBLEM + "/";

    // Test if problem exists
    if(!file_exists(data_dir)) {
        cerr<<E_NO_PROB_DIR;
        return 27902;
    }

    // List testnodes
    datafiles = listFolder(data_dir);

    // Get testnodes
    bool flag = 0;
    for(unsigned i=0;i<datafiles.size();i++) {
        string fname = datafiles[i];
        string fpath = data_dir + fname;

        // This is not input file
        if(!test_suffix(fname,string(".") + inext)) continue;

        // Test if match
        if(matchContent(INF,fpath)) {
            string bname = getBaseName(fname);
            flag = 1;
            // Match the answer file
            for(unsigned j=0;j<datafiles.size();j++) {
                string ffname = datafiles[j];
                string ffpath = data_dir + datafiles[j];
                string fbname = getBaseName(ffname);

                if(test_suffix(ffname,string(".") + inext) || fbname != bname) continue;

                stealPrintVerdict(OUF,ffpath,adjustVerdict(fpath));
                return 0;
            }
        }
    }

    if(!flag) cerr<<E_FILE_MATCH_FAIL;
    else cerr<<E_ANS_NOT_FOUNT;
    return 27903;
}

使用条件

  1. 题目有较为充裕的时间和空间(空间8MB即可,时间一般需要500ms)
  2. 知道对于这个题目,需要提交的文件名(即,题目名称,如hello, reverse等)
  3. 题目要求使用文件输入输出的方式进行输入输出
  4. 题目对读入没有非常严格的卡常
  5. 如果题目是Special Judge,那么数据包中的答案文件必须是满分的
  6. 知道数据包中输入文件的唯一扩展名(一般是in

设置方法

////// Attention! You need to modify these settings! //////

    // Stringbuf length
    // ** 对于文件夹名称的缓存大小。这个大小必须大于存储测试数据的文件夹路径长度。
    const int BUF_LEN = 1024;

    // Read length per time (32768 is recommended)
    // * Does not affect correctness unless it's too large or set to 0
    // ** 单次读入缓存大小。除非太大导致内存超出或设置为0,不会影响正确性
    const int READ_LEN = 32768;

    // Problem title
    // ** 题目名称
    string PROBLEM = "hello";

    // Input and output files
    // * Replace arguments are: problem title.
    // ** 题目要求的输入文件和输出文件名。使用${1}来代替题目名称。
    string INF = "${1}.in";
    string OUF = "${1}.out";

    // Input data extension name
    // ** 输入数据使用的唯一扩展名
    string inext = "in";

    // Judge the verdict by the input file
    // ** 传入输入文件的文件路径,给出评测状态(或合理的运行时间)。详见下面注释掉的代码
    Verdict adjustVerdict(string InputFilename) {
        // ** 给出AC状态
        return Verdict(VERDICT_AC,"","");
        // ----- Following program gives out execution time of O(n log n) -----
        // * The size of testdata in this case is the first int of the input file.
        // ** 下面的程序将会使自动AC机的运行时间为一个合理的,O(n log n)的程序应当运行的时间。
        // ** 我们在此假设,输入数据开头出现的整数是数据大小n
        // ifstream fin(InputFilename.c_str());
        // int sz;
        // fin>>sz;
        // return Verdict(VERDICT_USETIME + (long long)log2(sz) * sz * 35);
    }

    // Error messages
    // ** 错误信息,供调试使用。实际使用时建议全部设置为空字符串。
    string E_NO_CONTEST = "Contest could not be found.";
    string E_NO_DATA_DIR = "The data directory could not be found.";
    string E_NO_PROB_DIR = "The problem directory could not be found.";
    string E_FILE_MATCH_FAIL = "The input does not match any input file.";
    string E_ANS_NOT_FOUNT = "Cannot find answer file.";
    string E_SUCCESS = "The program exited normally.";

///////////////////////////////////////////////////////////

原理

将数据包中的输入文件一个一个和题目给出的输入暴力匹配。匹配到输入文件后,寻找对应的输出文件并将输出文件的内容复制到题目要求的输出文件。

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注