Advanced Usage

Continuity Special Judge

Sometimes, we need to use special judge not only for predicating the correctness of output result, but the quality of result as well. So we can return a tuple of values which represent correctness and score to do this. Like the following code which is named spj_continuity.py.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def continuity_spj_func(stdin, stdout):
    _expected_sum = int(stdin.read().strip())

    output_items = [int(item.strip()) for item in stdout.read().strip().split(' ') if item]
    _actual_sum = sum(output_items)
    _actual_cnt = len(output_items)

    if _expected_sum == _actual_sum:
        return (True, 1 - (_actual_cnt - 1) / _expected_sum), 'Correct result.', 'Oh yeah, well done ^_^.'
    else:
        return (False, 0), f'Result {_expected_sum} expected but {_actual_sum} found.'


__spj__ = continuity_spj_func

Here is an example of correct result, but it is obviously not the best one.

1
pyspj -i '15' -o '1 2 3 4 5' -s spj_continuity -p
1
2
3
4
5
6
{
    "correctness": true,
    "detail": "Oh yeah, well done ^_^.",
    "message": "Correct result.",
    "score": 0.7333333333333334
}

Another example of correct result, which is better than the abovementioned one.

1
pyspj -i '15' -o '7 8' -s spj_continuity -p
1
2
3
4
5
6
{
    "correctness": true,
    "detail": "Oh yeah, well done ^_^.",
    "message": "Correct result.",
    "score": 0.9333333333333333
}

And then a wrong example as shown in following part.

1
pyspj -i '15' -o '7 8 8' -s spj_continuity -p
1
2
3
4
5
6
{
    "correctness": false,
    "detail": "Result 15 expected but 23 found.",
    "message": "Result 15 expected but 23 found.",
    "score": 0.0
}

Addtional Arguments

In some complex cases, additional arguments need to be passed into the special judge function, such as the mode of special judge, or something like the extra required data file’s path. Like the following code which is named spj_additional.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def additional_spj_func(stdin, stdout, sep=' '):
    inputs = [int(item.strip()) for item in stdin.read().strip().split(sep) if item]
    _correct_sum = sum(inputs)

    outputs = stdout.read().strip().split(' ', maxsplit=2)
    if len(outputs) >= 1:
        _result = int(outputs[0])
    else:
        return False, 'No output found.'

    if _result == _correct_sum:
        return True, 'Correct result.', 'Oh yeah, well done ^_^.'
    else:
        return False, 'Result {correct} expected but {actual} found.'.format(
            correct=repr(_correct_sum), actual=repr(_result)
        )


__spj__ = additional_spj_func

Here is an common example

1
pyspj -i '1 2 3 4 5' -o 15 -s spj_additional -V 'sep= '
1
{"correctness": true, "detail": "Oh yeah, well done ^_^.", "message": "Correct result."}

And, if you want the input to be separated by ,, just use the -V option, like the command line below

1
pyspj -i '1,2,3,4,5' -o 15 -s spj_additional -V 'sep=,'
1
{"correctness": true, "detail": "Oh yeah, well done ^_^.", "message": "Correct result."}

Note

File import features (like -I and -O options`) are not supported in addtional arguments.

If you need to load the content of a data file, just pass the path of the data file by additional arguments, and then manually load the file in your special judge function.

Create My Runnable Special Judge CLI

You can create your own special judge CLI with the pyspj_entry function. Like the following code named spj_runnable.py.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pyspj.entry import pyspj_entry


def spj_func(stdin, stdout):
    inputs = [int(item.strip()) for item in stdin.read().strip().split(' ') if item]
    _correct_sum = sum(inputs)

    outputs = stdout.read().strip().split(' ', maxsplit=2)
    if len(outputs) >= 1:
        _result = int(outputs[0])
    else:
        return False, 'No output found.'

    if _result == _correct_sum:
        return True, 'Correct result.', 'Oh yeah, well done ^_^.'
    else:
        return False, 'Result {correct} expected but {actual} found.'.format(
            correct=repr(_correct_sum), actual=repr(_result)
        )


if __name__ == '__main__':
    pyspj_entry(
        'demo_pyspj', spj_func,
        version='2.3.3',  # optional
        author='spj-dev',  # optional
        email='spj-demo@my-email.com',  # optional
    )()

You can see its version information.

1
python spj_runnable.py -v
1
2
3
4
Special judge - demo_pyspj, version 2.3.3.
Developed by spj-dev, spj-demo@my-email.com.
Powered by pyspj, version 0.1.0.
Based on Python 3.7.13.

And see its help information, which is almost the same as the native pyspj CLI.

1
python spj_runnable.py -h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Usage: spj_runnable.py [OPTIONS]

  Demo_pyspj - test a pair of given input and output.

Options:
  -v, --version           Show special judge's version information.
  -i, --input TEXT        Input content of special judge.
  -o, --output TEXT       Output content of special judge
  -I, --input_file FILE   Input file of special judge (if -i is given, this
                          will be ignored).
  -O, --output_file FILE  Output file of special judge (if -o is given, this
                          will be ignored).
  -V, --value TEXT        Attached values for special judge (do not named as
                          "stdin" or "stdout").
  -p, --pretty            Use pretty mode to print json result.
  -h, --help              Show this message and exit.

The cli script spj_runnable.py created by yourself can be used like the pyspj CLI.

Besides, this runnable special judge script can be built to a standalone special judge executable file if needed.

1
pyinstaller -D -F -n demo_spj -c spj_runnable.py