rbx.cp 0.12.0__py3-none-any.whl → 0.13.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,232 @@
1
+ import pathlib
2
+ import shutil
3
+ from typing import List, Optional
4
+
5
+ import typer
6
+
7
+ from rbx import console, utils
8
+ from rbx.box import lang
9
+ from rbx.box.packaging.importer import BaseImporter
10
+ from rbx.box.packaging.polygon.xml_schema import File, Problem, Statement, Testset
11
+ from rbx.box.schema import CodeItem, Interactor, Package, TaskType, TestcaseGroup
12
+ from rbx.box.statements.schema import Statement as BoxStatement
13
+ from rbx.box.statements.schema import StatementType
14
+
15
+
16
+ def _get_main_testset(problem: Problem) -> Testset:
17
+ for testset in problem.judging.testsets:
18
+ if testset.name == 'tests':
19
+ return testset
20
+ console.console.print(
21
+ '[error][item]tests[/item] testset not found[/error]',
22
+ )
23
+ raise typer.Exit(1)
24
+
25
+
26
+ def _get_pdf_statements(problem: Problem) -> List[Statement]:
27
+ statements = []
28
+ for statement in problem.statements:
29
+ if statement.type == 'application/pdf':
30
+ statements.append(statement)
31
+ return statements
32
+
33
+
34
+ def _get_statement_path(statement: Statement) -> pathlib.Path:
35
+ return pathlib.Path('statements') / f'{statement.language}.pdf'
36
+
37
+
38
+ def _populate_tests(
39
+ testset: Testset, pkg: Package, pkg_path: pathlib.Path, into_path: pathlib.Path
40
+ ):
41
+ if not testset.answerPattern:
42
+ console.console.print(
43
+ '[error][item]answer pattern[/item] not found for testset[/error]',
44
+ )
45
+ raise typer.Exit(1)
46
+
47
+ for d, test in enumerate(testset.tests):
48
+ folder_name = 'tests/samples' if test.sample else 'tests/tests'
49
+ i = d + 1
50
+
51
+ input_path = pathlib.Path(testset.inputPattern % i)
52
+ dest_input_path = into_path / folder_name / f'{i:03d}.in'
53
+ dest_input_path.parent.mkdir(parents=True, exist_ok=True)
54
+ shutil.copy(pkg_path / input_path, dest_input_path)
55
+
56
+ answer_path = pathlib.Path(testset.answerPattern % i)
57
+ dest_answer_path = into_path / folder_name / f'{i:03d}.ans'
58
+ dest_answer_path.parent.mkdir(parents=True, exist_ok=True)
59
+ shutil.copy(pkg_path / answer_path, dest_answer_path)
60
+
61
+ pkg.testcases = [
62
+ TestcaseGroup(
63
+ name='samples',
64
+ testcaseGlob='tests/samples/*.in',
65
+ ),
66
+ TestcaseGroup(
67
+ name='tests',
68
+ testcaseGlob='tests/tests/*.in',
69
+ ),
70
+ ]
71
+
72
+
73
+ def _populate_statements(
74
+ problem: Problem,
75
+ pkg: Package,
76
+ pkg_path: pathlib.Path,
77
+ into_path: pathlib.Path,
78
+ main_language: Optional[str] = None,
79
+ ):
80
+ name_per_language = {name.language: name for name in problem.names}
81
+ pdf_statements = _get_pdf_statements(problem)
82
+ pkg_statements = []
83
+ found_main = False
84
+
85
+ for statement in pdf_statements:
86
+ if statement.language not in name_per_language:
87
+ continue
88
+ name = name_per_language[statement.language]
89
+ statement_path = into_path / _get_statement_path(statement)
90
+ statement_path.parent.mkdir(parents=True, exist_ok=True)
91
+ shutil.copy(pkg_path / statement.path, statement_path)
92
+
93
+ iso639_code = lang.lang_to_code(statement.language)
94
+
95
+ pkg_statement = BoxStatement(
96
+ name=f'statement-{name.language}',
97
+ title=name.value,
98
+ language=iso639_code,
99
+ path=_get_statement_path(statement),
100
+ type=StatementType.PDF,
101
+ )
102
+
103
+ if (
104
+ main_language is not None
105
+ and main_language == iso639_code
106
+ and not found_main
107
+ ):
108
+ # If main statement, add it to the front of the list
109
+ pkg_statements = [pkg_statement] + pkg_statements
110
+ found_main = True
111
+ continue
112
+
113
+ if name.main and not found_main:
114
+ # If main statement, add it to the front of the list
115
+ pkg_statements = [pkg_statement] + pkg_statements
116
+ found_main = True
117
+ continue
118
+
119
+ pkg_statements.append(pkg_statement)
120
+
121
+ pkg.statements = pkg_statements
122
+
123
+ if main_language is not None and not found_main:
124
+ console.console.print(
125
+ f'[error]Main statement of language [item]{main_language}[/item] not found.[/error]',
126
+ )
127
+ raise typer.Exit(1)
128
+
129
+
130
+ def _is_cpp_source(source: File) -> bool:
131
+ if source.type is None:
132
+ return False
133
+ return 'cpp' in source.type
134
+
135
+
136
+ def _copy_checker(
137
+ problem: Problem, pkg: Package, pkg_path: pathlib.Path, into_path: pathlib.Path
138
+ ):
139
+ if problem.checker is None:
140
+ return
141
+ if problem.checker.type != 'testlib' or not _is_cpp_source(problem.checker.source):
142
+ console.console.print(
143
+ f'[error][item]checker type[/item] not supported: [item]{problem.checker.type}[/item][/error]',
144
+ )
145
+ raise typer.Exit(1)
146
+ shutil.copy(pkg_path / problem.checker.source.path, into_path / 'checker.cpp')
147
+
148
+ pkg.checker = CodeItem(
149
+ path=pathlib.Path('checker.cpp'),
150
+ )
151
+
152
+
153
+ def _copy_interactor(
154
+ problem: Problem, pkg: Package, pkg_path: pathlib.Path, into_path: pathlib.Path
155
+ ):
156
+ if problem.interactor is None:
157
+ return
158
+ shutil.copy(pkg_path / problem.interactor.source.path, into_path / 'interactor.cpp')
159
+
160
+ if not _is_cpp_source(problem.interactor.source):
161
+ console.console.print(
162
+ f'[error]Only C++ interactor is supported, got [item]{problem.interactor.source.type}[/item][/error]',
163
+ )
164
+ raise typer.Exit(1)
165
+
166
+ pkg.type = TaskType.COMMUNICATION
167
+ pkg.interactor = Interactor(
168
+ path=pathlib.Path('interactor.cpp'),
169
+ legacy=True,
170
+ )
171
+
172
+
173
+ def _copy_headers(
174
+ problem: Problem, pkg: Package, pkg_path: pathlib.Path, into_path: pathlib.Path
175
+ ):
176
+ headers = []
177
+ for file in problem.files:
178
+ if file.type is None or not file.type.startswith('h.'):
179
+ continue
180
+ header_path = pkg_path / file.path
181
+ dest_path = into_path / header_path.name
182
+ if header_path.name == 'rbx.h':
183
+ dest_path = into_path / 'rbx.override.h'
184
+ shutil.copy(header_path, dest_path)
185
+ headers.append(dest_path.name)
186
+
187
+ if pkg.checker is not None:
188
+ pkg.checker.compilationFiles = headers
189
+
190
+ if pkg.interactor is not None:
191
+ pkg.interactor.compilationFiles = headers
192
+
193
+
194
+ class PolygonImporter(BaseImporter):
195
+ def __init__(self, main_language: Optional[str]):
196
+ self.main_language = main_language
197
+
198
+ @classmethod
199
+ def name(cls) -> str:
200
+ return 'polygon'
201
+
202
+ async def import_package(self, pkg_path: pathlib.Path, into_path: pathlib.Path):
203
+ problem_xml = pkg_path / 'problem.xml'
204
+ if not problem_xml.exists():
205
+ console.console.print(
206
+ '[error][item]problem.xml[/item] not found[/error]',
207
+ )
208
+ raise typer.Exit(1)
209
+
210
+ problem = Problem.from_xml(problem_xml.read_bytes())
211
+ testset = _get_main_testset(problem)
212
+
213
+ if testset.timelimit is None:
214
+ testset.timelimit = 1000
215
+
216
+ if testset.memorylimit is None:
217
+ testset.memorylimit = 256 * 1024 * 1024
218
+
219
+ pkg = Package(
220
+ name=problem.short_name,
221
+ timeLimit=testset.timelimit,
222
+ memoryLimit=testset.memorylimit // (1024 * 1024),
223
+ outputLimit=64 * 1024,
224
+ )
225
+
226
+ _populate_tests(testset, pkg, pkg_path, into_path)
227
+ _populate_statements(problem, pkg, pkg_path, into_path, self.main_language)
228
+ _copy_checker(problem, pkg, pkg_path, into_path)
229
+ _copy_interactor(problem, pkg, pkg_path, into_path)
230
+ _copy_headers(problem, pkg, pkg_path, into_path)
231
+
232
+ (into_path / 'problem.rbx.yml').write_text(utils.model_to_yaml(pkg))
@@ -6,7 +6,7 @@ import typer
6
6
 
7
7
  from rbx import console, utils
8
8
  from rbx.box import header, package
9
- from rbx.box.lang import code_to_langs, is_valid_lang_code
9
+ from rbx.box.lang import code_to_lang, code_to_langs, is_valid_lang_code
10
10
  from rbx.box.packaging.packager import (
11
11
  BaseContestPackager,
12
12
  BasePackager,
@@ -27,7 +27,31 @@ DAT_TEMPLATE = """
27
27
  """
28
28
 
29
29
 
30
+ def _select_main_language(
31
+ names: List[polygon_schema.Name], main_language: Optional[str]
32
+ ):
33
+ if names:
34
+ if main_language is not None:
35
+ lang_name = code_to_lang(main_language)
36
+ found = False
37
+ for name in names:
38
+ if name.language == lang_name:
39
+ name.main = True
40
+ found = True
41
+ break
42
+ if not found:
43
+ console.console.print(
44
+ f'[error]Main language [item]{main_language}[/item] not found.[/error]'
45
+ )
46
+ raise typer.Exit(1)
47
+ else:
48
+ names[0].main = True
49
+
50
+
30
51
  class PolygonPackager(BasePackager):
52
+ def __init__(self, main_language: Optional[str] = None):
53
+ self.main_language = main_language
54
+
31
55
  @classmethod
32
56
  def task_types(cls) -> List[TaskType]:
33
57
  return [TaskType.BATCH, TaskType.COMMUNICATION]
@@ -49,7 +73,7 @@ class PolygonPackager(BasePackager):
49
73
  raise typer.Exit(1)
50
74
 
51
75
  def _get_names(self) -> List[polygon_schema.Name]:
52
- return [
76
+ names = [
53
77
  polygon_schema.Name(
54
78
  language=code_to_langs([lang])[0],
55
79
  value=self.get_statement_for_language(lang).title,
@@ -57,6 +81,10 @@ class PolygonPackager(BasePackager):
57
81
  for lang in self.languages()
58
82
  ]
59
83
 
84
+ _select_main_language(names, self.main_language)
85
+
86
+ return names
87
+
60
88
  def _get_checker(self) -> polygon_schema.Checker:
61
89
  # TODO: support other checker languages
62
90
  return polygon_schema.Checker(
@@ -155,6 +183,7 @@ class PolygonPackager(BasePackager):
155
183
  pkg = package.find_problem_package_or_die()
156
184
 
157
185
  problem = polygon_schema.Problem(
186
+ short_name=pkg.name,
158
187
  names=self._get_names(),
159
188
  checker=self._get_checker(),
160
189
  judging=self._get_judging(),
@@ -209,6 +238,9 @@ class PolygonPackager(BasePackager):
209
238
 
210
239
 
211
240
  class PolygonContestPackager(BaseContestPackager):
241
+ def __init__(self, main_language: Optional[str] = None):
242
+ self.main_language = main_language
243
+
212
244
  @classmethod
213
245
  def name(cls) -> str:
214
246
  return 'polygon'
@@ -222,8 +254,7 @@ class PolygonContestPackager(BaseContestPackager):
222
254
  for lang in self.languages()
223
255
  if is_valid_lang_code(lang)
224
256
  ]
225
- if names:
226
- names[0].main = True
257
+ _select_main_language(names, self.main_language)
227
258
  return names
228
259
 
229
260
  def _process_statement(
@@ -319,4 +350,4 @@ class PolygonContestPackager(BaseContestPackager):
319
350
  # Zip all.
320
351
  shutil.make_archive(str(build_path / 'contest'), 'zip', into_path)
321
352
 
322
- return pathlib.Path('contest.zip')
353
+ return build_path / 'contest.zip'
@@ -295,19 +295,32 @@ def _upload_statement_resources(problem: api.Problem, statement: Statement):
295
295
  )
296
296
 
297
297
 
298
- def _upload_statement(problem: api.Problem, preserve_language: bool = False):
298
+ def _upload_statement(
299
+ problem: api.Problem, main_language: Optional[str], upload_as_english: bool = False
300
+ ):
299
301
  pkg = package.find_problem_package_or_die()
300
302
 
303
+ lang_list = []
301
304
  languages = set()
302
305
  for statement in pkg.expanded_statements:
303
306
  if not is_valid_lang_code(statement.language):
304
307
  continue
305
308
  languages.add(statement.language)
306
-
309
+ lang_list.append(statement.language)
307
310
  uploaded_languages = set()
308
311
 
312
+ if main_language is None:
313
+ main_language = lang_list[0]
314
+
315
+ # Put the main language first.
316
+ lang_list = list(languages)
317
+ for i in range(len(lang_list)):
318
+ if lang_list[i] == main_language:
319
+ lang_list[i], lang_list[0] = lang_list[0], lang_list[i]
320
+ break
321
+
309
322
  # Prioritize English statements.
310
- for language in ['en'] + list(languages):
323
+ for language in lang_list:
311
324
  statement = _get_statement_for_language(language)
312
325
  if statement is None:
313
326
  continue
@@ -317,15 +330,19 @@ def _upload_statement(problem: api.Problem, preserve_language: bool = False):
317
330
  console.console.print(
318
331
  f'Uploading statement for language [item]{language}[/item] (polygon language: [item]{statement_lang}[/item])...'
319
332
  )
320
- uploaded_language = statement_lang if preserve_language else 'english'
333
+ uploaded_language = statement_lang
334
+ if main_language == language:
335
+ if not upload_as_english:
336
+ console.console.print(
337
+ '[warning]By default, Polygon statements are uploaded respecting their original language.\n'
338
+ 'Codeforces does not work well with statements in other languages. If you want a better experience, '
339
+ 'use the [item]--upload-as-english[/item] option to force the main statement to be uploaded in English.[/warning]'
340
+ )
341
+ else:
342
+ uploaded_language = 'english'
321
343
  if uploaded_language in uploaded_languages:
322
344
  continue
323
345
  uploaded_languages.add(uploaded_language)
324
- if not preserve_language and statement_lang != 'english':
325
- console.console.print(
326
- '[warning]By default, Polygon statements are uploaded in English.\n'
327
- 'If you want to preserve the original language of your statement, use [item]--preserve-language[/item].'
328
- )
329
346
  blocks = _get_statement_blocks(statement)
330
347
  polygon_statement = api.Statement(
331
348
  encoding='utf-8',
@@ -350,7 +367,9 @@ def _normalize_problem_name(name: str) -> str:
350
367
  return name.replace(' ', '-').replace('_', '-').lower()
351
368
 
352
369
 
353
- async def upload_problem(name: str, preserve_language: bool = False):
370
+ async def upload_problem(
371
+ name: str, main_language: Optional[str], upload_as_english: bool = False
372
+ ):
354
373
  pkg = package.find_problem_package_or_die()
355
374
  name = _normalize_problem_name(name)
356
375
  problem = _find_or_create_problem(name)
@@ -369,7 +388,9 @@ async def upload_problem(name: str, preserve_language: bool = False):
369
388
 
370
389
  _upload_solutions(problem)
371
390
  _upload_testcases(problem)
372
- _upload_statement(problem, preserve_language=preserve_language)
391
+ _upload_statement(
392
+ problem, main_language=main_language, upload_as_english=upload_as_english
393
+ )
373
394
 
374
395
  # Commit.
375
396
  console.console.print('Committing changes...')
@@ -10,7 +10,7 @@ class Name(BaseXmlModel):
10
10
 
11
11
 
12
12
  class Statement(BaseXmlModel):
13
- charset: Optional[Literal['UTF-8']] = attr(default=None)
13
+ charset: Optional[str] = attr(default=None)
14
14
 
15
15
  language: str = attr()
16
16
 
@@ -43,9 +43,12 @@ class Testset(BaseXmlModel):
43
43
  size: int = element('test-count', default=None)
44
44
 
45
45
  inputPattern: str = element('input-path-pattern')
46
- answerPattern: str = element('answer-path-pattern')
46
+ outputPattern: Optional[str] = element('output-path-pattern', default=None)
47
+ answerPattern: Optional[str] = element('answer-path-pattern', default=None)
47
48
 
48
- tests: List[Test] = wrapped('tests', element(tag='test'), default_factory=list)
49
+ tests: List[Test] = wrapped(
50
+ 'tests', element(tag='test', default=None), default_factory=list
51
+ )
49
52
 
50
53
 
51
54
  class Judging(BaseXmlModel):
@@ -56,7 +59,7 @@ class Judging(BaseXmlModel):
56
59
 
57
60
 
58
61
  class Checker(BaseXmlModel):
59
- name: str = attr()
62
+ name: Optional[str] = attr(default=None)
60
63
  type: Literal['testlib'] = attr()
61
64
  source: File = element()
62
65
  binary: Optional[File] = element(default=None)
@@ -70,6 +73,8 @@ class Interactor(BaseXmlModel):
70
73
 
71
74
 
72
75
  class Problem(BaseXmlModel, tag='problem'):
76
+ short_name: str = attr('short-name')
77
+
73
78
  names: List[Name] = wrapped('names', element(tag='name'), default_factory=list)
74
79
 
75
80
  statements: List[Statement] = wrapped(
@@ -86,9 +91,13 @@ class Problem(BaseXmlModel, tag='problem'):
86
91
  default=[],
87
92
  )
88
93
 
89
- checker: Checker = wrapped('assets', element(tag='checker'))
94
+ checker: Optional[Checker] = wrapped(
95
+ 'assets', element(tag='checker', default=None), default=None
96
+ )
90
97
 
91
- interactor: Optional[Interactor] = wrapped('assets', element(tag='interactor'))
98
+ interactor: Optional[Interactor] = wrapped(
99
+ 'assets', element(tag='interactor', default=None), default=None
100
+ )
92
101
 
93
102
 
94
103
  class ContestProblem(BaseXmlModel):
rbx/box/stresses.py CHANGED
@@ -67,7 +67,7 @@ async def run_stress(
67
67
  raise typer.Exit(1)
68
68
  generator = generators.get_call_from_string(generator_call)
69
69
  stress = Stress(
70
- name=f'{generator.name}',
70
+ name=f'{pathlib.Path(generator.name).stem}',
71
71
  generator=generator,
72
72
  finder=finder,
73
73
  )
@@ -0,0 +1,76 @@
1
+ import pathlib
2
+ import tempfile
3
+ from typing import Optional
4
+
5
+ import typer
6
+
7
+ from rbx import console
8
+ from rbx.box import builder, cd, package
9
+ from rbx.box.environment import VerificationLevel
10
+ from rbx.box.packaging.boca.packager import BocaPackager
11
+ from rbx.box.packaging.importer import BaseImporter
12
+ from rbx.box.packaging.moj.packager import MojPackager
13
+ from rbx.box.packaging.packager import BasePackager, BuiltStatement
14
+ from rbx.box.packaging.polygon.importer import PolygonImporter
15
+ from rbx.box.packaging.polygon.packager import PolygonPackager
16
+ from rbx.box.statements.build_statements import build_statement
17
+
18
+ PACKAGER_REGISTRY = {
19
+ 'polygon': PolygonPackager,
20
+ 'boca': BocaPackager,
21
+ 'moj': MojPackager,
22
+ }
23
+
24
+ IMPORTER_REGISTRY = {
25
+ 'polygon': PolygonImporter,
26
+ }
27
+
28
+
29
+ def get_packager(source: str, **kwargs) -> BasePackager:
30
+ if source not in PACKAGER_REGISTRY:
31
+ console.console.print(f'Unknown packager: {source}')
32
+ raise typer.Exit(1)
33
+ return PACKAGER_REGISTRY[source](**kwargs)
34
+
35
+
36
+ def get_importer(source: str, **kwargs) -> BaseImporter:
37
+ if source not in IMPORTER_REGISTRY:
38
+ console.console.print(f'Unknown importer: {source}')
39
+ raise typer.Exit(1)
40
+ return IMPORTER_REGISTRY[source](**kwargs)
41
+
42
+
43
+ async def convert(
44
+ pkg_dir: pathlib.Path,
45
+ into_dir: pathlib.Path,
46
+ source: str,
47
+ destination: str,
48
+ main_language: Optional[str] = None,
49
+ ) -> pathlib.Path:
50
+ importer = get_importer(source, main_language=main_language)
51
+ packager = get_packager(destination)
52
+ await importer.import_package(pkg_dir, into_dir)
53
+
54
+ with cd.new_package_cd(into_dir):
55
+ package.clear_package_cache()
56
+
57
+ pkg = package.find_problem_package_or_die()
58
+
59
+ if not await builder.build(VerificationLevel.NONE.value):
60
+ console.console.print('[error]Failed to build the problem.[/error]')
61
+ raise typer.Exit(1)
62
+
63
+ built_statements = []
64
+ for statement_type in packager.statement_types():
65
+ for language in packager.languages():
66
+ statement = packager.get_statement_for_language(language)
67
+ statement_path = build_statement(statement, pkg, statement_type)
68
+ built_statements.append(
69
+ BuiltStatement(statement, statement_path, statement_type)
70
+ )
71
+
72
+ with tempfile.TemporaryDirectory() as td:
73
+ result_path = packager.package(
74
+ package.get_build_path(), pathlib.Path(td), built_statements
75
+ )
76
+ return result_path
rbx/box/tooling/main.py CHANGED
@@ -1,8 +1,61 @@
1
+ import atexit
2
+ import pathlib
3
+ import shutil
4
+ import tempfile
5
+ import zipfile
6
+ from typing import Annotated, Optional
7
+
8
+ import syncer
1
9
  import typer
2
10
 
3
- from rbx import annotations
11
+ from rbx import annotations, console
12
+ from rbx.box.tooling import converter
4
13
  from rbx.box.tooling.boca import main as boca_main
5
14
 
6
15
  app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
7
16
 
8
17
  app.add_typer(boca_main.app, name='boca')
18
+
19
+
20
+ @app.command('convert')
21
+ @syncer.sync
22
+ async def convert(
23
+ pkg: Annotated[pathlib.Path, typer.Argument(help='The package to convert.')],
24
+ source: Annotated[
25
+ str, typer.Option('-s', '--source', help='The format to convert from.')
26
+ ],
27
+ dest: Annotated[
28
+ str, typer.Option('-d', '--dest', help='The format to convert to.')
29
+ ],
30
+ output: Annotated[str, typer.Option('-o', '--output', help='The output path.')],
31
+ language: Annotated[
32
+ Optional[str],
33
+ typer.Option('--language', '-l', help='The main language of the problem.'),
34
+ ] = None,
35
+ ):
36
+ if pkg.suffix == '.zip':
37
+ temp_dir = tempfile.TemporaryDirectory()
38
+ with zipfile.ZipFile(pkg, 'r') as zip_ref:
39
+ zip_ref.extractall(temp_dir.name)
40
+ pkg = pathlib.Path(temp_dir.name)
41
+
42
+ atexit.register(temp_dir.cleanup)
43
+
44
+ if not pkg.is_dir():
45
+ console.console.print(f'[error]Package {pkg} is not a directory.[/error]')
46
+ raise typer.Exit(1)
47
+
48
+ with tempfile.TemporaryDirectory() as td:
49
+ result_path = await converter.convert(
50
+ pkg, pathlib.Path(td), source, dest, main_language=language
51
+ )
52
+ output_path = pathlib.Path(output)
53
+ if output_path.suffix == '.zip':
54
+ output_path.parent.mkdir(parents=True, exist_ok=True)
55
+ shutil.copy(result_path, output_path)
56
+ else:
57
+ output_path.mkdir(parents=True, exist_ok=True)
58
+ shutil.unpack_archive(result_path, output_path)
59
+ console.console.print(
60
+ f'[success]Converted package to [item]{output_path}[/item].[/success]'
61
+ )
@@ -5,9 +5,6 @@ timeLimit: 1000 # ms
5
5
  memoryLimit: 256 # MiB
6
6
  checker: {path: "wcmp.cpp"} # Download others from testlib with `rbx download checker`
7
7
  validator: {path: "validator.cpp"}
8
- generators:
9
- - path: "gens/gen.cpp"
10
- name: "gen"
11
8
  testcases:
12
9
  - name: "samples"
13
10
  testcaseGlob: "manual_tests/samples/*.in" # Pattern for the sample inputs.
@@ -39,7 +36,7 @@ statements:
39
36
  stresses:
40
37
  - name: "stress"
41
38
  generator:
42
- name: "gen"
39
+ name: "gens/gen"
43
40
  args: "[1..<MAX_N>] @" # `@` generates a random string
44
41
  finder: "[sols/wa.cpp] ~ INCORRECT"
45
42
  unitTests:
@@ -1,3 +1,3 @@
1
1
  # Generate 10 random testcases with n <= 1e9
2
2
  for i in range(10):
3
- print(f'gen 1000000000 {i}')
3
+ print(f'gens/gen 1000000000 {i}')
@@ -1,4 +1,2 @@
1
- gen 123456
2
- gen 12345678
3
- # Obtained by running `rbx stress -g 'gen [1..<MAX_N>] @' -f '[sols/wa.cpp] ~ INCORRECT'`
4
- gen 149403982 b139a2bd
1
+ gens/gen 123456
2
+ gens/gen 12345678
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.12.0
3
+ Version: 0.13.2
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9.1,<4.0.0
@@ -20,6 +20,7 @@ Requires-Dist: fastapi (>=0.115.8,<0.116.0)
20
20
  Requires-Dist: filelock (>=3.14.0,<4.0.0)
21
21
  Requires-Dist: gitignore-parser (>=0.1.12,<0.2.0)
22
22
  Requires-Dist: gitpython (>=3.1.43,<4.0.0)
23
+ Requires-Dist: iso639-lang (>=2.6.1,<3.0.0)
23
24
  Requires-Dist: jinja2 (>=3.1.4,<4.0.0)
24
25
  Requires-Dist: lark (>=1.2.2,<2.0.0)
25
26
  Requires-Dist: latexbuild (>=0.2.2,<0.3.0)
@@ -33,7 +34,6 @@ Requires-Dist: pydantic (==2.8.2)
33
34
  Requires-Dist: pydantic-xml[lxml] (>=2.11.0,<3.0.0)
34
35
  Requires-Dist: pypandoc (>=1.15,<2.0)
35
36
  Requires-Dist: pyte (>=0.8.2,<0.9.0)
36
- Requires-Dist: python-iso639 (>=2024.4.27,<2025.0.0)
37
37
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
38
38
  Requires-Dist: questionary (>=2.1.0,<3.0.0)
39
39
  Requires-Dist: requests (>=2.32.3,<3.0.0)