Skip to content
Open
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
16 changes: 13 additions & 3 deletions bin/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]:
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion bin/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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"]:
Expand Down
3 changes: 3 additions & 0 deletions bin/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ data:

- random:
generate: eval.py {seed} 1000 randstr(randrange(1, 101))
count: 100
count: 5
18 changes: 9 additions & 9 deletions test/problems/alternativeencryption/output_validator/validate.h
Original file line number Diff line number Diff line change
Expand Up @@ -1572,30 +1572,30 @@ namespace Multipass {
void init() {
judgeAssert<std::logic_error>(::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<std::runtime_error>(file.good(), "NEXT(): Could not open file: nextpass.in");
file << details::nextpassBuffer.str();
}
Expand Down
4 changes: 0 additions & 4 deletions test/problems/alternativeencryption/problem.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
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
url: https://2024.nwerc.eu
uuid: 8ee7605a-95a3-86c2-3995-0a10d365de46
license: cc by-sa
rights_owner: author
limits:
time_limit: 1.0
30 changes: 30 additions & 0 deletions test/problems/alternativeencryption/solution/solution.en.tex
Original file line number Diff line number Diff line change
@@ -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}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading