goapauto 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- goapauto-0.1.0/PKG-INFO +291 -0
- goapauto-0.1.0/README.md +284 -0
- goapauto-0.1.0/goapauto.egg-info/PKG-INFO +291 -0
- goapauto-0.1.0/goapauto.egg-info/SOURCES.txt +11 -0
- goapauto-0.1.0/goapauto.egg-info/dependency_links.txt +1 -0
- goapauto-0.1.0/goapauto.egg-info/top_level.txt +1 -0
- goapauto-0.1.0/models/actions.py +231 -0
- goapauto-0.1.0/models/goal.py +165 -0
- goapauto-0.1.0/models/goap_planner.py +297 -0
- goapauto-0.1.0/models/node.py +213 -0
- goapauto-0.1.0/models/worldstate.py +273 -0
- goapauto-0.1.0/pyproject.toml +7 -0
- goapauto-0.1.0/setup.cfg +4 -0
goapauto-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: goapauto
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A flexible Goal-Oriented Action Planning (GOAP) system for game AI and planning problems, featuring A* search, efficient state management, and modular design.
|
|
5
|
+
Requires-Python: >=3.13
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# GOAP Planner
|
|
9
|
+
|
|
10
|
+
A flexible Goal-Oriented Action Planning (GOAP) system designed for game AI, but applicable to any planning problem. This implementation provides a clean, modular architecture for defining actions, goals, and world states to create intelligent AI behaviors.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- 🎯 Goal-oriented planning with customizable actions and preconditions
|
|
15
|
+
- 🧠A* search algorithm for optimal plan generation
|
|
16
|
+
- âš¡ Efficient state management and caching
|
|
17
|
+
- 📊 Built-in planning statistics and debugging
|
|
18
|
+
- 🧩 Modular design for easy extension
|
|
19
|
+
- 🎮 Example implementations included
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### From PyPI
|
|
24
|
+
```bash
|
|
25
|
+
uv add goapauto
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### From Source
|
|
29
|
+
1. Clone the repository:
|
|
30
|
+
```bash
|
|
31
|
+
git clone https://github.com/IAmNo1Special/goapauto.git
|
|
32
|
+
cd goapauto
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from models.goap_planner import Planner, PlanResult
|
|
39
|
+
from models.goal import Goal
|
|
40
|
+
from models.actions import Action, Actions
|
|
41
|
+
from models.worldstate import WorldState
|
|
42
|
+
|
|
43
|
+
# Define your initial state using WorldState for better type safety and validation
|
|
44
|
+
initial_state = WorldState({
|
|
45
|
+
'is_open': False,
|
|
46
|
+
'is_focused': False,
|
|
47
|
+
'has_key': True,
|
|
48
|
+
'energy': 100
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
# Create actions collection
|
|
52
|
+
actions = Actions()
|
|
53
|
+
|
|
54
|
+
# Add actions with proper typing and validation
|
|
55
|
+
try:
|
|
56
|
+
actions.add_action(
|
|
57
|
+
name="open_door",
|
|
58
|
+
preconditions={'is_open': False, 'has_key': True},
|
|
59
|
+
effects={'is_open': True},
|
|
60
|
+
cost=1.0
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
actions.add_action(
|
|
64
|
+
name="consume_energy",
|
|
65
|
+
preconditions={'energy': (lambda x: x > 0)},
|
|
66
|
+
effects={'energy': (lambda x: x - 10)},
|
|
67
|
+
cost=1.0
|
|
68
|
+
)
|
|
69
|
+
except (ValueError, TypeError) as e:
|
|
70
|
+
print(f"Error creating actions: {e}")
|
|
71
|
+
raise
|
|
72
|
+
|
|
73
|
+
# Create a goal with validation
|
|
74
|
+
try:
|
|
75
|
+
goal = Goal(
|
|
76
|
+
name="Open Door",
|
|
77
|
+
priority=1,
|
|
78
|
+
target_state={'is_open': True}
|
|
79
|
+
)
|
|
80
|
+
except ValueError as e:
|
|
81
|
+
print(f"Invalid goal: {e}")
|
|
82
|
+
raise
|
|
83
|
+
|
|
84
|
+
# Create and run the planner
|
|
85
|
+
planner = Planner(actions)
|
|
86
|
+
plan, message = planner.generate_plan(initial_state, goal)
|
|
87
|
+
|
|
88
|
+
# The planner will automatically display the plan and statistics
|
|
89
|
+
# You can access the plan and execute it if needed
|
|
90
|
+
if plan is not None:
|
|
91
|
+
current_state = initial_state.copy()
|
|
92
|
+
for i, action_name in enumerate(plan, 1):
|
|
93
|
+
action = actions.get_action(action_name)
|
|
94
|
+
print(f"\nStep {i}: {action_name}")
|
|
95
|
+
current_state = action.apply(current_state)
|
|
96
|
+
print(f"New state: {current_state}")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Project Structure
|
|
100
|
+
|
|
101
|
+
- `/models` - Core GOAP implementation
|
|
102
|
+
- `goap_planner.py` - Main planner logic
|
|
103
|
+
- `goal.py` - Goal definition and management
|
|
104
|
+
- `actions.py` - Action definitions
|
|
105
|
+
- `worldstate.py` - World state management
|
|
106
|
+
- `node.py` - Search node implementation
|
|
107
|
+
|
|
108
|
+
- `/examples` - Example implementations
|
|
109
|
+
- `example1.py` - Basic usage example
|
|
110
|
+
- `example2.py` - Advanced scenario
|
|
111
|
+
|
|
112
|
+
## Advanced Usage
|
|
113
|
+
|
|
114
|
+
### Working with WorldState
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from models.worldstate import WorldState
|
|
118
|
+
|
|
119
|
+
# Create a state with validation
|
|
120
|
+
state = WorldState({
|
|
121
|
+
'health': 100,
|
|
122
|
+
'has_weapon': True,
|
|
123
|
+
'enemy_visible': False
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
# Safe state updates
|
|
127
|
+
state = state.merge({'health': 80}) # Returns new state
|
|
128
|
+
state = state.copy() # Create a deep copy
|
|
129
|
+
|
|
130
|
+
# Check conditions
|
|
131
|
+
if state.matches({'health': (lambda x: x > 50)}):
|
|
132
|
+
print("Health is above 50%")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Creating Custom Actions
|
|
136
|
+
|
|
137
|
+
Define actions using the `Action` class for better type safety and validation:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from models.actions import Action, Actions
|
|
141
|
+
|
|
142
|
+
# Create a single action
|
|
143
|
+
open_door = Action(
|
|
144
|
+
name="open_door",
|
|
145
|
+
preconditions={'door_locked': False},
|
|
146
|
+
effects={'door_open': True},
|
|
147
|
+
cost=1.0
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Or use the Actions collection to manage multiple actions
|
|
151
|
+
actions = Actions()
|
|
152
|
+
actions.add_action(
|
|
153
|
+
name="unlock_door",
|
|
154
|
+
preconditions={'has_key': True},
|
|
155
|
+
effects={'door_locked': False},
|
|
156
|
+
cost=1.5
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Add multiple actions at once
|
|
160
|
+
actions.add_actions([
|
|
161
|
+
("pick_up_item", {"near_item": True}, {"has_item": True}, 1.0),
|
|
162
|
+
("use_item", {"has_item": True}, {"effect_applied": True}, 1.0)
|
|
163
|
+
])
|
|
164
|
+
|
|
165
|
+
# Check if an action is applicable in a given state
|
|
166
|
+
if open_door.is_applicable(current_state):
|
|
167
|
+
new_state = open_door.apply(current_state)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Defininig Goals with target states and priorities
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from models.goal import Goal
|
|
174
|
+
|
|
175
|
+
goal = Goal(
|
|
176
|
+
name="Achieve Objective",
|
|
177
|
+
priority=1,
|
|
178
|
+
target_state={
|
|
179
|
+
'objective_complete': True
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Defining Goals with Conditions
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from models.goal import Goal
|
|
188
|
+
|
|
189
|
+
# Simple goal
|
|
190
|
+
goal = Goal(
|
|
191
|
+
name="Defeat Enemy",
|
|
192
|
+
priority=1,
|
|
193
|
+
target_state={
|
|
194
|
+
'enemy_defeated': True,
|
|
195
|
+
'health': (lambda x: x > 0) # Must have health remaining
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Goal with custom validation
|
|
200
|
+
try:
|
|
201
|
+
goal.validate()
|
|
202
|
+
except ValueError as e:
|
|
203
|
+
print(f"Invalid goal: {e}")
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Working with Plan Results
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
# After generating a plan
|
|
210
|
+
if plan is not None:
|
|
211
|
+
print(f"Plan found with {len(plan)} actions")
|
|
212
|
+
print(f"Total cost: {planner.stats.total_cost}")
|
|
213
|
+
print(f"Nodes expanded: {planner.stats.nodes_expanded}")
|
|
214
|
+
print(f"Planning time: {planner.stats.execution_time:.4f}s")
|
|
215
|
+
|
|
216
|
+
# Access individual plan steps
|
|
217
|
+
for i, action_name in enumerate(plan, 1):
|
|
218
|
+
action = actions.get_action(action_name)
|
|
219
|
+
print(f"{i}. {action_name} (cost: {action.cost})")
|
|
220
|
+
|
|
221
|
+
# Execute the plan and track state changes
|
|
222
|
+
current_state = initial_state.copy()
|
|
223
|
+
for i, action_name in enumerate(plan, 1):
|
|
224
|
+
action = actions.get_action(action_name)
|
|
225
|
+
print(f"\nStep {i}: {action_name}")
|
|
226
|
+
current_state = action.apply(current_state)
|
|
227
|
+
print(f"State after action: {current_state}")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Custom Heuristics
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from models.goap_planner import Planner
|
|
234
|
+
|
|
235
|
+
def custom_heuristic(state, goal_state):
|
|
236
|
+
"""Custom heuristic function for A* search."""
|
|
237
|
+
# Implement your heuristic logic here
|
|
238
|
+
# Lower values mean closer to goal
|
|
239
|
+
return 0 # Default to Dijkstra's algorithm
|
|
240
|
+
|
|
241
|
+
planner = Planner(actions, heuristic=custom_heuristic)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Performance Tips
|
|
245
|
+
|
|
246
|
+
1. **State Design**:
|
|
247
|
+
- Keep state keys simple and consistent
|
|
248
|
+
- Use immutable state objects for better caching
|
|
249
|
+
|
|
250
|
+
2. **Action Design**:
|
|
251
|
+
- Make preconditions as specific as possible
|
|
252
|
+
- Use lambda functions for dynamic conditions
|
|
253
|
+
- Keep effects minimal and focused
|
|
254
|
+
|
|
255
|
+
3. **Debugging**:
|
|
256
|
+
- Enable debug logging for detailed planning info
|
|
257
|
+
- Check PlanResult.stats for performance metrics
|
|
258
|
+
- Use state validation to catch issues early
|
|
259
|
+
|
|
260
|
+
## Troubleshooting
|
|
261
|
+
|
|
262
|
+
### Common Issues
|
|
263
|
+
|
|
264
|
+
1. **No Plan Found**
|
|
265
|
+
- Check if actions' preconditions are met
|
|
266
|
+
- Verify the goal is achievable
|
|
267
|
+
- Increase max_iterations if needed
|
|
268
|
+
|
|
269
|
+
2. **Performance Problems**
|
|
270
|
+
- Check for infinite state spaces
|
|
271
|
+
- Add more specific preconditions
|
|
272
|
+
- Consider using a custom heuristic
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
## Running Examples
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
# Run basic example
|
|
279
|
+
python examples/example1.py
|
|
280
|
+
|
|
281
|
+
# Run advanced example
|
|
282
|
+
python examples/example2.py
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Contributing
|
|
286
|
+
|
|
287
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
288
|
+
|
|
289
|
+
## License
|
|
290
|
+
|
|
291
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
goapauto-0.1.0/README.md
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# GOAP Planner
|
|
2
|
+
|
|
3
|
+
A flexible Goal-Oriented Action Planning (GOAP) system designed for game AI, but applicable to any planning problem. This implementation provides a clean, modular architecture for defining actions, goals, and world states to create intelligent AI behaviors.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎯 Goal-oriented planning with customizable actions and preconditions
|
|
8
|
+
- 🧠A* search algorithm for optimal plan generation
|
|
9
|
+
- âš¡ Efficient state management and caching
|
|
10
|
+
- 📊 Built-in planning statistics and debugging
|
|
11
|
+
- 🧩 Modular design for easy extension
|
|
12
|
+
- 🎮 Example implementations included
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### From PyPI
|
|
17
|
+
```bash
|
|
18
|
+
uv add goapauto
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### From Source
|
|
22
|
+
1. Clone the repository:
|
|
23
|
+
```bash
|
|
24
|
+
git clone https://github.com/IAmNo1Special/goapauto.git
|
|
25
|
+
cd goapauto
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from models.goap_planner import Planner, PlanResult
|
|
32
|
+
from models.goal import Goal
|
|
33
|
+
from models.actions import Action, Actions
|
|
34
|
+
from models.worldstate import WorldState
|
|
35
|
+
|
|
36
|
+
# Define your initial state using WorldState for better type safety and validation
|
|
37
|
+
initial_state = WorldState({
|
|
38
|
+
'is_open': False,
|
|
39
|
+
'is_focused': False,
|
|
40
|
+
'has_key': True,
|
|
41
|
+
'energy': 100
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
# Create actions collection
|
|
45
|
+
actions = Actions()
|
|
46
|
+
|
|
47
|
+
# Add actions with proper typing and validation
|
|
48
|
+
try:
|
|
49
|
+
actions.add_action(
|
|
50
|
+
name="open_door",
|
|
51
|
+
preconditions={'is_open': False, 'has_key': True},
|
|
52
|
+
effects={'is_open': True},
|
|
53
|
+
cost=1.0
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
actions.add_action(
|
|
57
|
+
name="consume_energy",
|
|
58
|
+
preconditions={'energy': (lambda x: x > 0)},
|
|
59
|
+
effects={'energy': (lambda x: x - 10)},
|
|
60
|
+
cost=1.0
|
|
61
|
+
)
|
|
62
|
+
except (ValueError, TypeError) as e:
|
|
63
|
+
print(f"Error creating actions: {e}")
|
|
64
|
+
raise
|
|
65
|
+
|
|
66
|
+
# Create a goal with validation
|
|
67
|
+
try:
|
|
68
|
+
goal = Goal(
|
|
69
|
+
name="Open Door",
|
|
70
|
+
priority=1,
|
|
71
|
+
target_state={'is_open': True}
|
|
72
|
+
)
|
|
73
|
+
except ValueError as e:
|
|
74
|
+
print(f"Invalid goal: {e}")
|
|
75
|
+
raise
|
|
76
|
+
|
|
77
|
+
# Create and run the planner
|
|
78
|
+
planner = Planner(actions)
|
|
79
|
+
plan, message = planner.generate_plan(initial_state, goal)
|
|
80
|
+
|
|
81
|
+
# The planner will automatically display the plan and statistics
|
|
82
|
+
# You can access the plan and execute it if needed
|
|
83
|
+
if plan is not None:
|
|
84
|
+
current_state = initial_state.copy()
|
|
85
|
+
for i, action_name in enumerate(plan, 1):
|
|
86
|
+
action = actions.get_action(action_name)
|
|
87
|
+
print(f"\nStep {i}: {action_name}")
|
|
88
|
+
current_state = action.apply(current_state)
|
|
89
|
+
print(f"New state: {current_state}")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Project Structure
|
|
93
|
+
|
|
94
|
+
- `/models` - Core GOAP implementation
|
|
95
|
+
- `goap_planner.py` - Main planner logic
|
|
96
|
+
- `goal.py` - Goal definition and management
|
|
97
|
+
- `actions.py` - Action definitions
|
|
98
|
+
- `worldstate.py` - World state management
|
|
99
|
+
- `node.py` - Search node implementation
|
|
100
|
+
|
|
101
|
+
- `/examples` - Example implementations
|
|
102
|
+
- `example1.py` - Basic usage example
|
|
103
|
+
- `example2.py` - Advanced scenario
|
|
104
|
+
|
|
105
|
+
## Advanced Usage
|
|
106
|
+
|
|
107
|
+
### Working with WorldState
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from models.worldstate import WorldState
|
|
111
|
+
|
|
112
|
+
# Create a state with validation
|
|
113
|
+
state = WorldState({
|
|
114
|
+
'health': 100,
|
|
115
|
+
'has_weapon': True,
|
|
116
|
+
'enemy_visible': False
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
# Safe state updates
|
|
120
|
+
state = state.merge({'health': 80}) # Returns new state
|
|
121
|
+
state = state.copy() # Create a deep copy
|
|
122
|
+
|
|
123
|
+
# Check conditions
|
|
124
|
+
if state.matches({'health': (lambda x: x > 50)}):
|
|
125
|
+
print("Health is above 50%")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Creating Custom Actions
|
|
129
|
+
|
|
130
|
+
Define actions using the `Action` class for better type safety and validation:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from models.actions import Action, Actions
|
|
134
|
+
|
|
135
|
+
# Create a single action
|
|
136
|
+
open_door = Action(
|
|
137
|
+
name="open_door",
|
|
138
|
+
preconditions={'door_locked': False},
|
|
139
|
+
effects={'door_open': True},
|
|
140
|
+
cost=1.0
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Or use the Actions collection to manage multiple actions
|
|
144
|
+
actions = Actions()
|
|
145
|
+
actions.add_action(
|
|
146
|
+
name="unlock_door",
|
|
147
|
+
preconditions={'has_key': True},
|
|
148
|
+
effects={'door_locked': False},
|
|
149
|
+
cost=1.5
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Add multiple actions at once
|
|
153
|
+
actions.add_actions([
|
|
154
|
+
("pick_up_item", {"near_item": True}, {"has_item": True}, 1.0),
|
|
155
|
+
("use_item", {"has_item": True}, {"effect_applied": True}, 1.0)
|
|
156
|
+
])
|
|
157
|
+
|
|
158
|
+
# Check if an action is applicable in a given state
|
|
159
|
+
if open_door.is_applicable(current_state):
|
|
160
|
+
new_state = open_door.apply(current_state)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Defininig Goals with target states and priorities
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from models.goal import Goal
|
|
167
|
+
|
|
168
|
+
goal = Goal(
|
|
169
|
+
name="Achieve Objective",
|
|
170
|
+
priority=1,
|
|
171
|
+
target_state={
|
|
172
|
+
'objective_complete': True
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Defining Goals with Conditions
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from models.goal import Goal
|
|
181
|
+
|
|
182
|
+
# Simple goal
|
|
183
|
+
goal = Goal(
|
|
184
|
+
name="Defeat Enemy",
|
|
185
|
+
priority=1,
|
|
186
|
+
target_state={
|
|
187
|
+
'enemy_defeated': True,
|
|
188
|
+
'health': (lambda x: x > 0) # Must have health remaining
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Goal with custom validation
|
|
193
|
+
try:
|
|
194
|
+
goal.validate()
|
|
195
|
+
except ValueError as e:
|
|
196
|
+
print(f"Invalid goal: {e}")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Working with Plan Results
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# After generating a plan
|
|
203
|
+
if plan is not None:
|
|
204
|
+
print(f"Plan found with {len(plan)} actions")
|
|
205
|
+
print(f"Total cost: {planner.stats.total_cost}")
|
|
206
|
+
print(f"Nodes expanded: {planner.stats.nodes_expanded}")
|
|
207
|
+
print(f"Planning time: {planner.stats.execution_time:.4f}s")
|
|
208
|
+
|
|
209
|
+
# Access individual plan steps
|
|
210
|
+
for i, action_name in enumerate(plan, 1):
|
|
211
|
+
action = actions.get_action(action_name)
|
|
212
|
+
print(f"{i}. {action_name} (cost: {action.cost})")
|
|
213
|
+
|
|
214
|
+
# Execute the plan and track state changes
|
|
215
|
+
current_state = initial_state.copy()
|
|
216
|
+
for i, action_name in enumerate(plan, 1):
|
|
217
|
+
action = actions.get_action(action_name)
|
|
218
|
+
print(f"\nStep {i}: {action_name}")
|
|
219
|
+
current_state = action.apply(current_state)
|
|
220
|
+
print(f"State after action: {current_state}")
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Custom Heuristics
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from models.goap_planner import Planner
|
|
227
|
+
|
|
228
|
+
def custom_heuristic(state, goal_state):
|
|
229
|
+
"""Custom heuristic function for A* search."""
|
|
230
|
+
# Implement your heuristic logic here
|
|
231
|
+
# Lower values mean closer to goal
|
|
232
|
+
return 0 # Default to Dijkstra's algorithm
|
|
233
|
+
|
|
234
|
+
planner = Planner(actions, heuristic=custom_heuristic)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Performance Tips
|
|
238
|
+
|
|
239
|
+
1. **State Design**:
|
|
240
|
+
- Keep state keys simple and consistent
|
|
241
|
+
- Use immutable state objects for better caching
|
|
242
|
+
|
|
243
|
+
2. **Action Design**:
|
|
244
|
+
- Make preconditions as specific as possible
|
|
245
|
+
- Use lambda functions for dynamic conditions
|
|
246
|
+
- Keep effects minimal and focused
|
|
247
|
+
|
|
248
|
+
3. **Debugging**:
|
|
249
|
+
- Enable debug logging for detailed planning info
|
|
250
|
+
- Check PlanResult.stats for performance metrics
|
|
251
|
+
- Use state validation to catch issues early
|
|
252
|
+
|
|
253
|
+
## Troubleshooting
|
|
254
|
+
|
|
255
|
+
### Common Issues
|
|
256
|
+
|
|
257
|
+
1. **No Plan Found**
|
|
258
|
+
- Check if actions' preconditions are met
|
|
259
|
+
- Verify the goal is achievable
|
|
260
|
+
- Increase max_iterations if needed
|
|
261
|
+
|
|
262
|
+
2. **Performance Problems**
|
|
263
|
+
- Check for infinite state spaces
|
|
264
|
+
- Add more specific preconditions
|
|
265
|
+
- Consider using a custom heuristic
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
## Running Examples
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# Run basic example
|
|
272
|
+
python examples/example1.py
|
|
273
|
+
|
|
274
|
+
# Run advanced example
|
|
275
|
+
python examples/example2.py
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Contributing
|
|
279
|
+
|
|
280
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|