git » alan.git » commit 9611ab3

Add hardness multiplier scaling for generation parameters

author Alan Dipert
2025-12-04 04:25:50 UTC
committer Alan Dipert
2025-12-04 04:25:50 UTC
parent cec2db1bf141e2110fbe6e9aa3a395e9534ab227

Add hardness multiplier scaling for generation parameters

README.md +1 -1
main.py +19 -8

diff --git a/README.md b/README.md
index 6af45ca..a8bae54 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@ Do **not** use the score as a sole hiring gate; treat it as one data point along
   - No prefix/suffix ordering violations.
   - Tense/number/gender surfaces remain distinct (no collapses).
   - JSON output validated against the canonical feature-structure schema (`meta_schema.py`).
-- **Regeneration:** `main.py` will retry up to 10 seeds until all properties pass; otherwise it fails loudly.
+- **Regeneration:** `main.py` will retry seeds until all properties pass; otherwise it fails loudly. Hardness knobs (`--min-irregular`, `--min-irregular-contrast`, `--min-ditransitive`, `--min-plural`, `--min-adjective`, `--min-fem-plural`, `--min-feature-load`) can be scaled in one go with `--hardness-multiplier` (e.g., `2.0` to roughly double thresholds). All chosen params are recorded in `generation_params` in the JSON and printed in the booklet for reproducibility.
 
 ## Proctoring Guidance
 - Keep the cheat sheet and dictionary visible with the booklet; candidates should not need prior linguistics knowledge.
diff --git a/main.py b/main.py
index 2632ca5..c145d48 100644
--- a/main.py
+++ b/main.py
@@ -24,6 +24,12 @@ def parse_args() -> argparse.Namespace:
     parser.add_argument("--min-adjective", type=int, default=12, help="Minimum adjective-bearing items.")
     parser.add_argument("--min-fem-plural", type=int, default=6, help="Minimum feminine-plural items.")
     parser.add_argument("--min-feature-load", type=int, default=1, help="Minimum feature load per item (hardness).")
+    parser.add_argument(
+        "--hardness-multiplier",
+        type=float,
+        default=1.0,
+        help="Scale all coverage/complexity thresholds (e.g., 2.0 ~ twice as hard).",
+    )
     return parser.parse_args()
 
 
@@ -33,16 +39,21 @@ def main() -> None:
     concepts = get_default_concepts()
     blueprint = get_default_blueprint()
     max_attempts = 50
+    total_items = sum(s.num_items for s in blueprint.sections)
+
+    def scale(base: int) -> int:
+        return min(total_items, int(round(base * args.hardness_multiplier)))
 
     params = {
-        "min_irregular": args.min_irregular,
-        "min_irregular_contrast": args.min_irregular_contrast,
-        "min_irregular_distractor": max(3, args.min_irregular_contrast),
-        "min_ditransitive": args.min_ditransitive,
-        "min_plural": args.min_plural,
-        "min_adjective": args.min_adjective,
-        "min_fem_plural": args.min_fem_plural,
-        "min_feature_load": args.min_feature_load,
+        "min_irregular": max(1, scale(args.min_irregular)),
+        "min_irregular_contrast": max(1, scale(args.min_irregular_contrast)),
+        "min_irregular_distractor": max(3, scale(args.min_irregular_contrast)),
+        "min_ditransitive": max(1, scale(args.min_ditransitive)),
+        "min_plural": max(1, scale(args.min_plural)),
+        "min_adjective": max(1, scale(args.min_adjective)),
+        "min_fem_plural": max(1, scale(args.min_fem_plural)),
+        "min_feature_load": max(args.min_feature_load, int(round(args.min_feature_load * args.hardness_multiplier))),
+        "hardness_multiplier": args.hardness_multiplier,
     }
 
     for attempt in range(max_attempts):