Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework contest types #249

Merged
merged 4 commits into from
Aug 6, 2024
Merged
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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ with features such as:
- [Why?](#why)
- [Installation](#installation)
- [Usage](#usage)
- [Contest types](#contest-types)
- [Reporting bugs and contributing code](#reporting-bugs-and-contributing-code)

### Why?
Expand Down Expand Up @@ -103,6 +104,22 @@ There are also available short aliases for the commands:
- `sinol-make v` for `sinol-make verify`
- `sm` for `sinol-make`

### Contest types

`sinol-make` changes its behavior depending on the contest type specified in `config.yml`. You can specify
the contest type with the `sinol_contest_type` key in config. Here is the table of available contest types and their
features:

| Feature | `default` | `oi` | `oij` | `icpc` |
|-----------------------------------------------------------------------------------------------------|-----------|-------|-------|--------|
| Max score | 100 | 100 | 100 | 1 |
| Default time tool | oiejq | oiejq | oiejq | time |
| Full score if took less than half of the time limit, <br/>otherwise linearly decreasing to 1. | ❌ | ✔️ | ❌ | ❌ |
| Full score if took less than the time limit | ✔️ | ❌ | ✔️ | ✔️ |
| Scores must sum up to 100 | ❌ | ✔️ | ✔️ | ❌ |
| Limits can be set for individual tests | ✔️ | ❌ | ✔️ | ✔️ |
| Verifies if tests are named correctly<br/> (letters within groups increase, group numbers increase) | ❌ | ✔️ | ✔️ | ✔️ |

### Reporting bugs and contributing code

- Want to report a bug or request a feature? [Open an issue](https://github.com/sio2project/sinol-make/issues).
Expand Down
12 changes: 1 addition & 11 deletions example_package/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,7 @@ sinol_task_id: abc
# sinol-make can behave differently depending on the value of `sinol_contest_type` key.
# Mainly, it affects how points are calculated.
# If the key is not specified, then `default` is used.
# Possible values are:
# - `default` - Points for a test can only be 100 or 0 (unless checker assigns points).
# Points for a group are calculated based of the lowest number of points for a test in this group.
# If scores are not defined in `scores` key, then all groups have the same number of points,
# summing up to 100.
# - `oi` - Points for a test are unchanged if the execution time is less or equal to the time limit.
# Otherwise, number of points decreases linearly to one depending on the execution time.
# Points for a group are calculated same as in `default` mode.
# - `icpc` - A test passes when the status is OK.
# A group passes when all tests in this group pass.
# A solution passes when all groups pass.
# See README.md for more information.
sinol_contest_type: oi

# You can specify which tests are static (handwritten). This allows sinol-make to differentiate between
Expand Down
7 changes: 4 additions & 3 deletions src/sinol_make/commands/inwer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from functools import cmp_to_key
from typing import Dict, List

from sinol_make import util
from sinol_make import util, contest_types
from sinol_make.structs.inwer_structs import TestResult, InwerExecution, VerificationResult, TableData
from sinol_make.helpers import package_util, printer, paths, parsers
from sinol_make.interfaces.BaseCommand import BaseCommand
Expand Down Expand Up @@ -206,6 +206,7 @@ def run(self, args: argparse.Namespace):

self.cpus = args.cpus or util.default_cpu_count()
self.tests = package_util.get_tests(self.task_id, args.tests)
self.contest_type = contest_types.get_contest_type()

if len(self.tests) == 0:
util.exit_with_error('No tests found.')
Expand All @@ -224,7 +225,7 @@ def run(self, args: argparse.Namespace):

if len(failed_tests) > 0:
util.exit_with_error(f'Verification failed for tests: {", ".join(failed_tests)}')
else:
elif self.contest_type.verify_tests_order():
print("Verifying tests order...")
self.verify_tests_order()
print(util.info('Verification successful.'))
print(util.info('Verification successful.'))
8 changes: 7 additions & 1 deletion src/sinol_make/commands/run/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,13 @@ def use_time():
return 'time', 'time'

timetool_path, timetool_name = None, None
use_default_timetool = use_oiejq if util.is_linux() else use_time
preferred_timetool = self.contest.preferred_timetool()
if preferred_timetool == 'oiejq' and util.is_linux():
use_default_timetool = use_oiejq
elif preferred_timetool == 'time':
use_default_timetool = use_time
else:
use_default_timetool = use_oiejq if util.is_linux() else use_time

if args.time_tool is None and self.config.get('sinol_undocumented_time_tool', '') != '':
if self.config.get('sinol_undocumented_time_tool', '') == 'oiejq':
Expand Down
2 changes: 2 additions & 0 deletions src/sinol_make/contest_types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
from sinol_make.contest_types.icpc import ICPCContest
from sinol_make.contest_types.oi import OIContest
from sinol_make.contest_types.oij import OIJContest
from sinol_make.helpers.func_cache import cache_result
from sinol_make.helpers.package_util import get_config
from sinol_make.interfaces.Errors import UnknownContestType


@cache_result(cwd=True)
def get_contest_type():
config = get_config()
contest_type = config.get("sinol_contest_type", "default").lower()
Expand Down
18 changes: 18 additions & 0 deletions src/sinol_make/contest_types/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,21 @@ def verify_pre_gen(self):
Called by verify command before generating tests.
"""
pass

def allow_per_test_limits(self):
"""
Returns True if limits in config can be set per test
"""
return True

def preferred_timetool(self):
"""
Returns preferred time tool
"""
return 'oiejq'

def verify_tests_order(self):
"""
Whether to verify tests order
"""
return False
6 changes: 6 additions & 0 deletions src/sinol_make/contest_types/icpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@ def min_score_per_test(self):

def max_score_per_test(self):
return 1

def preferred_timetool(self):
return "time"

def verify_tests_order(self):
return True
6 changes: 6 additions & 0 deletions src/sinol_make/contest_types/oi.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,9 @@ def verify_pre_gen(self):
total_score = sum(config['scores'].values())
if total_score != 100:
util.exit_with_error(f"Total score in config is {total_score}, but should be 100.")

def allow_per_test_limits(self):
return False

def verify_tests_order(self):
return True
3 changes: 3 additions & 0 deletions src/sinol_make/contest_types/oij.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ def verify_pre_gen(self):
total_score = sum(config['scores'].values())
if total_score != 100:
util.exit_with_error(f"Total score in config is {total_score}, but should be 100.")

def verify_tests_order(self):
return True
7 changes: 4 additions & 3 deletions src/sinol_make/helpers/package_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import List, Union, Dict, Any, Tuple, Type

from sinol_make.helpers.func_cache import cache_result
from sinol_make import util
from sinol_make import util, contest_types
from sinol_make.helpers import paths
from sinol_make.task_type import BaseTaskType

Expand Down Expand Up @@ -55,7 +55,7 @@ def get_test_key(test, task_id):
def get_config():
try:
with open(os.path.join(os.getcwd(), "config.yml"), "r") as config_file:
return yaml.load(config_file, Loader=yaml.FullLoader)
return yaml.load(config_file, Loader=yaml.FullLoader) or {}
except FileNotFoundError:
# Potentially redundant with util:exit_if_not_package
util.exit_with_error("You are not in a package directory (couldn't find config.yml in current directory).")
Expand Down Expand Up @@ -229,7 +229,8 @@ def _get_limit_from_dict(dict: Dict[str, Any], limit_type: LimitTypes, test_id:
def _get_limit(limit_type: LimitTypes, test_path: str, config: Dict[str, Any], lang: str, task_id: str):
test_id = extract_test_id(test_path, task_id)
test_group = str(get_group(test_path, task_id))
allow_test_limit = config.get("sinol_undocumented_test_limits", False)
contest_type = contest_types.get_contest_type()
allow_test_limit = config.get("sinol_undocumented_test_limits", False) or contest_type.allow_per_test_limits()
global_limit = _get_limit_from_dict(config, limit_type, test_id, test_group, test_path, allow_test_limit)
override_limits_dict = config.get("override_limits", {}).get(lang, {})
overriden_limit = _get_limit_from_dict(override_limits_dict, limit_type, test_id, test_group, test_path,
Expand Down
3 changes: 2 additions & 1 deletion tests/commands/verify/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from sinol_make import configure_parsers
from sinol_make import util as sm_util
from sinol_make.commands.verify import Command
from sinol_make.helpers import package_util, paths
from sinol_make.helpers import package_util, paths, func_cache
from tests import util
from tests.fixtures import create_package

Expand Down Expand Up @@ -137,6 +137,7 @@ def test_expected_contest_and_no_scores(capsys, create_package):

del config["scores"]
for contest_type in ["oi", "oij"]:
func_cache.clear_cache()
config["sinol_contest_type"] = contest_type
sm_util.save_config(config)
with pytest.raises(SystemExit) as e:
Expand Down
Loading
Loading