Skip to content

Commit 693427a

Browse files
chaemonkyuridenamida
authored andcommitted
Add interactive judge & muli-solution judge (kyuridenamida#164)
* interactiveを追加 * interactiveを変更 * stderrを出力するように変更 * MultiSolutionJudgeを追加。オプションsetを追加 * judgetype_setter.pyが追加されていなかったので追加 * testが通るように変更 * analyze_problem_contentをget_problem_contentに変更しproblem_contentに移動 * flake8が通らなかったので修正 * インタラクティブと複数解のテストを追加 * 不要なファイルの削除 * インタラクティブと複数解のテストを/tmpでやるように修正 * コンパイルしたファイルの実行権限を755にするように変更 * テスト用 * /tmp/test_run_single_test_multisolutionのパーミッションを777にする * テスト用にtravisを一時的に変更 * testerのg++オプションに-std=c++14を追加 * コンパイルエラーを修正 * subprocessのtextをはずす * interactiveのテストを変更 * interactiveの英語版も追加 * judge_statusのロジックを変更 * judge_statusをspecial_judge_statusに変更 * judge_typeのverifyでMultiSolutionとInteractiveについてNotImplementedErrorを出すように修正 * run_programに例外Judge_ERRORを追加 * run_programを整形 * default judge templateを追加 * judge_templateを追加
1 parent 45ad902 commit 693427a

File tree

29 files changed

+864
-71
lines changed

29 files changed

+864
-71
lines changed

atcodertools/atcoder_tools.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from atcodertools.tools.tester import main as tester_main
1010
from atcodertools.tools.submit import main as submit_main
1111
from atcodertools.tools.codegen import main as codegen_main
12+
from atcodertools.tools.judgetype_setter import main as judgetype_setter_main
1213
from atcodertools.release_management.version import __version__
1314
from colorama import Fore, Style
1415

@@ -38,7 +39,7 @@ def notify_if_latest_version_found():
3839
def main():
3940
notify_if_latest_version_found()
4041

41-
if len(sys.argv) < 2 or sys.argv[1] not in ("gen", "test", "submit", "codegen", "version"):
42+
if len(sys.argv) < 2 or sys.argv[1] not in ("gen", "test", "submit", "codegen", "set", "version"):
4243
print("Usage:")
4344
print("{} gen -- to generate workspace".format(sys.argv[0]))
4445
print("{} test -- to test codes in your workspace".format(sys.argv[0]))
@@ -63,5 +64,8 @@ def main():
6364
if sys.argv[1] == "codegen":
6465
codegen_main(prog, args)
6566

67+
if sys.argv[1] == "set":
68+
judgetype_setter_main(prog, args)
69+
6670
if sys.argv[1] == "version":
6771
print(__version__)

atcodertools/client/atcoder.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from atcodertools.fileutils.artifacts_cache import get_cache_file_path
1515
from atcodertools.client.models.contest import Contest
1616
from atcodertools.client.models.problem import Problem
17-
from atcodertools.client.models.problem_content import ProblemContent, InputFormatDetectionError, SampleDetectionError
17+
from atcodertools.client.models.problem_content import ProblemContent, get_problem_content
1818

1919

2020
class LoginError(Exception):
@@ -110,13 +110,13 @@ def download_problem_list(self, contest: Contest) -> List[Problem]:
110110
res.append(Problem(contest, alphabet, problem_id))
111111
return res
112112

113-
def download_problem_content(self, problem: Problem) -> ProblemContent:
113+
def download_problem_content_raw_html(self, problem: Problem) -> str:
114114
resp = self._request(problem.get_url())
115+
return resp.text
115116

116-
try:
117-
return ProblemContent.from_html(resp.text)
118-
except (InputFormatDetectionError, SampleDetectionError) as e:
119-
raise e
117+
def download_problem_content(self, problem: Problem) -> ProblemContent:
118+
html = self.download_problem_content_raw_html(problem)
119+
return get_problem_content(html)
120120

121121
def download_all_contests(self) -> List[Contest]:
122122
contest_ids = []

atcodertools/client/models/problem_content.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,10 @@ def _secondary_strategy(soup): # TODO: more descriptive name
128128
output_tags = sample_tags[1::2]
129129
input_format_tag = pre_tags[0]
130130
return input_format_tag, input_tags, output_tags
131+
132+
133+
def get_problem_content(original_html: str) -> ProblemContent:
134+
try:
135+
return ProblemContent.from_html(original_html)
136+
except (InputFormatDetectionError, SampleDetectionError) as e:
137+
raise e

atcodertools/common/judgetype.py

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22
# -*- coding: utf-8 -*-
33
from abc import ABCMeta, abstractmethod
44
from enum import Enum
5+
import platform
6+
7+
8+
class NoJudgeTypeException(Exception):
9+
pass
510

611

712
class JudgeType(Enum):
813
Normal = "normal"
914
Decimal = "decimal"
10-
Other = "other"
15+
MultiSolution = "multisolution"
16+
Interactive = "interactive"
1117

1218

1319
class ErrorType(Enum):
@@ -44,10 +50,13 @@ def from_dict(cls, dic):
4450
return r
4551

4652

53+
DEFAULT_EPS = 0.000000001
54+
55+
4756
class DecimalJudge(Judge):
4857
def __init__(self,
4958
error_type: ErrorType = ErrorType.AbsoluteOrRelative,
50-
diff: float = 0.0
59+
diff: float = DEFAULT_EPS
5160
):
5261
self.judge_type = JudgeType.Decimal
5362
self.error_type = error_type
@@ -91,6 +100,48 @@ def from_dict(cls, dic):
91100
return r
92101

93102

94-
class OtherJudge(Judge):
95-
# dummy
96-
pass
103+
def get_judge_exec_file_name():
104+
if platform.system() == "Windows":
105+
return "judge.exe"
106+
else:
107+
return "judge"
108+
109+
110+
class MultiSolutionJudge(Judge):
111+
def __init__(self):
112+
self.judge_type = JudgeType.MultiSolution
113+
self.judge_exec_file = get_judge_exec_file_name()
114+
115+
def verify(self, output, expected):
116+
raise NotImplementedError()
117+
118+
def to_dict(self):
119+
return {
120+
"judge_type": self.judge_type.value,
121+
"judge_exec_file": self.judge_exec_file,
122+
}
123+
124+
@classmethod
125+
def from_dict(cls, dic):
126+
r = MultiSolutionJudge()
127+
return r
128+
129+
130+
class InteractiveJudge(Judge):
131+
def __init__(self):
132+
self.judge_type = JudgeType.Interactive
133+
self.judge_exec_file = get_judge_exec_file_name()
134+
135+
def verify(self, output, expected):
136+
raise NotImplementedError()
137+
138+
def to_dict(self):
139+
return {
140+
"judge_type": self.judge_type.value,
141+
"judge_exec_file": self.judge_exec_file,
142+
}
143+
144+
@classmethod
145+
def from_dict(cls, dic):
146+
r = InteractiveJudge()
147+
return r

atcodertools/constprediction/constants_prediction.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from bs4 import BeautifulSoup
55

66
from atcodertools.client.models.problem_content import ProblemContent, InputFormatDetectionError, SampleDetectionError
7-
from atcodertools.common.judgetype import ErrorType, NormalJudge, DecimalJudge, Judge
7+
from atcodertools.common.judgetype import ErrorType, NormalJudge, DecimalJudge, InteractiveJudge, Judge
88
from atcodertools.common.logging import logger
99
from atcodertools.constprediction.models.problem_constant_set import ProblemConstantSet
1010

@@ -27,6 +27,8 @@ def __init__(self, cands):
2727

2828
MOD_ANCHORS = ["余り", "あまり", "mod", "割っ", "modulo"]
2929
DECIMAL_ANCHORS = ["誤差", " error "]
30+
MULTISOLUTION_ANCHORS = ["複数ある場合", "どれを出力しても構わない"]
31+
INTERACTIVE_ANCHORS = ["インタラクティブ", "リアクティブ", "interactive", "reactive"]
3032

3133
MOD_STRATEGY_RE_LIST = [
3234
re.compile("([0-9]+).?.?.?で割った"),
@@ -113,15 +115,27 @@ def normalize(sentence):
113115

114116
soup = BeautifulSoup(html, "html.parser")
115117
sentences = soup.get_text().split("\n")
116-
sentences = [normalize(s) for s in sentences if is_decimal_context(s)]
118+
119+
interactive_sentences = []
120+
121+
for s in sentences:
122+
for kw in INTERACTIVE_ANCHORS:
123+
if kw in s:
124+
interactive_sentences.append(s)
125+
126+
if len(interactive_sentences) > 0:
127+
return InteractiveJudge()
128+
129+
decimal_sentences = [normalize(s)
130+
for s in sentences if is_decimal_context(s)]
117131

118132
decimal_keyword_cands = set()
119133
decimal_val_cands = set()
120134

121-
if len(sentences) > 0: # Decimal
135+
if len(decimal_sentences) > 0: # Decimal
122136
is_absolute = False
123137
is_relative = False
124-
for s in sentences:
138+
for s in decimal_sentences:
125139
for regexp in DECIMAL_STRATEGY_RE_LIST_KEYWORD:
126140
r = regexp.findall(s)
127141
for t in r:
@@ -130,7 +144,7 @@ def normalize(sentence):
130144
elif t == "相対誤差" or t == "relative":
131145
is_relative = True
132146
decimal_keyword_cands.add(t)
133-
for s in sentences:
147+
for s in decimal_sentences:
134148
for regexp in DECIMAL_STRATEGY_RE_LIST_VAL:
135149
r = regexp.findall(s)
136150
for t in r:

0 commit comments

Comments
 (0)