Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ impl UIOpt {
/// Disable the Curses UI and fallback to PrintUI if verbose output is enabled.
pub fn disable_if_needed(&mut self, logger: &LoggerOpt) {
let mut show_warning = false;
if logger.should_diable_curses() {
if logger.should_disable_curses() {
if let task_maker_format::ui::UIType::Curses = self.ui {
// warning deferred to after the logger has been initialized
show_warning = true;
Expand Down Expand Up @@ -280,9 +280,10 @@ impl LoggerOpt {
std::env::set_var("RUST_BACKTRACE", "1");
}
match self.verbose {
0 => std::env::set_var("RUST_LOG", "warn,tabox=warn"),
1 => std::env::set_var("RUST_LOG", "info,tabox=info"),
2 => std::env::set_var("RUST_LOG", "debug,tabox=debug"),
0 => std::env::set_var("RUST_LOG", "error,tabox=error"),
1 => std::env::set_var("RUST_LOG", "warn,tabox=warn"),
2 => std::env::set_var("RUST_LOG", "info,tabox=info"),
3 => std::env::set_var("RUST_LOG", "debug,tabox=debug"),
_ => std::env::set_var("RUST_LOG", "trace,tabox=trace"),
}

Expand All @@ -292,7 +293,7 @@ impl LoggerOpt {
better_panic::install();
}

pub fn should_diable_curses(&self) -> bool {
pub fn should_disable_curses(&self) -> bool {
self.verbose > 0
}
}
Expand Down
130 changes: 130 additions & 0 deletions src/tools/export_solution_checks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use std::collections::HashMap;
use std::path::PathBuf;

use anyhow::{bail, Context, Error};
use clap::Parser;

use serde::{Deserialize, Serialize};
use task_maker_format::ioi::IOITask;
use task_maker_format::{
EvaluationConfig, EvaluationData, Solution, SolutionCheckResult, TaskFormat,
};

use crate::{FilterOpt, FindTaskOpt};

#[derive(Parser, Debug, Clone)]
pub struct ExportSolutionChecksOpt {
#[clap(flatten, next_help_heading = Some("TASK SEARCH"))]
pub find_task: FindTaskOpt,

#[clap(flatten, next_help_heading = Some("FILTER"))]
pub filter: FilterOpt,
}

#[derive(Serialize, Deserialize)]
struct SolutionWithChecks {
path: PathBuf,
checks: Vec<Option<SolutionCheckResult>>,
min_score: f64,
max_score: f64,
}

pub fn main_export_solution_checks(opt: ExportSolutionChecksOpt) -> Result<(), Error> {
let eval_config = EvaluationConfig {
solution_filter: opt.filter.filter,
booklet_solutions: false,
no_statement: true,
solution_paths: opt.filter.solution,
disabled_sanity_checks: Default::default(),
seed: Default::default(),
dry_run: true,
};
let task = opt
.find_task
.find_task(&eval_config)
.context("Failed to locate the task")?;

let TaskFormat::IOI(task) = task else {
bail!("Exporting solution checks is only supported for IOI tasks")
};

let (mut eval, _) = EvaluationData::new(task.path());
let solutions = eval_config.find_solutions(
task.path(),
vec!["sol/*"],
Some(task.grader_map.clone()),
&mut eval,
);

let subtasks = task
.subtasks
.iter()
.filter_map(|(_, info)| info.name.clone().map(|name| (name, info.id)))
.collect::<HashMap<_, _>>();

let checks = solutions
.iter()
.map(|solution| extract_solution_checks(&task, &subtasks, solution))
.collect::<Result<Vec<_>, _>>()?;

println!("{}", serde_json::to_string_pretty(&checks)?);

Ok(())
}

fn extract_solution_checks(
task: &IOITask,
subtasks: &HashMap<String, u32>,
solution: &Solution,
) -> anyhow::Result<SolutionWithChecks> {
let mut checks = vec![None; task.subtasks.len()];
for check in &solution.checks {
if let Some(&idx) = subtasks.get(&check.subtask_name_pattern) {
let idx: usize = idx.try_into()?;
if checks[idx].is_some() {
bail!(
"Found multiple checks for subtask {} in solution {}",
check.subtask_name_pattern,
solution.source_file.path.display()
);
}
checks[idx] = Some(check.result);
} else if check.subtask_name_pattern == "*" {
for subtask_check in checks.iter_mut() {
if subtask_check.is_some() {
bail!(
"Found multiple checks for subtask {} in solution {}",
check.subtask_name_pattern,
solution.source_file.path.display()
);
}
*subtask_check = Some(check.result);
}
} else {
bail!(
"Found invalid subtask check {} in solution {}",
check.subtask_name_pattern,
solution.source_file.path.display()
);
}
}

let mut min_score = 0.;
let mut max_score = 0.;

for (i, check) in checks.iter().enumerate() {
if *check == Some(SolutionCheckResult::Accepted) {
min_score += task.subtasks[&i.try_into()?].max_score;
max_score += task.subtasks[&i.try_into()?].max_score;
} else if *check == Some(SolutionCheckResult::PartialScore) || check.is_none() {
max_score += task.subtasks[&i.try_into()?].max_score;
}
}

Ok(SolutionWithChecks {
path: solution.source_file.path.clone(),
checks,
min_score,
max_score,
})
}
2 changes: 2 additions & 0 deletions src/tools/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use task_maker_rust::tools::add_solution_checks::main_add_solution_checks;
use task_maker_rust::tools::booklet::main_booklet;
use task_maker_rust::tools::clear::main_clear;
use task_maker_rust::tools::copy_competition_files::copy_competition_files_main;
use task_maker_rust::tools::export_solution_checks::main_export_solution_checks;
use task_maker_rust::tools::find_bad_case::main_find_bad_case;
use task_maker_rust::tools::fuzz_checker::main_fuzz_checker;
use task_maker_rust::tools::gen_autocompletion::main_get_autocompletion;
Expand Down Expand Up @@ -34,6 +35,7 @@ fn main() {
Tool::FuzzChecker(opt) => main_fuzz_checker(opt),
Tool::FindBadCase(opt) => main_find_bad_case(opt),
Tool::AddSolutionChecks(opt) => main_add_solution_checks(opt, base_opt.logger),
Tool::ExportSolutionChecks(opt) => main_export_solution_checks(opt),
Tool::InternalSandbox => return task_maker_rust::main_sandbox(),
}
.nice_unwrap()
Expand Down
1 change: 1 addition & 0 deletions src/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod add_solution_checks;
pub mod booklet;
pub mod clear;
pub mod copy_competition_files;
pub mod export_solution_checks;
pub mod find_bad_case;
pub mod fuzz_checker;
pub mod gen_autocompletion;
Expand Down
3 changes: 3 additions & 0 deletions src/tools/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::tools::add_solution_checks::AddSolutionChecksOpt;
use crate::tools::booklet::BookletOpt;
use crate::tools::clear::ClearOpt;
use crate::tools::copy_competition_files::CopyCompetitionFilesOpt;
use crate::tools::export_solution_checks::ExportSolutionChecksOpt;
use crate::tools::find_bad_case::FindBadCaseOpt;
use crate::tools::fuzz_checker::FuzzCheckerOpt;
use crate::tools::gen_autocompletion::GenAutocompletionOpt;
Expand Down Expand Up @@ -56,6 +57,8 @@ pub enum Tool {
FindBadCase(FindBadCaseOpt),
/// Add the @check comments to the solutions.
AddSolutionChecks(AddSolutionChecksOpt),
/// Exports solution checks to json.
ExportSolutionChecks(ExportSolutionChecksOpt),
/// Run the sandbox instead of the normal task-maker.
///
/// This option is left as undocumented as it's not part of the public API.
Expand Down
Loading