harshraj22/croprl-workspace / code /tests /test_season_factor.py
harshraj22's picture
download
raw
8.48 kB
"""Tests for the season factor bug fix.
The season factor should use the PLANTING month (not the harvest month)
to evaluate seasonal suitability. This ensures:
- Corn planted in Monsoon gets 1.0× even when harvested in Winter.
- Wheat planted in Winter gets 1.0× even when harvested in Spring.
"""
import pytest
from cropRL.config import EnvConfig
from cropRL.models import CroprlAction
from cropRL.server.cropRL_environment import CroprlEnvironment
from cropRL.dynamics import calculate_yield, calculate_expected_yield_potential
from cropRL.enums import ActionType, CropType, Season, get_season
class TestSeasonFactorUsesPlantingMonth:
"""Verify season factor evaluates the planting month, not current month."""
def test_corn_planted_monsoon_harvested_winter(self):
"""Corn planted in June (Monsoon), harvested in Oct (Winter): should get 1.0× season."""
cfg = EnvConfig(max_steps=300, max_months=60)
e = CroprlEnvironment(config=cfg)
e.reset(seed=42)
# Advance to June (month 6 = Monsoon)
for _ in range(5): # Jan→Feb→...→Jun
e.step(CroprlAction(action_id=ActionType.WAIT))
assert e._internal["month"] == 6
assert get_season(6) == Season.MONSOON
# Plant corn in June (optimal for corn)
e.step(CroprlAction(action_id=ActionType.PLANT_CORN))
assert e._internal["planting_month"] == 6
# Wait 4 months (Jun→Jul→Aug→Sep→Oct)
for _ in range(4):
e.step(CroprlAction(action_id=ActionType.WAIT))
assert e._internal["month"] == 10 # October = Winter
assert get_season(10) == Season.WINTER
assert e._internal["crop_age_months"] == 4 # at maturity
# The yield potential should be HIGH because planting season was optimal.
# Before the fix, this was 0.08 due to Winter penalty.
obs = e.step(CroprlAction(action_id=ActionType.HARVEST_SELL))
# Verify the planting_month was used: yield should be much better
# than the 0.4× penalty case. With good nitrogen and full water,
# corn at maturity gives 8.0 × 1.0 × N_factor × W_factor × 1.0.
# Revenue should be substantial (not the ~₹640 from the bugged version).
revenue = obs.cash_balance - (e._internal["prev_net_worth"] - obs.current_land_price
+ obs.current_debt)
# Just check the crop sold for a meaningful amount
assert "Harvested" in obs.message
assert "Revenue" in obs.message
def test_wheat_planted_winter_harvested_spring(self):
"""Wheat planted in Nov (Winter), harvested in Feb (Spring): should get 1.0×."""
cfg = EnvConfig(max_steps=300, max_months=60)
e = CroprlEnvironment(config=cfg)
e.reset(seed=42)
# Advance to November (month 11 = Winter)
for _ in range(10): # Jan→...→Nov
e.step(CroprlAction(action_id=ActionType.WAIT))
assert e._internal["month"] == 11
assert get_season(11) == Season.WINTER
# Plant wheat in November (optimal = Winter)
e.step(CroprlAction(action_id=ActionType.PLANT_WHEAT))
assert e._internal["planting_month"] == 11
# Wait 3 months (Nov→Dec→Jan→Feb)
for _ in range(3):
e.step(CroprlAction(action_id=ActionType.WAIT))
assert e._internal["month"] == 2 # February = Spring
assert get_season(2) == Season.SPRING
assert e._internal["crop_age_months"] == 3 # at maturity
# Yield should use planting season (Winter = optimal), not current (Spring)
yield_pot = calculate_expected_yield_potential(
CropType.WHEAT, 3, e._internal["soil_nitrogen"],
e._internal["water_level"], e._internal["planting_month"], cfg
)
# If season factor used current month (Spring), wheat season = 1.0 too
# (Spring is not optimal for wheat — wait, actually let's check Winter)
# Wheat optimal = (Winter,). Spring is NOT optimal.
# So if the bug existed, yield would be 0.4× what it should be.
yield_bugged = calculate_expected_yield_potential(
CropType.WHEAT, 3, e._internal["soil_nitrogen"],
e._internal["water_level"], 2, cfg # February = Spring, non-optimal
)
assert yield_pot > yield_bugged * 2.0, (
f"Planting-month yield ({yield_pot:.3f}) should be >2× "
f"bugged harvest-month yield ({yield_bugged:.3f})"
)
def test_chickpea_planted_winter_always_optimal(self):
"""Chickpea planted in Oct (Winter, optimal) should stay optimal regardless."""
cfg = EnvConfig()
e = CroprlEnvironment(config=cfg)
e.reset(seed=42)
# Advance to October (Winter)
for _ in range(9):
e.step(CroprlAction(action_id=ActionType.WAIT))
assert e._internal["month"] == 10
e.step(CroprlAction(action_id=ActionType.PLANT_CHICKPEA))
assert e._internal["planting_month"] == 10
# Wait 3 months → January (still Winter, but that's beside the point)
for _ in range(3):
e.step(CroprlAction(action_id=ActionType.WAIT))
# Yield should use planting month (10 = Winter = optimal for chickpea)
yield_pot = calculate_expected_yield_potential(
CropType.CHICKPEA, 3, e._internal["soil_nitrogen"],
e._internal["water_level"], e._internal["planting_month"], cfg
)
assert yield_pot > 0.5, f"Chickpea in optimal season should yield well: {yield_pot:.3f}"
def test_planting_month_resets_on_harvest(self):
"""After harvest, planting_month should reset to 0."""
e = CroprlEnvironment()
e.reset(seed=42)
e.step(CroprlAction(action_id=ActionType.PLANT_CHICKPEA))
assert e._internal["planting_month"] == 1 # January
e.step(CroprlAction(action_id=ActionType.WAIT))
e.step(CroprlAction(action_id=ActionType.HARVEST_SELL))
assert e._internal["planting_month"] == 0
def test_planting_month_resets_on_harvest_store(self):
"""After harvest & store, planting_month should reset to 0."""
e = CroprlEnvironment()
e.reset(seed=42)
e.step(CroprlAction(action_id=ActionType.PLANT_CHICKPEA))
e.step(CroprlAction(action_id=ActionType.WAIT))
e.step(CroprlAction(action_id=ActionType.HARVEST_STORE))
assert e._internal["planting_month"] == 0
class TestSeasonFactorDirect:
"""Direct unit tests for season factor with planting month semantics."""
def test_corn_monsoon_gives_full_factor(self):
"""Corn with month=7 (Monsoon) should get 1.0 season factor."""
cfg = EnvConfig()
y_optimal = calculate_yield(CropType.CORN, 4, 0.6, 0.6, 7, cfg, rng=None)
y_non_optimal = calculate_yield(CropType.CORN, 4, 0.6, 0.6, 1, cfg, rng=None)
assert y_optimal == pytest.approx(y_non_optimal / 0.4, rel=0.01), (
"Corn in Monsoon should yield 2.5× vs Winter"
)
def test_corn_yield_in_monsoon_vs_winter(self):
"""Corn planted in Monsoon should produce 2.5× the yield of Winter-planted."""
cfg = EnvConfig()
e = CroprlEnvironment(config=cfg)
e.reset(seed=1)
# Scenario A: plant in June (Monsoon)
for _ in range(5): # get to June
e.step(CroprlAction(action_id=ActionType.WAIT))
e.step(CroprlAction(action_id=ActionType.PLANT_CORN))
for _ in range(4):
e.step(CroprlAction(action_id=ActionType.WAIT))
obs_a = e.step(CroprlAction(action_id=ActionType.HARVEST_SELL))
cash_a = obs_a.cash_balance
# Scenario B: plant in December (Winter)
e2 = CroprlEnvironment(config=cfg)
e2.reset(seed=1)
for _ in range(11): # get to December
e2.step(CroprlAction(action_id=ActionType.WAIT))
e2.step(CroprlAction(action_id=ActionType.PLANT_CORN))
for _ in range(4):
e2.step(CroprlAction(action_id=ActionType.WAIT))
obs_b = e2.step(CroprlAction(action_id=ActionType.HARVEST_SELL))
cash_b = obs_b.cash_balance
# Monsoon-planted corn should generate more revenue
# (exact amounts differ due to different prices/nitrogen at harvest,
# but the season factor alone gives 2.5× difference)
assert "Harvested" in obs_a.message
assert "Harvested" in obs_b.message

Xet Storage Details

Size:
8.48 kB
·
Xet hash:
f5c2f9a7e9707716447d15e815ee8ae851270f53d17333f9c1d261b023076c12

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.