From e499753d2d332ef2eb0af93e9797160a3cafceb9 Mon Sep 17 00:00:00 2001 From: Hocky Yudhiono Date: Tue, 29 Oct 2024 14:43:07 +0800 Subject: [PATCH] Add updates on tcframe --- .github/workflows/main.yml | 30 --- include/tcframe/driver/TestCaseDriver.hpp | 4 +- include/tcframe/runner/grader/Grader.hpp | 21 ++- include/tcframe/runner/os/OperatingSystem.hpp | 3 +- include/tcframe/runner/verdict/Verdict.hpp | 4 + include/tcframe/spec/constraint/Subtask.hpp | 4 +- include/tcframe/spec/core/Magic.hpp | 10 +- include/tcframe/spec/io/IOFormat.hpp | 4 +- include/tcframe/spec/io/LinesIOSegment.hpp | 2 +- .../spec/io/LinesIOSegmentManipulator.hpp | 2 +- include/tcframe/spec/random/Random.hpp | 8 +- include/tcframe/spec/variable/Vector.hpp | 1 - scripts/genConfig.py | 91 +++++++++ scripts/tcframe | 176 +++++++++++++++++- 14 files changed, 299 insertions(+), 61 deletions(-) delete mode 100644 .github/workflows/main.yml create mode 100644 scripts/genConfig.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index b601150e..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: ci - -on: - push: - pull_request: - branches: - - master - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 50 - - - name: Build CMake project - run: cmake . && cmake --build . - - - name: Run unit tests - run: ./test_unit - - - name: Run integration tests - run: ./test_integration - - - name: Run ete tests - run: ./test_ete - - - name: Upload code coverage report - run: bash <(curl -s https://codecov.io/bash) diff --git a/include/tcframe/driver/TestCaseDriver.hpp b/include/tcframe/driver/TestCaseDriver.hpp index 1de9e0a1..12fb4af6 100644 --- a/include/tcframe/driver/TestCaseDriver.hpp +++ b/include/tcframe/driver/TestCaseDriver.hpp @@ -38,8 +38,8 @@ class TestCaseDriver { IOManipulator* ioManipulator, Verifier* verifier, MultipleTestCasesConfig multipleTestCasesConfig) - : ioManipulator_(ioManipulator) - , rawIOManipulator_(rawIOManipulator) + : rawIOManipulator_(rawIOManipulator) + , ioManipulator_(ioManipulator) , verifier_(verifier) , multipleTestCasesConfig_(move(multipleTestCasesConfig)) {} diff --git a/include/tcframe/runner/grader/Grader.hpp b/include/tcframe/runner/grader/Grader.hpp index 71e5ad15..d5c0f525 100644 --- a/include/tcframe/runner/grader/Grader.hpp +++ b/include/tcframe/runner/grader/Grader.hpp @@ -4,7 +4,6 @@ #include #include #include - #include "GradingOptions.hpp" #include "GraderLogger.hpp" #include "TestCaseGrader.hpp" @@ -78,7 +77,7 @@ class Grader { private: map getSubtaskPoints(const GradingOptions& options) { map subtaskPointsByIds; - for (int id = 1; id <= options.subtaskPoints().size(); id++) { + for (unsigned id = 1; id <= options.subtaskPoints().size(); id++) { subtaskPointsByIds[id] = options.subtaskPoints()[id - 1]; } if (subtaskPointsByIds.empty()) { @@ -107,21 +106,29 @@ class Grader { .build(); gradeTestCase(testCase, options, verdictsBySubtaskId); } else { + bool skipping = 0; for (const TestCase& testCase : testGroup.testCases()) { - gradeTestCase(testCase, options, verdictsBySubtaskId); + TestCaseVerdict verdict = gradeTestCase(testCase, options, verdictsBySubtaskId, skipping); + if(verdict.verdict().code() != "OK" && verdict.verdict().code() != "AC") { + // comment this code if you dont want to skip + skipping = 1; + } } } } - void gradeTestCase( + TestCaseVerdict gradeTestCase( const TestCase& testCase, const GradingOptions& options, - map>& verdictsBySubtaskId) { - - TestCaseVerdict verdict = testCaseGrader_->grade(testCase, options); + map>& verdictsBySubtaskId, + bool skipTestCase = 0) { + TestCaseVerdict verdict; + if(skipTestCase) verdict = TestCaseVerdict(Verdict::skip()); + else verdict = testCaseGrader_->grade(testCase, options); for (int subtaskId : testCase.subtaskIds()) { verdictsBySubtaskId[subtaskId].push_back(verdict); } + return verdict; } }; diff --git a/include/tcframe/runner/os/OperatingSystem.hpp b/include/tcframe/runner/os/OperatingSystem.hpp index 620997aa..9c433dba 100644 --- a/include/tcframe/runner/os/OperatingSystem.hpp +++ b/include/tcframe/runner/os/OperatingSystem.hpp @@ -100,7 +100,8 @@ class OperatingSystem { private: static void runCommand(const string& command) { - system(command.c_str()); + int val = system(command.c_str()); + std::cerr << "[tcframe] exit code: " << val << "\n"; } }; diff --git a/include/tcframe/runner/verdict/Verdict.hpp b/include/tcframe/runner/verdict/Verdict.hpp index 3d6d28f4..9ff97bdd 100644 --- a/include/tcframe/runner/verdict/Verdict.hpp +++ b/include/tcframe/runner/verdict/Verdict.hpp @@ -47,6 +47,10 @@ struct Verdict { return {"ERR", "Internal Error", 99}; } + static Verdict skip() { + return {"SKIP", "Skipped", -1}; + } + const string& code() const { return code_; } diff --git a/include/tcframe/spec/constraint/Subtask.hpp b/include/tcframe/spec/constraint/Subtask.hpp index adece05c..90613b7a 100644 --- a/include/tcframe/spec/constraint/Subtask.hpp +++ b/include/tcframe/spec/constraint/Subtask.hpp @@ -34,8 +34,8 @@ struct Subtask { Subtask(int id, double points, vector constraints) : id_(id) - , points_(points) - , constraints_(move(constraints)) {} + , constraints_(move(constraints)) + , points_(points) {} int id() const { return id_; diff --git a/include/tcframe/spec/core/Magic.hpp b/include/tcframe/spec/core/Magic.hpp index 4d88497f..099dc0fb 100644 --- a/include/tcframe/spec/core/Magic.hpp +++ b/include/tcframe/spec/core/Magic.hpp @@ -36,8 +36,8 @@ namespace tcframe { struct VectorSize { function size; - explicit VectorSize(function size) - : size(move(size)) {} + explicit VectorSize(function _size) + : size(move(_size)) {} }; template @@ -55,9 +55,9 @@ struct MatrixSize { function rows; function columns; - MatrixSize(function rows, function columns) - : rows(move(rows)) - , columns(move(columns)) {} + MatrixSize(function _rows, function _columns) + : rows(move(_rows)) + , columns(move(_columns)) {} }; class VariableNamesExtractor { diff --git a/include/tcframe/spec/io/IOFormat.hpp b/include/tcframe/spec/io/IOFormat.hpp index e5fda074..9f98d818 100644 --- a/include/tcframe/spec/io/IOFormat.hpp +++ b/include/tcframe/spec/io/IOFormat.hpp @@ -49,7 +49,7 @@ struct IOFormat { if (a.size() != b.size()) { return false; } - for (int i = 0; i < a.size(); i++) { + for (unsigned i = 0; i < a.size(); i++) { if (!a[i]->equals(b[i])) { return false; } @@ -61,7 +61,7 @@ struct IOFormat { if (a.size() != b.size()) { return false; } - for (int i = 0; i < a.size(); i++) { + for (unsigned i = 0; i < a.size(); i++) { if (!equals(a[i], b[i])) { return false; } diff --git a/include/tcframe/spec/io/LinesIOSegment.hpp b/include/tcframe/spec/io/LinesIOSegment.hpp index fde3efa7..4bfa12f8 100644 --- a/include/tcframe/spec/io/LinesIOSegment.hpp +++ b/include/tcframe/spec/io/LinesIOSegment.hpp @@ -46,7 +46,7 @@ struct LinesIOSegment : public IOSegment { if (variables_.size() != o.variables_.size()) { return false; } - for (int i = 0; i < variables_.size(); i++) { + for (unsigned i = 0; i < variables_.size(); i++) { if (!variables_[i]->equals(o.variables_[i])) { return false; } diff --git a/include/tcframe/spec/io/LinesIOSegmentManipulator.hpp b/include/tcframe/spec/io/LinesIOSegmentManipulator.hpp index 87a25593..7b9b957d 100644 --- a/include/tcframe/spec/io/LinesIOSegmentManipulator.hpp +++ b/include/tcframe/spec/io/LinesIOSegmentManipulator.hpp @@ -65,7 +65,7 @@ class LinesIOSegmentManipulator { int size = getSize(segment); for (int j = 0; j < size; j++) { - for (int i = 0; i < segment->variables().size(); i++) { + for (unsigned i = 0; i < segment->variables().size(); i++) { Variable *variable = segment->variables()[i]; if (variable->type() == VariableType::VECTOR) { if (i > 0) { diff --git a/include/tcframe/spec/random/Random.hpp b/include/tcframe/spec/random/Random.hpp index 8c699e50..dde42340 100644 --- a/include/tcframe/spec/random/Random.hpp +++ b/include/tcframe/spec/random/Random.hpp @@ -4,6 +4,8 @@ #include using std::mt19937; +using std::mt19937_64; +using std::random_shuffle; using std::uniform_real_distribution; using std::uniform_int_distribution; @@ -47,8 +49,12 @@ class Random { std::shuffle(first, last, engine); } + mt19937_64 *getEngine() { + return &engine; + } + private: - mt19937 engine; + mt19937_64 engine; }; } diff --git a/include/tcframe/spec/variable/Vector.hpp b/include/tcframe/spec/variable/Vector.hpp index 19a2ccdd..452d171f 100644 --- a/include/tcframe/spec/variable/Vector.hpp +++ b/include/tcframe/spec/variable/Vector.hpp @@ -111,7 +111,6 @@ class RawVectorImpl : public VectorImpl { , var_(&var) {} void parseAndAddElementFrom(istream* in) { - int index = size(); string element; Variable::parseRawLine(in, element); var_->push_back(element); diff --git a/scripts/genConfig.py b/scripts/genConfig.py new file mode 100644 index 00000000..62b4c416 --- /dev/null +++ b/scripts/genConfig.py @@ -0,0 +1,91 @@ +import re +import json + +def custom_json_dump(obj, indent=2): + def serialize(obj, indent_level=0): + ind = ' ' * indent * indent_level + if (isinstance(obj, list)) and all(isinstance(i, (int, float, str)) for i in obj): + return '[' + ', '.join(json.dumps(i) for i in obj) + ']' + elif isinstance(obj, dict): + items = [f'"{k}": {serialize(v, indent_level+1)}' for k, v in obj.items()] + return '{\n' + ',\n'.join(ind + ' ' + item for item in items) + '\n' + ind + '}' + elif isinstance(obj, list): + items = [f'{serialize(v, indent_level+1)}' for v in obj] + return '[\n' + ',\n'.join(ind + ' ' + item for item in items) + '\n' + ind + ']' + else: + return json.dumps(obj) + return serialize(obj) + + +def read_spec_file(spec_file_path): + with open(spec_file_path, 'r') as file: + return file.read() + +def matcher_extract(matches): + subtask_match_dict = dict() + for num, sub in matches: + content = [] + try: + content = list(map(int, sub.replace(' ', '').replace('{', '').replace('}', '').split(','))) + except Exception as e: + content = [] + subtask_match_dict[int(num)] = content + + return subtask_match_dict + +def extractor(spec_content, front, back): + matches = re.findall(front + r'(\d+)[\s\S.]+?' + back + r'\((.*?)\)', spec_content) + return matcher_extract(matches) + +def extract_samples(spec_content): + return extractor(spec_content, 'SampleTestCase', 'Subtasks') + +def extract_test_groups(spec_content): + return extractor(spec_content, 'TestGroup', 'Subtasks') + +def extract_points(spec_content): + return extractor(spec_content, 'Subtask', 'Points') + +def extract_time_limit(spec_content): + time_limit = re.search(r'TimeLimit\((\d+)\);', spec_content) + return int(time_limit.group(1)) if time_limit else 0 + +def extract_memory_limit(spec_content): + memory_limit = re.search(r'MemoryLimit\((\d+)\);', spec_content) + return int(memory_limit.group(1)) if memory_limit else 0 + +def extract_interactive(spec_content): + is_interactive = re.search(r'InteractiveEvaluator', spec_content) + return True if is_interactive else False + + +def generate_subtask_dict(spec_file_path): + spec_content = read_spec_file(spec_file_path) + points = extract_points(spec_content) + test_groups = extract_test_groups(spec_content) + samples = extract_samples(spec_content) + subtask_dict = { + "time_limit": extract_time_limit(spec_content), + "memory_limit": extract_memory_limit(spec_content), + "samples": [None]*max(samples.keys()), + "test_groups": [None]*max(test_groups.keys()), + "points": [0]*max(points.keys()), + "interactive": extract_interactive(spec_content) + } + + for i, test_group in enumerate(test_groups, start=1): + subtask_dict["test_groups"][test_group-1] = test_groups[test_group] + + for i, sample in enumerate(samples, start=1): + subtask_dict["samples"][sample-1] = samples[sample] + + for i, point in enumerate(points, start=1): + subtask_dict["points"][point-1] = points[point][0] + + return subtask_dict + +if __name__ == "__main__": + spec_file_path = "spec.cpp" # replace with your file path + subtask_dict = generate_subtask_dict(spec_file_path) + with open('config.json', 'w') as file: + print(custom_json_dump(subtask_dict, 2), file=file) diff --git a/scripts/tcframe b/scripts/tcframe index 229494e5..8dbaa59f 100755 --- a/scripts/tcframe +++ b/scripts/tcframe @@ -1,11 +1,48 @@ #!/usr/bin/env bash +ulimit -v 4194304; +ulimit -s 4194304; +TCFRAME_PREFIX="tcframe" + +echo_colored() { + local message=$1 + local color=$2 + local is_bold=$3 + + local color_start="" + local color_end="" + local bold_start="" + local bold_end="" + local tcframe_start="\033[1m" + local tcframe_end="\033[0m" + + if [ -n "$color" ]; then + color_start="\033[${color}m" + color_end="\033[0m" + fi + + if [ "$is_bold" == "bold" ]; then + bold_start="\033[1m" + bold_end="\033[0m" + fi + + echo -e "${tcframe_start}${TCFRAME_PREFIX}${tcframe_end}:${bold_start}${color_start} ${message}${color_end}${bold_end}" +} + print_usage() { - echo "usage: tcframe " + echo_colored "usage: " echo - echo "Available commands:" - echo " build Compile spec file into runner program" - echo " version Print tcframe version" + echo_colored "Available commands:" 33 + echo_colored " build Compile spec file into runner program" 36 + echo_colored " version Print tcframe version" 36 + echo_colored " run Run the runner program (build first if necessary)" 36 + echo_colored " config Generate config" 36 + echo_colored " sols Compile all solutions in ./solutions/" 36 + echo_colored " brun Build and run" 36 + echo_colored " grade --batch Grade all compiled solutions in ./solutions/ directory" 36 + echo_colored " grade --solution= Grade specific solutions e.g: --solution=./solutions/subtask-1" 36 + echo_colored " grade --verbose Grade with verbose output" 36 + echo_colored " clean Clean shitty files generated mid out" 36 } build() { @@ -13,19 +50,128 @@ build() { RUNNER_EXEC="$PWD/runner" if [ ! -f "$SPEC_FILE" ]; then - echo "tcframe: build error: spec file '$SPEC_FILE' does not exist" + echo_colored "build error: spec file '$SPEC_FILE' does not exist" 31 exit 1 fi - g++ -std=c++17 -D__TCFRAME_SPEC_FILE__="\"$SPEC_FILE\"" -I "$TCFRAME_HOME/include" $TCFRAME_CXX_FLAGS -o "$RUNNER_EXEC" "$TCFRAME_HOME/src/tcframe/runner.cpp" + g++ -std=c++17 -Wunused -Wall -Wshadow -Wreturn-type -O3 -D__TCFRAME_SPEC_FILE__="\"$SPEC_FILE\"" -I "$TCFRAME_HOME/include" $TCFRAME_CXX_FLAGS -o "$RUNNER_EXEC" "$TCFRAME_HOME/src/tcframe/runner.cpp" + echo_colored "Build OK" 32 +} + +run() { + RUNNER_EXEC="$PWD/runner" + + if [ ! -f "$RUNNER_EXEC" ]; then + echo_colored "runner not found, building..." 33 + build + fi + + if [ -f "$RUNNER_EXEC" ]; then + echo_colored "running '$RUNNER_EXEC'" 32 + "$RUNNER_EXEC" + else + echo_colored "run error: unable to execute '$RUNNER_EXEC'" 31 + exit 1 + fi +} + +grade_solution() { + RUNNER_EXEC="$PWD/runner" + solution=$1 + verbose=$2 + echo_colored "grading ${solution}" 33 + if [ ! -n "$solution" ]; then + solution="./solution" + fi + if [ -f "$solution" ]; then + if [ "$verbose" = "verbose" ]; then + "$RUNNER_EXEC" grade --solution="$solution" + else + result=$("$RUNNER_EXEC" grade --solution="$solution") + if echo "$result" | grep -q "\[ SUBTASK VERDICTS \]"; then + # Print from "[ SUBTASK VERDICTS ]" to the end + echo "$result" | sed -n '/\[ SUBTASK VERDICTS \]/,$p' + else + # Print from "[ VERDICT ]" to the end + echo "$result" | sed -n '/\[ VERDICT \]/,$p' + fi + fi + else + echo_colored "error: solution '$solution' does not exist" 31 + fi +} + +grade() { + + RUNNER_EXEC="$PWD/runner" + + if [ ! -f "$RUNNER_EXEC" ]; then + echo_colored "grade error: runner does not exist" 31 + exit 1 + fi + + verbose="" + solution="" + batch=false + + for arg in "$@"; do + case "$arg" in + --verbose) + verbose="verbose" + ;; + --batch) + batch=true + ;; + --solution=*) + solution=${arg#*=} + ;; + *) + echo_colored "error: unknown option $arg" 31 + exit 1 + ;; + esac + done + + if [ "$batch" = true ]; then + for solution_file in solutions/*.cpp; do + solution_exec=${solution_file%.cpp} + echo_colored "found ${solution_exec}" 32 bold + grade_solution "$solution_exec" "$verbose" + done + elif [ -n "$solution" ]; then + grade_solution "$solution" "$verbose" + else + grade_solution "" "$verbose" + fi } version() { - echo "tcframe 1.6.0" + echo_colored "version 1.7.0" "" bold +} + +clean() { + rm -rf "__tcframe**.out" +} + +config() { + python3 $TCFRAME_HOME/scripts/genConfig.py +} + +compile_solutions() { + for solution_file in ./solutions/*.cpp; do + solution_exec=${solution_file%.cpp} + g++ -std=c++17 -Wall -Wunused -Wshadow $solution_file -o $solution_exec + if [ $? -eq 0 ]; then + echo_colored "Compile successful for $solution_file" 32 bold + else + echo_colored "Compile failed for $solution_file" 31 bold + fi + done } + if [ -z "$TCFRAME_HOME" ]; then - echo "tcframe: error: env variable TCFRAME_HOME not set" + echo_colored "error: env variable TCFRAME_HOME not set" 31 exit 1 fi @@ -40,6 +186,20 @@ if [ $1 = "build" ]; then build elif [ $1 = "version" ]; then version +elif [ $1 = "run" ]; then + run +elif [ $1 = "brun" ]; then + build + run +elif [ $1 = "config" ]; then + config +elif [ $1 = "grade" ]; then + shift + grade $@ +elif [ $1 = "sols" ]; then + compile_solutions +elif [ $1 = "clean" ]; then + clean else print_usage exit 1