yxc20098 commited on
Commit
7a25eb3
·
1 Parent(s): 1481b7f

Catalog C13: harvest-economy packs — closes #14 (user economy families)

Browse files

Now 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.

SCENARIO_CATALOG.md CHANGED
@@ -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 deferred to engine task **#14**
84
- (S0 ore source + S1 silo storage) — earn-rate allocation is not
85
- discriminating with zero income.
 
 
 
 
 
 
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
 
openra_bench/rust_adapter.py CHANGED
@@ -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": [
openra_bench/scenarios/packs/economy-harvest-investment.yaml ADDED
@@ -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
openra_bench/scenarios/packs/economy-harvest-timebox.yaml ADDED
@@ -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
tests/test_economy_harvest.py ADDED
@@ -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