upgrade run.py with click and complete 31829.py

This commit is contained in:
2024-05-16 06:47:34 +09:00
parent 3d49cd555a
commit 47d3ab89ce
4 changed files with 164 additions and 111 deletions

View File

@@ -1,31 +0,0 @@
import sys
import os
import glob
TC_TARGET_DIR = "_testcases"
DEFAULT_COUNT = 1
if __name__ == "__main__":
count = DEFAULT_COUNT
match len(sys.argv):
case 1:
pass
case 2 if sys.argv[1].isdigit() and int(sys.argv[1]) > 0:
count = int(sys.argv[1])
case _:
print("Invalid arguments.")
sys.exit()
# default make
if not os.path.exists("./" + TC_TARGET_DIR):
os.mkdir(TC_TARGET_DIR)
for i in range(1, count + 1):
default_in_path = "./" + TC_TARGET_DIR + f"/{i}.in"
default_out_path = "./" + TC_TARGET_DIR + f"/{i}.out"
with open(default_in_path, "w", encoding="utf-8") as _:
pass
with open(default_out_path, "w", encoding="utf-8") as _:
pass

1
requirements.txt Normal file
View File

@@ -0,0 +1 @@
click>=8.1

172
run.py
View File

@@ -6,6 +6,8 @@ from dataclasses import dataclass
import re import re
import subprocess import subprocess
import click
TC_TARGET_DIR = "./_testcases" TC_TARGET_DIR = "./_testcases"
@@ -15,6 +17,7 @@ class ProblemRunType:
dir: str dir: str
runner: str runner: str
prefix: str prefix: str
prerunner: str = ""
class ProblemRunEnum: class ProblemRunEnum:
@@ -27,24 +30,29 @@ class ProblemRunEnum:
zeta_C: ProblemRunType = ProblemRunType( zeta_C: ProblemRunType = ProblemRunType(
name="zeta_C", name="zeta_C",
dir="./zeta_C", dir="./zeta_C",
runner="gcc -std=c11 {source} && a.out", runner="./a.out",
prefix="c", prefix="c",
prerunner="gcc -std=c11 {source}",
) )
zeta_cpp: ProblemRunType = ProblemRunType( zeta_cpp: ProblemRunType = ProblemRunType(
name="zeta_cpp", name="zeta_cpp",
dir="./zeta_cpp", dir="./zeta_cpp",
runner="g++ -std=c++17 {source} && ./a.out < {input}", runner="./a.out",
prefix="cpp", prefix="cpp",
prerunner="g++ -std=c++17 {source}",
) )
zeta_kotlin: ProblemRunType = ProblemRunType( zeta_kotlin: ProblemRunType = ProblemRunType(
name="zeta_kotlin", name="zeta_kotlin",
dir="./zeta_kotlin", dir="./zeta_kotlin",
runner="kotlinc-jvm {source} -include-runtime -d a.jar && java -jar a.jar -Dfile.encoding=UTF-8 < {input}", runner="java -jar a.jar -Dfile.encoding=UTF-8 < {input}",
prefix="kt", prefix="kt",
prerunner="kotlinc-jvm {source} -include-runtime -d a.jar",
) )
# WIP
zeta_lua: ProblemRunType = ProblemRunType( zeta_lua: ProblemRunType = ProblemRunType(
name="zeta_lua", dir="./zeta_lua", runner="", prefix="lua" name="zeta_lua", dir="./zeta_lua", runner="", prefix="lua"
) )
# ExceptionType
err: ProblemRunType = ProblemRunType(name="err", dir="", runner="", prefix="") err: ProblemRunType = ProblemRunType(name="err", dir="", runner="", prefix="")
@staticmethod @staticmethod
@@ -64,89 +72,90 @@ class ProblemRunEnum:
return ProblemRunEnum.err return ProblemRunEnum.err
if __name__ == "__main__": @click.group()
rtype: ProblemRunType def cli():
problem_code: int
cnt: int
verbose_flag: bool = False
match len(sys.argv):
case 3:
rtype = ProblemRunEnum.quicklink(sys.argv[1])
problem_code = sys.argv[2] if sys.argv[2].isdigit() else None
cnt = 1
case 4:
rtype = ProblemRunEnum.quicklink(sys.argv[1])
problem_code = int(sys.argv[2]) if sys.argv[2].isdigit() else None
if sys.argv[3] == "--verbose":
verbose_flag = True
cnt = 1
else:
cnt = (
int(sys.argv[3])
if sys.argv[3].isdigit() and int(sys.argv[3]) > 0
else 1
)
case 5:
rtype = ProblemRunEnum.quicklink(sys.argv[1])
problem_code = int(sys.argv[2]) if sys.argv[2].isdigit() else None
cnt = (
int(sys.argv[3])
if sys.argv[3].isdigit() and int(sys.argv[3]) > 0
else 1
)
if sys.argv[4] == "--verbose":
verbose_flag = True
else:
pass pass
case _:
print("Invalid argument count.")
sys.exit()
if rtype is ProblemRunEnum.err:
print(f"Invalid run type: `{sys.argv[1]}`.")
sys.exit()
problem_code_path = f"{rtype.dir}/{problem_code}.{rtype.prefix}" @click.command()
@click.argument("runtype_string", type=str, nargs=1, required=True)
@click.argument("problem_code", type=str, nargs=1, required=True)
@click.argument("count", type=int, nargs=1, required=False, default=1)
@click.option("--verbose", "-v", is_flag=True)
@click.option("--profile", "-p", is_flag=True)
def run(
runtype_string: str, problem_code: str, count: int, verbose: bool, profile: bool
):
runtype: ProblemRunType = ProblemRunEnum.quicklink(runtype_string)
if runtype is ProblemRunEnum.err:
raise click.UsageError("Error RUNTYPE.")
if count < 1:
raise click.UsageError("COUNT must be greater than 1.")
problem_code_path = f"{runtype.dir}/{problem_code}.{runtype.prefix}"
if not os.path.exists(problem_code_path): if not os.path.exists(problem_code_path):
print(f"Invalid problem code: `{problem_code}`") raise click.UsageError("Problem does not exist.")
sys.exit()
io_files: list[str] = [ tc_files: list[str] = [
fpath fpath
for fpath in os.listdir(TC_TARGET_DIR) for fpath in os.listdir(TC_TARGET_DIR)
if os.path.isfile(os.path.join(TC_TARGET_DIR, fpath)) if os.path.isfile(os.path.join(TC_TARGET_DIR, fpath))
] ]
for c in range(1, cnt + 1): for c in range(1, count + 1):
inputs = None inpf = None
outputs = [] outputfs = []
for name in io_files: for name in tc_files:
ix = re.match(f"{c}.in", name) ix = re.match(f"{c}.in", name)
ox = re.match(f"{c}.out[0-9]*", name) ox = re.match(f"{c}.out[0-9]*", name)
if ix: if ix:
inputs = name inpf = name
elif ox: elif ox:
outputs.append(name) outputfs.append(name)
if (not inputs) or (not outputs):
print(f"{c:02d}: Pass") if (not inpf) or (not outputfs):
click.echo(f"{c:02d}: {None}")
continue continue
else: else:
with open(os.path.join(TC_TARGET_DIR, inputs), 'r', encoding='utf-8') as inputf: # check
stdout = subprocess.run( with open(os.path.join(TC_TARGET_DIR, inpf), "r", encoding="utf-8") as f:
rtype.runner.format( if runtype.prerunner:
source=problem_code_path, input= os.path.join(TC_TARGET_DIR, inputs) try:
_ = subprocess.run(
runtype.prerunner.format(
source=problem_code_path,
).split(), ).split(),
check=True, check=True,
capture_output=True, capture_output=True,
text=True, text=True,
stdin = inputf )
except subprocess.CalledProcessError as e:
click.echo(">>>>>> [Error while PRERUNNER process] >>>>>>")
click.echo(f"{e.stderr}")
click.echo(">>>>>> [Error while PRERUNNER process] >>>>>>")
continue
try:
stdout = subprocess.run(
runtype.runner.format(
source=problem_code_path,
).split(),
check=True,
capture_output=True,
text=True,
stdin=f,
).stdout ).stdout
except subprocess.CalledProcessError as e:
click.echo(">>>>>> [Error while RUNNER process] >>>>>>")
click.echo(f"{e.stderr}")
click.echo(f"returncode: {e.returncode}")
click.echo(">>>>>> [Error while RUNNER process] >>>>>>")
stdout = ""
continue
flag = False flag = False
for outputpath in outputs: for outputpath in outputfs:
with open( with open(
os.path.join(TC_TARGET_DIR, outputpath), "r", encoding="utf-8" os.path.join(TC_TARGET_DIR, outputpath), "r", encoding="utf-8"
) as f: ) as f:
@@ -154,12 +163,35 @@ if __name__ == "__main__":
flag = True flag = True
break break
print(f"{c:02d}: {flag}") click.echo(f"{c:02d}: {flag}")
if verbose_flag: if verbose:
print("===") click.echo("===" * 3)
print(stdout) click.echo(stdout)
print("===") click.echo("===" * 3)
### TODO: --profile argument 추가를 위한 command dispatcher 개선 @click.command(name="init")
### TODO: WIP @click.argument("count", type=int)
def init(count: int):
if count < 1:
raise click.UsageError("COUNT must be greater than 1.")
else:
if not os.path.exists("./" + TC_TARGET_DIR):
os.mkdir(TC_TARGET_DIR)
for i in range(1, count + 1):
default_in_path = "./" + TC_TARGET_DIR + f"/{i}.in"
default_out_path = "./" + TC_TARGET_DIR + f"/{i}.out"
with open(default_in_path, "w", encoding="utf-8") as _:
pass
with open(default_out_path, "w", encoding="utf-8") as _:
pass
click.echo("Testcases was made successfully!")
cli.add_command(run)
cli.add_command(init)
if __name__ == "__main__":
cli()

View File

@@ -0,0 +1,51 @@
import sys
import heapq
input = sys.stdin.readline
def solve(N, K, X, Y, E_X, E_Y):
G_X = [[] for _ in range(N + 1)]
G_Y = [[] for _ in range(N + 1)]
for s, e, d in E_X:
G_X[s].append((e, d))
G_X[e].append((s, d))
for s, e, d in E_Y:
G_Y[s].append((e, d))
G_Y[e].append((s, d))
dist = [float("inf") for _ in range(N + 1)]
dist[1] = 0
pq = []
heapq.heappush(pq, (dist[1], 1))
while pq:
now_dist, now_pos = heapq.heappop(pq)
if dist[now_pos] < now_dist:
continue
for target, cost in G_X[now_pos]:
new_cost = now_dist + cost
if new_cost < dist[target]:
dist[target] = new_cost
heapq.heappush(pq, (new_cost, target))
for target, cost in G_Y[now_pos]:
if now_dist < K:
new_cost = K + cost
else:
new_cost = now_dist + cost
if new_cost < dist[target]:
dist[target] = new_cost
heapq.heappush(pq, (new_cost, target))
return dist[N]
if __name__ == "__main__":
N, K, X, Y = map(int, input().split())
E_X = list(map(int, input().split()) for _ in range(X))
E_Y = list(map(int, input().split()) for _ in range(Y))
print(solve(N, K, X, Y, E_X, E_Y))