Buckets:
| """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.