Spaces:
Running on Zero
Running on Zero
Download rules.py from ageraustine/HAWK-ONE: direct link, hf CLI and curl.
- Browser
- Download file 7.45 kB
-
https://huggingface.co/spaces/ageraustine/HAWK-ONE/resolve/main/rules.py
- Command line
-
hf download hf://spaces/ageraustine/HAWK-ONE/rules.py
-
curl -L -o rules.py https://huggingface.co/spaces/ageraustine/HAWK-ONE/resolve/main/rules.py
7.45 kB
| """HAWK ONE rules layer. | |
| One simulation core, two RuleProfiles. The engine never asks "is this NFL?"; | |
| it asks the active profile (profile.catch_feet_required, profile.dpi_mode, ...). | |
| Adding a level (e.g. high school) = adding one more profile. | |
| Every parameter marked verify=True in RULE_ROWS must be confirmed against the | |
| current-season rulebook by the coach before being treated as authoritative. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Optional | |
| FIELD_LENGTH_YD = 120.0 # incl. two 10-yd end zones | |
| FIELD_WIDTH_YD = 160 / 3 # 53 1/3 yd, same at both levels | |
| class RuleProfile: | |
| key: str | |
| label: str | |
| hash_width_ft: float # distance between the two hash lines | |
| catch_feet_required: int # feet in bounds for a catch (or other body part) | |
| down_requires_contact: bool # runner must be touched to be down | |
| ineligible_downfield_yd: int # yards OL may be downfield before the pass | |
| dpi_mode: str # "spot" (spot foul) | "flat15" | |
| dpi_flat_yd: int = 15 | |
| dpi_auto_first_down: bool = True | |
| two_minute_warning: bool = False | |
| ot_summary: str = "" | |
| ot_two_point_from_period: Optional[int] = None | |
| kickoff_summary: str = "" | |
| targeting_summary: str = "" | |
| clock_summary: str = "" | |
| def hash_half_yd(self) -> float: | |
| return self.hash_width_ft / 3 / 2 | |
| def hash_width_yd(self) -> float: | |
| return self.hash_width_ft / 3 | |
| NFL = RuleProfile( | |
| key="NFL", label="NFL", | |
| hash_width_ft=18.5, | |
| catch_feet_required=2, | |
| down_requires_contact=True, | |
| ineligible_downfield_yd=1, | |
| dpi_mode="spot", | |
| two_minute_warning=True, | |
| ot_summary="Timed period (10 min regular season); both teams get a possession; sudden death after.", | |
| kickoff_summary="Dynamic kickoff: coverage team lined at receiving team's 40, receiving team in a setup zone; " | |
| "touchback spot depends on where the kick lands.", | |
| targeting_summary="No NCAA-style targeting rule; lowering-the-head / unnecessary-roughness fouls, " | |
| "ejection by officials' or league review.", | |
| clock_summary="Two-minute warning. Stoppage/restart table (out of bounds, incomplete, 10-sec runoff) -> ClockPolicy.", | |
| ) | |
| NCAA = RuleProfile( | |
| key="NCAA", label="College (NCAA)", | |
| hash_width_ft=40.0, | |
| catch_feet_required=1, | |
| down_requires_contact=False, | |
| ineligible_downfield_yd=3, | |
| dpi_mode="flat15", | |
| two_minute_warning=False, | |
| ot_summary="Untimed alternating possessions from the 25-yd line; 2-point tries only in later periods.", | |
| ot_two_point_from_period=3, | |
| kickoff_summary="Kick from own 35; touchback / fair-catch spot is the 25; traditional kickoff formations.", | |
| targeting_summary="Targeting: 15 yds + ejection, replay-reviewed; half-game carry-over suspension possible.", | |
| clock_summary="No two-minute warning. Different first-down / out-of-bounds restart treatment late in each half -> ClockPolicy.", | |
| ) | |
| PROFILES = {"NFL": NFL, "NCAA": NCAA} | |
| LEVEL_CHOICES = [("NFL", "NFL"), ("College (NCAA)", "NCAA")] | |
| def other(p: RuleProfile) -> RuleProfile: | |
| return NCAA if p.key == "NFL" else NFL | |
| # ---------------------------------------------------------------- rule functions | |
| def catch_is_complete(p: RuleProfile, control: bool, feet_in_bounds: int, | |
| other_body_part_in_bounds: bool = False) -> tuple[bool, str]: | |
| if not control: | |
| return False, "No secure control of the ball." | |
| if feet_in_bounds >= p.catch_feet_required: | |
| return True, f"Control + {feet_in_bounds} foot/feet in bounds (needs {p.catch_feet_required})." | |
| if other_body_part_in_bounds: | |
| return True, "Control + a body part other than the hands in bounds." | |
| return False, f"Needs {p.catch_feet_required} foot/feet in bounds; only {feet_in_bounds}." | |
| def runner_is_down(p: RuleProfile, body_down: bool, contacted: bool, | |
| slid_or_gave_up: bool = False) -> tuple[bool, str]: | |
| if not body_down: | |
| return False, "No knee/body part (other than hands/feet) on the ground." | |
| if not p.down_requires_contact: | |
| return True, "Knee/body down - ball is dead, contact not required." | |
| if contacted: | |
| return True, "Body down + contacted by a defender." | |
| if slid_or_gave_up: | |
| return True, "Runner slid / gave himself up." | |
| return False, "Down without contact: runner may get up and keep going." | |
| def dpi_enforcement(p: RuleProfile, foul_spot_yd: float, yds_to_goal: float) -> dict: | |
| """foul_spot_yd = yards downfield of LOS where the foul occurred.""" | |
| if p.dpi_mode == "spot": | |
| yards = min(foul_spot_yd, max(yds_to_goal - 1, 0)) # in end zone -> 1-yd line | |
| note = "Spot foul" + (" (placed at 1-yd line)" if foul_spot_yd >= yds_to_goal else "") | |
| else: | |
| yards = min(p.dpi_flat_yd, yds_to_goal / 2) # half-the-distance rule | |
| note = f"{p.dpi_flat_yd}-yd penalty from previous spot (half-distance to goal applies)" | |
| return {"yards": round(yards, 1), "first_down": p.dpi_auto_first_down, "note": note} | |
| def ineligible_downfield_ok(p: RuleProfile, yards_beyond_los: float) -> tuple[bool, str]: | |
| ok = yards_beyond_los <= p.ineligible_downfield_yd | |
| return ok, (f"Within the {p.ineligible_downfield_yd}-yd allowance." if ok | |
| else f"Beyond the {p.ineligible_downfield_yd}-yd allowance - ineligible downfield.") | |
| def overtime_plan(p: RuleProfile, period: int) -> str: | |
| if p.key == "NFL": | |
| return f"Period {period}: " + p.ot_summary | |
| if p.ot_two_point_from_period and period >= p.ot_two_point_from_period: | |
| return f"OT {period}: alternating 2-point tries from the 3-yd line (no drive)." | |
| return f"OT {period}: alternating possessions from the opponent's 25-yd line." | |
| # ---------------------------------------------------------------- comparison rows | |
| # (id, topic, NFL, NCAA, engine hook, status, verify) | |
| RULE_ROWS = [ | |
| (1, "Catch", "Control + two feet (or other body part) in bounds", | |
| "Control + one foot (or other body part) in bounds", "catch_is_complete()", "Live in Playground", False), | |
| (2, "Down by contact", "Runner must be touched (or slide / give up)", | |
| "Down when knee/body touches ground, no contact needed", "runner_is_down()", "Live in Playground", False), | |
| (3, "Hash marks", "18 ft 6 in apart (6.17 yd)", "40 ft apart (13.33 yd)", | |
| "profile.hash_half_yd -> field, spacing, alignments", "Live on field", False), | |
| (4, "Game clock", NFL.clock_summary, NCAA.clock_summary, | |
| "ClockPolicy (stop/start table per event)", "Phase 2", True), | |
| (5, "Pass interference (D)", "Spot foul (1-yd line if in end zone)", | |
| "15 yds (half-distance to goal); confirm flat vs capped", "dpi_enforcement()", "Live in Playground", True), | |
| (6, "Ineligible OL downfield", "1 yd past LOS before the pass", "3 yds past LOS before the pass", | |
| "ineligible_downfield_ok()", "Live on field + Playground", False), | |
| (7, "Overtime", NFL.ot_summary, NCAA.ot_summary, "overtime_plan() -> OTStateMachine", "Playground; state machine Phase 2", True), | |
| (8, "Kickoffs", NFL.kickoff_summary, NCAA.kickoff_summary, "KickoffFormation per profile", "Phase 2", True), | |
| (9, "Targeting / safety", NFL.targeting_summary, NCAA.targeting_summary, | |
| "PenaltyTable + EjectionPolicy", "Phase 2", True), | |
| ] | |