Skip to content

Commit

Permalink
read task.yaml.orig before task.yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
Virv12 committed Apr 6, 2024
1 parent 9842d67 commit ba0fc12
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 8 deletions.
115 changes: 108 additions & 7 deletions task-maker-format/src/ioi/format/italian_yaml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@
//! The subtask also does not have a name, the default one (`subtask2`) will be used.
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::BufWriter;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
Expand Down Expand Up @@ -361,6 +362,83 @@ struct TaskYAML {
/// Can be either "std_io" for using stdin/stdout, or "fifo_io" for using pipes given in argv.
/// Defaults to "fifo_io".
pub user_io: Option<String>,

/// Compatibility with cms, unused.
pub score_mode: Option<String>,
/// Compatibility with cms, unused.
pub token_mode: Option<String>,
/// Compatibility with cms, unused.
pub public_testcases: Option<String>,
/// Compatibility with cms, unused.
pub feedback_level: Option<String>,
}

/// Deserialized data from the task.yaml of a IOI format task.
#[derive(Debug, Serialize, Deserialize)]
struct TaskYAMLOrig {
/// The name of the task (the short one).
#[serde(default)]
pub name: String,
/// The title of the task (the long one).
pub title: String,
/// The score type to use for this task.
pub score_type: Option<String>,
/// The number of decimal digits when displaying the scores.
#[serde(default)]
pub score_precision: usize,

/// The time limit for the execution of the solutions.
pub time_limit: f64,
/// The memory limit in MiB of the execution of the solution.
pub memory_limit: u64,

/// Whether this is an output only task. Defaults to false.
#[serde(default)]
pub output_only: bool,
/// The input file for the solutions, usually 'input.txt' or '' (stdin). Defaults to `''`.
#[serde(default)]
pub infile: String,
/// The output file for the solutions, usually 'output.txt' or '' (stdout). Defaults to `''`.
#[serde(default)]
pub outfile: String,

/// An integer that defines the difficulty of the task. Used only in booklet compilations.
pub difficulty: Option<u8>,
/// An integer that defines the level inside a _syllabus_ (for example for the Olympiads in
/// Teams). Used only in booklet compilations.
pub syllabuslevel: Option<u8>,

/// Number of solution processes to spawn in parallel in a communication task.
pub num_processes: Option<u8>,
/// The type of communication for the solution in a communication task.
///
/// Can be either "std_io" for using stdin/stdout, or "fifo_io" for using pipes given in argv.
/// Defaults to "fifo_io".
pub user_io: Option<String>,
}

impl TaskYAMLOrig {
fn into_task_yaml(self, task_dir: &Path) -> TaskYAML {
TaskYAML {
name: task_dir.file_name().unwrap().to_string_lossy().to_string(),
title: self.title,
score_type: self.score_type,
score_precision: self.score_precision,
time_limit: self.time_limit,
memory_limit: self.memory_limit,
output_only: self.output_only,
infile: self.infile,
outfile: self.outfile,
difficulty: self.difficulty,
syllabuslevel: self.syllabuslevel,
num_processes: self.num_processes,
user_io: self.user_io,
score_mode: Some("max_subtask".into()),
token_mode: Some("disabled".into()),
public_testcases: Some("all".into()),
feedback_level: Some("full".into()),
}
}
}

/// The iterator item type when following the task input testcases.
Expand All @@ -377,7 +455,7 @@ pub(crate) enum TaskInputEntry {
/// `italian_yaml`.
///
/// `italian_yaml` format is structured as follow:
/// * `task.yaml` - file with the task information
/// * `task.yaml.orig` or `task.yaml` - file with the task information
/// * `gen/` - folder with the generator and validator
/// * `generator.xxx` (also `generatore`)
/// * `validator.xxx` (also `valida`)
Expand All @@ -398,11 +476,26 @@ pub fn parse_task<P: AsRef<Path>>(
eval_config: &EvaluationConfig,
) -> Result<IOITask, Error> {
let task_dir = task_dir.as_ref();
let path = task_dir.join("task.yaml");
let file = fs::File::open(&path)
.with_context(|| format!("Cannot open task.yaml from {}", path.display()))?;
let yaml: TaskYAML =
serde_yaml::from_reader(file).context("Failed to deserialize task.yaml")?;

let task_yaml_overwrite: bool;
let yaml: TaskYAML;
if task_dir.join("task.yaml.orig").exists() {
task_yaml_overwrite = true;
let path = task_dir.join(task_dir.join("task.yaml.orig"));
let file = File::open(&path)
.with_context(|| format!("Cannot open task.yaml.orig from {}", path.display()))?;
let yaml_orig: TaskYAMLOrig =
serde_yaml::from_reader(file).context("Failed to deserialize task.yaml.orig")?;
yaml = yaml_orig.into_task_yaml(&task_dir);
} else if task_dir.join("task.yaml").exists() {
task_yaml_overwrite = false;
let path = task_dir.join(task_dir.join("task.yaml"));
let file = File::open(&path)
.with_context(|| format!("Cannot open task.yaml from {}", path.display()))?;
yaml = serde_yaml::from_reader(file).context("Failed to deserialize task.yaml")?;
} else {
bail!("No task.yaml found in {}", task_dir.display());
}
debug!("The yaml is {:#?}", yaml);

let map_file = |file: String| -> Option<PathBuf> {
Expand Down Expand Up @@ -484,6 +577,14 @@ pub fn parse_task<P: AsRef<Path>>(
subtasks.insert(subtask.id, subtask);
}

if task_yaml_overwrite {
let path = task_dir.join("task.yaml");
let file = File::create(&path)
.with_context(|| format!("Cannot open task.yaml from {}", path.display()))?;
serde_yaml::to_writer(BufWriter::new(file), &yaml)
.context("Failed to serialize task.yaml")?;
}

let mut task = IOITask {
path: task_dir.into(),
task_type,
Expand Down
2 changes: 1 addition & 1 deletion task-maker-format/src/ioi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ impl IOITask {

/// Check if in the provided path there could be a IOI-like task.
pub fn is_valid<P: AsRef<Path>>(path: P) -> bool {
path.as_ref().join("task.yaml").exists()
path.as_ref().join("task.yaml").exists() || path.as_ref().join("task.yaml.orig").exists()
}

/// Get the root directory of the task.
Expand Down
27 changes: 27 additions & 0 deletions tests/task-yaml-orig.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
mod common;
use common::TestInterface;

fn classic(test: TestInterface) {
test.success()
.time_limit(1.0)
.memory_limit(64)
.max_score(100.0)
.subtask_scores(vec![5.0, 45.0, 50.0])
.must_compile("generatore.cpp")
.must_compile("solution.cpp")
.must_compile("wrong_file.cpp")
.solution_score("solution.cpp", vec![5.0, 45.0, 50.0])
.solution_score("wrong_file.cpp", vec![0.0, 0.0, 0.0]);
}

#[test]
fn classic_local() {
better_panic::install();
classic(TestInterface::run_local("task-yaml-orig"));
}

#[test]
fn classic_remote() {
better_panic::install();
classic(TestInterface::run_remote("task-yaml-orig"));
}
18 changes: 18 additions & 0 deletions tests/tasks/task-yaml-orig/gen/GEN
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This is a comment, this line should be ignored

# The task is to echo the first parameter
# If you exit fast enough and don't use much memory you will score 100 :P

# Copy some file from some random folder
#ST: 5
#COPY: gen/hard.txt

# Small N
#ST: 45
500
900

# Big N
#ST: 50
1300
2000
10 changes: 10 additions & 0 deletions tests/tasks/task-yaml-orig/gen/generatore.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <iostream>

int main(int argc, char** argv) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " N" << std::endl; // NOLINT
return 1;
}
std::cout << argv[1] << std::endl; // NOLINT
std::cerr << "This string should not appear in the input.txt" << std::endl;
}
1 change: 1 addition & 0 deletions tests/tasks/task-yaml-orig/gen/hard.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
11
3 changes: 3 additions & 0 deletions tests/tasks/task-yaml-orig/gen/limiti.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env python3

MAX_N = 5000
12 changes: 12 additions & 0 deletions tests/tasks/task-yaml-orig/gen/valida.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env python3

# pylint: disable=wildcard-import
# pylint: disable=invalid-name

import sys
from limiti import *

assert len(sys.argv) == 3
infile = open(sys.argv[1]).read().splitlines()
assert 0 <= int(infile[0]) <= MAX_N
assert 1 <= int(sys.argv[2]) <= 3
7 changes: 7 additions & 0 deletions tests/tasks/task-yaml-orig/sol/solution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <iostream>

int main() {
int n;
std::cin >> n;
std::cout << n;
}
10 changes: 10 additions & 0 deletions tests/tasks/task-yaml-orig/sol/wrong_file.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <fstream>

int main() {
std::ifstream in("input.txt");
std::ofstream out("output.txt");

int n;
in >> n;
out << n << '\n';
}
17 changes: 17 additions & 0 deletions tests/tasks/task-yaml-orig/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: task-yaml-orig
title: Testing task.yaml.orig
score_type: null
score_precision: 0
time_limit: 1.0
memory_limit: 64
output_only: 'False'
infile: ''
outfile: ''
difficulty: null
syllabuslevel: null
num_processes: null
user_io: null
score_mode: max_subtask
token_mode: disabled
public_testcases: all
feedback_level: full
3 changes: 3 additions & 0 deletions tests/tasks/task-yaml-orig/task.yaml.orig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
title: Testing task.yaml.orig
time_limit: 1
memory_limit: 64

0 comments on commit ba0fc12

Please sign in to comment.