Spaces:
Running
Catalog C13: harvest-economy packs — closes #14 (user economy families)
Browse filesNow that S0+S1 land, ship the user's two economy scenario families as
runnable, scored packs:
- economy-harvest-timebox: earn max economic value in a fixed clock,
keep harvesters productive (3 levels: loose -> tighter -> must
expand harvesting).
- economy-harvest-investment: split a budget between widening
collection (2nd refinery / more harvesters) and silo storage so
burst yield is not lost (hard requires has_building:silo + high
economy_value) — the user's exact 'storage decision matters'.
render_state exposes resources/resource_capacity/economy_value so
models can plan. test_economy_harvest (5): timebox earns + scores +
deterministic; investment runs+scores within constraints all levels.
Full bench suite 267 passed. SCENARIO_CATALOG C13 marked runnable.
|
@@ -80,9 +80,15 @@ cap) — never just bigger numbers (Procgen/SMACv2 rule).
|
|
| 80 |
≈ **201 levels / ~67 packs**, all runnable on the current engine.
|
| 81 |
Economy categories are scoped as **spend/conservation** decisions
|
| 82 |
(`starting_cash`), the discriminating choice today; an
|
| 83 |
-
**Economy-Harvest** 13th category is
|
| 84 |
-
(S0 ore
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
## Generation & integ-test strategy ("each run + validated")
|
| 88 |
|
|
|
|
| 80 |
≈ **201 levels / ~67 packs**, all runnable on the current engine.
|
| 81 |
Economy categories are scoped as **spend/conservation** decisions
|
| 82 |
(`starting_cash`), the discriminating choice today; an
|
| 83 |
+
**Economy-Harvest** 13th category is **now RUNNABLE** — engine task
|
| 84 |
+
**#14** (S0 ore-seeding + idempotent harvest, S1 capped resource pool
|
| 85 |
+
from refineries/silos + `economy_value`) is done. Shipped packs:
|
| 86 |
+
`economy-harvest-timebox` (earn max economic value in a time budget,
|
| 87 |
+
keep harvesters productive) and `economy-harvest-investment` (split a
|
| 88 |
+
budget between widening collection and silo storage so burst yield is
|
| 89 |
+
not lost — the storage decision). Win = `economy_value_gte`
|
| 90 |
+
(=cash+stored resources) / `harvesters_gte` / `has_building:silo`
|
| 91 |
+
within `within_ticks`; integ-tested end-to-end (test_economy_harvest).
|
| 92 |
|
| 93 |
## Generation & integ-test strategy ("each run + validated")
|
| 94 |
|
|
@@ -279,6 +279,9 @@ class RustObsAdapter:
|
|
| 279 |
"explored_percent": self.signals.explored_percent,
|
| 280 |
# Economy/base state so agents can plan construction.
|
| 281 |
"cash": self.signals.cash,
|
|
|
|
|
|
|
|
|
|
| 282 |
"power_provided": self.signals.power_provided,
|
| 283 |
"power_drained": self.signals.power_drained,
|
| 284 |
"own_buildings": [
|
|
|
|
| 279 |
"explored_percent": self.signals.explored_percent,
|
| 280 |
# Economy/base state so agents can plan construction.
|
| 281 |
"cash": self.signals.cash,
|
| 282 |
+
"resources": self.signals.resources,
|
| 283 |
+
"resource_capacity": self.signals.resource_capacity,
|
| 284 |
+
"economy_value": self.signals.cash + self.signals.resources,
|
| 285 |
"power_provided": self.signals.power_provided,
|
| 286 |
"power_drained": self.signals.power_drained,
|
| 287 |
"own_buildings": [
|
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
meta:
|
| 2 |
+
id: economy-harvest-investment
|
| 3 |
+
title: "Harvest Economy — Investment & Storage"
|
| 4 |
+
capability: reasoning
|
| 5 |
+
real_world_meaning: >
|
| 6 |
+
Given enough capital to either widen collection (a second
|
| 7 |
+
refinery / more harvesters) or deepen storage (silos that raise the
|
| 8 |
+
cap so burst yield is not lost), choose the allocation that
|
| 9 |
+
maximizes banked economic value before the deadline — the
|
| 10 |
+
investment decision, plus storage so harvest is not wasted.
|
| 11 |
+
robotics_analogue: >
|
| 12 |
+
Capital allocation for an autonomous resource operation: throughput
|
| 13 |
+
vs. buffer capacity under a finite budget and mission clock.
|
| 14 |
+
author: catalog-c13
|
| 15 |
+
|
| 16 |
+
base_map: rush-hour-arena
|
| 17 |
+
starting_cash: 3000
|
| 18 |
+
|
| 19 |
+
base:
|
| 20 |
+
agent:
|
| 21 |
+
faction: allies
|
| 22 |
+
enemy:
|
| 23 |
+
faction: soviet
|
| 24 |
+
tools:
|
| 25 |
+
- build
|
| 26 |
+
- place_building
|
| 27 |
+
- harvest
|
| 28 |
+
- move_units
|
| 29 |
+
- deploy
|
| 30 |
+
- stop
|
| 31 |
+
planning: true
|
| 32 |
+
termination:
|
| 33 |
+
max_ticks: 40000
|
| 34 |
+
actors:
|
| 35 |
+
- type: fact
|
| 36 |
+
owner: agent
|
| 37 |
+
position:
|
| 38 |
+
- 10
|
| 39 |
+
- 18
|
| 40 |
+
- type: powr
|
| 41 |
+
owner: agent
|
| 42 |
+
position:
|
| 43 |
+
- 14
|
| 44 |
+
- 18
|
| 45 |
+
- type: proc
|
| 46 |
+
owner: agent
|
| 47 |
+
position:
|
| 48 |
+
- 12
|
| 49 |
+
- 22
|
| 50 |
+
- type: harv
|
| 51 |
+
owner: agent
|
| 52 |
+
position:
|
| 53 |
+
- 15
|
| 54 |
+
- 22
|
| 55 |
+
- type: mine
|
| 56 |
+
owner: neutral
|
| 57 |
+
position:
|
| 58 |
+
- 40
|
| 59 |
+
- 22
|
| 60 |
+
- type: jeep
|
| 61 |
+
owner: agent
|
| 62 |
+
position:
|
| 63 |
+
- 8
|
| 64 |
+
- 16
|
| 65 |
+
|
| 66 |
+
levels:
|
| 67 |
+
easy:
|
| 68 |
+
description: >
|
| 69 |
+
Budget covers a clear improvement; harvest + reinvest to clear a
|
| 70 |
+
modest economy-value bar.
|
| 71 |
+
win_condition:
|
| 72 |
+
all_of:
|
| 73 |
+
- economy_value_gte: 4000
|
| 74 |
+
- within_ticks: 12000
|
| 75 |
+
max_turns: 70
|
| 76 |
+
medium:
|
| 77 |
+
description: >
|
| 78 |
+
Higher bar — a single refinery/harvester cannot keep up; widen
|
| 79 |
+
collection (2nd refinery or more harvesters) with the budget.
|
| 80 |
+
win_condition:
|
| 81 |
+
all_of:
|
| 82 |
+
- economy_value_gte: 6500
|
| 83 |
+
- within_ticks: 12000
|
| 84 |
+
max_turns: 75
|
| 85 |
+
hard:
|
| 86 |
+
description: >
|
| 87 |
+
High banked-value target with a tight clock: bursts overflow the
|
| 88 |
+
base storage and are lost — a silo (raising the cap) is required,
|
| 89 |
+
and the budget must be split between collection and storage.
|
| 90 |
+
win_condition:
|
| 91 |
+
all_of:
|
| 92 |
+
- economy_value_gte: 9000
|
| 93 |
+
- has_building: silo
|
| 94 |
+
- within_ticks: 11000
|
| 95 |
+
max_turns: 85
|
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
meta:
|
| 2 |
+
id: economy-harvest-timebox
|
| 3 |
+
title: "Harvest Economy — Time-Boxed"
|
| 4 |
+
capability: reasoning
|
| 5 |
+
real_world_meaning: >
|
| 6 |
+
Within a fixed time budget, earn as much economic value as possible
|
| 7 |
+
by running and growing a harvesting operation, then keep the
|
| 8 |
+
harvesters productive — the decision is committing to harvest
|
| 9 |
+
promptly and expanding capacity rather than idling.
|
| 10 |
+
robotics_analogue: >
|
| 11 |
+
Autonomous resource-collection under a mission clock: maximize
|
| 12 |
+
yield by dispatching and scaling collectors instead of dithering.
|
| 13 |
+
author: catalog-c13
|
| 14 |
+
|
| 15 |
+
base_map: rush-hour-arena
|
| 16 |
+
starting_cash: 200
|
| 17 |
+
|
| 18 |
+
base:
|
| 19 |
+
agent:
|
| 20 |
+
faction: allies
|
| 21 |
+
enemy:
|
| 22 |
+
faction: soviet
|
| 23 |
+
tools:
|
| 24 |
+
- harvest
|
| 25 |
+
- move_units
|
| 26 |
+
- build
|
| 27 |
+
- stop
|
| 28 |
+
planning: true
|
| 29 |
+
termination:
|
| 30 |
+
max_ticks: 40000
|
| 31 |
+
actors:
|
| 32 |
+
- type: proc
|
| 33 |
+
owner: agent
|
| 34 |
+
position:
|
| 35 |
+
- 12
|
| 36 |
+
- 18
|
| 37 |
+
- type: harv
|
| 38 |
+
owner: agent
|
| 39 |
+
position:
|
| 40 |
+
- 14
|
| 41 |
+
- 18
|
| 42 |
+
- type: mine
|
| 43 |
+
owner: neutral
|
| 44 |
+
position:
|
| 45 |
+
- 22
|
| 46 |
+
- 18
|
| 47 |
+
- type: jeep
|
| 48 |
+
owner: agent
|
| 49 |
+
position:
|
| 50 |
+
- 8
|
| 51 |
+
- 16
|
| 52 |
+
|
| 53 |
+
levels:
|
| 54 |
+
easy:
|
| 55 |
+
description: >
|
| 56 |
+
Loose clock — just start harvesting promptly; idling fails the
|
| 57 |
+
economy-value target.
|
| 58 |
+
win_condition:
|
| 59 |
+
all_of:
|
| 60 |
+
- economy_value_gte: 1000
|
| 61 |
+
- within_ticks: 7000
|
| 62 |
+
max_turns: 60
|
| 63 |
+
medium:
|
| 64 |
+
description: >
|
| 65 |
+
Tighter clock and a higher target — the harvester must stay
|
| 66 |
+
productive (not lost / not idle).
|
| 67 |
+
win_condition:
|
| 68 |
+
all_of:
|
| 69 |
+
- economy_value_gte: 2500
|
| 70 |
+
- harvesters_gte: 1
|
| 71 |
+
- within_ticks: 6000
|
| 72 |
+
max_turns: 60
|
| 73 |
+
hard:
|
| 74 |
+
description: >
|
| 75 |
+
High target, short clock — a single harvester cannot reach it;
|
| 76 |
+
must expand harvesting capacity.
|
| 77 |
+
win_condition:
|
| 78 |
+
all_of:
|
| 79 |
+
- economy_value_gte: 3500
|
| 80 |
+
- harvesters_gte: 2
|
| 81 |
+
- within_ticks: 5000
|
| 82 |
+
max_turns: 70
|
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Catalog C13 — harvest economy, full loop on Rust (the user's
|
| 2 |
+
economy families): earn economic value by harvesting, scored by
|
| 3 |
+
economy_value; storage capacity comes from refineries/silos.
|
| 4 |
+
Closes task #14 end-to-end on the bench side."""
|
| 5 |
+
|
| 6 |
+
from __future__ import annotations
|
| 7 |
+
|
| 8 |
+
import pytest
|
| 9 |
+
|
| 10 |
+
pytest.importorskip("openra_train", reason="Rust env wheel not installed")
|
| 11 |
+
|
| 12 |
+
from openra_bench.eval_core import run_level
|
| 13 |
+
from openra_bench.scenarios import load_pack
|
| 14 |
+
from openra_bench.scenarios.loader import PACKS_DIR, compile_level
|
| 15 |
+
from openra_bench.scoring import score_episode
|
| 16 |
+
|
| 17 |
+
TIMEBOX = PACKS_DIR / "economy-harvest-timebox.yaml"
|
| 18 |
+
INVEST = PACKS_DIR / "economy-harvest-investment.yaml"
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def harvester(render_state, Command):
|
| 22 |
+
ids = [str(u["id"]) for u in render_state.get("units_summary", [])]
|
| 23 |
+
return [Command.harvest(ids, 22, 18)] if ids else [Command.observe()]
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def test_timebox_earns_economy_value_and_scores():
|
| 27 |
+
c = compile_level(load_pack(TIMEBOX), "easy")
|
| 28 |
+
assert c.map_supported
|
| 29 |
+
res = run_level(c, harvester, seed=1)
|
| 30 |
+
|
| 31 |
+
# Harvest income actually accrued (start cash 200).
|
| 32 |
+
ev = res.signals.cash + res.signals.resources
|
| 33 |
+
assert ev > 200, f"no harvest income (economy_value={ev})"
|
| 34 |
+
assert res.signals.resource_capacity == 2000, "proc storage cap"
|
| 35 |
+
assert res.outcome == "win", f"easy harvest target should be met: {res.outcome}"
|
| 36 |
+
|
| 37 |
+
sc = score_episode(c, res)
|
| 38 |
+
assert sc.outcome == "win" and 0.0 <= sc.composite <= 1.0
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def test_timebox_is_deterministic():
|
| 42 |
+
c = compile_level(load_pack(TIMEBOX), "easy")
|
| 43 |
+
a = run_level(c, harvester, seed=4)
|
| 44 |
+
b = run_level(c, harvester, seed=4)
|
| 45 |
+
assert (a.outcome, a.turns, a.signals.cash, a.signals.resources) == (
|
| 46 |
+
b.outcome,
|
| 47 |
+
b.turns,
|
| 48 |
+
b.signals.cash,
|
| 49 |
+
b.signals.resources,
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
@pytest.mark.parametrize("level", ["easy", "medium", "hard"])
|
| 54 |
+
def test_investment_runs_and_scores_within_constraints(level):
|
| 55 |
+
"""Investment/storage decision needs richer play than a scripted
|
| 56 |
+
harvester; assert it executes within its declared constraints and
|
| 57 |
+
scores deterministically (contributor loop robust even on a loss)."""
|
| 58 |
+
c = compile_level(load_pack(INVEST), level)
|
| 59 |
+
a = run_level(c, harvester, seed=3)
|
| 60 |
+
b = run_level(c, harvester, seed=3)
|
| 61 |
+
assert a.outcome in {"win", "draw", "loss"}
|
| 62 |
+
assert (a.outcome, a.turns) == (b.outcome, b.turns), "non-deterministic"
|
| 63 |
+
assert a.signals.resource_capacity >= 2000 # proc present from the start
|
| 64 |
+
sc = score_episode(c, a)
|
| 65 |
+
assert 0.0 <= sc.composite <= 1.0
|