@@ -41,6 +41,17 @@ The following are the most commonly set keys in this file:
4141 seconds.
4242- `memory_limit` : the maximum amount of memory that a solution can use, in
4343 mebibytes.
44+ - `controller_time_limit` : the maximum amount of time that the controller can run
45+ for, in seconds (defaults to `time_limit + 1.0` ).
46+ - `controller_wall_time_limit` : the maximum amount of wall time that the
47+ controller can run for, in seconds (defaults to `controller_time_limit * 3 + 1.0` ).
48+ - `controller_memory_limit` : the maximum amount of memory that the controller can
49+ use, in mebibytes (defaults to `memory_limit` ).
50+ - `controller_process_limit` : the maximum number of processes the controller can
51+ spawn (defaults to 200).
52+ - `interactive_concurrent` : whether the solution processes are assumed to be
53+ concurrent (wall time max-ed, memory summed) or sequential (wall time sum-ed,
54+ memory max-ed). Defaults to `true` .
4455- `score_precision` : the number of decimal digits to round scores for this task
4556 to (defaults to 0, i.e. integers).
4657- `user_io` : set this value to `fifo_io` to have solutions in communication
@@ -490,11 +501,14 @@ we suggest including at least the following:
490501= `check` folder
491502
492503The `check` folder can contain either a `checker.<ext>` file, or a
493- `manager.<ext>` file, or be empty.
504+ `manager.<ext>` file, or a `controller.<ext>` file, or be empty.
494505
495506If it contains `manager.<ext>` , the task is interpreted as a communication
496507task, for which details are given in # ref (<communication> ).
497508
509+ If it contains `controller.<ext>` , the task is interpreted as an interactive
510+ task, for which details are given in # ref (<interactive> ).
511+
498512The `checker.<ext>` file will be compiled by `task-maker-rust` (if necessary),
499513and gets executed both by `task-maker-rust` and by CMS with three command line
500514arguments, in order:
@@ -608,6 +622,112 @@ static FILE *to_manager, *from_manager;
608622int main(int argc, char **argv) {
609623 from_manager = fopen(argv[2], "r");
610624 to_manager = fopen(argv[1], "w");
611- // Call into the contestant's solution here.
625+ // call into the contestant's solution here.
626+ }
627+ ```
628+
629+ = Interactive tasks
630+ <interactive>
631+
632+ An interactive task is identified by the presence of a `check/controller.<ext>`
633+ file (or `cor/controller.<ext>` ). This type of task is similar to a communication task, but the controller
634+ has full control over the execution of one or more solution processes.
635+
636+ The controller communicates with `task-maker-rust` via standard output and standard input,
637+ and reports the score and messages via standard error.
638+
639+ == Controller protocol
640+
641+ When the controller starts, it should print `START_SOLUTION` followed by a newline
642+ to standard output for each solution process it wants to start.
643+
644+ After each `START_SOLUTION` , it should read two integers from standard input,
645+ representing the file descriptors (fds) for writing to and reading from the
646+ solution, respectively.
647+
648+ The controller then communicates with the solution(s) using these fds (e.g.,
649+ using `fdopen` ).
650+
651+ To report the score and messages, the controller must print to standard error
652+ using the following prefixes:
653+ - `SCORE: <score>` : the score of the testcase, as a float between `0.0` and `1.0` .
654+ - `USER_MESSAGE: <message>` : a message for the contestant.
655+ - `ADMIN_MESSAGE: <message>` : an optional message for the administrators.
656+
657+ Like checkers and managers, the special messages `translate:success` ,
658+ `translate:wrong` and `translate:partial` are supported.
659+
660+ == Example controller (`controller.cpp` )
661+
662+ ```cpp
663+ #include "controller_lib.h"
664+
665+ int handler(FILE *to_sol, FILE *from_sol) {
666+ // Interaction logic here...
667+ grade(1.0, "translate:success", nullptr);
668+ return 0;
669+ }
670+
671+ int main() {
672+ return start_one_solution(handler);
673+ }
674+ ```
675+
676+ == `controller_lib.h`
677+
678+ ```cpp
679+ #include <assert.h>
680+ #include <stdio.h>
681+ #include <stdlib.h>
682+
683+ __attribute__((noreturn)) void grade(double score, const char *msg,
684+ const char *admin_msg) {
685+ fprintf(stderr, "SCORE: %f\n", score);
686+ fprintf(stderr, "USER_MESSAGE: %s\n", msg);
687+ if (admin_msg) {
688+ fprintf(stderr, "ADMIN_MESSAGE: %s\n", admin_msg);
689+ }
690+ exit(0);
691+ }
692+
693+ int start_one_solution(int (*handler)(FILE *to_solution, FILE *from_solution)) {
694+ printf("START_SOLUTION\n");
695+ fflush(stdout);
696+ int fdin, fdout;
697+ if (scanf("%d %d", &fdin, &fdout) != 2) return 1;
698+ FILE *to_solution = fdopen(fdin, "w");
699+ FILE *from_solution = fdopen(fdout, "r");
700+ assert(to_solution);
701+ assert(from_solution);
702+ int ret = handler(to_solution, from_solution);
703+ fclose(to_solution);
704+ fclose(from_solution);
705+ return ret;
706+ }
707+
708+ int start_many_solutions(int (*handler)(FILE **to_solution,
709+ FILE **from_solution, int num),
710+ int num) {
711+ FILE **to_solution = (FILE **)malloc(sizeof(FILE *) * num);
712+ FILE **from_solution = (FILE **)malloc(sizeof(FILE *) * num);
713+ for (int i = 0; i < num; i++) {
714+ printf("START_SOLUTION\n");
715+ fflush(stdout);
716+ int fdin, fdout;
717+ if (scanf("%d %d", &fdin, &fdout) != 2) return 1;
718+ to_solution[i] = fdopen(fdin, "w");
719+ from_solution[i] = fdopen(fdout, "r");
720+ assert(to_solution[i]);
721+ assert(from_solution[i]);
722+ }
723+ int ret = handler(to_solution, from_solution, num);
724+ for (int i = 0; i < num; i++) {
725+ fclose(to_solution[i]);
726+ fclose(from_solution[i]);
727+ }
728+ free(to_solution);
729+ free(from_solution);
730+ return ret;
612731}
613732```
733+
0 commit comments