algobattle-base 4.3.2__tar.gz → 4.3.4__tar.gz

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.
Files changed (90) hide show
  1. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/PKG-INFO +2 -4
  2. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/battle.py +48 -49
  3. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/cli.py +105 -97
  4. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/match.py +31 -30
  5. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/problem.py +16 -26
  6. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/program.py +49 -51
  7. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/__init__.py +5 -5
  8. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/types.py +43 -53
  9. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/util.py +11 -16
  10. algobattle_base-4.3.4/pyproject.toml +156 -0
  11. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/test_battles.py +7 -4
  12. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/test_docker.py +16 -3
  13. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/test_match.py +7 -6
  14. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/test_types.py +2 -2
  15. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/test_util.py +4 -4
  16. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/problem.py +2 -1
  17. algobattle_base-4.3.2/pyproject.toml +0 -63
  18. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/LICENSE +0 -0
  19. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/README.md +0 -0
  20. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/__init__.py +0 -0
  21. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/py.typed +0 -0
  22. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/c/.gitignore +0 -0
  23. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/c/Dockerfile +0 -0
  24. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/c/src/CMakeLists.txt +0 -0
  25. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/c/src/main.c +0 -0
  26. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/cpp/.gitignore +0 -0
  27. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/cpp/Dockerfile +0 -0
  28. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/cpp/src/CMakeLists.txt +0 -0
  29. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/cpp/src/main.cpp +0 -0
  30. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/csharp/Dockerfile.jinja +0 -0
  31. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/csharp/Program.cs +0 -0
  32. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/csharp/{{program.capitalize()}}.csproj +0 -0
  33. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/go/.gitignore +0 -0
  34. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/go/Dockerfile +0 -0
  35. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/go/go.mod.jinja +0 -0
  36. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/go/main.go +0 -0
  37. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/java/.dockerignore +0 -0
  38. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/java/.gitignore +0 -0
  39. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/java/Dockerfile.jinja +0 -0
  40. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/java/pom.xml.jinja +0 -0
  41. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/java/src/main/java/com/{{team_normalized}}/{{program.capitalize()}}.java.jinja +0 -0
  42. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/javascript/.gitignore +0 -0
  43. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/javascript/Dockerfile.jinja +0 -0
  44. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/javascript/package-lock.json.jinja +0 -0
  45. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/javascript/package.json.jinja +0 -0
  46. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/javascript/{{program}}.mjs.jinja +0 -0
  47. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/plain/Dockerfile.jinja +0 -0
  48. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/problem.py.jinja +0 -0
  49. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/python/.gitignore +0 -0
  50. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/python/Dockerfile.jinja +0 -0
  51. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/python/pyproject.toml.jinja +0 -0
  52. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/python/{{program}}.py.jinja +0 -0
  53. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/rust/.gitignore +0 -0
  54. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/rust/Cargo.toml.jinja +0 -0
  55. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/rust/Dockerfile.jinja +0 -0
  56. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/rust/src/main.rs.jinja +0 -0
  57. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/typescript/.gitignore +0 -0
  58. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/typescript/Dockerfile +0 -0
  59. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/typescript/package-lock.json.jinja +0 -0
  60. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/typescript/package.json.jinja +0 -0
  61. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/typescript/src/main.ts +0 -0
  62. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/algobattle/templates/typescript/tsconfig.json +0 -0
  63. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/README.md +0 -0
  64. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/__init__.py +0 -0
  65. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/configs/empty.toml +0 -0
  66. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/configs/teams.toml +0 -0
  67. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/configs/teams_incorrect.toml +0 -0
  68. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/configs/test.toml +0 -0
  69. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/README.md +0 -0
  70. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/__init__.py +0 -0
  71. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/build_error/Dockerfile +0 -0
  72. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/build_timeout/Dockerfile +0 -0
  73. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/generator/Dockerfile +0 -0
  74. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/generator/instance.json +0 -0
  75. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/generator_execution_error/Dockerfile +0 -0
  76. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/generator_semantics_error/Dockerfile +0 -0
  77. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/generator_semantics_error/instance.json +0 -0
  78. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/generator_syntax_error/Dockerfile +0 -0
  79. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/generator_syntax_error/instance.json +0 -0
  80. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/generator_timeout/Dockerfile +0 -0
  81. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/generator_timeout/instance.json +0 -0
  82. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/solver/Dockerfile +0 -0
  83. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/solver/solution.json +0 -0
  84. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/solver_execution_error/Dockerfile +0 -0
  85. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/solver_semantics_error/Dockerfile +0 -0
  86. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/solver_semantics_error/solution.json +0 -0
  87. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/solver_syntax_error/Dockerfile +0 -0
  88. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/solver_syntax_error/solution.json +0 -0
  89. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/solver_timeout/Dockerfile +0 -0
  90. {algobattle_base-4.3.2 → algobattle_base-4.3.4}/tests/testsproblem/solver_timeout/solution.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: algobattle-base
3
- Version: 4.3.2
3
+ Version: 4.3.4
4
4
  Summary: The Algobattle lab course package.
5
5
  Author: Imogen Hergeth, Jan Dreier, Henri Lotze
6
6
  License: MIT
@@ -22,9 +22,7 @@ Requires-Dist: typing-extensions>=4.15.0
22
22
  Requires-Dist: tomlkit>=0.13.3
23
23
  Requires-Dist: jinja2>=3.1.6
24
24
  Provides-Extra: dev
25
- Requires-Dist: black>=23.12.1; extra == "dev"
26
- Requires-Dist: flake8>=6.1.0; extra == "dev"
27
- Requires-Dist: flake8-docstrings>=1.7.0; extra == "dev"
25
+ Requires-Dist: ruff>=0.13.2; extra == "dev"
28
26
  Requires-Dist: mkdocs>=1.5.3; extra == "dev"
29
27
  Requires-Dist: mkdocs-material>=9.5.3; extra == "dev"
30
28
  Requires-Dist: pymdown-extensions>=10.7; extra == "dev"
@@ -3,10 +3,12 @@
3
3
  This module contains the :class:`Battle` class, which speciefies how each type of battle is fought and scored,
4
4
  some basic battle types, and related classed.
5
5
  """
6
+
7
+ from abc import abstractmethod
8
+ from collections.abc import Iterable
6
9
  from dataclasses import dataclass, field
7
10
  from enum import StrEnum
8
11
  from importlib.metadata import entry_points
9
- from abc import abstractmethod
10
12
  from inspect import isclass
11
13
  from itertools import count
12
14
  from pathlib import Path
@@ -16,7 +18,6 @@ from typing import (
16
18
  Annotated,
17
19
  Any,
18
20
  ClassVar,
19
- Iterable,
20
21
  Literal,
21
22
  ParamSpec,
22
23
  Protocol,
@@ -26,9 +27,8 @@ from typing import (
26
27
  Unpack,
27
28
  overload,
28
29
  )
29
- from typing_extensions import TypedDict
30
- from annotated_types import Ge
31
30
 
31
+ from annotated_types import Ge
32
32
  from pydantic import (
33
33
  ConfigDict,
34
34
  Field,
@@ -39,11 +39,10 @@ from pydantic import (
39
39
  ValidatorFunctionWrapHandler,
40
40
  )
41
41
  from pydantic_core import CoreSchema
42
- from pydantic_core.core_schema import (
43
- tagged_union_schema,
44
- with_info_wrap_validator_function,
45
- )
42
+ from pydantic_core.core_schema import tagged_union_schema, with_info_wrap_validator_function
43
+ from typing_extensions import TypedDict
46
44
 
45
+ from algobattle.problem import InstanceModel, Problem, SolutionModel
47
46
  from algobattle.program import (
48
47
  Generator,
49
48
  GeneratorResult,
@@ -53,15 +52,7 @@ from algobattle.program import (
53
52
  Solver,
54
53
  SolverResult,
55
54
  )
56
- from algobattle.problem import InstanceModel, Problem, SolutionModel
57
- from algobattle.util import (
58
- Encodable,
59
- EncodableModel,
60
- ExceptionInfo,
61
- BaseModel,
62
- Role,
63
- )
64
-
55
+ from algobattle.util import BaseModel, Encodable, EncodableModel, ExceptionInfo, Role
65
56
 
66
57
  _BattleConfig: TypeAlias = Any
67
58
  """Type alias used to generate correct typings when subclassing :class:`Battle`.
@@ -73,7 +64,7 @@ the new battle type directly.
73
64
  """
74
65
  T = TypeVar("T")
75
66
  P = ParamSpec("P")
76
- Type = type
67
+ _type = type
77
68
 
78
69
 
79
70
  class ProgramLogConfigTime(StrEnum):
@@ -91,7 +82,7 @@ class ProgramLogConfigLocation(StrEnum):
91
82
  inline = "inline"
92
83
 
93
84
 
94
- class ProgramLogConfigView(Protocol): # noqa: D101
85
+ class ProgramLogConfigView(Protocol):
95
86
  when: ProgramLogConfigTime = ProgramLogConfigTime.error
96
87
  output: ProgramLogConfigLocation = ProgramLogConfigLocation.inline
97
88
 
@@ -100,7 +91,7 @@ class ProgramRunInfo(BaseModel):
100
91
  """Data about a program's execution."""
101
92
 
102
93
  runtime: float = 0
103
- overriden: RunConfigOverride = Field(default_factory=dict)
94
+ overriden: RunConfigOverride = Field(default_factory=RunConfigOverride, validate_default=True)
104
95
  error: ExceptionInfo | None = None
105
96
  battle_data: SerializeAsAny[EncodableModel] | None = None
106
97
  instance: SerializeAsAny[InstanceModel] | None = None
@@ -177,7 +168,7 @@ class FightUi(ProgramUi, Protocol):
177
168
  """Informs the ui that the fight has finished running and has been added to the battle's `.fight_results`."""
178
169
 
179
170
 
180
- class RunKwargs(TypedDict, total=False):
171
+ class RunKwargs(TypedDict, total=False, closed=True):
181
172
  """The keyword arguments used by the FightHandler.run family of functions."""
182
173
 
183
174
  timeout_generator: float | None
@@ -211,8 +202,7 @@ class FightHandler:
211
202
  *,
212
203
  with_results: Literal[False] = False,
213
204
  **kwargs: Unpack[RunKwargs],
214
- ) -> Fight:
215
- ...
205
+ ) -> Fight: ...
216
206
 
217
207
  @overload
218
208
  async def run(
@@ -221,8 +211,7 @@ class FightHandler:
221
211
  *,
222
212
  with_results: Literal[True],
223
213
  **kwargs: Unpack[RunKwargs],
224
- ) -> tuple[Fight, GeneratorResult, SolverResult | None]:
225
- ...
214
+ ) -> tuple[Fight, GeneratorResult, SolverResult | None]: ...
226
215
 
227
216
  async def run(
228
217
  self,
@@ -260,12 +249,7 @@ class FightHandler:
260
249
  The resulting info about the executed fight, and the results if the flag has been set.
261
250
  """
262
251
  gen_result, sol_result = await self.run_raw(max_size=max_size, **kwargs)
263
- if gen_result.instance is None or gen_result.solution is None:
264
- score = 1
265
- elif sol_result is None or sol_result.solution is None:
266
- score = 0
267
- else:
268
- score = self.calculate_score(gen_result, sol_result)
252
+ score = self.calculate_score(gen_result, sol_result)
269
253
  fight = Fight.from_results(
270
254
  score=score,
271
255
  max_size=max_size,
@@ -331,7 +315,7 @@ class FightHandler:
331
315
  )
332
316
  return gen_result, sol_result
333
317
 
334
- def calculate_score(self, gen_result: GeneratorResult, sol_result: SolverResult) -> float:
318
+ def calculate_score(self, gen_result: GeneratorResult, sol_result: SolverResult | None) -> float:
335
319
  """Calculates the score achieved by the solver in this fight.
336
320
 
337
321
  Both results need to contain all instance and/or solution data required.
@@ -343,9 +327,25 @@ class FightHandler:
343
327
  Returns:
344
328
  A number in [0, 1] with higher numbers meaning the solver performed better.
345
329
  """
346
- assert gen_result.instance is not None
347
- assert sol_result.solution is not None
330
+ # We first need to check whether the generator somehow failed. This can happen in three cases:
331
+ # it doesn't produce an instance, it creates an invalid instance or it doesn't create a needed solution
332
+ # note that gen_result.instance and gen_result.error will both contain data if the generator outputted an
333
+ # invalid instance and/or solution
334
+ if (
335
+ gen_result.instance is None
336
+ or gen_result.error is not None
337
+ or (self.problem.with_solution and gen_result.solution is None)
338
+ ):
339
+ return 1
340
+ # sol_result is None only if the generator failed, which needs to be caught by the above check
341
+ assert sol_result is not None
342
+ # The solver failed if it didn't output a solution or the solution contains some error
343
+ if sol_result.solution is None or sol_result.error is not None:
344
+ return 0
345
+
348
346
  if self.problem.with_solution:
347
+ # we need this assert since type checkers can't see the dependency between self.problem.with_solution
348
+ # and gen_result.solution that is created by the last check in the first "if"
349
349
  assert gen_result.solution is not None
350
350
  score = self.problem.score(
351
351
  gen_result.instance, solver_solution=sol_result.solution, generator_solution=gen_result.solution
@@ -394,7 +394,7 @@ class Battle(BaseModel):
394
394
  """Type of battle that will be used."""
395
395
 
396
396
  @classmethod
397
- def __get_pydantic_core_schema__(cls, source: Type, handler: GetCoreSchemaHandler) -> CoreSchema:
397
+ def __get_pydantic_core_schema__(cls, source: _type, handler: GetCoreSchemaHandler) -> CoreSchema:
398
398
  # there's two bugs we need to catch:
399
399
  # 1. this function is called during the pydantic BaseModel metaclass's __new__, so the BattleConfig class
400
400
  # won't be ready at that point and be missing in the namespace
@@ -446,7 +446,7 @@ class Battle(BaseModel):
446
446
  installed = ", ".join(b.name() for b in Battle._battle_types.values())
447
447
  raise ValueError(
448
448
  f"The specified battle type '{passed}' is not installed. Installed types are: {installed}"
449
- )
449
+ ) from e
450
450
 
451
451
  return with_info_wrap_validator_function(check_installed, subclass_schema)
452
452
 
@@ -459,8 +459,7 @@ class Battle(BaseModel):
459
459
 
460
460
  if TYPE_CHECKING:
461
461
  # to hint that we're gonna fill this with arbitrary data belonging to some supposed battle type
462
- def __getattr__(self, __attr: str) -> Any:
463
- ...
462
+ def __getattr__(self, attr: str, /) -> Any: ...
464
463
 
465
464
  class UiData(BaseModel):
466
465
  """Object containing custom diplay data.
@@ -560,7 +559,7 @@ class Iterated(Battle):
560
559
  exit after that many failures, or `"unlimited"` to never exit early.
561
560
  """
562
561
 
563
- class UiData(Battle.UiData): # noqa: D106
562
+ class UiData(Battle.UiData):
564
563
  reached: list[int]
565
564
  cap: int
566
565
  note: str
@@ -620,7 +619,7 @@ class Iterated(Battle):
620
619
  return 0 if len(self.results) == 0 else sum(self.results) / len(self.results)
621
620
 
622
621
  @staticmethod
623
- def format_score(score: float) -> str: # noqa: D102
622
+ def format_score(score: float) -> str:
624
623
  return str(int(score))
625
624
 
626
625
 
@@ -637,7 +636,7 @@ class Averaged(Battle):
637
636
  num_fights: int = 10
638
637
  """Number of iterations in each round."""
639
638
 
640
- class UiData(Battle.UiData): # noqa: D106
639
+ class UiData(Battle.UiData):
641
640
  round: int
642
641
 
643
642
  async def run_battle(self, fight: FightHandler, config: Config, min_size: int, ui: BattleUi) -> None:
@@ -659,7 +658,7 @@ class Averaged(Battle):
659
658
  return sum(f.score for f in self.fights) / len(self.fights)
660
659
 
661
660
  @staticmethod
662
- def format_score(score: float) -> str: # noqa: D102
661
+ def format_score(score: float) -> str:
663
662
  return format(score, ".0%")
664
663
 
665
664
 
@@ -681,7 +680,7 @@ class FightHistory(Encodable):
681
680
  gen_sols: set[Role]
682
681
  sol_sols: set[Role]
683
682
 
684
- def encode(self, target: Path, role: Role) -> None: # noqa: D102
683
+ def encode(self, target: Path, role: Role) -> None:
685
684
  target.mkdir()
686
685
  for i, fight in enumerate(self.history):
687
686
  fight_dir = target / str(i)
@@ -715,16 +714,16 @@ class Improving(Battle):
715
714
  """Number of fights that will be fought."""
716
715
  weighting: Annotated[float, Ge(0)] = 1.1
717
716
  """How much each successive fight should be weighted more than the previous."""
718
- scores: set[Role] = {Role.generator, Role.solver}
717
+ scores: set[Role] = {Role.generator, Role.solver} # noqa: RUF012
719
718
  """Who to show each fight's scores to."""
720
- instances: set[Role] = {Role.generator, Role.solver}
719
+ instances: set[Role] = {Role.generator, Role.solver} # noqa: RUF012
721
720
  """Who to show the instances to."""
722
- generator_solutions: set[Role] = {Role.generator}
721
+ generator_solutions: set[Role] = {Role.generator} # noqa: RUF012
723
722
  """Who to show the generator's solutions to, if the problem requires them."""
724
- solver_solutions: set[Role] = {Role.solver}
723
+ solver_solutions: set[Role] = {Role.solver} # noqa: RUF012
725
724
  """Who to show the solver's solutions to."""
726
725
 
727
- class UiData(Battle.UiData): # noqa: D106
726
+ class UiData(Battle.UiData):
728
727
  round: int
729
728
 
730
729
  async def run_battle(self, fight: FightHandler, config: Config, min_size: int, ui: BattleUi) -> None:
@@ -760,5 +759,5 @@ class Improving(Battle):
760
759
  return total / quotient
761
760
 
762
761
  @staticmethod
763
- def format_score(score: float) -> str: # noqa: D102
762
+ def format_score(score: float) -> str:
764
763
  return format(score, ".0%")