1. Skip to content

Grab our RSS feed

Scarky.com

This post, aimed at developers, shows how to write a custom judge in the C programming language on the example of standard Scarky judge comparing benchmark output with the output produced by the user’s program. Before reading it, it is advisable to get familiar with the following three tutorials:

The code presented below is written in C programming language. Creating a judge in other programming language is also possible – the examples will be published on this blog in the near future.

The judge consists of one file and can be written – as mentioned before – in any programming language supported by Scarky. Local standard project for the Scarky judge should consist of 3 files: spoj.c, spoj.h and your_judge.c. The first two are delivered by Scarky development team (when creating a challenge do not upload them; upload only your_judge.c) and they look like this:

spoj.c:

#include <spoj.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

/* file handles */
FILE
 *spoj_p_in,             /* problem's input */
 *spoj_p_out,    /* problem's output */
 *spoj_t_out,    /* tested program's output */
 *spoj_t_src,    /* tested program's source */
 *spoj_score,    /* score for the program, for challenge problems */
 *spoj_u_info,   /* additional info - psetter only */
 *spoj_p_info;   /* additional info - psetter and solution's owner */

#define __WARN_IF_NULL(x) if (errno=0, (x)==NULL)\
 fprintf(stderr, "warning: \"" #x "\" -> %s\n", strerror(errno))

#define __BUF_SIZE 1024*1024

static char buffer[__BUF_SIZE+1];

static void spoj_finalize();

void spoj_init() {
 __WARN_IF_NULL(spoj_p_in = fdopen(SPOJ_P_IN_FD,"r"));
 __WARN_IF_NULL(spoj_p_out = fdopen(SPOJ_P_OUT_FD,"r"));
 __WARN_IF_NULL(spoj_t_out = fdopen(SPOJ_T_OUT_FD,"r"));
 __WARN_IF_NULL(spoj_t_src = fdopen(SPOJ_T_SRC_FD,"r"));
 __WARN_IF_NULL(spoj_score = fdopen(SPOJ_SCORE_FD,"w"));
 __WARN_IF_NULL(spoj_p_info = fdopen(SPOJ_P_INFO_FD,"w"));
 __WARN_IF_NULL(spoj_u_info = fdopen(SPOJ_U_INFO_FD,"w"));
 atexit(spoj_finalize);
}

static void spoj_finalize() {
 fclose(spoj_p_in);
 fclose(spoj_p_out);
 fclose(spoj_t_out);
 fclose(spoj_t_src);
 fclose(spoj_score);
 fclose(spoj_p_info);
 fclose(spoj_u_info);
}

unsigned spoj_file_length(FILE *file) {
 int fd = fileno(file);
 unsigned size = 0;
 unsigned temp;
 while ((temp = read(fd, buffer, __BUF_SIZE))>0)
 size+=temp;
 return size;
}

void __spoj_assert_fail(char *s, int r) {
 if (!r) {
 fprintf(spoj_p_info, "%s\n", s);
 exit(SPOJ_RV_NEGATIVE);
 }
}

For local debug it is useful to change slightly functions spoj_init() and spoj_finalize() (for example instead of reading data from streams, read them from files).

Standard available streams:

  • spoj_p_in – problem input data
  • spoj_p_out – problem output data
  • spoj_t_out – output generated by user program
  • spoj_t_src – source code of tested solution
  • spoj_score – score
  • spoj_p_info – additional info (not supported in Scarky at the moment; ignore it)

spoj.h:

#ifdef __cplusplus
extern "C"
{
#endif

#ifndef __SPOJ_H__
#define __SPOJ_H__

#include <stdio.h>

/* return values */
#define SPOJ_RV_POSITIVE        0
#define SPOJ_RV_NEGATIVE        1
#define SPOJ_RV_IE                      2

/* descriptor numbers */
#define SPOJ_P_IN_FD            0
#define SPOJ_P_OUT_FD           4
#define SPOJ_T_OUT_FD           3
#define SPOJ_T_SRC_FD           5
#define SPOJ_SCORE_FD           1
#define SPOJ_P_INFO_FD          6
#define SPOJ_U_INFO_FD          7

/* file handles */
extern FILE
 *spoj_p_in,             /* problem's input */
 *spoj_p_out,    /* problem's output */
 *spoj_t_out,    /* tested program's output */
 *spoj_t_src,    /* tested program's source */
 *spoj_score,    /* score for the program, for challenge problems */
 *spoj_p_info,   /* additional info - problemsetter only */
 *spoj_u_info;   /* additional info - psetter and solution's owner */

/* must be called before using other functions or variables */
void spoj_init();

/* spoj "files" are really pipes, this is the recommended way to tell size */
/* the file's data is unaccessible later, so use it only if it doesn't matter */
unsigned spoj_file_length(FILE*);

/* use spoj_assert()! */
/* usual assert() from assert.h will cause "judge's internal error" in the future! */
#define spoj_assert(x) __spoj_assert_fail(__FILE__ ":" __spoj_xstr(__LINE__) ": SPOJ assertion `" #x "' failed.", x)
#define __spoj_str(x) #x
#define __spoj_xstr(x) __spoj_str(x)
void __spoj_assert_fail(char*, int);

#endif
#ifdef __cplusplus
}
#endif

And here is judge used in three rank types (Standard binary, sort by date; Standard binary, sort by time; Standard binary, sort by memory – details available here):

#include "spoj.h"
#include 

using namespace std;

// <0-255> normal, - 1 white, -2 eof
int getChar(FILE *f, bool ignWhite) {
    bool white;
    int ch;
    do {
        if ((ch = getc(f))==EOF)
            return -2;
        if (ch==' ' || ch=='\n' || ch=='\t' || ch=='\r')
            white=true;
        else white=false;
    } while (ignWhite && white);
    if (white)
        return -1;
    return ch;
}

int main(void) {
    spoj_init();
    int ch1 = getChar(spoj_t_out, true);
    int ch2 = getChar(spoj_p_out, true);
    while (ch1 == ch2) {
        if (ch1==-2)
            exit(SPOJ_RV_POSITIVE);
        bool ignWhite;
        if (ch1==-1)
            ignWhite =true;
        else
            ignWhite=false;
        ch1 = getChar(spoj_t_out, ignWhite);
        ch2 = getChar(spoj_p_out, ignWhite);
    };
    if (ch1 == -2 && ch2 == -1 && getChar(spoj_p_out, true)==-2 )
        return SPOJ_RV_POSITIVE;
    if (ch2 == -2 && ch1 == -1 && getChar(spoj_t_out, true)==-2)
        return SPOJ_RV_POSITIVE;
    return SPOJ_RV_NEGATIVE;
}

Comments are closed