aboutsummaryrefslogtreecommitdiffstats
path: root/judge/check_reactgrader.c
diff options
context:
space:
mode:
Diffstat (limited to 'judge/check_reactgrader.c')
-rw-r--r--judge/check_reactgrader.c571
1 files changed, 571 insertions, 0 deletions
diff --git a/judge/check_reactgrader.c b/judge/check_reactgrader.c
new file mode 100644
index 0000000..f283bc5
--- /dev/null
+++ b/judge/check_reactgrader.c
@@ -0,0 +1,571 @@
+#include<stdio.h>
+#include<stdlib.h>
+#include<limits.h>
+#include<fcntl.h>
+#include<signal.h>
+#include<pthread.h>
+#include<semaphore.h>
+#include<termios.h>
+
+#include"judge_def.h"
+#include"judgx.h"
+
+struct check_thread_info{
+ int status;
+ int score;
+ int maxscore;
+ sem_t *done_sem;
+};
+
+int mptd;
+char ptname[PATH_MAX + 1];
+int infd;
+
+// Opponent program for Guess that Cow
+// Originally based on soln-opt by Hal Burch
+// Extensive changes by Matt Craighead
+#include <ctype.h>
+#include <string.h>
+
+/*
+guess.in: input case
+standard input/output: student running program
+stderr: grading output
+ */
+
+
+// Shared solver code for Guess that Cow
+// Used by soln-mjc, opponent, and testgen
+// Originally based on soln-opt by Hal Burch
+// Extensive changes by Matt Craighead
+
+#define MAXPROP 8
+#define MAXVAL 3
+#define MAXITEM 50
+#define NAME "guess"
+
+#define HASHSIZE 904069
+
+// Mask of cows. Would use a 64-bit int, but this is more portable and lets me
+// optimize things the compiler would do a poor job at.
+typedef struct {
+ unsigned int lo;
+ unsigned int hi;
+} CowMask;
+
+typedef struct state {
+ CowMask member;
+ int score;
+ struct state *next;
+} state_t;
+
+int item[MAXITEM][MAXPROP];
+int nitem, nprop;
+int maxSearchDepth = 100;
+int noHashTable;
+CowMask maskhistory[200];
+char query[200][100];
+int resp[200];
+int nhist;
+
+int nQuestionsUsed, nOptimalQuestions;
+
+state_t *hashTable[HASHSIZE];
+
+CowMask updateMasks[MAXPROP][1 << MAXVAL];
+
+void FindBestQuestion(CowMask poss, int depth, int *question, int *score);
+
+CowMask BuildCowMask(int i)
+{
+ CowMask cm;
+ if (i < 32) {
+ cm.lo = 1 << i;
+ cm.hi = 0;
+ } else {
+ cm.lo = 0;
+ cm.hi = 1 << (i-32);
+ }
+ return cm;
+}
+
+state_t *HashFind(CowMask poss, int depth)
+{
+ static state_t fakeHashEntry;
+ int h, temp;
+ state_t *lp;
+
+ if (noHashTable) {
+ h = 0;
+ lp = &fakeHashEntry;
+ } else {
+ // Simple hash function
+ h = (poss.lo + poss.hi*7) % HASHSIZE;
+
+ // Check hash table to see if this node has already been searched
+ for (lp = hashTable[h]; lp; lp = lp->next) {
+ if ((poss.lo == lp->member.lo) && (poss.hi == lp->member.hi)) {
+ return lp;
+ }
+ }
+
+ // Alloc a hash entry and link it in
+ lp = (state_t *)malloc(sizeof(state_t));
+ }
+
+ // Recursively search to find score for this node
+ FindBestQuestion(poss, depth+1, &temp, &lp->score);
+ lp->member = poss;
+ if (!noHashTable) {
+ lp->next = hashTable[h];
+ hashTable[h] = lp;
+ }
+
+ return lp;
+}
+
+void CleanHashTable(void)
+{
+ int i;
+ state_t *p, *next;
+ for (i = 0; i < HASHSIZE; i++) {
+ p = hashTable[i];
+ while (p) {
+ next = p->next;
+ free(p);
+ p = next;
+ }
+ hashTable[i] = NULL;
+ }
+}
+
+int CountBits(CowMask x)
+{
+ static int table[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
+ int bits;
+ bits = 0;
+ while (x.lo) {
+ bits += table[x.lo & 0xF];
+ x.lo >>= 4;
+ }
+ while (x.hi) {
+ bits += table[x.hi & 0xF];
+ x.hi >>= 4;
+ }
+ return bits;
+}
+
+void FindBestResponse(CowMask poss, int question, CowMask *newPoss, int *answer)
+{
+ int attr = question % MAXPROP;
+ int quest = question / MAXPROP;
+ int fcnt, tcnt, fquestions, tquestions;
+ CowMask fposs, tposs;
+ state_t *s;
+ int oldNoHashTable;
+
+ oldNoHashTable = noHashTable;
+ noHashTable = 0;
+
+ // Must choose answer of true or false, so run the
+ // solver on each case
+ fposs.lo = poss.lo & ~updateMasks[attr][quest].lo;
+ fposs.hi = poss.hi & ~updateMasks[attr][quest].hi;
+ tposs.lo = poss.lo & updateMasks[attr][quest].lo;
+ tposs.hi = poss.hi & updateMasks[attr][quest].hi;
+ fcnt = CountBits(fposs);
+ tcnt = CountBits(tposs);
+ if (fcnt <= 3) {
+ fquestions = fcnt-1;
+ } else {
+ s = HashFind(fposs, 0); fquestions = s->score;
+ }
+ if (tcnt <= 3) {
+ tquestions = tcnt-1;
+ } else {
+ s = HashFind(tposs, 0); tquestions = s->score;
+ }
+
+ // First go by # of questions; if there's a tie, go by
+ // # of cows
+ if (fquestions > tquestions) {
+ *answer = 0;
+ } else if (fquestions < tquestions) {
+ *answer = 1;
+ } else if (fcnt > tcnt) {
+ *answer = 0;
+ } else if (fcnt < tcnt) {
+ *answer = 1;
+ } else {
+ // arbitrary
+ *answer = 0;
+ if (nitem == 4 && nQuestionsUsed == 2) *answer = 1;
+ }
+
+ *newPoss = *answer ? tposs : fposs;
+
+ noHashTable = oldNoHashTable;
+}
+
+void FindBestQuestion(CowMask poss, int depth, int *question, int *score)
+{
+ CowMask fposs, tposs;
+ int lq, lp;
+ int bestScore, bestQuestion;
+ int tcnt, fcnt;
+ state_t *s;
+
+ bestScore = 1000;
+ bestQuestion = -1;
+
+ // Consider all properties to ask about
+ for (lp = 0; lp < nprop; lp++) {
+ // Consider all useful sets of values to ask about
+ // An empty list of values to ask about is useless
+ // Omit the last property so as to not repeat questions, because
+ // all questions can be phrased in two ways
+ for (lq = 1; lq < (1 << (MAXVAL-1)); lq++) {
+ // Get the sets of cows remaining after each answer
+ fposs.lo = poss.lo & ~updateMasks[lp][lq].lo;
+ fposs.hi = poss.hi & ~updateMasks[lp][lq].hi;
+ tposs.lo = poss.lo & updateMasks[lp][lq].lo;
+ tposs.hi = poss.hi & updateMasks[lp][lq].hi;
+ fcnt = CountBits(fposs);
+ tcnt = CountBits(tposs);
+
+ // If one answer would leave no cows left, this question
+ // doesn't really give us any information
+ if ((fcnt == 0) || (tcnt == 0)) continue;
+
+ // How many questions will be required after this one to
+ // solve the puzzle if we get an answer of "yes"?
+ if ((tcnt <= 3) || (depth >= maxSearchDepth)) {
+ // Small cases are easy
+ tcnt = tcnt - 1;
+ } else {
+ // Look in the hash table
+ s = HashFind(tposs, depth);
+ tcnt = s->score;
+ }
+
+ // Quit early if no better than the best so far
+ if (tcnt >= bestScore) continue;
+
+ // How many questions will be required after this one to
+ // solve the puzzle if we get an answer of "no"?
+ if ((fcnt <= 3) || (depth >= maxSearchDepth)) {
+ // Small cases are easy
+ fcnt = fcnt - 1;
+ } else {
+ // Look in the hash table
+ s = HashFind(fposs, depth);
+ fcnt = s->score;
+ }
+
+ // Pick the worse of the two
+ if (tcnt < fcnt) tcnt = fcnt;
+ if (tcnt + 1 < bestScore) {
+ bestScore = tcnt + 1;
+ bestQuestion = lp + lq*MAXPROP;
+ }
+ }
+ }
+
+ //assert(bestScore < 1000);
+ //assert(bestQuestion != -1);
+
+ *question = bestQuestion;
+ *score = bestScore;
+}
+
+void ParseInputFile(FILE *f)
+{
+ int lv, lv2;
+ char str[3];
+ fscanf(f, "%d %d", &nitem, &nprop);
+
+ for (lv = 0; lv < nitem; lv++) {
+ for (lv2 = 0; lv2 < nprop; lv2++) {
+ fscanf(f, "%s", str);
+ item[lv][lv2] = str[0] - 'X';
+ }
+ }
+
+}
+
+void BuildUpdateMasks(void)
+{
+ int i, lp, lq;
+
+ // Precompute sets of cows remaining after each possible question
+ for (lp = 0; lp < nprop; lp++) {
+ for (lq = 0; lq < (1 << MAXVAL); lq++) {
+ updateMasks[lp][lq].lo = 0;
+ updateMasks[lp][lq].hi = 0;
+ for (i = 0; i < nitem; i++) {
+ if (lq & (1 << item[i][lp])) {
+ CowMask cm = BuildCowMask(i);
+ updateMasks[lp][lq].lo |= cm.lo;
+ updateMasks[lp][lq].hi |= cm.hi;
+ }
+ }
+ }
+ }
+}
+
+int judge(struct check_thread_info *thread_info){
+ FILE *ioin;
+ FILE *ioout;
+ FILE *fin;
+ char line[256];
+ int score, answer, quest, i;
+ CowMask poss;
+
+ ioin = fdopen(mptd,"r");
+ ioout = fdopen(mptd,"w");
+ fin = fdopen(infd,"r");
+
+ CleanHashTable();
+ ParseInputFile(fin);
+ BuildUpdateMasks();
+
+ // At first, all cows are possible
+ poss.lo = 0;
+ poss.hi = 0;
+ for (i = 0; i < nitem; i++) {
+ CowMask cm = BuildCowMask(i);
+ poss.lo |= cm.lo;
+ poss.hi |= cm.hi;
+ }
+
+ nOptimalQuestions = 100;
+
+ // Solve the problem to get the best possible score
+ FindBestQuestion(poss, 0, &quest, &nOptimalQuestions);
+
+ nQuestionsUsed = 0;
+ maskhistory[nQuestionsUsed] = poss;
+ for (;;) {
+ if(fgets(line, sizeof(line), ioin) == NULL){
+ return 1;
+ }
+
+ if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
+ if (line[0] == 'Q') {
+ int parsed, i, attr, val[3];
+
+ nQuestionsUsed++;
+ if(nQuestionsUsed > 100){
+ fprintf(ioout, "ABORT\n");
+ fprintf(stderr, "WRONG\nYour program asked more than 100 questions.\n");
+ return 0;
+ }
+ if (!isdigit(line[2])) {
+ fprintf(stderr, "Wrong: unexpected input format: %s\n", line);
+ return 1;
+ }
+ attr = line[2] - '0';
+ parsed = 0; i = 3;
+ while (line[i] != 0) {
+ if ((line[i] != ' ') || line[i+1] < 'X' || line[i+1] > 'Z') {
+ fprintf(stderr, "Wrong: unexpected input format: %s\n", line);
+ return 1;
+ }
+ val[parsed] = line[i+1]-'X'+1;
+ parsed++;
+ i += 2;
+ }
+ if (parsed < 1) {
+ fprintf(stderr, "Wrong: unexpected input format: %s\n", line);
+ return 1;
+ }
+ attr--;
+ quest = 0;
+ for (i = 0; i < parsed; i++) {
+ if ((val[i] < 1) || (val[i] > MAXVAL)) {
+ fprintf(stderr, "Incorrect: invalid attribute value in question\n");
+ return 1;
+ }
+ quest |= 1 << (val[i]-1);
+ }
+
+ FindBestResponse(poss, attr + quest*MAXPROP, &poss, &answer);
+ strcpy(query[nQuestionsUsed-1], line);
+ maskhistory[nQuestionsUsed-1] = poss;
+ resp[nQuestionsUsed-1] = answer;
+
+ fprintf(ioout,"%d\n", answer);
+ fflush(ioout);
+ } else if (line[0] == 'C') {
+ // Done, check the answer
+ break;
+ } else {
+ fprintf(stderr, "Wrong: unexpected input format: %s\n", line);
+ return 1;
+ }
+ }
+
+ if ((line[1] != ' ') || !isdigit(line[2])) {
+ fprintf(stderr, "Wrong: unexpected input format: %s", line);
+ return 1;
+ }
+ if (line[3] != 0) {
+ if (!isdigit(line[3]) || (line[4] != 0)) {
+ fprintf(stderr, "Wrong: unexpected input format: %s", line);
+ return 1;
+ }
+ }
+ sscanf(line, "C %d", &answer);
+ answer--;
+
+ // To be correct, this cow must be the only remaining possible cow
+ if (CountBits(poss) != 1) {
+ fprintf(ioout, "ABORT\n");
+ fprintf(stderr, "WRONG\n");
+ fprintf(stderr, "The following cows are still consistent with the answers given:\n");
+ fprintf(stderr, " ");
+ for(i=0; i<32; i++){
+ if(poss.lo&(1<<i)){
+ fprintf(stderr, " %d", i+1);
+ }
+ }
+ for(i=0;i<32; i++){
+ if(poss.hi&(1<<i)){
+ fprintf(stderr, " %d", i+32+1);
+ }
+ }
+ fprintf(stderr, "\n");
+ return 0;
+ } else {
+ CowMask cm = BuildCowMask(answer);
+ if ((poss.lo != cm.lo) || (poss.hi != cm.hi)) {
+ for(i=0; i<nQuestionsUsed; i++){
+ if(!(maskhistory[i].lo&cm.lo)&&!(maskhistory[i].hi&cm.hi))
+ break;
+ }
+ fprintf(ioout, "ABORT\n");
+ fprintf(stderr, "WRONG\nYour program claims the solution is cow %d,\n", answer+1);
+ fprintf(stderr, "but cow %d was eliminated by query #%d (%s) with response %d.\n", answer+1, i+1, query[i], resp[i]);
+ return 0;
+ } else {
+ if (nQuestionsUsed <= nOptimalQuestions) {
+ score = 10;
+ } else if (nQuestionsUsed == nOptimalQuestions+1) {
+ score = 8;
+ } else if (nQuestionsUsed == nOptimalQuestions+2) {
+ score = 5;
+ } else if (nQuestionsUsed <= nOptimalQuestions+5) {
+ score = 4;
+ } else {
+ score = 3;
+ }
+ if (score < 10) {
+ fprintf(stderr, "OK %d\n", score);
+ } else {
+ fprintf(stderr, "OK\n");
+ }
+ if(nQuestionsUsed < nOptimalQuestions){
+ fprintf(stderr, "Student is better than us.\n");
+ }
+
+ thread_info->score = score;
+ if(nQuestionsUsed < nOptimalQuestions){
+ thread_info->score += (nOptimalQuestions - nQuestionsUsed);
+ }
+ }
+ }
+
+ fprintf(ioout, "DONE\n");
+
+ if(thread_info->score > 10){
+ thread_info->maxscore = thread_info->score;
+ }else{
+ thread_info->maxscore = 10;
+ }
+
+ if(thread_info->score == thread_info->maxscore){
+ thread_info->status = JUDGE_AC;
+ }else{
+ thread_info->status = JUDGE_WA;
+ }
+
+ return 0;
+}
+
+DLL_PUBLIC int init(char *runpath,char *datapath){
+ struct termios tes;
+ char tpath[PATH_MAX + 1];
+ char newpath[PATH_MAX + 1];
+
+ mptd = posix_openpt(O_RDWR);
+ grantpt(mptd);
+ unlockpt(mptd);
+ ptsname_r(mptd,ptname,sizeof(ptname));
+ tcgetattr(mptd,&tes);
+ cfmakeraw(&tes);
+ tcsetattr(mptd,TCSANOW,&tes);
+
+ printf("check1\n");
+
+ snprintf(tpath,sizeof(tpath),"%s/in.txt",datapath);
+ snprintf(newpath,sizeof(newpath),"%s/guess.in",runpath);
+ if(link(tpath,newpath) == -1){
+ unlink(newpath);
+ link(tpath,newpath);
+ }
+ infd = open(tpath,O_RDONLY);
+ if(infd == -1){
+ goto error;
+ }
+
+ printf("check2\n");
+
+ return 0;
+
+error:
+
+ close(mptd);
+ close(infd);
+
+ return -1;
+}
+
+static void thread_clean(void *arg){
+ close(mptd);
+ close(infd);
+ return;
+}
+DLL_PUBLIC void* thread(void *arg){
+ struct check_thread_info *thread_info;
+
+ pthread_cleanup_push(thread_clean,NULL);
+ thread_info = (struct check_thread_info*)arg;
+
+ thread_info->status = JUDGE_WA;
+ thread_info->score = 0;
+ thread_info->maxscore = 10;
+
+ judge(thread_info);
+
+ pthread_cleanup_pop(thread_clean);
+ sem_post(thread_info->done_sem);
+ return NULL;
+}
+DLL_PUBLIC int stop(void){
+ return 0;
+}
+
+DLL_PUBLIC int run(){
+ int sptd;
+ struct termios tes;
+
+ sptd = open(ptname,O_RDWR);
+ tcgetattr(sptd,&tes);
+ cfmakeraw(&tes);
+ tcsetattr(sptd,TCSANOW,&tes);
+
+ dup2(sptd,0);
+ dup2(sptd,1);
+ dup2(sptd,2);
+ return 0;
+}