From c2ce2d9bf554268a1909b902eab13c8b03e745a9 Mon Sep 17 00:00:00 2001 From: mzuenni Date: Sat, 10 May 2025 16:17:54 +0200 Subject: [PATCH 1/2] use multipass dir --- bin/run.py | 16 ++++++++-- bin/validate.py | 3 ++ .../output_validator/validate.h | 18 +++++------ .../alternativeencryption/problem.yaml | 4 --- .../solution/solution.en.tex | 30 ++++++++++++++++++ .../ragnar-write-to-hidden-file.py | 23 ++++++++++++++ .../ragnar-write-to-local-file.py | 23 ++++++++++++++ .../ragnar-write-to-tmp-file.py | 23 ++++++++++++++ .../interctive_multipass_validator.py | 31 +++++++++++++++---- 9 files changed, 149 insertions(+), 22 deletions(-) create mode 100644 test/problems/alternativeencryption/solution/solution.en.tex create mode 100644 test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-hidden-file.py create mode 100644 test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-local-file.py create mode 100644 test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-tmp-file.py diff --git a/bin/run.py b/bin/run.py index c03472d32..e1914fdf1 100644 --- a/bin/run.py +++ b/bin/run.py @@ -207,7 +207,9 @@ def _continue_with_tle(self, verdict, timeout_expired): def _prepare_nextpass(self, nextpass): if not nextpass or not nextpass.is_file(): return False - # clear all files outside of feedbackdir + # TODO: keep feedback files and concatenate them? + # - judgemessage.txt + # - judgeerror.txt for f in self.tmpdir.iterdir(): if f == self.feedbackdir: continue @@ -216,7 +218,15 @@ def _prepare_nextpass(self, nextpass): elif f.exists(): shutil.rmtree(f) # use nextpass.in as next input - shutil.move(nextpass, self.in_path) + nextpass.rename(self.in_path) + # clear all files outside of "feedbackdir / multipass" + for f in self.feedbackdir.iterdir(): + if f == self.feedbackdir / "multipass": + continue + if f.is_file(): + f.unlink() + elif f.exists(): + shutil.rmtree(f) return True def _validate_output(self, bar: BAR_TYPE) -> Optional[ExecResult]: @@ -441,7 +451,7 @@ def process_run(run: Run): for f in run.feedbackdir.iterdir(): if f.name.startswith("."): continue # skip "hidden" files - if f.name in ["judgemessage.txt", "judgeerror.txt"]: + if f.name in ["judgemessage.txt", "judgeerror.txt", "multipass"]: continue if f.name.startswith("judgeimage.") or f.name.startswith("teamimage."): data += f"{f.name}: {shorten_path(self.problem, f.parent) / f.name}\n" diff --git a/bin/validate.py b/bin/validate.py index c3c8e9128..2b42d5e05 100644 --- a/bin/validate.py +++ b/bin/validate.py @@ -403,6 +403,9 @@ def run( cwd, constraints_path, arglist = self._run_helper(testcase, constraints, args) if not isinstance(mode, Mode): cwd = mode.feedbackdir + if self.problem.multi_pass: + multipassdir = cwd / "multipass" + multipassdir.mkdir(parents=True, exist_ok=True) invocation = self.run_command + [in_path, ans_path, cwd] with path.open("rb") as file: diff --git a/test/problems/alternativeencryption/output_validator/validate.h b/test/problems/alternativeencryption/output_validator/validate.h index 9ca408781..f8d862a33 100644 --- a/test/problems/alternativeencryption/output_validator/validate.h +++ b/test/problems/alternativeencryption/output_validator/validate.h @@ -1572,30 +1572,30 @@ namespace Multipass { void init() { judgeAssert(::details::initialized(), "validate.h: Multipass::init() was called before init(argc, argv)!"); - auto path = std::filesystem::path(arguments[3]) / ".pass"; + auto multipassdir = std::filesystem::path(arguments[3]) / "multipass"; + auto passfile = multipassdir / ".pass"; std::string nextfile = ".state0"; std::string prevfile = ".state1"; - if (std::filesystem::exists(path)) { - std::ifstream in(path); + if (std::filesystem::exists(passfile)) { + std::ifstream in(passfile); in >> pass; pass++; if ((pass & 1) != 0) { std::swap(nextfile, prevfile); } - prevstate = InputStream(std::filesystem::path(arguments[3]) / prevfile, false, true, juryOut, Verdicts::FAIL); + prevstate = InputStream(multipassdir / prevfile, false, true, juryOut, Verdicts::FAIL); } else { pass = 0; } - std::filesystem::remove(std::filesystem::path(arguments[3]) / nextfile); - nextstate = OutputStream(std::filesystem::path(arguments[3]) / nextfile, std::ios::out); + std::filesystem::remove(multipassdir / nextfile); + nextstate = OutputStream(multipassdir / nextfile, std::ios::out); nextpass = OutputStream(details::nextpassBuffer); - std::ofstream out(path); - out << pass; + std::ofstream(passfile) << pass; } [[noreturn]] void NEXT() { { - std::ofstream file(std::filesystem::path(arguments[3]) / "nextpass.in"); + std::ofstream file(std::filesystem::path(arguments[3]) / "nextpass.in"); // this should not be in the multipass subdirectory! judgeAssert(file.good(), "NEXT(): Could not open file: nextpass.in"); file << details::nextpassBuffer.str(); } diff --git a/test/problems/alternativeencryption/problem.yaml b/test/problems/alternativeencryption/problem.yaml index 5aae2a5a2..765320dc5 100644 --- a/test/problems/alternativeencryption/problem.yaml +++ b/test/problems/alternativeencryption/problem.yaml @@ -1,10 +1,8 @@ problem_format_version: 2023-07-draft type: multi-pass name: - #lang: name en: Alternative Encryption credits: -# Contest name and year authors: Thomas Beuman source: name: NWERC 2024 Test Session @@ -12,5 +10,3 @@ source: uuid: 8ee7605a-95a3-86c2-3995-0a10d365de46 license: cc by-sa rights_owner: author -limits: - time_limit: 1.0 diff --git a/test/problems/alternativeencryption/solution/solution.en.tex b/test/problems/alternativeencryption/solution/solution.en.tex new file mode 100644 index 000000000..d327f27ad --- /dev/null +++ b/test/problems/alternativeencryption/solution/solution.en.tex @@ -0,0 +1,30 @@ +\begin{frame} + \frametitle{\problemtitle} + \begin{block}{Problem} + This is a multi-pass problem, where in each pass, you should: + \vspace{-0.5em} + \begin{enumerate} + \item Encrypt text, such that the length stays the same and every character differs. + \item Decrypt the text that you encrypted, such that you retrieve the original input. + \end{enumerate} + \end{block} + \pause + \begin{block}{Solution} + Some of the many possible solutions (there were some resubmissions): + \begin{itemize} + \addtolength{\leftmargini}{15ex} + \small + \item ($70\times$) Use a Caesar cipher with offset $1 \leq x < 26$ for encrypting, and offset $26 - x$ for decrypting. + \item ($14\times$) Use a Caesar cipher with offset $13$ for both encrypting and decrypting. + \item ($2\times$) Assuming $0$-based \texttt{char} values, XOR the last bit of each value + (`\texttt{a}' $\leftrightarrow$ `\texttt{b}', + `\texttt{c}' $\leftrightarrow$ `\texttt{d}', \dots). + \item ($1\times$) Atbash: Mirror the characters (`\texttt{a}' $\leftrightarrow$ `\texttt{z}', + `\texttt{b}' $\leftrightarrow$ `\texttt{y}', \dots) + for both encrypting and decrypting. + \item ($1\times$) Generate a (seeded) random permutation to encrypt, and use its inverse to decrypt. + \end{itemize} + \end{block} + \pause + \solvestats +\end{frame} diff --git a/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-hidden-file.py b/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-hidden-file.py new file mode 100644 index 000000000..3a76c98a9 --- /dev/null +++ b/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-hidden-file.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +from pathlib import Path + +mode = input() +n = int(input()) + + +def rot(x): + return chr(ord("a") + (ord(x) - ord("a") + 13) % 26) + + +words = [] + +for _ in range(n): + word = input() + words.append(word) + print(*(rot(x) for x in word), sep="") + +file = Path(".data") +if mode == "encrypt": + file.write_text("\n".join(words)) +if mode == "decrypt": + words = file.read_text() diff --git a/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-local-file.py b/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-local-file.py new file mode 100644 index 000000000..7294b8225 --- /dev/null +++ b/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-local-file.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +from pathlib import Path + +mode = input() +n = int(input()) + + +def rot(x): + return chr(ord("a") + (ord(x) - ord("a") + 13) % 26) + + +words = [] + +for _ in range(n): + word = input() + words.append(word) + print(*(rot(x) for x in word), sep="") + +file = Path("data") +if mode == "encrypt": + file.write_text("\n".join(words)) +if mode == "decrypt": + words = file.read_text() diff --git a/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-tmp-file.py b/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-tmp-file.py new file mode 100644 index 000000000..60c4841cf --- /dev/null +++ b/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-tmp-file.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +from pathlib import Path + +mode = input() +n = int(input()) + + +def rot(x): + return chr(ord("a") + (ord(x) - ord("a") + 13) % 26) + + +words = [] + +for _ in range(n): + word = input() + words.append(word) + print(*(rot(x) for x in word), sep="") + +file = Path("/tmp/nwerc_alternativeencryption_data") +if mode == "encrypt": + file.write_text("\n".join(words)) +if mode == "decrypt": + words = file.read_text() diff --git a/test/problems/interactivemultipass/output_validator/interctive_multipass_validator.py b/test/problems/interactivemultipass/output_validator/interctive_multipass_validator.py index e13206626..7a6691bd4 100755 --- a/test/problems/interactivemultipass/output_validator/interctive_multipass_validator.py +++ b/test/problems/interactivemultipass/output_validator/interctive_multipass_validator.py @@ -2,19 +2,38 @@ import sys import pathlib + +def wrong_answer(message: str): + judgemessage = pathlib.Path(sys.argv[3]) / "judgemessage.txt" + judgemessage.write_text(message) + sys.exit(43) # WA + + test_in = int(pathlib.Path(sys.argv[1]).read_text()) print(test_in) # Simulate behaviour of normal multipass problem -team_ans = int(input()) +try: + team_ans_string = input() + try: + team_ans = int(team_ans_string) + except ValueError: + wrong_answer(f"team output '{team_ans_string}' is not an integer") +except EOFError: + wrong_answer("no input from team") + if test_in < 0: # first pass if team_ans < 0: - sys.exit(43) # WA + wrong_answer(f"1st pass: team output ({team_ans}) is negative") nextpass = pathlib.Path(sys.argv[3]) / "nextpass.in" nextpass.write_text(str(team_ans)) sys.exit(42) # AC + nextpass.in => next run else: # second pass - if test_in == team_ans: - sys.exit(42) # AC - else: - sys.exit(43) # WA + if test_in != team_ans: + wrong_answer(f"2nd pass: team output ({team_ans}) is not equal to test input ({test_in})") + +try: + more_input = input() + wrong_answer(f'extra input from team, starting with "{more_input}"') +except EOFError: + sys.exit(42) # AC From d6c8b285bc51f26d59c60e6760734318175b9dc5 Mon Sep 17 00:00:00 2001 From: mzuenni Date: Sat, 10 May 2025 16:37:14 +0200 Subject: [PATCH 2/2] disbale some submissions --- bin/tools.py | 2 +- .../data/sample/{001.ans => 01.ans} | 0 .../data/sample/{001.in => 01.in} | 0 .../{001.interaction => 01.interaction} | 0 .../generators/generators.yaml | 2 +- .../ragnar-write-to-hidden-file.py | 23 ------------------- .../ragnar-write-to-local-file.py | 23 ------------------- .../ragnar-write-to-tmp-file.py | 23 ------------------- 8 files changed, 2 insertions(+), 71 deletions(-) rename test/problems/alternativeencryption/data/sample/{001.ans => 01.ans} (100%) rename test/problems/alternativeencryption/data/sample/{001.in => 01.in} (100%) rename test/problems/alternativeencryption/data/sample/{001.interaction => 01.interaction} (100%) delete mode 100644 test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-hidden-file.py delete mode 100644 test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-local-file.py delete mode 100644 test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-tmp-file.py diff --git a/bin/tools.py b/bin/tools.py index 7ec91d275..547f8889c 100755 --- a/bin/tools.py +++ b/bin/tools.py @@ -703,6 +703,7 @@ def build_parser(): "--visualizer", dest="no_visualizer", action="store_false", + default=True, help="Also run the output visualizer.", ) runparser.add_argument( @@ -1179,7 +1180,6 @@ def run_parsed_arguments(args): config.args.jobs = (os.cpu_count() or 1) // 2 config.args.add = None config.args.verbose = 0 - config.args.no_visualizer = True success &= generate.generate(problem) config.args = old_args if action in ["fuzz"]: diff --git a/test/problems/alternativeencryption/data/sample/001.ans b/test/problems/alternativeencryption/data/sample/01.ans similarity index 100% rename from test/problems/alternativeencryption/data/sample/001.ans rename to test/problems/alternativeencryption/data/sample/01.ans diff --git a/test/problems/alternativeencryption/data/sample/001.in b/test/problems/alternativeencryption/data/sample/01.in similarity index 100% rename from test/problems/alternativeencryption/data/sample/001.in rename to test/problems/alternativeencryption/data/sample/01.in diff --git a/test/problems/alternativeencryption/data/sample/001.interaction b/test/problems/alternativeencryption/data/sample/01.interaction similarity index 100% rename from test/problems/alternativeencryption/data/sample/001.interaction rename to test/problems/alternativeencryption/data/sample/01.interaction diff --git a/test/problems/alternativeencryption/generators/generators.yaml b/test/problems/alternativeencryption/generators/generators.yaml index 2d4f34a5a..87bf21afb 100644 --- a/test/problems/alternativeencryption/generators/generators.yaml +++ b/test/problems/alternativeencryption/generators/generators.yaml @@ -31,4 +31,4 @@ data: - random: generate: eval.py {seed} 1000 randstr(randrange(1, 101)) - count: 100 + count: 5 diff --git a/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-hidden-file.py b/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-hidden-file.py deleted file mode 100644 index 3a76c98a9..000000000 --- a/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-hidden-file.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -from pathlib import Path - -mode = input() -n = int(input()) - - -def rot(x): - return chr(ord("a") + (ord(x) - ord("a") + 13) % 26) - - -words = [] - -for _ in range(n): - word = input() - words.append(word) - print(*(rot(x) for x in word), sep="") - -file = Path(".data") -if mode == "encrypt": - file.write_text("\n".join(words)) -if mode == "decrypt": - words = file.read_text() diff --git a/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-local-file.py b/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-local-file.py deleted file mode 100644 index 7294b8225..000000000 --- a/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-local-file.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -from pathlib import Path - -mode = input() -n = int(input()) - - -def rot(x): - return chr(ord("a") + (ord(x) - ord("a") + 13) % 26) - - -words = [] - -for _ in range(n): - word = input() - words.append(word) - print(*(rot(x) for x in word), sep="") - -file = Path("data") -if mode == "encrypt": - file.write_text("\n".join(words)) -if mode == "decrypt": - words = file.read_text() diff --git a/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-tmp-file.py b/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-tmp-file.py deleted file mode 100644 index 60c4841cf..000000000 --- a/test/problems/alternativeencryption/submissions/run_time_error/ragnar-write-to-tmp-file.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -from pathlib import Path - -mode = input() -n = int(input()) - - -def rot(x): - return chr(ord("a") + (ord(x) - ord("a") + 13) % 26) - - -words = [] - -for _ in range(n): - word = input() - words.append(word) - print(*(rot(x) for x in word), sep="") - -file = Path("/tmp/nwerc_alternativeencryption_data") -if mode == "encrypt": - file.write_text("\n".join(words)) -if mode == "decrypt": - words = file.read_text()