DarkyCodez/precision-ag-env
0
1class GreenhouseEnv:
2 """
3 Agricultural environment simulator with continuous state variables.
4
5 State variables:
6 - soil_moisture: 0.0-1.0
7 - nitrogen_level: 0.0-1.0
8 - crop_health: 0.0-1.0
9 - turn_count: int
10
11 Actions:
12 - 0: Do Nothing
13 - 1: Irrigate (+0.2 moisture)
14 - 2: Fertilize (+0.2 nitrogen)
15 - 3: Harvest
16 """
17
18 def __init__(self):
19 """Initialize the greenhouse environment."""
20 # State variables
21 self.soil_moisture = 0.5
22 self.nitrogen_level = 0.5
23 self.crop_health = 0.5
24 self.turn_count = 0
25
26 # Action space: 4 discrete actions
27 self.action_space_n = 4
28
29 def step(self, action):
30 """
31 Execute one step of the environment.
32
33 Args:
34 action: int, one of [0, 1, 2, 3]
35 0: Do Nothing
36 1: Irrigate
37 2: Fertilize
38 3: Harvest
39
40 Returns:
41 reward: float strictly between 0.0 and 1.0
42 """
43 # Increment turn counter
44 self.turn_count += 1
45
46 # Apply natural decay every turn
47 self.soil_moisture -= 0.05
48 self.nitrogen_level -= 0.02
49
50 # Clamp values to valid range [0.0, 1.0]
51 self.soil_moisture = max(0.0, min(1.0, self.soil_moisture))
52 self.nitrogen_level = max(0.0, min(1.0, self.nitrogen_level))
53
54 # Apply action
55 if action == 0: # Do Nothing
56 pass
57 elif action == 1: # Irrigate
58 self.soil_moisture = max(0.0, min(1.0, self.soil_moisture + 0.2))
59 elif action == 2: # Fertilize
60 self.nitrogen_level = max(0.0, min(1.0, self.nitrogen_level + 0.2))
61 elif action == 3: # Harvest
62 # Harvest action doesn't modify state, but affects reward
63 pass
64
65 # Reduce crop health if moisture is outside optimal range [0.4, 0.6]
66 if self.soil_moisture < 0.4 or self.soil_moisture > 0.6:
67 self.crop_health -= 0.05
68
69 # Clamp crop health to valid range
70 self.crop_health = max(0.0, min(1.0, self.crop_health))
71
72 # Calculate reward (strictly between 0.0 and 1.0)
73 reward = self._calculate_reward(action)
74
75 return reward
76
77 def _calculate_reward(self, action):
78 """
79 Calculate reward based on current state and action.
80
81 Rewards:
82 - 0.05 for maintaining optimal soil moisture (0.4-0.6)
83 - 0.02 for maintaining adequate nitrogen (>0.3)
84 - 0.03 for good crop health (>0.5)
85 - Up to 0.8 for harvesting with high crop health
86
87 Returns:
88 reward: float strictly between 0.0 and 1.0
89 """
90 reward = 0.0
91
92 # Partial reward for maintaining optimal moisture (0.4-0.6)
93 if 0.4 <= self.soil_moisture <= 0.6:
94 reward += 0.05
95
96 # Reward for maintaining adequate nitrogen levels
97 if self.nitrogen_level > 0.3:
98 reward += 0.02
99
100 # Reward for good crop health
101 if self.crop_health > 0.5:
102 reward += 0.03
103
104 # Large reward for harvesting when crop health is high
105 if action == 3: # Harvest action
106 reward += self.crop_health * 0.8 # Scales from 0 to 0.8
107
108 # Ensure reward is strictly between 0.0 and 1.0
109 # Use tight bounds to guarantee strictly between (not equal to) endpoints
110 reward = max(0.001, min(0.999, reward))
111
112 return reward
113
114 def reset(self):
115 """Reset the environment to initial state."""
116 self.soil_moisture = 0.5
117 self.nitrogen_level = 0.5
118 self.crop_health = 0.5
119 self.turn_count = 0
120
121 def get_state(self):
122 """Return the current state as a dictionary."""
123 return {
124 "soil_moisture": self.soil_moisture,
125 "nitrogen_level": self.nitrogen_level,
126 "crop_health": self.crop_health,
127 "turn_count": self.turn_count
128 }
129 