Spaces:
Configuration error
Configuration error
EZTIME2025 commited on
Commit ·
c7f3125
1
Parent(s): 9ecc8c1
create tools, fix promts
Browse files- examples/ai_testing/my_games/current_session.txt +1 -1
- examples/ai_testing/my_games/session_20260109_020107/A/A.md +35 -0
- examples/ai_testing/my_games/session_20260109_020107/A/prompts/prompt_2.json +95 -0
- examples/ai_testing/my_games/session_20260109_020107/A/prompts/prompt_2.txt +88 -0
- examples/ai_testing/my_games/session_20260109_020107/A/responses/response_1.json +24 -0
- examples/ai_testing/my_games/session_20260109_020107/A/responses/response_2.json +25 -0
- examples/ai_testing/my_games/session_20260109_022638/A/A.md +51 -0
- examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_1.json +95 -0
- examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_1.txt +87 -0
- examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_2.json +95 -0
- examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_2.txt +88 -0
- examples/ai_testing/my_games/session_20260109_022638/A/responses/response_1.json +24 -0
- examples/ai_testing/my_games/session_20260109_022638/A/responses/response_2.json +25 -0
- examples/ai_testing/my_games/session_20260109_022638/session_metadata.json +5 -0
- examples/ai_testing/my_games/session_20260109_023135/A/A.md +55 -0
- examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_1.json +95 -0
- examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_1.txt +87 -0
- examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_2.json +107 -0
- examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_2.txt +100 -0
- examples/ai_testing/my_games/session_20260109_023135/A/responses/response_1.json +25 -0
- examples/ai_testing/my_games/session_20260109_023135/A/responses/response_2.json +25 -0
- examples/ai_testing/my_games/session_20260109_023135/chat_history.json +8 -0
- examples/ai_testing/my_games/session_20260109_023135/session_metadata.json +5 -0
- examples/example_agent_tools_integration.py +273 -0
- examples/test_agent_tools.py +222 -0
- examples/test_agent_tools_v2.py +301 -0
- pycatan/ai/AGENT_TOOLS_README.md +320 -0
- pycatan/ai/__init__.py +7 -0
- pycatan/ai/agent_tools.py +539 -0
- pycatan/ai/ai_manager.py +17 -1
- pycatan/ai/ai_user.py +32 -1
examples/ai_testing/my_games/current_session.txt
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
|
|
|
|
| 1 |
+
session_20260109_023135
|
examples/ai_testing/my_games/session_20260109_020107/A/A.md
CHANGED
|
@@ -18,3 +18,38 @@
|
|
| 18 |
### Prompt Sent
|
| 19 |
See: [prompt_1.json](prompts/prompt_1.json)
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
### Prompt Sent
|
| 19 |
See: [prompt_1.json](prompts/prompt_1.json)
|
| 20 |
|
| 21 |
+
### Response Received ✅
|
| 22 |
+
|
| 23 |
+
**Latency:** 30.71s | **Tokens:** 8668
|
| 24 |
+
|
| 25 |
+
**Thinking:** The game has just started, and it's my turn to place the first settlement. My strategy is to secure a diverse set of essential resources (Wood, Brick, Sheep, Wheat) with high-probability numbers (6, 8...
|
| 26 |
+
|
| 27 |
+
**Note to Self:** My first settlement is on Wood(11), Brick(6), Sheep(8). For the second settlement, I need to prioritize a good source of Wheat and ideally some Ore, or another strong Wood/Brick/Sheep combination if Wheat/Ore is weak elsewhere. Look for nodes with 5, 9, 4, 10, or 3, 11 for these resources.
|
| 28 |
+
|
| 29 |
+
**Says:** "Hello everyone! I'll kick things off by settling on a spot that gives me a good mix of Wood, Brick, and Sheep."
|
| 30 |
+
|
| 31 |
+
---
|
| 32 |
+
|
| 33 |
+
## Request #2 - 🎯 ACTIVE TURN
|
| 34 |
+
|
| 35 |
+
**Time:** 02:05:13
|
| 36 |
+
|
| 37 |
+
### What Happened
|
| 38 |
+
• Game 3a7a3455-7174-4727-b718-fef2f35d47f9 has started with 2 players!
|
| 39 |
+
• Turn 0: Player 0's turn begins.
|
| 40 |
+
• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT
|
| 41 |
+
|
| 42 |
+
### Prompt Sent
|
| 43 |
+
See: [prompt_2.json](prompts/prompt_2.json)
|
| 44 |
+
|
| 45 |
+
### Response Received ✅
|
| 46 |
+
|
| 47 |
+
**Latency:** 23.60s | **Tokens:** 6568
|
| 48 |
+
|
| 49 |
+
**Thinking:** My agent, 'A', has just placed its first settlement at Node 20. This node provides access to Wood (H[9]=W11), Brick (H[5]=B6), and Sheep (H[4]=S8), offering a solid foundation for early game developme...
|
| 50 |
+
|
| 51 |
+
**Note to Self:** My first settlement is at Node 20 (W11, B6, S8). My primary goal for the second settlement is Node 9 (W12, S8, ?3 port) to secure a strong resource base and early trade flexibility. I need to observe the opponent's moves carefully to see if they attempt to block my path or claim this key node/port.
|
| 52 |
+
|
| 53 |
+
**Says:** "Alright, setting up my first road! Aiming for a good connection here."
|
| 54 |
+
|
| 55 |
+
---
|
examples/ai_testing/my_games/session_20260109_020107/A/prompts/prompt_2.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 2,
|
| 3 |
+
"timestamp": "2026-01-09T02:05:13.555776",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"is_active_turn": true,
|
| 6 |
+
"prompt": {
|
| 7 |
+
"meta_data": {
|
| 8 |
+
"agent_name": "A",
|
| 9 |
+
"role": null
|
| 10 |
+
},
|
| 11 |
+
"task_context": {
|
| 12 |
+
"what_just_happened": "• Game 3a7a3455-7174-4727-b718-fef2f35d47f9 has started with 2 players!\n• Turn 0: Player 0's turn begins.\n• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT",
|
| 13 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 14 |
+
},
|
| 15 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[[20,\"A\",\"S\"]],\"rds\":[]},\"players\":{\"A\":{\"vp\":1,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 16 |
+
"constraints": {
|
| 17 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 18 |
+
"allowed_actions": [
|
| 19 |
+
{
|
| 20 |
+
"type": "place_starting_road",
|
| 21 |
+
"description": "Place your starting road",
|
| 22 |
+
"example_parameters": "{\"from\": X, \"to\": Y}"
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"type": "wait_for_response",
|
| 26 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 27 |
+
"example_parameters": "{}"
|
| 28 |
+
}
|
| 29 |
+
]
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
"response_schema": {
|
| 33 |
+
"type": "object",
|
| 34 |
+
"required": [
|
| 35 |
+
"internal_thinking",
|
| 36 |
+
"action"
|
| 37 |
+
],
|
| 38 |
+
"properties": {
|
| 39 |
+
"internal_thinking": {
|
| 40 |
+
"type": "string",
|
| 41 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 42 |
+
"minLength": 1000
|
| 43 |
+
},
|
| 44 |
+
"note_to_self": {
|
| 45 |
+
"type": "string",
|
| 46 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 47 |
+
"maxLength": 100
|
| 48 |
+
},
|
| 49 |
+
"say_outloud": {
|
| 50 |
+
"type": "string",
|
| 51 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 52 |
+
"maxLength": 100
|
| 53 |
+
},
|
| 54 |
+
"action": {
|
| 55 |
+
"type": "object",
|
| 56 |
+
"required": [
|
| 57 |
+
"type"
|
| 58 |
+
],
|
| 59 |
+
"properties": {
|
| 60 |
+
"type": {
|
| 61 |
+
"type": "string",
|
| 62 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 63 |
+
},
|
| 64 |
+
"parameters": {
|
| 65 |
+
"type": "string",
|
| 66 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 67 |
+
}
|
| 68 |
+
},
|
| 69 |
+
"propertyOrdering": [
|
| 70 |
+
"type",
|
| 71 |
+
"parameters"
|
| 72 |
+
]
|
| 73 |
+
}
|
| 74 |
+
},
|
| 75 |
+
"propertyOrdering": [
|
| 76 |
+
"internal_thinking",
|
| 77 |
+
"note_to_self",
|
| 78 |
+
"say_outloud",
|
| 79 |
+
"action"
|
| 80 |
+
]
|
| 81 |
+
},
|
| 82 |
+
"what_happened": "• Game 3a7a3455-7174-4727-b718-fef2f35d47f9 has started with 2 players!\n• Turn 0: Player 0's turn begins.\n• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT",
|
| 83 |
+
"allowed_actions": [
|
| 84 |
+
{
|
| 85 |
+
"type": "place_starting_road",
|
| 86 |
+
"description": "Place your starting road",
|
| 87 |
+
"example_parameters": "{\"from\": X, \"to\": Y}"
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"type": "wait_for_response",
|
| 91 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 92 |
+
"example_parameters": "{}"
|
| 93 |
+
}
|
| 94 |
+
]
|
| 95 |
+
}
|
examples/ai_testing/my_games/session_20260109_020107/A/prompts/prompt_2.txt
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
=== Prompt #2 for A ===
|
| 2 |
+
Timestamp: 2026-01-09T02:05:13.556665
|
| 3 |
+
Active Turn: True
|
| 4 |
+
|
| 5 |
+
--- What Happened ---
|
| 6 |
+
• Game 3a7a3455-7174-4727-b718-fef2f35d47f9 has started with 2 players!
|
| 7 |
+
• Turn 0: Player 0's turn begins.
|
| 8 |
+
• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT
|
| 9 |
+
|
| 10 |
+
--- Response Schema ---
|
| 11 |
+
{
|
| 12 |
+
"type": "object",
|
| 13 |
+
"required": [
|
| 14 |
+
"internal_thinking",
|
| 15 |
+
"action"
|
| 16 |
+
],
|
| 17 |
+
"properties": {
|
| 18 |
+
"internal_thinking": {
|
| 19 |
+
"type": "string",
|
| 20 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 21 |
+
"minLength": 1000
|
| 22 |
+
},
|
| 23 |
+
"note_to_self": {
|
| 24 |
+
"type": "string",
|
| 25 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 26 |
+
"maxLength": 100
|
| 27 |
+
},
|
| 28 |
+
"say_outloud": {
|
| 29 |
+
"type": "string",
|
| 30 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 31 |
+
"maxLength": 100
|
| 32 |
+
},
|
| 33 |
+
"action": {
|
| 34 |
+
"type": "object",
|
| 35 |
+
"required": [
|
| 36 |
+
"type"
|
| 37 |
+
],
|
| 38 |
+
"properties": {
|
| 39 |
+
"type": {
|
| 40 |
+
"type": "string",
|
| 41 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 42 |
+
},
|
| 43 |
+
"parameters": {
|
| 44 |
+
"type": "string",
|
| 45 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 46 |
+
}
|
| 47 |
+
},
|
| 48 |
+
"propertyOrdering": [
|
| 49 |
+
"type",
|
| 50 |
+
"parameters"
|
| 51 |
+
]
|
| 52 |
+
}
|
| 53 |
+
},
|
| 54 |
+
"propertyOrdering": [
|
| 55 |
+
"internal_thinking",
|
| 56 |
+
"note_to_self",
|
| 57 |
+
"say_outloud",
|
| 58 |
+
"action"
|
| 59 |
+
]
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
--- Prompt Content ---
|
| 63 |
+
{
|
| 64 |
+
"meta_data": {
|
| 65 |
+
"agent_name": "A",
|
| 66 |
+
"role": null
|
| 67 |
+
},
|
| 68 |
+
"task_context": {
|
| 69 |
+
"what_just_happened": "• Game 3a7a3455-7174-4727-b718-fef2f35d47f9 has started with 2 players!\n• Turn 0: Player 0's turn begins.\n• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT",
|
| 70 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 71 |
+
},
|
| 72 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[[20,\"A\",\"S\"]],\"rds\":[]},\"players\":{\"A\":{\"vp\":1,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 73 |
+
"constraints": {
|
| 74 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 75 |
+
"allowed_actions": [
|
| 76 |
+
{
|
| 77 |
+
"type": "place_starting_road",
|
| 78 |
+
"description": "Place your starting road",
|
| 79 |
+
"example_parameters": "{\"from\": X, \"to\": Y}"
|
| 80 |
+
},
|
| 81 |
+
{
|
| 82 |
+
"type": "wait_for_response",
|
| 83 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 84 |
+
"example_parameters": "{}"
|
| 85 |
+
}
|
| 86 |
+
]
|
| 87 |
+
}
|
| 88 |
+
}
|
examples/ai_testing/my_games/session_20260109_020107/A/responses/response_1.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 1,
|
| 3 |
+
"timestamp": "2026-01-09T02:01:41.308583",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"success": true,
|
| 6 |
+
"raw_content": "{\n \"internal_thinking\": \"The game has just started, and it's my turn to place the first settlement. My strategy is to secure a diverse set of essential resources (Wood, Brick, Sheep, Wheat) with high-probability numbers (6, 8, 5, 9) to facilitate early game development. I will prioritize nodes that offer at least three distinct resources, ideally including Wood and Brick for roads and settlements, and Sheep for additional settlements or development cards. Wheat is also highly desired for cities and development cards, but sometimes it's better to prioritize the W/B/S combination for the first spot. I will evaluate nodes by assigning points to dice rolls (6, 8 = 5 pts; 5, 9 = 4 pts; 4, 10 = 3 pts; 3, 11 = 2 pts; 2, 12 = 1 pt) and applying a multiplier to essential resources (W, B, S, Wh = 1.5x; O = 1.0x; D = 0x).\\n\\nAfter reviewing the board, several nodes stand out:\\n- Node 20: Hexes [9,5,4] -> H[9]=W11, H[5]=B6, H[4]=S8. Resources: Wood (11), Brick (6), Sheep (8). Score: (1.5*2) + (1.5*5) + (1.5*5) = 3 + 7.5 + 7.5 = 18.0. This offers excellent diversity (W, B, S) with two high-probability numbers (6, 8).\\n- Node 10: Hexes [5,4,1] -> H[5]=B6, H[4]=S8, H[1]=W12. Resources: Brick (6), Sheep (8), Wood (12). Score: (1.5*5) + (1.5*5) + (1.5*1) = 7.5 + 7.5 + 1.5 = 16.5. Good diversity, but Wood 12 is a very low probability.\\n- Node 12: Hexes [6,5,2] -> H[6]=W3, H[5]=B6, H[2]=S5. Resources: Wood (3), Brick (6), Sheep (5). Score: (1.5*2) + (1.5*5) + (1.5*4) = 3 + 7.5 + 6 = 16.5. Good diversity, but Wood 3 is a low probability.\\n- Node 41: Hexes [18,15,14] -> H[18]=Wh9, H[15]=Wh6, H[14]=Wh9. Resources: Wheat (9), Wheat (6), Wheat (9). Score: (1.5*4) + (1.5*5) + (1.5*4) = 6 + 7.5 + 6 = 19.5. While it has a very high score due to strong Wheat production, it lacks resource diversity, which is crucial for a first settlement.\\n\\nNode 20 provides the best balance of resource diversity (Wood, Brick, Sheep) and strong dice rolls (6, 8, 11). This combination will allow me to build roads and additional settlements quickly. The 6 and 8 are the highest probability rolls, ensuring consistent production of Brick and Sheep. While Wheat is not present, I can aim for a strong Wheat and possibly Ore spot with my second settlement.\\n\\nTherefore, placing the first settlement at Node 20 is the optimal move.\",\n \"note_to_self\": \"My first settlement is on Wood(11), Brick(6), Sheep(8). For the second settlement, I need to prioritize a good source of Wheat and ideally some Ore, or another strong Wood/Brick/Sheep combination if Wheat/Ore is weak elsewhere. Look for nodes with 5, 9, 4, 10, or 3, 11 for these resources.\",\n \"say_outloud\": \"Hello everyone! I'll kick things off by settling on a spot that gives me a good mix of Wood, Brick, and Sheep.\",\n \"action\": {\n \"type\": \"place_starting_settlement\",\n \"parameters\": \"{\\\"node\\\": 20}\"\n }\n}",
|
| 7 |
+
"parsed": {
|
| 8 |
+
"internal_thinking": "The game has just started, and it's my turn to place the first settlement. My strategy is to secure a diverse set of essential resources (Wood, Brick, Sheep, Wheat) with high-probability numbers (6, 8, 5, 9) to facilitate early game development. I will prioritize nodes that offer at least three distinct resources, ideally including Wood and Brick for roads and settlements, and Sheep for additional settlements or development cards. Wheat is also highly desired for cities and development cards, but sometimes it's better to prioritize the W/B/S combination for the first spot. I will evaluate nodes by assigning points to dice rolls (6, 8 = 5 pts; 5, 9 = 4 pts; 4, 10 = 3 pts; 3, 11 = 2 pts; 2, 12 = 1 pt) and applying a multiplier to essential resources (W, B, S, Wh = 1.5x; O = 1.0x; D = 0x).\n\nAfter reviewing the board, several nodes stand out:\n- Node 20: Hexes [9,5,4] -> H[9]=W11, H[5]=B6, H[4]=S8. Resources: Wood (11), Brick (6), Sheep (8). Score: (1.5*2) + (1.5*5) + (1.5*5) = 3 + 7.5 + 7.5 = 18.0. This offers excellent diversity (W, B, S) with two high-probability numbers (6, 8).\n- Node 10: Hexes [5,4,1] -> H[5]=B6, H[4]=S8, H[1]=W12. Resources: Brick (6), Sheep (8), Wood (12). Score: (1.5*5) + (1.5*5) + (1.5*1) = 7.5 + 7.5 + 1.5 = 16.5. Good diversity, but Wood 12 is a very low probability.\n- Node 12: Hexes [6,5,2] -> H[6]=W3, H[5]=B6, H[2]=S5. Resources: Wood (3), Brick (6), Sheep (5). Score: (1.5*2) + (1.5*5) + (1.5*4) = 3 + 7.5 + 6 = 16.5. Good diversity, but Wood 3 is a low probability.\n- Node 41: Hexes [18,15,14] -> H[18]=Wh9, H[15]=Wh6, H[14]=Wh9. Resources: Wheat (9), Wheat (6), Wheat (9). Score: (1.5*4) + (1.5*5) + (1.5*4) = 6 + 7.5 + 6 = 19.5. While it has a very high score due to strong Wheat production, it lacks resource diversity, which is crucial for a first settlement.\n\nNode 20 provides the best balance of resource diversity (Wood, Brick, Sheep) and strong dice rolls (6, 8, 11). This combination will allow me to build roads and additional settlements quickly. The 6 and 8 are the highest probability rolls, ensuring consistent production of Brick and Sheep. While Wheat is not present, I can aim for a strong Wheat and possibly Ore spot with my second settlement.\n\nTherefore, placing the first settlement at Node 20 is the optimal move.",
|
| 9 |
+
"note_to_self": "My first settlement is on Wood(11), Brick(6), Sheep(8). For the second settlement, I need to prioritize a good source of Wheat and ideally some Ore, or another strong Wood/Brick/Sheep combination if Wheat/Ore is weak elsewhere. Look for nodes with 5, 9, 4, 10, or 3, 11 for these resources.",
|
| 10 |
+
"say_outloud": "Hello everyone! I'll kick things off by settling on a spot that gives me a good mix of Wood, Brick, and Sheep.",
|
| 11 |
+
"action_type": "place_starting_settlement",
|
| 12 |
+
"parameters": {
|
| 13 |
+
"node": 20
|
| 14 |
+
}
|
| 15 |
+
},
|
| 16 |
+
"model": "gemini-2.5-flash",
|
| 17 |
+
"tokens": {
|
| 18 |
+
"prompt": 1629,
|
| 19 |
+
"completion": 994,
|
| 20 |
+
"total": 8668
|
| 21 |
+
},
|
| 22 |
+
"latency_seconds": 30.708115339279175,
|
| 23 |
+
"error": null
|
| 24 |
+
}
|
examples/ai_testing/my_games/session_20260109_020107/A/responses/response_2.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 2,
|
| 3 |
+
"timestamp": "2026-01-09T02:05:37.162566",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"success": true,
|
| 6 |
+
"raw_content": "{\n \"internal_thinking\": \"My agent, 'A', has just placed its first settlement at Node 20. This node provides access to Wood (H[9]=W11), Brick (H[5]=B6), and Sheep (H[4]=S8), offering a solid foundation for early game development. My current task is to place the first starting road, which must connect to Node 20. The possible adjacent nodes for road placement are 19, 21, and 10. I need to analyze each option to determine the most strategic placement.\\n\\n1. **Road from 20 to 19**: This path connects to Hexes 9 (W11) and 4 (S8). Node 19 is also adjacent to Hex 8 (B10). This direction leads towards Nodes 18 and 30, which offer more Brick and Wood. While providing additional resources, this path does not immediately open up access to a port or a particularly high-value settlement location.\\n\\n2. **Road from 20 to 21**: This path connects to Hexes 9 (W11) and 5 (B6). Node 21 is also adjacent to Hex 10 (Desert). This direction leads towards Nodes 22 and 32. The presence of the desert hex (10) makes this route less appealing for immediate resource expansion. Although Node 32 offers Wheat (Wh9), the overall potential from this direction is not as strong as other options.\\n\\n3. **Road from 20 to 10**: This path connects to Hexes 5 (B6) and 4 (S8). Crucially, Node 10 is also adjacent to Hex 1 (W12), adding a valuable Wood resource with a good roll number. This route leads towards Nodes 9 and 11.\\n * **Node 9**: Adjacent to Hexes 4 (S8) and 1 (W12). More importantly, Node 9 is a 'Any 3:1' port (?3). Securing access to a port early is a significant strategic advantage, providing flexibility in trading resources. The combined resources of W12 and S8 are also excellent.\\n * **Node 11**: Adjacent to Hexes 5 (B6), 2 (S5), and 1 (W12). This node also offers strong, diverse resources.\\n\\nComparing these options, placing the road from Node 20 to Node 10 is the most advantageous. This move directly positions me to potentially claim Node 9 as my second settlement, which would grant me a strong resource income (W12, S8) and, critically, access to a ?3 port. This port will dramatically improve my ability to trade for needed resources throughout the game, mitigating potential resource imbalances. Even if Node 9 is taken by an opponent, Node 11 remains a strong alternative. This choice prioritizes both resource acquisition and early trade flexibility, setting up a robust long-term strategy.\",\n \"note_to_self\": \"My first settlement is at Node 20 (W11, B6, S8). My primary goal for the second settlement is Node 9 (W12, S8, ?3 port) to secure a strong resource base and early trade flexibility. I need to observe the opponent's moves carefully to see if they attempt to block my path or claim this key node/port.\",\n \"say_outloud\": \"Alright, setting up my first road! Aiming for a good connection here.\",\n \"action\": {\n \"type\": \"place_starting_road\",\n \"parameters\": \"{\\\"from\\\": 20, \\\"to\\\": 10}\"\n }\n}",
|
| 7 |
+
"parsed": {
|
| 8 |
+
"internal_thinking": "My agent, 'A', has just placed its first settlement at Node 20. This node provides access to Wood (H[9]=W11), Brick (H[5]=B6), and Sheep (H[4]=S8), offering a solid foundation for early game development. My current task is to place the first starting road, which must connect to Node 20. The possible adjacent nodes for road placement are 19, 21, and 10. I need to analyze each option to determine the most strategic placement.\n\n1. **Road from 20 to 19**: This path connects to Hexes 9 (W11) and 4 (S8). Node 19 is also adjacent to Hex 8 (B10). This direction leads towards Nodes 18 and 30, which offer more Brick and Wood. While providing additional resources, this path does not immediately open up access to a port or a particularly high-value settlement location.\n\n2. **Road from 20 to 21**: This path connects to Hexes 9 (W11) and 5 (B6). Node 21 is also adjacent to Hex 10 (Desert). This direction leads towards Nodes 22 and 32. The presence of the desert hex (10) makes this route less appealing for immediate resource expansion. Although Node 32 offers Wheat (Wh9), the overall potential from this direction is not as strong as other options.\n\n3. **Road from 20 to 10**: This path connects to Hexes 5 (B6) and 4 (S8). Crucially, Node 10 is also adjacent to Hex 1 (W12), adding a valuable Wood resource with a good roll number. This route leads towards Nodes 9 and 11.\n * **Node 9**: Adjacent to Hexes 4 (S8) and 1 (W12). More importantly, Node 9 is a 'Any 3:1' port (?3). Securing access to a port early is a significant strategic advantage, providing flexibility in trading resources. The combined resources of W12 and S8 are also excellent.\n * **Node 11**: Adjacent to Hexes 5 (B6), 2 (S5), and 1 (W12). This node also offers strong, diverse resources.\n\nComparing these options, placing the road from Node 20 to Node 10 is the most advantageous. This move directly positions me to potentially claim Node 9 as my second settlement, which would grant me a strong resource income (W12, S8) and, critically, access to a ?3 port. This port will dramatically improve my ability to trade for needed resources throughout the game, mitigating potential resource imbalances. Even if Node 9 is taken by an opponent, Node 11 remains a strong alternative. This choice prioritizes both resource acquisition and early trade flexibility, setting up a robust long-term strategy.",
|
| 9 |
+
"note_to_self": "My first settlement is at Node 20 (W11, B6, S8). My primary goal for the second settlement is Node 9 (W12, S8, ?3 port) to secure a strong resource base and early trade flexibility. I need to observe the opponent's moves carefully to see if they attempt to block my path or claim this key node/port.",
|
| 10 |
+
"say_outloud": "Alright, setting up my first road! Aiming for a good connection here.",
|
| 11 |
+
"action_type": "place_starting_road",
|
| 12 |
+
"parameters": {
|
| 13 |
+
"from": 20,
|
| 14 |
+
"to": 10
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"model": "gemini-2.5-flash",
|
| 18 |
+
"tokens": {
|
| 19 |
+
"prompt": 1661,
|
| 20 |
+
"completion": 821,
|
| 21 |
+
"total": 6568
|
| 22 |
+
},
|
| 23 |
+
"latency_seconds": 23.603745937347412,
|
| 24 |
+
"error": null
|
| 25 |
+
}
|
examples/ai_testing/my_games/session_20260109_022638/A/A.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AI Agent Log: A
|
| 2 |
+
|
| 3 |
+
**Session:** session_20260109_022638
|
| 4 |
+
**Started:** 2026-01-09 02:26:38
|
| 5 |
+
**Model:** gemini-2.0-flash
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
## Request #1 - 🎯 ACTIVE TURN
|
| 11 |
+
|
| 12 |
+
**Time:** 02:26:40
|
| 13 |
+
|
| 14 |
+
### What Happened
|
| 15 |
+
• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!
|
| 16 |
+
• Turn 0: Player 0's turn begins.
|
| 17 |
+
|
| 18 |
+
### Prompt Sent
|
| 19 |
+
See: [prompt_1.json](prompts/prompt_1.json)
|
| 20 |
+
|
| 21 |
+
### Response Received ✅
|
| 22 |
+
|
| 23 |
+
**Latency:** 47.67s | **Tokens:** 12763
|
| 24 |
+
|
| 25 |
+
**Thinking:** The game is in the 'SETUP_FIRST_ROUND' phase, requiring me to place my first settlement and an adjacent road. My primary objective is to secure a strong and diverse resource income, focusing on Wood, ...
|
| 26 |
+
|
| 27 |
+
**Note to Self:** Player A is starting with a strong WBS production at Node 20 (W11, B6, S8). This should allow for quick road building and expansion. My next priority should be to secure Wheat and Ore with the second settlement. Node 25 (S4, O3, Wh8) looks like an excellent target for the second settlement to diversify income and gain access to Ore and Wheat, which are currently missing.
|
| 28 |
+
|
| 29 |
+
**Says:** "I'm looking for a balanced start to build up my resources quickly. Good luck everyone!"
|
| 30 |
+
|
| 31 |
+
---
|
| 32 |
+
|
| 33 |
+
## Request #2 - 🎯 ACTIVE TURN
|
| 34 |
+
|
| 35 |
+
**Time:** 02:28:42
|
| 36 |
+
|
| 37 |
+
### What Happened
|
| 38 |
+
• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!
|
| 39 |
+
• Turn 0: Player 0's turn begins.
|
| 40 |
+
• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT
|
| 41 |
+
|
| 42 |
+
### Prompt Sent
|
| 43 |
+
See: [prompt_2.json](prompts/prompt_2.json)
|
| 44 |
+
|
| 45 |
+
### Response Received ✅
|
| 46 |
+
|
| 47 |
+
**Latency:** 21.49s | **Tokens:** 6391
|
| 48 |
+
|
| 49 |
+
**Thinking:** Player A has just placed their first settlement at Node 20, which yields Wood (11), Brick (6), and Sheep (8). The next step is to place a starting road. I have three options for road placement from No...
|
| 50 |
+
|
| 51 |
+
---
|
examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_1.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 1,
|
| 3 |
+
"timestamp": "2026-01-09T02:26:40.677227",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"is_active_turn": true,
|
| 6 |
+
"prompt": {
|
| 7 |
+
"meta_data": {
|
| 8 |
+
"agent_name": "A",
|
| 9 |
+
"role": null
|
| 10 |
+
},
|
| 11 |
+
"task_context": {
|
| 12 |
+
"what_just_happened": "• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!\n• Turn 0: Player 0's turn begins.",
|
| 13 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 14 |
+
},
|
| 15 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[],\"rds\":[]},\"players\":{\"A\":{\"vp\":0,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 16 |
+
"constraints": {
|
| 17 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 18 |
+
"allowed_actions": [
|
| 19 |
+
{
|
| 20 |
+
"type": "place_starting_settlement",
|
| 21 |
+
"description": "Place your starting settlement",
|
| 22 |
+
"example_parameters": "{\"node\": X}"
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"type": "wait_for_response",
|
| 26 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 27 |
+
"example_parameters": "{}"
|
| 28 |
+
}
|
| 29 |
+
]
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
"response_schema": {
|
| 33 |
+
"type": "object",
|
| 34 |
+
"required": [
|
| 35 |
+
"internal_thinking",
|
| 36 |
+
"action"
|
| 37 |
+
],
|
| 38 |
+
"properties": {
|
| 39 |
+
"internal_thinking": {
|
| 40 |
+
"type": "string",
|
| 41 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 42 |
+
"minLength": 1000
|
| 43 |
+
},
|
| 44 |
+
"note_to_self": {
|
| 45 |
+
"type": "string",
|
| 46 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 47 |
+
"maxLength": 100
|
| 48 |
+
},
|
| 49 |
+
"say_outloud": {
|
| 50 |
+
"type": "string",
|
| 51 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 52 |
+
"maxLength": 100
|
| 53 |
+
},
|
| 54 |
+
"action": {
|
| 55 |
+
"type": "object",
|
| 56 |
+
"required": [
|
| 57 |
+
"type"
|
| 58 |
+
],
|
| 59 |
+
"properties": {
|
| 60 |
+
"type": {
|
| 61 |
+
"type": "string",
|
| 62 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 63 |
+
},
|
| 64 |
+
"parameters": {
|
| 65 |
+
"type": "string",
|
| 66 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 67 |
+
}
|
| 68 |
+
},
|
| 69 |
+
"propertyOrdering": [
|
| 70 |
+
"type",
|
| 71 |
+
"parameters"
|
| 72 |
+
]
|
| 73 |
+
}
|
| 74 |
+
},
|
| 75 |
+
"propertyOrdering": [
|
| 76 |
+
"internal_thinking",
|
| 77 |
+
"note_to_self",
|
| 78 |
+
"say_outloud",
|
| 79 |
+
"action"
|
| 80 |
+
]
|
| 81 |
+
},
|
| 82 |
+
"what_happened": "• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!\n• Turn 0: Player 0's turn begins.",
|
| 83 |
+
"allowed_actions": [
|
| 84 |
+
{
|
| 85 |
+
"type": "place_starting_settlement",
|
| 86 |
+
"description": "Place your starting settlement",
|
| 87 |
+
"example_parameters": "{\"node\": X}"
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"type": "wait_for_response",
|
| 91 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 92 |
+
"example_parameters": "{}"
|
| 93 |
+
}
|
| 94 |
+
]
|
| 95 |
+
}
|
examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_1.txt
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
=== Prompt #1 for A ===
|
| 2 |
+
Timestamp: 2026-01-09T02:26:40.678640
|
| 3 |
+
Active Turn: True
|
| 4 |
+
|
| 5 |
+
--- What Happened ---
|
| 6 |
+
• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!
|
| 7 |
+
• Turn 0: Player 0's turn begins.
|
| 8 |
+
|
| 9 |
+
--- Response Schema ---
|
| 10 |
+
{
|
| 11 |
+
"type": "object",
|
| 12 |
+
"required": [
|
| 13 |
+
"internal_thinking",
|
| 14 |
+
"action"
|
| 15 |
+
],
|
| 16 |
+
"properties": {
|
| 17 |
+
"internal_thinking": {
|
| 18 |
+
"type": "string",
|
| 19 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 20 |
+
"minLength": 1000
|
| 21 |
+
},
|
| 22 |
+
"note_to_self": {
|
| 23 |
+
"type": "string",
|
| 24 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 25 |
+
"maxLength": 100
|
| 26 |
+
},
|
| 27 |
+
"say_outloud": {
|
| 28 |
+
"type": "string",
|
| 29 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 30 |
+
"maxLength": 100
|
| 31 |
+
},
|
| 32 |
+
"action": {
|
| 33 |
+
"type": "object",
|
| 34 |
+
"required": [
|
| 35 |
+
"type"
|
| 36 |
+
],
|
| 37 |
+
"properties": {
|
| 38 |
+
"type": {
|
| 39 |
+
"type": "string",
|
| 40 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 41 |
+
},
|
| 42 |
+
"parameters": {
|
| 43 |
+
"type": "string",
|
| 44 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 45 |
+
}
|
| 46 |
+
},
|
| 47 |
+
"propertyOrdering": [
|
| 48 |
+
"type",
|
| 49 |
+
"parameters"
|
| 50 |
+
]
|
| 51 |
+
}
|
| 52 |
+
},
|
| 53 |
+
"propertyOrdering": [
|
| 54 |
+
"internal_thinking",
|
| 55 |
+
"note_to_self",
|
| 56 |
+
"say_outloud",
|
| 57 |
+
"action"
|
| 58 |
+
]
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
--- Prompt Content ---
|
| 62 |
+
{
|
| 63 |
+
"meta_data": {
|
| 64 |
+
"agent_name": "A",
|
| 65 |
+
"role": null
|
| 66 |
+
},
|
| 67 |
+
"task_context": {
|
| 68 |
+
"what_just_happened": "• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!\n• Turn 0: Player 0's turn begins.",
|
| 69 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 70 |
+
},
|
| 71 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[],\"rds\":[]},\"players\":{\"A\":{\"vp\":0,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 72 |
+
"constraints": {
|
| 73 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 74 |
+
"allowed_actions": [
|
| 75 |
+
{
|
| 76 |
+
"type": "place_starting_settlement",
|
| 77 |
+
"description": "Place your starting settlement",
|
| 78 |
+
"example_parameters": "{\"node\": X}"
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"type": "wait_for_response",
|
| 82 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 83 |
+
"example_parameters": "{}"
|
| 84 |
+
}
|
| 85 |
+
]
|
| 86 |
+
}
|
| 87 |
+
}
|
examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_2.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 2,
|
| 3 |
+
"timestamp": "2026-01-09T02:28:42.301493",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"is_active_turn": true,
|
| 6 |
+
"prompt": {
|
| 7 |
+
"meta_data": {
|
| 8 |
+
"agent_name": "A",
|
| 9 |
+
"role": null
|
| 10 |
+
},
|
| 11 |
+
"task_context": {
|
| 12 |
+
"what_just_happened": "• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!\n• Turn 0: Player 0's turn begins.\n• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT",
|
| 13 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 14 |
+
},
|
| 15 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[[20,\"A\",\"S\"]],\"rds\":[]},\"players\":{\"A\":{\"vp\":1,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 16 |
+
"constraints": {
|
| 17 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 18 |
+
"allowed_actions": [
|
| 19 |
+
{
|
| 20 |
+
"type": "place_starting_road",
|
| 21 |
+
"description": "Place your starting road",
|
| 22 |
+
"example_parameters": "{\"from\": X, \"to\": Y}"
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"type": "wait_for_response",
|
| 26 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 27 |
+
"example_parameters": "{}"
|
| 28 |
+
}
|
| 29 |
+
]
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
"response_schema": {
|
| 33 |
+
"type": "object",
|
| 34 |
+
"required": [
|
| 35 |
+
"internal_thinking",
|
| 36 |
+
"action"
|
| 37 |
+
],
|
| 38 |
+
"properties": {
|
| 39 |
+
"internal_thinking": {
|
| 40 |
+
"type": "string",
|
| 41 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 42 |
+
"minLength": 1000
|
| 43 |
+
},
|
| 44 |
+
"note_to_self": {
|
| 45 |
+
"type": "string",
|
| 46 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 47 |
+
"maxLength": 100
|
| 48 |
+
},
|
| 49 |
+
"say_outloud": {
|
| 50 |
+
"type": "string",
|
| 51 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 52 |
+
"maxLength": 100
|
| 53 |
+
},
|
| 54 |
+
"action": {
|
| 55 |
+
"type": "object",
|
| 56 |
+
"required": [
|
| 57 |
+
"type"
|
| 58 |
+
],
|
| 59 |
+
"properties": {
|
| 60 |
+
"type": {
|
| 61 |
+
"type": "string",
|
| 62 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 63 |
+
},
|
| 64 |
+
"parameters": {
|
| 65 |
+
"type": "string",
|
| 66 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 67 |
+
}
|
| 68 |
+
},
|
| 69 |
+
"propertyOrdering": [
|
| 70 |
+
"type",
|
| 71 |
+
"parameters"
|
| 72 |
+
]
|
| 73 |
+
}
|
| 74 |
+
},
|
| 75 |
+
"propertyOrdering": [
|
| 76 |
+
"internal_thinking",
|
| 77 |
+
"note_to_self",
|
| 78 |
+
"say_outloud",
|
| 79 |
+
"action"
|
| 80 |
+
]
|
| 81 |
+
},
|
| 82 |
+
"what_happened": "• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!\n• Turn 0: Player 0's turn begins.\n• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT",
|
| 83 |
+
"allowed_actions": [
|
| 84 |
+
{
|
| 85 |
+
"type": "place_starting_road",
|
| 86 |
+
"description": "Place your starting road",
|
| 87 |
+
"example_parameters": "{\"from\": X, \"to\": Y}"
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"type": "wait_for_response",
|
| 91 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 92 |
+
"example_parameters": "{}"
|
| 93 |
+
}
|
| 94 |
+
]
|
| 95 |
+
}
|
examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_2.txt
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
=== Prompt #2 for A ===
|
| 2 |
+
Timestamp: 2026-01-09T02:28:42.302438
|
| 3 |
+
Active Turn: True
|
| 4 |
+
|
| 5 |
+
--- What Happened ---
|
| 6 |
+
• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!
|
| 7 |
+
• Turn 0: Player 0's turn begins.
|
| 8 |
+
• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT
|
| 9 |
+
|
| 10 |
+
--- Response Schema ---
|
| 11 |
+
{
|
| 12 |
+
"type": "object",
|
| 13 |
+
"required": [
|
| 14 |
+
"internal_thinking",
|
| 15 |
+
"action"
|
| 16 |
+
],
|
| 17 |
+
"properties": {
|
| 18 |
+
"internal_thinking": {
|
| 19 |
+
"type": "string",
|
| 20 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 21 |
+
"minLength": 1000
|
| 22 |
+
},
|
| 23 |
+
"note_to_self": {
|
| 24 |
+
"type": "string",
|
| 25 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 26 |
+
"maxLength": 100
|
| 27 |
+
},
|
| 28 |
+
"say_outloud": {
|
| 29 |
+
"type": "string",
|
| 30 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 31 |
+
"maxLength": 100
|
| 32 |
+
},
|
| 33 |
+
"action": {
|
| 34 |
+
"type": "object",
|
| 35 |
+
"required": [
|
| 36 |
+
"type"
|
| 37 |
+
],
|
| 38 |
+
"properties": {
|
| 39 |
+
"type": {
|
| 40 |
+
"type": "string",
|
| 41 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 42 |
+
},
|
| 43 |
+
"parameters": {
|
| 44 |
+
"type": "string",
|
| 45 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 46 |
+
}
|
| 47 |
+
},
|
| 48 |
+
"propertyOrdering": [
|
| 49 |
+
"type",
|
| 50 |
+
"parameters"
|
| 51 |
+
]
|
| 52 |
+
}
|
| 53 |
+
},
|
| 54 |
+
"propertyOrdering": [
|
| 55 |
+
"internal_thinking",
|
| 56 |
+
"note_to_self",
|
| 57 |
+
"say_outloud",
|
| 58 |
+
"action"
|
| 59 |
+
]
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
--- Prompt Content ---
|
| 63 |
+
{
|
| 64 |
+
"meta_data": {
|
| 65 |
+
"agent_name": "A",
|
| 66 |
+
"role": null
|
| 67 |
+
},
|
| 68 |
+
"task_context": {
|
| 69 |
+
"what_just_happened": "• Game a55d43a6-95c2-4efa-8774-b200601da0ee has started with 2 players!\n• Turn 0: Player 0's turn begins.\n• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT",
|
| 70 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 71 |
+
},
|
| 72 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[[20,\"A\",\"S\"]],\"rds\":[]},\"players\":{\"A\":{\"vp\":1,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 73 |
+
"constraints": {
|
| 74 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 75 |
+
"allowed_actions": [
|
| 76 |
+
{
|
| 77 |
+
"type": "place_starting_road",
|
| 78 |
+
"description": "Place your starting road",
|
| 79 |
+
"example_parameters": "{\"from\": X, \"to\": Y}"
|
| 80 |
+
},
|
| 81 |
+
{
|
| 82 |
+
"type": "wait_for_response",
|
| 83 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 84 |
+
"example_parameters": "{}"
|
| 85 |
+
}
|
| 86 |
+
]
|
| 87 |
+
}
|
| 88 |
+
}
|
examples/ai_testing/my_games/session_20260109_022638/A/responses/response_1.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 1,
|
| 3 |
+
"timestamp": "2026-01-09T02:27:29.836098",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"success": true,
|
| 6 |
+
"raw_content": "{\n \"internal_thinking\": \"The game is in the 'SETUP_FIRST_ROUND' phase, requiring me to place my first settlement and an adjacent road. My primary objective is to secure a strong and diverse resource income, focusing on Wood, Brick, and Sheep/Wheat for early expansion, and considering Ore for later development. High-probability dice rolls (6, 8, 5, 9) are highly desirable. I have carefully reviewed all nodes, ensuring correct interpretation of the `N` array structure to identify the hexes each node touches, along with their associated resources and dice numbers from the `H` array. This corrected analysis is crucial for optimal decision-making. Node 20 stands out as the strongest initial placement. It touches Hexes 9, 5, and 4, yielding Wood on 11 (2 pips), Brick on 6 (5 pips), and Sheep on 8 (5 pips). This provides a critical combination of Wood, Brick, and Sheep, which are essential for building roads and additional settlements. The total pip count for Node 20 is 12 (2+5+5), which is excellent. More importantly, it features two high-probability rolls (6 and 8) for Brick and Sheep, ensuring consistent income of these vital resources. While Wood on 11 is a lower probability, it completes the essential resource set. Other strong nodes, like Node 12 (W3, B6, S5 - 11 pips) and Node 10 (B6, S8, W12 - 11 pips), also offer WBS, but Node 20's specific combination of dice numbers (two 5-pip rolls) makes it superior. Node 25 (S4, O3, Wh8 - 10 pips) is an attractive option for its Sheep, Ore, and Wheat, especially the Wh8, but starting with WBS is generally more advantageous for early road-building and expansion. Therefore, Node 20 provides the most balanced and high-yield start for securing fundamental building materials, setting up a strong economic foundation for the rest of the game.\",\n \"note_to_self\": \"Player A is starting with a strong WBS production at Node 20 (W11, B6, S8). This should allow for quick road building and expansion. My next priority should be to secure Wheat and Ore with the second settlement. Node 25 (S4, O3, Wh8) looks like an excellent target for the second settlement to diversify income and gain access to Ore and Wheat, which are currently missing.\",\n \"say_outloud\": \"I'm looking for a balanced start to build up my resources quickly. Good luck everyone!\",\n \"action\": {\n \"type\": \"place_starting_settlement\",\n \"parameters\": \"{\\\"node\\\": 20}\"\n }\n}",
|
| 7 |
+
"parsed": {
|
| 8 |
+
"internal_thinking": "The game is in the 'SETUP_FIRST_ROUND' phase, requiring me to place my first settlement and an adjacent road. My primary objective is to secure a strong and diverse resource income, focusing on Wood, Brick, and Sheep/Wheat for early expansion, and considering Ore for later development. High-probability dice rolls (6, 8, 5, 9) are highly desirable. I have carefully reviewed all nodes, ensuring correct interpretation of the `N` array structure to identify the hexes each node touches, along with their associated resources and dice numbers from the `H` array. This corrected analysis is crucial for optimal decision-making. Node 20 stands out as the strongest initial placement. It touches Hexes 9, 5, and 4, yielding Wood on 11 (2 pips), Brick on 6 (5 pips), and Sheep on 8 (5 pips). This provides a critical combination of Wood, Brick, and Sheep, which are essential for building roads and additional settlements. The total pip count for Node 20 is 12 (2+5+5), which is excellent. More importantly, it features two high-probability rolls (6 and 8) for Brick and Sheep, ensuring consistent income of these vital resources. While Wood on 11 is a lower probability, it completes the essential resource set. Other strong nodes, like Node 12 (W3, B6, S5 - 11 pips) and Node 10 (B6, S8, W12 - 11 pips), also offer WBS, but Node 20's specific combination of dice numbers (two 5-pip rolls) makes it superior. Node 25 (S4, O3, Wh8 - 10 pips) is an attractive option for its Sheep, Ore, and Wheat, especially the Wh8, but starting with WBS is generally more advantageous for early road-building and expansion. Therefore, Node 20 provides the most balanced and high-yield start for securing fundamental building materials, setting up a strong economic foundation for the rest of the game.",
|
| 9 |
+
"note_to_self": "Player A is starting with a strong WBS production at Node 20 (W11, B6, S8). This should allow for quick road building and expansion. My next priority should be to secure Wheat and Ore with the second settlement. Node 25 (S4, O3, Wh8) looks like an excellent target for the second settlement to diversify income and gain access to Ore and Wheat, which are currently missing.",
|
| 10 |
+
"say_outloud": "I'm looking for a balanced start to build up my resources quickly. Good luck everyone!",
|
| 11 |
+
"action_type": "place_starting_settlement",
|
| 12 |
+
"parameters": {
|
| 13 |
+
"node": 20
|
| 14 |
+
}
|
| 15 |
+
},
|
| 16 |
+
"model": "gemini-2.5-flash",
|
| 17 |
+
"tokens": {
|
| 18 |
+
"prompt": 1625,
|
| 19 |
+
"completion": 622,
|
| 20 |
+
"total": 12763
|
| 21 |
+
},
|
| 22 |
+
"latency_seconds": 47.66546058654785,
|
| 23 |
+
"error": null
|
| 24 |
+
}
|
examples/ai_testing/my_games/session_20260109_022638/A/responses/response_2.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 2,
|
| 3 |
+
"timestamp": "2026-01-09T02:29:03.797423",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"success": true,
|
| 6 |
+
"raw_content": "{\n \"internal_thinking\": \"Player A has just placed their first settlement at Node 20, which yields Wood (11), Brick (6), and Sheep (8). The next step is to place a starting road. I have three options for road placement from Node 20: to Node 19, Node 21, or Node 10.Analyzing the options:1. Road 20-21: Node 21 shares the exact same hexes (9, 5, 4) as Node 20 (W11, B6, S8). Placing a road here does not expand my resource diversity or provide access to new strategic locations or ports. This is a weak option.2. Road 20-10: Node 10 is adjacent to hexes 5, 4, 1 (B6, S8, W12). This would add W12 (a good wood number) to my potential future access. This path also leads to Node 9, which is a ?3 port and is adjacent to hexes 4 and 1 (S8, W12). A ?3 port offers general trading flexibility.3. Road 20-19: Node 19 is adjacent to hexes 9, 8, 4 (W11, B10, S8). This would add B10 (a good brick number) to my potential future access, which is better than my current B6. This path also leads to Node 18, which is a S2 port and is adjacent to hexes 8 and 4 (B10, S8).Comparing strategic value:My current settlement at Node 20 provides Wood (11), Brick (6), and Sheep (8). To build roads and settlements, I need Wood and Brick. To build cities, I will eventually need Wheat and Ore.Both Node 10 and Node 19 lead to ports.Road 20-10 leads to a ?3 port at Node 9. This is flexible but less efficient for specific resources.Road 20-19 leads to a S2 port at Node 18. Since I already have a decent Sheep production (S8), gaining a S2 port for Sheep would significantly improve my trading efficiency for one of my primary resources. Furthermore, Node 19 itself has B10, which is a better brick yield than my current B6. This strengthens my brick production potential.Given that I have Sheep (S8) from my initial settlement, securing a S2 port for Sheep is a strong strategic move to leverage my existing production. It allows me to convert excess sheep into other needed resources at a favorable rate. The alternative of a ?3 port is less specific and therefore less immediately impactful for my current resource profile. Therefore, choosing to place the road between Node 20 and Node 19 is the optimal move.\",\n \"action\": {\n \"type\": \"place_starting_road\",\n \"parameters\": \"{\\\"from\\\": 20, \\\"to\\\": 19}\"\n }\n}",
|
| 7 |
+
"parsed": {
|
| 8 |
+
"internal_thinking": "Player A has just placed their first settlement at Node 20, which yields Wood (11), Brick (6), and Sheep (8). The next step is to place a starting road. I have three options for road placement from Node 20: to Node 19, Node 21, or Node 10.Analyzing the options:1. Road 20-21: Node 21 shares the exact same hexes (9, 5, 4) as Node 20 (W11, B6, S8). Placing a road here does not expand my resource diversity or provide access to new strategic locations or ports. This is a weak option.2. Road 20-10: Node 10 is adjacent to hexes 5, 4, 1 (B6, S8, W12). This would add W12 (a good wood number) to my potential future access. This path also leads to Node 9, which is a ?3 port and is adjacent to hexes 4 and 1 (S8, W12). A ?3 port offers general trading flexibility.3. Road 20-19: Node 19 is adjacent to hexes 9, 8, 4 (W11, B10, S8). This would add B10 (a good brick number) to my potential future access, which is better than my current B6. This path also leads to Node 18, which is a S2 port and is adjacent to hexes 8 and 4 (B10, S8).Comparing strategic value:My current settlement at Node 20 provides Wood (11), Brick (6), and Sheep (8). To build roads and settlements, I need Wood and Brick. To build cities, I will eventually need Wheat and Ore.Both Node 10 and Node 19 lead to ports.Road 20-10 leads to a ?3 port at Node 9. This is flexible but less efficient for specific resources.Road 20-19 leads to a S2 port at Node 18. Since I already have a decent Sheep production (S8), gaining a S2 port for Sheep would significantly improve my trading efficiency for one of my primary resources. Furthermore, Node 19 itself has B10, which is a better brick yield than my current B6. This strengthens my brick production potential.Given that I have Sheep (S8) from my initial settlement, securing a S2 port for Sheep is a strong strategic move to leverage my existing production. It allows me to convert excess sheep into other needed resources at a favorable rate. The alternative of a ?3 port is less specific and therefore less immediately impactful for my current resource profile. Therefore, choosing to place the road between Node 20 and Node 19 is the optimal move.",
|
| 9 |
+
"note_to_self": null,
|
| 10 |
+
"say_outloud": null,
|
| 11 |
+
"action_type": "place_starting_road",
|
| 12 |
+
"parameters": {
|
| 13 |
+
"from": 20,
|
| 14 |
+
"to": 19
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"model": "gemini-2.5-flash",
|
| 18 |
+
"tokens": {
|
| 19 |
+
"prompt": 1657,
|
| 20 |
+
"completion": 666,
|
| 21 |
+
"total": 6391
|
| 22 |
+
},
|
| 23 |
+
"latency_seconds": 21.49210524559021,
|
| 24 |
+
"error": null
|
| 25 |
+
}
|
examples/ai_testing/my_games/session_20260109_022638/session_metadata.json
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"session_id": "session_20260109_022638",
|
| 3 |
+
"start_time": "2026-01-09T02:26:38.619791",
|
| 4 |
+
"version": "2.0"
|
| 5 |
+
}
|
examples/ai_testing/my_games/session_20260109_023135/A/A.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AI Agent Log: A
|
| 2 |
+
|
| 3 |
+
**Session:** session_20260109_023135
|
| 4 |
+
**Started:** 2026-01-09 02:31:35
|
| 5 |
+
**Model:** gemini-2.0-flash
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
## Request #1 - 🎯 ACTIVE TURN
|
| 11 |
+
|
| 12 |
+
**Time:** 02:31:37
|
| 13 |
+
|
| 14 |
+
### What Happened
|
| 15 |
+
• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!
|
| 16 |
+
• Turn 0: Player 0's turn begins.
|
| 17 |
+
|
| 18 |
+
### Prompt Sent
|
| 19 |
+
See: [prompt_1.json](prompts/prompt_1.json)
|
| 20 |
+
|
| 21 |
+
### Response Received ✅
|
| 22 |
+
|
| 23 |
+
**Latency:** 45.72s | **Tokens:** 11861
|
| 24 |
+
|
| 25 |
+
**Thinking:** This is the first turn of the game (SETUP_FIRST_ROUND), and I am player A, so I get the first pick for a settlement and an adjacent road. My strategy is to secure a node with a strong combination of r...
|
| 26 |
+
|
| 27 |
+
**Note to Self:** First pick: Node 20 (W11, S8, B6). Road to Node 19. This secures Wood, Sheep, Brick for early expansion. For the second settlement (my last pick), prioritize a strong Wheat/Ore spot, ideally Node 41 (O5, Wh9, B10) if available, or Node 24 (O3, Wh8, W3), Node 25 (S4, O3, Wh8), or Node 35 (S11, Wh6, O3).
|
| 28 |
+
|
| 29 |
+
**Says:** "I'm starting strong with a focus on early expansion! Let's see what the board has in store."
|
| 30 |
+
|
| 31 |
+
---
|
| 32 |
+
|
| 33 |
+
## Request #2 - 🎯 ACTIVE TURN
|
| 34 |
+
|
| 35 |
+
**Time:** 02:33:20
|
| 36 |
+
|
| 37 |
+
### What Happened
|
| 38 |
+
• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!
|
| 39 |
+
• Turn 0: Player 0's turn begins.
|
| 40 |
+
• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT
|
| 41 |
+
|
| 42 |
+
### Prompt Sent
|
| 43 |
+
See: [prompt_2.json](prompts/prompt_2.json)
|
| 44 |
+
|
| 45 |
+
### Response Received ✅
|
| 46 |
+
|
| 47 |
+
**Latency:** 6.47s | **Tokens:** 3038
|
| 48 |
+
|
| 49 |
+
**Thinking:** The game is in the SETUP_FIRST_ROUND phase, and I have just placed my first settlement at Node 20. My `note_from_last_turn` clearly outlined this move: "First pick: Node 20 (W11, S8, B6). Road to Node...
|
| 50 |
+
|
| 51 |
+
**Note to Self:** Confirmed first settlement at Node 20 (W11, B6, S8) and road to Node 19. For the second settlement (my last pick in this round), I still need to prioritize a strong Wheat/Ore spot. Node 41 (O5, Wh9, B10) remains a high priority if available, followed by Node 24 (O3, Wh8, W3), Node 25 (S4, O3, Wh8), or Node 35 (S11, Wh6, O3). I will need to observe Player B's next move to see what nodes they claim, which will influence my final decision.
|
| 52 |
+
|
| 53 |
+
**Says:** "My first settlement is placed, and now my first road. I'm focusing on securing resources for a strong early game build out!"
|
| 54 |
+
|
| 55 |
+
---
|
examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_1.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 1,
|
| 3 |
+
"timestamp": "2026-01-09T02:31:37.730367",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"is_active_turn": true,
|
| 6 |
+
"prompt": {
|
| 7 |
+
"meta_data": {
|
| 8 |
+
"agent_name": "A",
|
| 9 |
+
"role": null
|
| 10 |
+
},
|
| 11 |
+
"task_context": {
|
| 12 |
+
"what_just_happened": "• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!\n• Turn 0: Player 0's turn begins.",
|
| 13 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 14 |
+
},
|
| 15 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[],\"rds\":[]},\"players\":{\"A\":{\"vp\":0,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 16 |
+
"constraints": {
|
| 17 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 18 |
+
"allowed_actions": [
|
| 19 |
+
{
|
| 20 |
+
"type": "place_starting_settlement",
|
| 21 |
+
"description": "Place your starting settlement",
|
| 22 |
+
"example_parameters": "{\"node\": X}"
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"type": "wait_for_response",
|
| 26 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 27 |
+
"example_parameters": "{}"
|
| 28 |
+
}
|
| 29 |
+
]
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
"response_schema": {
|
| 33 |
+
"type": "object",
|
| 34 |
+
"required": [
|
| 35 |
+
"internal_thinking",
|
| 36 |
+
"action"
|
| 37 |
+
],
|
| 38 |
+
"properties": {
|
| 39 |
+
"internal_thinking": {
|
| 40 |
+
"type": "string",
|
| 41 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 42 |
+
"minLength": 1000
|
| 43 |
+
},
|
| 44 |
+
"note_to_self": {
|
| 45 |
+
"type": "string",
|
| 46 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 47 |
+
"maxLength": 100
|
| 48 |
+
},
|
| 49 |
+
"say_outloud": {
|
| 50 |
+
"type": "string",
|
| 51 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 52 |
+
"maxLength": 100
|
| 53 |
+
},
|
| 54 |
+
"action": {
|
| 55 |
+
"type": "object",
|
| 56 |
+
"required": [
|
| 57 |
+
"type"
|
| 58 |
+
],
|
| 59 |
+
"properties": {
|
| 60 |
+
"type": {
|
| 61 |
+
"type": "string",
|
| 62 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 63 |
+
},
|
| 64 |
+
"parameters": {
|
| 65 |
+
"type": "string",
|
| 66 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 67 |
+
}
|
| 68 |
+
},
|
| 69 |
+
"propertyOrdering": [
|
| 70 |
+
"type",
|
| 71 |
+
"parameters"
|
| 72 |
+
]
|
| 73 |
+
}
|
| 74 |
+
},
|
| 75 |
+
"propertyOrdering": [
|
| 76 |
+
"internal_thinking",
|
| 77 |
+
"note_to_self",
|
| 78 |
+
"say_outloud",
|
| 79 |
+
"action"
|
| 80 |
+
]
|
| 81 |
+
},
|
| 82 |
+
"what_happened": "• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!\n• Turn 0: Player 0's turn begins.",
|
| 83 |
+
"allowed_actions": [
|
| 84 |
+
{
|
| 85 |
+
"type": "place_starting_settlement",
|
| 86 |
+
"description": "Place your starting settlement",
|
| 87 |
+
"example_parameters": "{\"node\": X}"
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"type": "wait_for_response",
|
| 91 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 92 |
+
"example_parameters": "{}"
|
| 93 |
+
}
|
| 94 |
+
]
|
| 95 |
+
}
|
examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_1.txt
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
=== Prompt #1 for A ===
|
| 2 |
+
Timestamp: 2026-01-09T02:31:37.732336
|
| 3 |
+
Active Turn: True
|
| 4 |
+
|
| 5 |
+
--- What Happened ---
|
| 6 |
+
• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!
|
| 7 |
+
• Turn 0: Player 0's turn begins.
|
| 8 |
+
|
| 9 |
+
--- Response Schema ---
|
| 10 |
+
{
|
| 11 |
+
"type": "object",
|
| 12 |
+
"required": [
|
| 13 |
+
"internal_thinking",
|
| 14 |
+
"action"
|
| 15 |
+
],
|
| 16 |
+
"properties": {
|
| 17 |
+
"internal_thinking": {
|
| 18 |
+
"type": "string",
|
| 19 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 20 |
+
"minLength": 1000
|
| 21 |
+
},
|
| 22 |
+
"note_to_self": {
|
| 23 |
+
"type": "string",
|
| 24 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 25 |
+
"maxLength": 100
|
| 26 |
+
},
|
| 27 |
+
"say_outloud": {
|
| 28 |
+
"type": "string",
|
| 29 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 30 |
+
"maxLength": 100
|
| 31 |
+
},
|
| 32 |
+
"action": {
|
| 33 |
+
"type": "object",
|
| 34 |
+
"required": [
|
| 35 |
+
"type"
|
| 36 |
+
],
|
| 37 |
+
"properties": {
|
| 38 |
+
"type": {
|
| 39 |
+
"type": "string",
|
| 40 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 41 |
+
},
|
| 42 |
+
"parameters": {
|
| 43 |
+
"type": "string",
|
| 44 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 45 |
+
}
|
| 46 |
+
},
|
| 47 |
+
"propertyOrdering": [
|
| 48 |
+
"type",
|
| 49 |
+
"parameters"
|
| 50 |
+
]
|
| 51 |
+
}
|
| 52 |
+
},
|
| 53 |
+
"propertyOrdering": [
|
| 54 |
+
"internal_thinking",
|
| 55 |
+
"note_to_self",
|
| 56 |
+
"say_outloud",
|
| 57 |
+
"action"
|
| 58 |
+
]
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
--- Prompt Content ---
|
| 62 |
+
{
|
| 63 |
+
"meta_data": {
|
| 64 |
+
"agent_name": "A",
|
| 65 |
+
"role": null
|
| 66 |
+
},
|
| 67 |
+
"task_context": {
|
| 68 |
+
"what_just_happened": "• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!\n• Turn 0: Player 0's turn begins.",
|
| 69 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 70 |
+
},
|
| 71 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[],\"rds\":[]},\"players\":{\"A\":{\"vp\":0,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 72 |
+
"constraints": {
|
| 73 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 74 |
+
"allowed_actions": [
|
| 75 |
+
{
|
| 76 |
+
"type": "place_starting_settlement",
|
| 77 |
+
"description": "Place your starting settlement",
|
| 78 |
+
"example_parameters": "{\"node\": X}"
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"type": "wait_for_response",
|
| 82 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 83 |
+
"example_parameters": "{}"
|
| 84 |
+
}
|
| 85 |
+
]
|
| 86 |
+
}
|
| 87 |
+
}
|
examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_2.json
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 2,
|
| 3 |
+
"timestamp": "2026-01-09T02:33:20.748104",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"is_active_turn": true,
|
| 6 |
+
"prompt": {
|
| 7 |
+
"meta_data": {
|
| 8 |
+
"agent_name": "A",
|
| 9 |
+
"role": null
|
| 10 |
+
},
|
| 11 |
+
"task_context": {
|
| 12 |
+
"what_just_happened": "• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!\n• Turn 0: Player 0's turn begins.\n• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT",
|
| 13 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 14 |
+
},
|
| 15 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[[20,\"A\",\"S\"]],\"rds\":[]},\"players\":{\"A\":{\"vp\":1,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 16 |
+
"social_context": {
|
| 17 |
+
"recent_chat": [
|
| 18 |
+
{
|
| 19 |
+
"from": "A",
|
| 20 |
+
"message": "I'm starting strong with a focus on early expansion! Let's see what the board has in store.",
|
| 21 |
+
"timestamp": 1767918800.7420254
|
| 22 |
+
}
|
| 23 |
+
]
|
| 24 |
+
},
|
| 25 |
+
"memory": {
|
| 26 |
+
"note_from_last_turn": "First pick: Node 20 (W11, S8, B6). Road to Node 19. This secures Wood, Sheep, Brick for early expansion. For the second settlement (my last pick), prioritize a strong Wheat/Ore spot, ideally Node 41 (O5, Wh9, B10) if available, or Node 24 (O3, Wh8, W3), Node 25 (S4, O3, Wh8), or Node 35 (S11, Wh6, O3)."
|
| 27 |
+
},
|
| 28 |
+
"constraints": {
|
| 29 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 30 |
+
"allowed_actions": [
|
| 31 |
+
{
|
| 32 |
+
"type": "place_starting_road",
|
| 33 |
+
"description": "Place your starting road",
|
| 34 |
+
"example_parameters": "{\"from\": X, \"to\": Y}"
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"type": "wait_for_response",
|
| 38 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 39 |
+
"example_parameters": "{}"
|
| 40 |
+
}
|
| 41 |
+
]
|
| 42 |
+
}
|
| 43 |
+
},
|
| 44 |
+
"response_schema": {
|
| 45 |
+
"type": "object",
|
| 46 |
+
"required": [
|
| 47 |
+
"internal_thinking",
|
| 48 |
+
"action"
|
| 49 |
+
],
|
| 50 |
+
"properties": {
|
| 51 |
+
"internal_thinking": {
|
| 52 |
+
"type": "string",
|
| 53 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 54 |
+
"minLength": 1000
|
| 55 |
+
},
|
| 56 |
+
"note_to_self": {
|
| 57 |
+
"type": "string",
|
| 58 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 59 |
+
"maxLength": 100
|
| 60 |
+
},
|
| 61 |
+
"say_outloud": {
|
| 62 |
+
"type": "string",
|
| 63 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 64 |
+
"maxLength": 100
|
| 65 |
+
},
|
| 66 |
+
"action": {
|
| 67 |
+
"type": "object",
|
| 68 |
+
"required": [
|
| 69 |
+
"type"
|
| 70 |
+
],
|
| 71 |
+
"properties": {
|
| 72 |
+
"type": {
|
| 73 |
+
"type": "string",
|
| 74 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 75 |
+
},
|
| 76 |
+
"parameters": {
|
| 77 |
+
"type": "string",
|
| 78 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 79 |
+
}
|
| 80 |
+
},
|
| 81 |
+
"propertyOrdering": [
|
| 82 |
+
"type",
|
| 83 |
+
"parameters"
|
| 84 |
+
]
|
| 85 |
+
}
|
| 86 |
+
},
|
| 87 |
+
"propertyOrdering": [
|
| 88 |
+
"internal_thinking",
|
| 89 |
+
"note_to_self",
|
| 90 |
+
"say_outloud",
|
| 91 |
+
"action"
|
| 92 |
+
]
|
| 93 |
+
},
|
| 94 |
+
"what_happened": "• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!\n• Turn 0: Player 0's turn begins.\n• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT",
|
| 95 |
+
"allowed_actions": [
|
| 96 |
+
{
|
| 97 |
+
"type": "place_starting_road",
|
| 98 |
+
"description": "Place your starting road",
|
| 99 |
+
"example_parameters": "{\"from\": X, \"to\": Y}"
|
| 100 |
+
},
|
| 101 |
+
{
|
| 102 |
+
"type": "wait_for_response",
|
| 103 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 104 |
+
"example_parameters": "{}"
|
| 105 |
+
}
|
| 106 |
+
]
|
| 107 |
+
}
|
examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_2.txt
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
=== Prompt #2 for A ===
|
| 2 |
+
Timestamp: 2026-01-09T02:33:20.748979
|
| 3 |
+
Active Turn: True
|
| 4 |
+
|
| 5 |
+
--- What Happened ---
|
| 6 |
+
• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!
|
| 7 |
+
• Turn 0: Player 0's turn begins.
|
| 8 |
+
• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT
|
| 9 |
+
|
| 10 |
+
--- Response Schema ---
|
| 11 |
+
{
|
| 12 |
+
"type": "object",
|
| 13 |
+
"required": [
|
| 14 |
+
"internal_thinking",
|
| 15 |
+
"action"
|
| 16 |
+
],
|
| 17 |
+
"properties": {
|
| 18 |
+
"internal_thinking": {
|
| 19 |
+
"type": "string",
|
| 20 |
+
"description": "Private strategy. What's your plan and why? Do not invent resources. Verify indices in Array N carefully. Write a detailed analysis (600+ chars) only AFTER verifying the node data",
|
| 21 |
+
"minLength": 1000
|
| 22 |
+
},
|
| 23 |
+
"note_to_self": {
|
| 24 |
+
"type": "string",
|
| 25 |
+
"description": "Save important observations and plans for future turns. Examples: opponent strategies, key nodes to claim, resource priorities, or threats to watch. This helps maintain continuity between turns.",
|
| 26 |
+
"maxLength": 100
|
| 27 |
+
},
|
| 28 |
+
"say_outloud": {
|
| 29 |
+
"type": "string",
|
| 30 |
+
"description": "Communicate with other players! Use for: trade proposals, warnings, bluffs, alliance hints, or strategic banter. Makes the game more interesting and can influence opponents.",
|
| 31 |
+
"maxLength": 100
|
| 32 |
+
},
|
| 33 |
+
"action": {
|
| 34 |
+
"type": "object",
|
| 35 |
+
"required": [
|
| 36 |
+
"type"
|
| 37 |
+
],
|
| 38 |
+
"properties": {
|
| 39 |
+
"type": {
|
| 40 |
+
"type": "string",
|
| 41 |
+
"description": "The action type (must match one from allowed_actions in constraints)"
|
| 42 |
+
},
|
| 43 |
+
"parameters": {
|
| 44 |
+
"type": "string",
|
| 45 |
+
"description": "Action parameters as JSON string. Example: {\"node\": 14} or {} if no parameters needed"
|
| 46 |
+
}
|
| 47 |
+
},
|
| 48 |
+
"propertyOrdering": [
|
| 49 |
+
"type",
|
| 50 |
+
"parameters"
|
| 51 |
+
]
|
| 52 |
+
}
|
| 53 |
+
},
|
| 54 |
+
"propertyOrdering": [
|
| 55 |
+
"internal_thinking",
|
| 56 |
+
"note_to_self",
|
| 57 |
+
"say_outloud",
|
| 58 |
+
"action"
|
| 59 |
+
]
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
--- Prompt Content ---
|
| 63 |
+
{
|
| 64 |
+
"meta_data": {
|
| 65 |
+
"agent_name": "A",
|
| 66 |
+
"role": null
|
| 67 |
+
},
|
| 68 |
+
"task_context": {
|
| 69 |
+
"what_just_happened": "• Game 6f4766ee-7795-4acd-9359-afd4ed606326 has started with 2 players!\n• Turn 0: Player 0's turn begins.\n• Player 0 performed action: ActionType.PLACE_STARTING_SETTLEMENT",
|
| 70 |
+
"instructions": "Analyze the game state and select the optimal move from 'allowed_actions'. You have 2 possible actions. If you wish to negotiate or wait for other players, select the 'wait_for_response' action."
|
| 71 |
+
},
|
| 72 |
+
"game_state": "\n 1. LOOKUP TABLES:\n • \"H\" (Hexes): Array where Index = HexID. Value = Resource+Num.\n Example: H[1]=\"W12\" -> Hex 1 is Wood 12.\n • \"N\" (Nodes): Array where Index = NodeID.\n Format: [ [Neighbors], [HexIDs], Port? ]\n Logic: To find yield of Node 10, check N[10]. Get HexIDs (e.g. [1,5]). Look up H[1] and H[5].\n\n2. CODES: W=Wood, B=Brick, S=Sheep, Wh=Wheat, O=Ore, D=Desert.\n ?3=Any 3:1 port, X2=Specific Resource 2:1 port.\n\n3. STATE: \"bld\"=[NodeID, Owner, Type], \"rds\"=[[From,To], Owner].\n\n4. PLAYERS: \"res\"={Resource:Count}, \"dev\"={\"h\":[Hidden Cards], \"r\":[Revealed] (K=Knight)}, \n \"stat\"=[\"LR\" (Longest Road), \"LA\" (Largest Army)].\n\n5. ROBBER: Located at HexID specified in \"meta.robber\". H[id] is blocked.\n\nJSON:\n{\"meta\":{\"curr\":\"A\",\"phase\":\"SETUP_FIRST_ROUND\",\"robber\":10,\"dice\":null},\"H\":[\"\",\"W12\",\"S5\",\"W4\",\"S8\",\"B6\",\"W3\",\"Wh8\",\"B10\",\"W11\",\"D\",\"O3\",\"S4\",\"B10\",\"Wh9\",\"Wh6\",\"S11\",\"O5\",\"Wh9\",\"O2\"],\"N\":[null,[[2,9],[1]],[[1,3],[1],\"Wh2\"],[[2,4,11],[2,1],\"Wh2\"],[[3,5],[2]],[[4,6,13],[3,2]],[[5,7],[3],\"B2\"],[[6,15],[3],\"B2\"],[[9,18],[4],\"?3\"],[[8,10,1],[4,1],\"?3\"],[[9,11,20],[5,4,1]],[[10,12,3],[5,2,1]],[[11,13,22],[6,5,2]],[[12,14,5],[6,3,2]],[[13,15,24],[7,6,3]],[[14,16,7],[7,3]],[[15,26],[7],\"O2\"],[[18,28],[8],\"S2\"],[[17,19,8],[8,4]],[[18,20,30],[9,8,4]],[[19,21,10],[9,5,4]],[[20,22,32],[10,9,5]],[[21,23,12],[10,6,5]],[[22,24,34],[11,10,6]],[[23,25,14],[11,7,6]],[[24,26,36],[12,11,7]],[[25,27,16],[12,7],\"O2\"],[[26,38],[12]],[[29,17],[8],\"S2\"],[[28,30,39],[13,8]],[[29,31,19],[13,9,8]],[[30,32,41],[14,13,9]],[[31,33,21],[14,10,9]],[[32,34,43],[15,14,10]],[[33,35,23],[15,11,10]],[[34,36,45],[16,15,11]],[[35,37,25],[16,12,11]],[[36,38,47],[16,12],\"?3\"],[[37,27],[12],\"?3\"],[[40,29],[13]],[[39,41,48],[17,13],\"W2\"],[[40,42,31],[17,14,13]],[[41,43,50],[18,17,14]],[[42,44,33],[18,15,14]],[[43,45,52],[19,18,15]],[[44,46,35],[19,16,15]],[[45,47,54],[19,16]],[[46,37],[16]],[[49,40],[17],\"W2\"],[[48,50],[17]],[[49,51,42],[18,17],\"?3\"],[[50,52],[18],\"?3\"],[[51,53,44],[19,18]],[[52,54],[19],\"?3\"],[[53,46],[19],\"?3\"]],\"state\":{\"bld\":[[20,\"A\",\"S\"]],\"rds\":[]},\"players\":{\"A\":{\"vp\":1,\"res\":{}},\"B\":{\"vp\":0,\"res\":{}}}}",
|
| 73 |
+
"social_context": {
|
| 74 |
+
"recent_chat": [
|
| 75 |
+
{
|
| 76 |
+
"from": "A",
|
| 77 |
+
"message": "I'm starting strong with a focus on early expansion! Let's see what the board has in store.",
|
| 78 |
+
"timestamp": 1767918800.7420254
|
| 79 |
+
}
|
| 80 |
+
]
|
| 81 |
+
},
|
| 82 |
+
"memory": {
|
| 83 |
+
"note_from_last_turn": "First pick: Node 20 (W11, S8, B6). Road to Node 19. This secures Wood, Sheep, Brick for early expansion. For the second settlement (my last pick), prioritize a strong Wheat/Ore spot, ideally Node 41 (O5, Wh9, B10) if available, or Node 24 (O3, Wh8, W3), Node 25 (S4, O3, Wh8), or Node 35 (S11, Wh6, O3)."
|
| 84 |
+
},
|
| 85 |
+
"constraints": {
|
| 86 |
+
"usage_instructions": "Choose one action type from the list below. Populate the 'parameters' field in your response strictly according to the 'example_parameters' structure provided.",
|
| 87 |
+
"allowed_actions": [
|
| 88 |
+
{
|
| 89 |
+
"type": "place_starting_road",
|
| 90 |
+
"description": "Place your starting road",
|
| 91 |
+
"example_parameters": "{\"from\": X, \"to\": Y}"
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"type": "wait_for_response",
|
| 95 |
+
"description": "Do nothing on the board, just wait or communicate. Use this when you want to talk, negotiate, or think without taking a game action.",
|
| 96 |
+
"example_parameters": "{}"
|
| 97 |
+
}
|
| 98 |
+
]
|
| 99 |
+
}
|
| 100 |
+
}
|
examples/ai_testing/my_games/session_20260109_023135/A/responses/response_1.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 1,
|
| 3 |
+
"timestamp": "2026-01-09T02:32:24.733786",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"success": true,
|
| 6 |
+
"raw_content": "{\n \"internal_thinking\": \"This is the first turn of the game (SETUP_FIRST_ROUND), and I am player A, so I get the first pick for a settlement and an adjacent road. My strategy is to secure a node with a strong combination of resources and high-probability dice rolls. I need to balance early game expansion needs (Wood, Brick, Sheep for roads and settlements) with long-term development needs (Wheat, Ore for cities and development cards). I will also consider access to ports for future trading.\\n\\nI evaluated several nodes:\\n\\n1. **Node 41 (O5, Wh9, B10)**: This node offers Ore (5), Wheat (9), and Brick (10). The rolls (5, 9, 10) are excellent, providing a strong foundation for mid-to-late game development (cities and dev cards). It lacks Wood and Sheep, which are crucial for early expansion. It is adjacent to Node 40, which has a W2 port (N[40] = [[39,41,48],[17,13],\\\"W2\\\"]). However, a W2 port is most beneficial if I produce Wood, which I would not from Node 41. It would allow me to trade 2 Wood for 1 of another resource, but I'd still need to acquire Wood at 4:1 or 3:1.\\n\\n2. **Node 20 (W11, S8, B6)**: This node offers Wood (11), Sheep (8), and Brick (6). The rolls (11, 8, 6) are very good, with 8 and 6 being high-probability numbers. This combination provides the essential resources for early game expansion (roads and settlements). It lacks Wheat and Ore, which are crucial for cities and development cards.\\n\\nComparing these two primary options:\\n* Node 41 has a combined pip value of 4+3+3=10.\\n* Node 20 has a combined pip value of 3+5+5=13.\\n\\nNode 20 offers higher overall probability production and provides the resources (Wood, Brick, Sheep) that are immediately needed for building roads and additional settlements. While Node 41's resources (Ore, Wheat, Brick) are powerful for the mid-game, securing early expansion capability is generally more critical for the first settlement. I can aim for a strong Wheat/Ore spot as my second settlement, after Player B has made their two picks.\\n\\nTherefore, I will choose Node 20 for my first settlement due to its superior rolls and the crucial Wood, Brick, and Sheep production for early expansion. For the road, I will choose to build towards Node 19 (N[19] = [[18,20,30],[9,8,4]]), which connects to hexes W11, B10, S8. This extends my reach into a good resource area and keeps options open for connecting to a ?3 port later on via Node 9.\",\n \"note_to_self\": \"First pick: Node 20 (W11, S8, B6). Road to Node 19. This secures Wood, Sheep, Brick for early expansion. For the second settlement (my last pick), prioritize a strong Wheat/Ore spot, ideally Node 41 (O5, Wh9, B10) if available, or Node 24 (O3, Wh8, W3), Node 25 (S4, O3, Wh8), or Node 35 (S11, Wh6, O3).\",\n \"say_outloud\": \"I'm starting strong with a focus on early expansion! Let's see what the board has in store.\",\n \"action\": {\n \"type\": \"place_starting_settlement\",\n \"parameters\": \"{\\\"node\\\": 20, \\\"road\\\": 19}\"\n }\n}",
|
| 7 |
+
"parsed": {
|
| 8 |
+
"internal_thinking": "This is the first turn of the game (SETUP_FIRST_ROUND), and I am player A, so I get the first pick for a settlement and an adjacent road. My strategy is to secure a node with a strong combination of resources and high-probability dice rolls. I need to balance early game expansion needs (Wood, Brick, Sheep for roads and settlements) with long-term development needs (Wheat, Ore for cities and development cards). I will also consider access to ports for future trading.\n\nI evaluated several nodes:\n\n1. **Node 41 (O5, Wh9, B10)**: This node offers Ore (5), Wheat (9), and Brick (10). The rolls (5, 9, 10) are excellent, providing a strong foundation for mid-to-late game development (cities and dev cards). It lacks Wood and Sheep, which are crucial for early expansion. It is adjacent to Node 40, which has a W2 port (N[40] = [[39,41,48],[17,13],\"W2\"]). However, a W2 port is most beneficial if I produce Wood, which I would not from Node 41. It would allow me to trade 2 Wood for 1 of another resource, but I'd still need to acquire Wood at 4:1 or 3:1.\n\n2. **Node 20 (W11, S8, B6)**: This node offers Wood (11), Sheep (8), and Brick (6). The rolls (11, 8, 6) are very good, with 8 and 6 being high-probability numbers. This combination provides the essential resources for early game expansion (roads and settlements). It lacks Wheat and Ore, which are crucial for cities and development cards.\n\nComparing these two primary options:\n* Node 41 has a combined pip value of 4+3+3=10.\n* Node 20 has a combined pip value of 3+5+5=13.\n\nNode 20 offers higher overall probability production and provides the resources (Wood, Brick, Sheep) that are immediately needed for building roads and additional settlements. While Node 41's resources (Ore, Wheat, Brick) are powerful for the mid-game, securing early expansion capability is generally more critical for the first settlement. I can aim for a strong Wheat/Ore spot as my second settlement, after Player B has made their two picks.\n\nTherefore, I will choose Node 20 for my first settlement due to its superior rolls and the crucial Wood, Brick, and Sheep production for early expansion. For the road, I will choose to build towards Node 19 (N[19] = [[18,20,30],[9,8,4]]), which connects to hexes W11, B10, S8. This extends my reach into a good resource area and keeps options open for connecting to a ?3 port later on via Node 9.",
|
| 9 |
+
"note_to_self": "First pick: Node 20 (W11, S8, B6). Road to Node 19. This secures Wood, Sheep, Brick for early expansion. For the second settlement (my last pick), prioritize a strong Wheat/Ore spot, ideally Node 41 (O5, Wh9, B10) if available, or Node 24 (O3, Wh8, W3), Node 25 (S4, O3, Wh8), or Node 35 (S11, Wh6, O3).",
|
| 10 |
+
"say_outloud": "I'm starting strong with a focus on early expansion! Let's see what the board has in store.",
|
| 11 |
+
"action_type": "place_starting_settlement",
|
| 12 |
+
"parameters": {
|
| 13 |
+
"node": 20,
|
| 14 |
+
"road": 19
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"model": "gemini-2.5-flash",
|
| 18 |
+
"tokens": {
|
| 19 |
+
"prompt": 1624,
|
| 20 |
+
"completion": 873,
|
| 21 |
+
"total": 11861
|
| 22 |
+
},
|
| 23 |
+
"latency_seconds": 45.71647930145264,
|
| 24 |
+
"error": null
|
| 25 |
+
}
|
examples/ai_testing/my_games/session_20260109_023135/A/responses/response_2.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_number": 2,
|
| 3 |
+
"timestamp": "2026-01-09T02:33:27.218158",
|
| 4 |
+
"player_name": "A",
|
| 5 |
+
"success": true,
|
| 6 |
+
"raw_content": "{\n \"internal_thinking\": \"The game is in the SETUP_FIRST_ROUND phase, and I have just placed my first settlement at Node 20. My `note_from_last_turn` clearly outlined this move: \\\"First pick: Node 20 (W11, S8, B6). Road to Node 19.\\\" I have verified that Node 20 is indeed connected to Hexes 9 (W11), 5 (B6), and 4 (S8), providing a solid initial resource base of wood, brick, and sheep. The next required action in this phase is to place a starting road. According to my plan, the road should extend from Node 20 to Node 19. Node 19 is a valid neighbor of Node 20, as confirmed by N[20]'s neighbor list. This action directly follows my pre-established strategy for the first settlement and road placement, aiming for early expansion potential by securing key resources. This move sets the stage for my second settlement placement, where I'll prioritize wheat and ore, as noted in my previous turn's memory.\",\n \"note_to_self\": \"Confirmed first settlement at Node 20 (W11, B6, S8) and road to Node 19. For the second settlement (my last pick in this round), I still need to prioritize a strong Wheat/Ore spot. Node 41 (O5, Wh9, B10) remains a high priority if available, followed by Node 24 (O3, Wh8, W3), Node 25 (S4, O3, Wh8), or Node 35 (S11, Wh6, O3). I will need to observe Player B's next move to see what nodes they claim, which will influence my final decision.\",\n \"say_outloud\": \"My first settlement is placed, and now my first road. I'm focusing on securing resources for a strong early game build out!\",\n \"action\": {\n \"type\": \"place_starting_road\",\n \"parameters\": \"{\\\"from\\\": 20, \\\"to\\\": 19}\"\n }\n}",
|
| 7 |
+
"parsed": {
|
| 8 |
+
"internal_thinking": "The game is in the SETUP_FIRST_ROUND phase, and I have just placed my first settlement at Node 20. My `note_from_last_turn` clearly outlined this move: \"First pick: Node 20 (W11, S8, B6). Road to Node 19.\" I have verified that Node 20 is indeed connected to Hexes 9 (W11), 5 (B6), and 4 (S8), providing a solid initial resource base of wood, brick, and sheep. The next required action in this phase is to place a starting road. According to my plan, the road should extend from Node 20 to Node 19. Node 19 is a valid neighbor of Node 20, as confirmed by N[20]'s neighbor list. This action directly follows my pre-established strategy for the first settlement and road placement, aiming for early expansion potential by securing key resources. This move sets the stage for my second settlement placement, where I'll prioritize wheat and ore, as noted in my previous turn's memory. [Response was too brief]",
|
| 9 |
+
"note_to_self": "Confirmed first settlement at Node 20 (W11, B6, S8) and road to Node 19. For the second settlement (my last pick in this round), I still need to prioritize a strong Wheat/Ore spot. Node 41 (O5, Wh9, B10) remains a high priority if available, followed by Node 24 (O3, Wh8, W3), Node 25 (S4, O3, Wh8), or Node 35 (S11, Wh6, O3). I will need to observe Player B's next move to see what nodes they claim, which will influence my final decision.",
|
| 10 |
+
"say_outloud": "My first settlement is placed, and now my first road. I'm focusing on securing resources for a strong early game build out!",
|
| 11 |
+
"action_type": "place_starting_road",
|
| 12 |
+
"parameters": {
|
| 13 |
+
"from": 20,
|
| 14 |
+
"to": 19
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"model": "gemini-2.5-flash",
|
| 18 |
+
"tokens": {
|
| 19 |
+
"prompt": 1883,
|
| 20 |
+
"completion": 474,
|
| 21 |
+
"total": 3038
|
| 22 |
+
},
|
| 23 |
+
"latency_seconds": 6.466033220291138,
|
| 24 |
+
"error": null
|
| 25 |
+
}
|
examples/ai_testing/my_games/session_20260109_023135/chat_history.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"timestamp": "2026-01-09T02:33:20.742162",
|
| 4 |
+
"from": "A",
|
| 5 |
+
"to": "all",
|
| 6 |
+
"message": "I'm starting strong with a focus on early expansion! Let's see what the board has in store."
|
| 7 |
+
}
|
| 8 |
+
]
|
examples/ai_testing/my_games/session_20260109_023135/session_metadata.json
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"session_id": "session_20260109_023135",
|
| 3 |
+
"start_time": "2026-01-09T02:31:35.675685",
|
| 4 |
+
"version": "2.0"
|
| 5 |
+
}
|
examples/example_agent_tools_integration.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Example: Using Agent Tools with AIManager
|
| 3 |
+
|
| 4 |
+
This example shows how to integrate the 3 agent tools
|
| 5 |
+
with the AIManager for enhanced AI decision-making.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from pycatan.ai import AIManager, AgentTools
|
| 9 |
+
import json
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def example_1_tools_standalone():
|
| 13 |
+
"""Example 1: Using tools standalone (without AIManager)."""
|
| 14 |
+
print("\n" + "="*60)
|
| 15 |
+
print("EXAMPLE 1: Using Tools Standalone")
|
| 16 |
+
print("="*60)
|
| 17 |
+
|
| 18 |
+
tools = AgentTools()
|
| 19 |
+
|
| 20 |
+
# Scenario: AI has some resources and needs to decide what to build
|
| 21 |
+
my_resources = {
|
| 22 |
+
"wood": 2,
|
| 23 |
+
"brick": 1,
|
| 24 |
+
"wheat": 1,
|
| 25 |
+
"sheep": 0,
|
| 26 |
+
"ore": 1
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
print("\n📦 My Resources:", my_resources)
|
| 30 |
+
|
| 31 |
+
# Check what I can afford
|
| 32 |
+
print("\n💰 Can I afford a settlement?")
|
| 33 |
+
result = tools.calculate_building_costs("settlement", my_resources)
|
| 34 |
+
print(json.dumps(result, indent=2))
|
| 35 |
+
|
| 36 |
+
print("\n💰 Can I afford a road?")
|
| 37 |
+
result = tools.calculate_building_costs("road", my_resources)
|
| 38 |
+
print(json.dumps(result, indent=2))
|
| 39 |
+
|
| 40 |
+
print("\n💰 Can I afford a city?")
|
| 41 |
+
result = tools.calculate_building_costs("city", my_resources)
|
| 42 |
+
print(json.dumps(result, indent=2))
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def example_2_evaluate_positions():
|
| 46 |
+
"""Example 2: Compare multiple settlement positions."""
|
| 47 |
+
print("\n" + "="*60)
|
| 48 |
+
print("EXAMPLE 2: Evaluating Settlement Positions")
|
| 49 |
+
print("="*60)
|
| 50 |
+
|
| 51 |
+
tools = AgentTools()
|
| 52 |
+
|
| 53 |
+
# Three potential positions to compare
|
| 54 |
+
positions = [
|
| 55 |
+
{
|
| 56 |
+
"name": "Position A (coastal with harbor)",
|
| 57 |
+
"tiles": [
|
| 58 |
+
{"resource": "wood", "number": 9},
|
| 59 |
+
{"resource": "wheat", "number": 10}
|
| 60 |
+
],
|
| 61 |
+
"harbor": True,
|
| 62 |
+
"harbor_type": "3:1"
|
| 63 |
+
},
|
| 64 |
+
{
|
| 65 |
+
"name": "Position B (inland high-value)",
|
| 66 |
+
"tiles": [
|
| 67 |
+
{"resource": "wheat", "number": 6},
|
| 68 |
+
{"resource": "wood", "number": 8},
|
| 69 |
+
{"resource": "brick", "number": 5}
|
| 70 |
+
],
|
| 71 |
+
"harbor": False
|
| 72 |
+
},
|
| 73 |
+
{
|
| 74 |
+
"name": "Position C (ore-focused with harbor)",
|
| 75 |
+
"tiles": [
|
| 76 |
+
{"resource": "ore", "number": 6},
|
| 77 |
+
{"resource": "ore", "number": 8},
|
| 78 |
+
{"resource": "wheat", "number": 4}
|
| 79 |
+
],
|
| 80 |
+
"harbor": True,
|
| 81 |
+
"harbor_type": "ore"
|
| 82 |
+
}
|
| 83 |
+
]
|
| 84 |
+
|
| 85 |
+
print("\n📍 Comparing 3 positions:\n")
|
| 86 |
+
|
| 87 |
+
results = []
|
| 88 |
+
for pos in positions:
|
| 89 |
+
result = tools.evaluate_node_value(
|
| 90 |
+
pos["tiles"],
|
| 91 |
+
pos["harbor"],
|
| 92 |
+
pos.get("harbor_type")
|
| 93 |
+
)
|
| 94 |
+
results.append({
|
| 95 |
+
"name": pos["name"],
|
| 96 |
+
"score": result["overall_score"],
|
| 97 |
+
"details": result
|
| 98 |
+
})
|
| 99 |
+
|
| 100 |
+
print(f"\n{pos['name']}:")
|
| 101 |
+
print(f" Score: {result['overall_score']}")
|
| 102 |
+
print(f" Pips: {result['total_pip_value']}")
|
| 103 |
+
print(f" Diversity: {result['resource_diversity']} resources")
|
| 104 |
+
print(f" Resources: {result['resources']}")
|
| 105 |
+
if result['has_harbor']:
|
| 106 |
+
print(f" Harbor: {result.get('harbor_type', 'generic')}")
|
| 107 |
+
|
| 108 |
+
# Find best position
|
| 109 |
+
best = max(results, key=lambda x: x["score"])
|
| 110 |
+
print(f"\n🏆 BEST POSITION: {best['name']} (Score: {best['score']})")
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def example_3_winning_strategy():
|
| 114 |
+
"""Example 3: Plan path to victory."""
|
| 115 |
+
print("\n" + "="*60)
|
| 116 |
+
print("EXAMPLE 3: Planning Path to Victory")
|
| 117 |
+
print("="*60)
|
| 118 |
+
|
| 119 |
+
tools = AgentTools()
|
| 120 |
+
|
| 121 |
+
# Scenario: Mid-game situation
|
| 122 |
+
print("\n📊 Current Situation:")
|
| 123 |
+
print(" Victory Points: 7")
|
| 124 |
+
print(" Settlements: 3")
|
| 125 |
+
print(" Cities: 1")
|
| 126 |
+
print(" Longest Road: Yes (2 VP)")
|
| 127 |
+
print(" Largest Army: No")
|
| 128 |
+
|
| 129 |
+
result = tools.check_winning_path(
|
| 130 |
+
current_vp=7,
|
| 131 |
+
settlements=3,
|
| 132 |
+
cities=1,
|
| 133 |
+
longest_road_owned=True,
|
| 134 |
+
largest_army_owned=False
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
print(f"\n🎯 Need {result['vp_needed']} more VP to win!")
|
| 138 |
+
|
| 139 |
+
print("\n📈 Paths to Victory:")
|
| 140 |
+
for i, path in enumerate(result['paths_to_victory'], 1):
|
| 141 |
+
print(f" {i}. {path}")
|
| 142 |
+
|
| 143 |
+
print("\n💡 Recommendations:")
|
| 144 |
+
for rec in result['recommendations']:
|
| 145 |
+
print(f" • {rec}")
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def example_4_integrated_decision():
|
| 149 |
+
"""Example 4: Complete decision-making scenario."""
|
| 150 |
+
print("\n" + "="*60)
|
| 151 |
+
print("EXAMPLE 4: Integrated Decision Making")
|
| 152 |
+
print("="*60)
|
| 153 |
+
|
| 154 |
+
tools = AgentTools()
|
| 155 |
+
|
| 156 |
+
# Full scenario: AI needs to decide its strategy
|
| 157 |
+
game_state = {
|
| 158 |
+
"my_resources": {
|
| 159 |
+
"wood": 2,
|
| 160 |
+
"brick": 2,
|
| 161 |
+
"wheat": 3,
|
| 162 |
+
"sheep": 1,
|
| 163 |
+
"ore": 2
|
| 164 |
+
},
|
| 165 |
+
"my_vp": 6,
|
| 166 |
+
"settlements": 3,
|
| 167 |
+
"cities": 1,
|
| 168 |
+
"longest_road": False,
|
| 169 |
+
"largest_army": False
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
print("\n🎮 Current Game State:")
|
| 173 |
+
print(f" Resources: {game_state['my_resources']}")
|
| 174 |
+
print(f" VP: {game_state['my_vp']}")
|
| 175 |
+
print(f" Buildings: {game_state['settlements']} settlements, {game_state['cities']} cities")
|
| 176 |
+
|
| 177 |
+
print("\n\n🤔 AI Decision Process:\n")
|
| 178 |
+
|
| 179 |
+
# Step 1: Check winning path
|
| 180 |
+
print("1️⃣ Checking winning strategy...")
|
| 181 |
+
winning = tools.check_winning_path(
|
| 182 |
+
game_state['my_vp'],
|
| 183 |
+
game_state['settlements'],
|
| 184 |
+
game_state['cities'],
|
| 185 |
+
game_state['longest_road'],
|
| 186 |
+
game_state['largest_army']
|
| 187 |
+
)
|
| 188 |
+
print(f" Need {winning['vp_needed']} VP to win")
|
| 189 |
+
print(f" Top recommendation: {winning['recommendations'][0]}")
|
| 190 |
+
|
| 191 |
+
# Step 2: Check if can afford recommended action
|
| 192 |
+
print("\n2️⃣ Checking what I can afford...")
|
| 193 |
+
|
| 194 |
+
# Cities seem like a good path
|
| 195 |
+
city = tools.calculate_building_costs("city", game_state['my_resources'])
|
| 196 |
+
print(f" City: {'✅ Can afford!' if city['can_afford'] else '❌ Cannot afford'}")
|
| 197 |
+
if not city['can_afford']:
|
| 198 |
+
print(f" Missing: {city['missing']}")
|
| 199 |
+
|
| 200 |
+
# Check settlement
|
| 201 |
+
settlement = tools.calculate_building_costs("settlement", game_state['my_resources'])
|
| 202 |
+
print(f" Settlement: {'✅ Can afford!' if settlement['can_afford'] else '❌ Cannot afford'}")
|
| 203 |
+
|
| 204 |
+
# Check dev card
|
| 205 |
+
dev = tools.calculate_building_costs("development_card", game_state['my_resources'])
|
| 206 |
+
print(f" Dev Card: {'✅ Can afford!' if dev['can_afford'] else '❌ Cannot afford'}")
|
| 207 |
+
|
| 208 |
+
# Step 3: Make decision
|
| 209 |
+
print("\n3️⃣ Final Decision:")
|
| 210 |
+
if city['can_afford']:
|
| 211 |
+
print(" 🏛️ BUILD CITY - Gets 2 VP, moves me to 8 VP (only 2 VP from winning!)")
|
| 212 |
+
elif settlement['can_afford']:
|
| 213 |
+
print(" 🏠 BUILD SETTLEMENT - Gets 1 VP, steady progress")
|
| 214 |
+
elif dev['can_afford']:
|
| 215 |
+
print(" 🎴 BUY DEVELOPMENT CARD - Could get VP card or knights for Largest Army")
|
| 216 |
+
else:
|
| 217 |
+
print(" 💰 TRADE or WAIT - Need to get more resources first")
|
| 218 |
+
|
| 219 |
+
print("\n" + "="*60)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def example_5_tool_schema_for_llm():
|
| 223 |
+
"""Example 5: Get tool schemas for LLM integration."""
|
| 224 |
+
print("\n" + "="*60)
|
| 225 |
+
print("EXAMPLE 5: Tool Schemas for LLM")
|
| 226 |
+
print("="*60)
|
| 227 |
+
|
| 228 |
+
tools = AgentTools()
|
| 229 |
+
|
| 230 |
+
# Get schemas that can be sent to LLM APIs
|
| 231 |
+
schemas = tools.get_tools_schema()
|
| 232 |
+
|
| 233 |
+
print("\n📋 Generated schemas for 3 tools:")
|
| 234 |
+
print(f" Total tools: {len(schemas)}")
|
| 235 |
+
|
| 236 |
+
for tool in schemas:
|
| 237 |
+
print(f"\n • {tool['name']}")
|
| 238 |
+
print(f" Description: {tool['description']}")
|
| 239 |
+
print(f" Parameters: {list(tool['parameters']['properties'].keys())}")
|
| 240 |
+
|
| 241 |
+
print("\n💡 These schemas can be passed to:")
|
| 242 |
+
print(" - OpenAI GPT-4 (function calling)")
|
| 243 |
+
print(" - Anthropic Claude (tool use)")
|
| 244 |
+
print(" - Google Gemini (function declarations)")
|
| 245 |
+
print(" - Or included in system prompt as tool descriptions")
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def main():
|
| 249 |
+
"""Run all examples."""
|
| 250 |
+
print("\n" + "="*60)
|
| 251 |
+
print(" AGENT TOOLS INTEGRATION EXAMPLES")
|
| 252 |
+
print(" Demonstrating 3 helper tools for AI agents")
|
| 253 |
+
print("="*60)
|
| 254 |
+
|
| 255 |
+
example_1_tools_standalone()
|
| 256 |
+
example_2_evaluate_positions()
|
| 257 |
+
example_3_winning_strategy()
|
| 258 |
+
example_4_integrated_decision()
|
| 259 |
+
example_5_tool_schema_for_llm()
|
| 260 |
+
|
| 261 |
+
print("\n" + "="*60)
|
| 262 |
+
print(" ✅ ALL EXAMPLES COMPLETED")
|
| 263 |
+
print("="*60)
|
| 264 |
+
print("\nNext steps:")
|
| 265 |
+
print(" 1. Integrate tools with AIManager.process_agent_turn()")
|
| 266 |
+
print(" 2. Update prompts to mention available tools")
|
| 267 |
+
print(" 3. Parse tool calls from LLM responses")
|
| 268 |
+
print(" 4. Log tool usage in AILogger")
|
| 269 |
+
print()
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
if __name__ == "__main__":
|
| 273 |
+
main()
|
examples/test_agent_tools.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test script for Agent Tools
|
| 3 |
+
|
| 4 |
+
This script demonstrates how the 3 agent tools work and validates
|
| 5 |
+
that they provide useful information for the AI.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from pycatan.ai.agent_tools import AgentTools
|
| 9 |
+
import json
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def print_section(title: str):
|
| 13 |
+
"""Print a section header."""
|
| 14 |
+
print(f"\n{'='*60}")
|
| 15 |
+
print(f" {title}")
|
| 16 |
+
print(f"{'='*60}\n")
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def test_calculate_building_costs():
|
| 20 |
+
"""Test the building costs calculator."""
|
| 21 |
+
print_section("TOOL 1: Calculate Building Costs")
|
| 22 |
+
|
| 23 |
+
tools = AgentTools()
|
| 24 |
+
|
| 25 |
+
# Test 1: Can afford settlement
|
| 26 |
+
print("Test 1: Check if we can build a settlement")
|
| 27 |
+
print("Resources: wood=1, brick=1, wheat=1, sheep=1")
|
| 28 |
+
result = tools.calculate_building_costs(
|
| 29 |
+
"settlement",
|
| 30 |
+
{"wood": 1, "brick": 1, "wheat": 1, "sheep": 1}
|
| 31 |
+
)
|
| 32 |
+
print(json.dumps(result, indent=2))
|
| 33 |
+
|
| 34 |
+
# Test 2: Missing resources
|
| 35 |
+
print("\n\nTest 2: Missing brick for settlement")
|
| 36 |
+
print("Resources: wood=1, brick=0, wheat=2, sheep=1")
|
| 37 |
+
result = tools.calculate_building_costs(
|
| 38 |
+
"settlement",
|
| 39 |
+
{"wood": 1, "brick": 0, "wheat": 2, "sheep": 1}
|
| 40 |
+
)
|
| 41 |
+
print(json.dumps(result, indent=2))
|
| 42 |
+
|
| 43 |
+
# Test 3: City costs
|
| 44 |
+
print("\n\nTest 3: Check city costs")
|
| 45 |
+
print("Resources: wheat=3, ore=2")
|
| 46 |
+
result = tools.calculate_building_costs(
|
| 47 |
+
"city",
|
| 48 |
+
{"wheat": 3, "ore": 2}
|
| 49 |
+
)
|
| 50 |
+
print(json.dumps(result, indent=2))
|
| 51 |
+
|
| 52 |
+
# Test 4: Development card
|
| 53 |
+
print("\n\nTest 4: Can afford development card")
|
| 54 |
+
print("Resources: wheat=1, sheep=1, ore=1")
|
| 55 |
+
result = tools.calculate_building_costs(
|
| 56 |
+
"development_card",
|
| 57 |
+
{"wheat": 1, "sheep": 1, "ore": 1}
|
| 58 |
+
)
|
| 59 |
+
print(json.dumps(result, indent=2))
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def test_evaluate_node_value():
|
| 63 |
+
"""Test the node value evaluator."""
|
| 64 |
+
print_section("TOOL 2: Evaluate Node Value")
|
| 65 |
+
|
| 66 |
+
tools = AgentTools()
|
| 67 |
+
|
| 68 |
+
# Test 1: Excellent position (6, 8, 5)
|
| 69 |
+
print("Test 1: Excellent position - diverse and high probability")
|
| 70 |
+
print("Tiles: wheat(6), wood(8), brick(5)")
|
| 71 |
+
result = tools.evaluate_node_value([
|
| 72 |
+
{"resource": "wheat", "number": 6},
|
| 73 |
+
{"resource": "wood", "number": 8},
|
| 74 |
+
{"resource": "brick", "number": 5}
|
| 75 |
+
])
|
| 76 |
+
print(json.dumps(result, indent=2))
|
| 77 |
+
|
| 78 |
+
# Test 2: Poor position (2, 12)
|
| 79 |
+
print("\n\nTest 2: Poor position - low probability numbers")
|
| 80 |
+
print("Tiles: ore(2), sheep(12)")
|
| 81 |
+
result = tools.evaluate_node_value([
|
| 82 |
+
{"resource": "ore", "number": 2},
|
| 83 |
+
{"resource": "sheep", "number": 12}
|
| 84 |
+
])
|
| 85 |
+
print(json.dumps(result, indent=2))
|
| 86 |
+
|
| 87 |
+
# Test 3: With harbor
|
| 88 |
+
print("\n\nTest 3: Decent position with 3:1 harbor")
|
| 89 |
+
print("Tiles: wood(9), wheat(10), Harbor: 3:1")
|
| 90 |
+
result = tools.evaluate_node_value(
|
| 91 |
+
[
|
| 92 |
+
{"resource": "wood", "number": 9},
|
| 93 |
+
{"resource": "wheat", "number": 10}
|
| 94 |
+
],
|
| 95 |
+
include_harbor=True,
|
| 96 |
+
harbor_type="3:1"
|
| 97 |
+
)
|
| 98 |
+
print(json.dumps(result, indent=2))
|
| 99 |
+
|
| 100 |
+
# Test 4: With specialized harbor
|
| 101 |
+
print("\n\nTest 4: Ore heavy position with ore harbor")
|
| 102 |
+
print("Tiles: ore(6), ore(8), wheat(4), Harbor: ore")
|
| 103 |
+
result = tools.evaluate_node_value(
|
| 104 |
+
[
|
| 105 |
+
{"resource": "ore", "number": 6},
|
| 106 |
+
{"resource": "ore", "number": 8},
|
| 107 |
+
{"resource": "wheat", "number": 4}
|
| 108 |
+
],
|
| 109 |
+
include_harbor=True,
|
| 110 |
+
harbor_type="ore"
|
| 111 |
+
)
|
| 112 |
+
print(json.dumps(result, indent=2))
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def test_check_winning_path():
|
| 116 |
+
"""Test the winning path analyzer."""
|
| 117 |
+
print_section("TOOL 3: Check Winning Path")
|
| 118 |
+
|
| 119 |
+
tools = AgentTools()
|
| 120 |
+
|
| 121 |
+
# Test 1: Close to winning
|
| 122 |
+
print("Test 1: Close to winning (8 VP)")
|
| 123 |
+
print("VP: 8, Settlements: 2, Cities: 2, Longest Road: Yes")
|
| 124 |
+
result = tools.check_winning_path(
|
| 125 |
+
current_vp=8,
|
| 126 |
+
settlements=2,
|
| 127 |
+
cities=2,
|
| 128 |
+
longest_road_owned=True
|
| 129 |
+
)
|
| 130 |
+
print(json.dumps(result, indent=2))
|
| 131 |
+
|
| 132 |
+
# Test 2: Early game
|
| 133 |
+
print("\n\nTest 2: Early game (4 VP)")
|
| 134 |
+
print("VP: 4, Settlements: 4, Cities: 0")
|
| 135 |
+
result = tools.check_winning_path(
|
| 136 |
+
current_vp=4,
|
| 137 |
+
settlements=4,
|
| 138 |
+
cities=0
|
| 139 |
+
)
|
| 140 |
+
print(json.dumps(result, indent=2))
|
| 141 |
+
|
| 142 |
+
# Test 3: With special cards
|
| 143 |
+
print("\n\nTest 3: Mid-game with Largest Army")
|
| 144 |
+
print("VP: 6, Settlements: 3, Cities: 1, Largest Army: Yes")
|
| 145 |
+
result = tools.check_winning_path(
|
| 146 |
+
current_vp=6,
|
| 147 |
+
settlements=3,
|
| 148 |
+
cities=1,
|
| 149 |
+
largest_army_owned=True
|
| 150 |
+
)
|
| 151 |
+
print(json.dumps(result, indent=2))
|
| 152 |
+
|
| 153 |
+
# Test 4: Already won
|
| 154 |
+
print("\n\nTest 4: Already won!")
|
| 155 |
+
print("VP: 10, Settlements: 2, Cities: 3, Both special cards")
|
| 156 |
+
result = tools.check_winning_path(
|
| 157 |
+
current_vp=10,
|
| 158 |
+
settlements=2,
|
| 159 |
+
cities=3,
|
| 160 |
+
longest_road_owned=True,
|
| 161 |
+
largest_army_owned=True
|
| 162 |
+
)
|
| 163 |
+
print(json.dumps(result, indent=2))
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def test_tool_schema():
|
| 167 |
+
"""Test tool schema generation for LLM."""
|
| 168 |
+
print_section("Tool Schema for LLM Function Calling")
|
| 169 |
+
|
| 170 |
+
tools = AgentTools()
|
| 171 |
+
schema = tools.get_tools_schema()
|
| 172 |
+
|
| 173 |
+
print("Generated schema for 3 tools:")
|
| 174 |
+
print(json.dumps(schema, indent=2))
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def test_tool_dispatcher():
|
| 178 |
+
"""Test the tool dispatcher/executor."""
|
| 179 |
+
print_section("Tool Dispatcher/Executor")
|
| 180 |
+
|
| 181 |
+
tools = AgentTools()
|
| 182 |
+
|
| 183 |
+
print("Test: Execute tool by name")
|
| 184 |
+
print("Tool: calculate_building_costs")
|
| 185 |
+
print("Parameters: building_type='settlement', current_resources={'wood': 1, 'brick': 1, 'wheat': 0, 'sheep': 1}")
|
| 186 |
+
|
| 187 |
+
result = tools.execute_tool(
|
| 188 |
+
"calculate_building_costs",
|
| 189 |
+
{
|
| 190 |
+
"building_type": "settlement",
|
| 191 |
+
"current_resources": {"wood": 1, "brick": 1, "wheat": 0, "sheep": 1}
|
| 192 |
+
}
|
| 193 |
+
)
|
| 194 |
+
print(json.dumps(result, indent=2))
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def main():
|
| 198 |
+
"""Run all tests."""
|
| 199 |
+
print("\n" + "="*60)
|
| 200 |
+
print(" AGENT TOOLS - TEST SUITE")
|
| 201 |
+
print(" Testing 3 helper tools for LLM AI Agents")
|
| 202 |
+
print("="*60)
|
| 203 |
+
|
| 204 |
+
try:
|
| 205 |
+
test_calculate_building_costs()
|
| 206 |
+
test_evaluate_node_value()
|
| 207 |
+
test_check_winning_path()
|
| 208 |
+
test_tool_schema()
|
| 209 |
+
test_tool_dispatcher()
|
| 210 |
+
|
| 211 |
+
print("\n" + "="*60)
|
| 212 |
+
print(" ✅ ALL TESTS COMPLETED SUCCESSFULLY!")
|
| 213 |
+
print("="*60 + "\n")
|
| 214 |
+
|
| 215 |
+
except Exception as e:
|
| 216 |
+
print(f"\n❌ ERROR: {e}")
|
| 217 |
+
import traceback
|
| 218 |
+
traceback.print_exc()
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
if __name__ == "__main__":
|
| 222 |
+
main()
|
examples/test_agent_tools_v2.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test Agent Tools - Testing the 3 new tools
|
| 3 |
+
|
| 4 |
+
This script tests the agent tools with a mock game state to ensure
|
| 5 |
+
they work correctly and prevent hallucinations.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import json
|
| 9 |
+
from pycatan.ai.agent_tools import AgentTools
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def create_mock_game_state():
|
| 13 |
+
"""Create a mock game state for testing."""
|
| 14 |
+
return {
|
| 15 |
+
"board": {
|
| 16 |
+
"nodes": [
|
| 17 |
+
{
|
| 18 |
+
"id": 10,
|
| 19 |
+
"adjacent_tiles": [1, 2, 3],
|
| 20 |
+
"neighbors": [9, 11, 20],
|
| 21 |
+
"port": None,
|
| 22 |
+
"building": None
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"id": 18,
|
| 26 |
+
"adjacent_tiles": [4, 5],
|
| 27 |
+
"neighbors": [17, 19, 27],
|
| 28 |
+
"port": None,
|
| 29 |
+
"building": None
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"id": 40,
|
| 33 |
+
"adjacent_tiles": [6, 7, 8],
|
| 34 |
+
"neighbors": [39, 41],
|
| 35 |
+
"port": "3:1",
|
| 36 |
+
"building": None
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"id": 15,
|
| 40 |
+
"adjacent_tiles": [9, 10],
|
| 41 |
+
"neighbors": [14, 16, 25],
|
| 42 |
+
"port": "Wheat",
|
| 43 |
+
"building": {"owner": "Alice", "type": "settlement"}
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
"id": 9,
|
| 47 |
+
"adjacent_tiles": [11],
|
| 48 |
+
"neighbors": [8, 10, 19],
|
| 49 |
+
"port": None,
|
| 50 |
+
"building": None
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"id": 11,
|
| 54 |
+
"adjacent_tiles": [12, 13],
|
| 55 |
+
"neighbors": [10, 12, 21],
|
| 56 |
+
"port": None,
|
| 57 |
+
"building": None
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"id": 20,
|
| 61 |
+
"adjacent_tiles": [14, 15],
|
| 62 |
+
"neighbors": [10, 19, 21],
|
| 63 |
+
"port": None,
|
| 64 |
+
"building": {"owner": "Bob", "type": "settlement"}
|
| 65 |
+
}
|
| 66 |
+
],
|
| 67 |
+
"tiles": [
|
| 68 |
+
{"id": 1, "type": "Brick", "number": 6},
|
| 69 |
+
{"id": 2, "type": "Sheep", "number": 8},
|
| 70 |
+
{"id": 3, "type": "Wood", "number": 12},
|
| 71 |
+
{"id": 4, "type": "Wheat", "number": 4},
|
| 72 |
+
{"id": 5, "type": "Ore", "number": 10},
|
| 73 |
+
{"id": 6, "type": "Wheat", "number": 6},
|
| 74 |
+
{"id": 7, "type": "Ore", "number": 8},
|
| 75 |
+
{"id": 8, "type": "Wood", "number": 5},
|
| 76 |
+
{"id": 9, "type": "Wheat", "number": 9},
|
| 77 |
+
{"id": 10, "type": "Ore", "number": 11},
|
| 78 |
+
{"id": 11, "type": "Desert", "number": 0},
|
| 79 |
+
{"id": 12, "type": "Wood", "number": 3},
|
| 80 |
+
{"id": 13, "type": "Brick", "number": 5},
|
| 81 |
+
{"id": 14, "type": "Sheep", "number": 11},
|
| 82 |
+
{"id": 15, "type": "Wood", "number": 9}
|
| 83 |
+
]
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def print_section(title: str):
|
| 89 |
+
"""Print a section header."""
|
| 90 |
+
print(f"\n{'='*70}")
|
| 91 |
+
print(f" {title}")
|
| 92 |
+
print(f"{'='*70}\n")
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def test_inspect_node():
|
| 96 |
+
"""Test the inspect_node tool."""
|
| 97 |
+
print_section("TEST 1: Inspect Node (Prevents Hallucinations)")
|
| 98 |
+
|
| 99 |
+
tools = AgentTools(create_mock_game_state())
|
| 100 |
+
|
| 101 |
+
# Test 1: Node 10 - Good spot
|
| 102 |
+
print("Test 1a: Inspect node 10 (should exist with good resources)")
|
| 103 |
+
result = tools.inspect_node(10)
|
| 104 |
+
print(json.dumps(result, indent=2))
|
| 105 |
+
print(f"\n✓ Node exists: {result['exists']}")
|
| 106 |
+
print(f"✓ Total pips: {result['total_pips']}")
|
| 107 |
+
print(f"✓ Can build: {result['can_build_here']}")
|
| 108 |
+
|
| 109 |
+
# Test 2: Node 18 - Different spot
|
| 110 |
+
print("\n\nTest 1b: Inspect node 18")
|
| 111 |
+
result = tools.inspect_node(18)
|
| 112 |
+
print(json.dumps(result, indent=2))
|
| 113 |
+
|
| 114 |
+
# Test 3: Node 40 - With port
|
| 115 |
+
print("\n\nTest 1c: Inspect node 40 (has port)")
|
| 116 |
+
result = tools.inspect_node(40)
|
| 117 |
+
print(json.dumps(result, indent=2))
|
| 118 |
+
print(f"\n✓ Has port: {result['port'] is not None}")
|
| 119 |
+
|
| 120 |
+
# Test 4: Node 15 - Occupied
|
| 121 |
+
print("\n\nTest 1d: Inspect node 15 (occupied)")
|
| 122 |
+
result = tools.inspect_node(15)
|
| 123 |
+
print(json.dumps(result, indent=2))
|
| 124 |
+
print(f"\n✓ Occupied: {result['occupied']}")
|
| 125 |
+
print(f"✓ Cannot build: {not result['can_build_here']}")
|
| 126 |
+
|
| 127 |
+
# Test 5: Node 9 - Too close to occupied node
|
| 128 |
+
print("\n\nTest 1e: Inspect node 9 (too close to node 10's neighbor which is occupied)")
|
| 129 |
+
result = tools.inspect_node(9)
|
| 130 |
+
print(json.dumps(result, indent=2))
|
| 131 |
+
|
| 132 |
+
# Test 6: Non-existent node
|
| 133 |
+
print("\n\nTest 1f: Inspect non-existent node 999")
|
| 134 |
+
result = tools.inspect_node(999)
|
| 135 |
+
print(json.dumps(result, indent=2))
|
| 136 |
+
print(f"\n✓ Error detected: {not result['exists']}")
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def test_find_best_nodes():
|
| 140 |
+
"""Test the find_best_nodes tool."""
|
| 141 |
+
print_section("TEST 2: Find Best Nodes (Prevents Missing Opportunities)")
|
| 142 |
+
|
| 143 |
+
tools = AgentTools(create_mock_game_state())
|
| 144 |
+
|
| 145 |
+
# Test 1: Find all good nodes (min 10 pips)
|
| 146 |
+
print("Test 2a: Find nodes with at least 10 pips")
|
| 147 |
+
result = tools.find_best_nodes(min_pips=10, exclude_blocked=True)
|
| 148 |
+
print(f"Query: {json.dumps(result['query'], indent=2)}")
|
| 149 |
+
print(f"Total found: {result['total_found']}")
|
| 150 |
+
print(f"\nTop results:")
|
| 151 |
+
for i, node in enumerate(result['nodes'][:3], 1):
|
| 152 |
+
print(f" {i}. Node {node['node_id']}: {node['total_pips']} pips, score {node['score']}")
|
| 153 |
+
print(f" Resources: {node['resources']}")
|
| 154 |
+
print(f" Port: {node['port']}")
|
| 155 |
+
|
| 156 |
+
# Test 2: Find nodes with Wheat
|
| 157 |
+
print("\n\nTest 2b: Find nodes with Wheat resource")
|
| 158 |
+
result = tools.find_best_nodes(must_have_resource="Wheat", exclude_blocked=True)
|
| 159 |
+
print(f"Total found: {result['total_found']}")
|
| 160 |
+
print(f"\nTop results:")
|
| 161 |
+
for i, node in enumerate(result['nodes'][:3], 1):
|
| 162 |
+
print(f" {i}. Node {node['node_id']}: {node['resources']}")
|
| 163 |
+
|
| 164 |
+
# Test 3: Find nodes preferring ports
|
| 165 |
+
print("\n\nTest 2c: Find nodes, preferring ports")
|
| 166 |
+
result = tools.find_best_nodes(prefer_port=True, exclude_blocked=True, limit=5)
|
| 167 |
+
print(f"Total found: {result['total_found']}")
|
| 168 |
+
print(f"\nTop results:")
|
| 169 |
+
for i, node in enumerate(result['nodes'], 1):
|
| 170 |
+
print(f" {i}. Node {node['node_id']}: Port={node['port']}, Score={node['score']}")
|
| 171 |
+
|
| 172 |
+
# Test 4: Include blocked nodes
|
| 173 |
+
print("\n\nTest 2d: Find all nodes (including blocked)")
|
| 174 |
+
result = tools.find_best_nodes(exclude_blocked=False, limit=10)
|
| 175 |
+
print(f"Total found: {result['total_found']}")
|
| 176 |
+
blocked = [n for n in result['nodes'] if not n['can_build']]
|
| 177 |
+
print(f"Blocked nodes: {len(blocked)}")
|
| 178 |
+
if blocked:
|
| 179 |
+
print(f"Example blocked: Node {blocked[0]['node_id']} (occupied={blocked[0]['occupied']})")
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def test_analyze_path_potential():
|
| 183 |
+
"""Test the analyze_path_potential tool."""
|
| 184 |
+
print_section("TEST 3: Analyze Path Potential (Helps Road Strategy)")
|
| 185 |
+
|
| 186 |
+
tools = AgentTools(create_mock_game_state())
|
| 187 |
+
|
| 188 |
+
# Test 1: Analyze all directions from node 10
|
| 189 |
+
print("Test 3a: Analyze all path directions from node 10")
|
| 190 |
+
result = tools.analyze_path_potential(from_node=10, max_depth=2)
|
| 191 |
+
print(f"From node: {result['from_node']}")
|
| 192 |
+
print(f"Total directions: {result['total_directions']}")
|
| 193 |
+
|
| 194 |
+
for i, path in enumerate(result['paths'][:2], 1): # Show top 2
|
| 195 |
+
print(f"\n Path {i}: Direction to node {path['direction']}")
|
| 196 |
+
print(f" Score: {path['score']}")
|
| 197 |
+
if path['depth_1']:
|
| 198 |
+
print(f" Depth 1: Node {path['depth_1']['node_id']}")
|
| 199 |
+
print(f" Resources: {path['depth_1']['resources']}")
|
| 200 |
+
print(f" Pips: {path['depth_1']['total_pips']}")
|
| 201 |
+
print(f" Port: {path['depth_1']['port']}")
|
| 202 |
+
if path['highlights']:
|
| 203 |
+
print(f" Highlights:")
|
| 204 |
+
for highlight in path['highlights']:
|
| 205 |
+
print(f" • {highlight}")
|
| 206 |
+
|
| 207 |
+
# Test 2: Analyze specific direction
|
| 208 |
+
print("\n\nTest 3b: Analyze specific direction from node 10 to node 11")
|
| 209 |
+
result = tools.analyze_path_potential(from_node=10, direction_node=11)
|
| 210 |
+
path = result['paths'][0]
|
| 211 |
+
print(json.dumps(path, indent=2))
|
| 212 |
+
|
| 213 |
+
# Test 3: Depth 1 only
|
| 214 |
+
print("\n\nTest 3c: Analyze with depth 1 only")
|
| 215 |
+
result = tools.analyze_path_potential(from_node=10, max_depth=1)
|
| 216 |
+
path = result['paths'][0]
|
| 217 |
+
print(f"Direction to {path['direction']}")
|
| 218 |
+
print(f"Depth 2 data: {path['depth_2']}") # Should be None
|
| 219 |
+
|
| 220 |
+
# Test 4: Invalid node
|
| 221 |
+
print("\n\nTest 3d: Try to analyze from invalid node")
|
| 222 |
+
result = tools.analyze_path_potential(from_node=999)
|
| 223 |
+
print(json.dumps(result, indent=2))
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def test_tool_schema():
|
| 227 |
+
"""Test tool schema generation."""
|
| 228 |
+
print_section("TEST 4: Tool Schema Generation")
|
| 229 |
+
|
| 230 |
+
tools = AgentTools()
|
| 231 |
+
schemas = tools.get_tools_schema()
|
| 232 |
+
|
| 233 |
+
print(f"Generated {len(schemas)} tool schemas:")
|
| 234 |
+
for i, schema in enumerate(schemas, 1):
|
| 235 |
+
print(f"\n{i}. {schema['name']}")
|
| 236 |
+
print(f" Description: {schema['description'][:80]}...")
|
| 237 |
+
print(f" Parameters: {list(schema['parameters']['properties'].keys())}")
|
| 238 |
+
print(f" Required: {schema['parameters'].get('required', [])}")
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def test_execute_tool():
|
| 242 |
+
"""Test tool execution dispatcher."""
|
| 243 |
+
print_section("TEST 5: Tool Execution Dispatcher")
|
| 244 |
+
|
| 245 |
+
tools = AgentTools(create_mock_game_state())
|
| 246 |
+
|
| 247 |
+
# Test 1: Execute inspect_node
|
| 248 |
+
print("Test 5a: Execute 'inspect_node' via dispatcher")
|
| 249 |
+
result = tools.execute_tool("inspect_node", {"node_id": 10})
|
| 250 |
+
print(f"Result: Node {result['node_id']}, pips={result['total_pips']}")
|
| 251 |
+
|
| 252 |
+
# Test 2: Execute find_best_nodes
|
| 253 |
+
print("\n\nTest 5b: Execute 'find_best_nodes' via dispatcher")
|
| 254 |
+
result = tools.execute_tool("find_best_nodes", {"min_pips": 12, "limit": 3})
|
| 255 |
+
print(f"Found {result['total_found']} nodes")
|
| 256 |
+
|
| 257 |
+
# Test 3: Execute analyze_path_potential
|
| 258 |
+
print("\n\nTest 5c: Execute 'analyze_path_potential' via dispatcher")
|
| 259 |
+
result = tools.execute_tool("analyze_path_potential", {"from_node": 10})
|
| 260 |
+
print(f"Analyzed {result['total_directions']} directions from node {result['from_node']}")
|
| 261 |
+
|
| 262 |
+
# Test 4: Invalid tool
|
| 263 |
+
print("\n\nTest 5d: Try to execute invalid tool")
|
| 264 |
+
try:
|
| 265 |
+
result = tools.execute_tool("invalid_tool", {})
|
| 266 |
+
print("ERROR: Should have raised ValueError!")
|
| 267 |
+
except ValueError as e:
|
| 268 |
+
print(f"✓ Correctly raised error: {e}")
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
def main():
|
| 272 |
+
"""Run all tests."""
|
| 273 |
+
print("\n" + "="*70)
|
| 274 |
+
print(" AGENT TOOLS TEST SUITE")
|
| 275 |
+
print(" Testing 3 tools that prevent hallucinations")
|
| 276 |
+
print("="*70)
|
| 277 |
+
|
| 278 |
+
try:
|
| 279 |
+
test_inspect_node()
|
| 280 |
+
test_find_best_nodes()
|
| 281 |
+
test_analyze_path_potential()
|
| 282 |
+
test_tool_schema()
|
| 283 |
+
test_execute_tool()
|
| 284 |
+
|
| 285 |
+
print("\n" + "="*70)
|
| 286 |
+
print(" ✅ ALL TESTS COMPLETED SUCCESSFULLY!")
|
| 287 |
+
print("="*70)
|
| 288 |
+
print("\n📋 Summary:")
|
| 289 |
+
print(" 1. inspect_node - Prevents hallucinations by giving accurate node data")
|
| 290 |
+
print(" 2. find_best_nodes - Prevents missing opportunities by searching board")
|
| 291 |
+
print(" 3. analyze_path_potential - Helps road strategy by showing what's ahead")
|
| 292 |
+
print()
|
| 293 |
+
|
| 294 |
+
except Exception as e:
|
| 295 |
+
print(f"\n❌ ERROR: {e}")
|
| 296 |
+
import traceback
|
| 297 |
+
traceback.print_exc()
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
if __name__ == "__main__":
|
| 301 |
+
main()
|
pycatan/ai/AGENT_TOOLS_README.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Agent Tools - Helper Functions for LLM AI Agents
|
| 2 |
+
|
| 3 |
+
## 📋 Overview
|
| 4 |
+
|
| 5 |
+
This module provides **3 powerful tools** that help the LLM AI agent make better decisions in Catan:
|
| 6 |
+
|
| 7 |
+
1. **calculate_building_costs** - Check resource requirements and affordability
|
| 8 |
+
2. **evaluate_node_value** - Rate settlement positions by resource value
|
| 9 |
+
3. **check_winning_path** - Analyze strategies to reach 10 victory points
|
| 10 |
+
|
| 11 |
+
These tools are designed to be:
|
| 12 |
+
- ✅ **Easy to use** - Simple function calls with clear outputs
|
| 13 |
+
- ✅ **LLM-friendly** - Return structured data that LLMs can understand
|
| 14 |
+
- ✅ **Strategic** - Help agent reason about game decisions
|
| 15 |
+
- ✅ **Extensible** - Easy to add more tools in the future
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
## 🛠️ The 3 Tools
|
| 20 |
+
|
| 21 |
+
### Tool 1: Calculate Building Costs
|
| 22 |
+
|
| 23 |
+
**Purpose:** Help the agent understand what resources it needs to build something.
|
| 24 |
+
|
| 25 |
+
**Use cases:**
|
| 26 |
+
- "Can I afford to build a city?"
|
| 27 |
+
- "What resources am I missing for a settlement?"
|
| 28 |
+
- "Should I trade for brick to complete a road?"
|
| 29 |
+
|
| 30 |
+
**Example:**
|
| 31 |
+
```python
|
| 32 |
+
from pycatan.ai.agent_tools import AgentTools
|
| 33 |
+
|
| 34 |
+
tools = AgentTools()
|
| 35 |
+
result = tools.calculate_building_costs(
|
| 36 |
+
building_type="settlement",
|
| 37 |
+
current_resources={"wood": 1, "brick": 0, "wheat": 1, "sheep": 1}
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
print(result)
|
| 41 |
+
# Output:
|
| 42 |
+
# {
|
| 43 |
+
# "can_afford": False,
|
| 44 |
+
# "required": {"wood": 1, "brick": 1, "wheat": 1, "sheep": 1},
|
| 45 |
+
# "missing": {"brick": 1},
|
| 46 |
+
# "excess": {}
|
| 47 |
+
# }
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
**Building types:**
|
| 51 |
+
- `settlement` - Requires: wood, brick, wheat, sheep (1 each)
|
| 52 |
+
- `city` - Requires: wheat (2), ore (3)
|
| 53 |
+
- `road` - Requires: wood, brick (1 each)
|
| 54 |
+
- `development_card` - Requires: wheat, sheep, ore (1 each)
|
| 55 |
+
|
| 56 |
+
---
|
| 57 |
+
|
| 58 |
+
### Tool 2: Evaluate Node Value
|
| 59 |
+
|
| 60 |
+
**Purpose:** Rate how valuable a settlement position is based on surrounding tiles.
|
| 61 |
+
|
| 62 |
+
**Use cases:**
|
| 63 |
+
- "Which of these 3 positions should I choose for my starting settlement?"
|
| 64 |
+
- "Is this position with a harbor better than one with high-value tiles?"
|
| 65 |
+
- "What resources will I get from this location?"
|
| 66 |
+
|
| 67 |
+
**Example:**
|
| 68 |
+
```python
|
| 69 |
+
from pycatan.ai.agent_tools import AgentTools
|
| 70 |
+
|
| 71 |
+
tools = AgentTools()
|
| 72 |
+
result = tools.evaluate_node_value(
|
| 73 |
+
node_tiles=[
|
| 74 |
+
{"resource": "wheat", "number": 6}, # High probability (5 pips)
|
| 75 |
+
{"resource": "wood", "number": 8}, # High probability (5 pips)
|
| 76 |
+
{"resource": "brick", "number": 5} # Good probability (4 pips)
|
| 77 |
+
],
|
| 78 |
+
include_harbor=False
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
print(result)
|
| 82 |
+
# Output:
|
| 83 |
+
# {
|
| 84 |
+
# "total_pip_value": 14, # Sum of probability dots
|
| 85 |
+
# "resource_diversity": 3, # 3 different resources
|
| 86 |
+
# "resources": { # Resources weighted by probability
|
| 87 |
+
# "wheat": 5,
|
| 88 |
+
# "wood": 5,
|
| 89 |
+
# "brick": 4
|
| 90 |
+
# },
|
| 91 |
+
# "best_number": 6, # Best dice number here
|
| 92 |
+
# "overall_score": 15.5 # Overall value rating
|
| 93 |
+
# }
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
**Scoring system:**
|
| 97 |
+
- **Pip values** (probability): 6/8 = 5 pips, 5/9 = 4 pips, 4/10 = 3 pips, 3/11 = 2 pips, 2/12 = 1 pip
|
| 98 |
+
- **Diversity bonus**: +0.5 per unique resource type
|
| 99 |
+
- **Harbor bonus**: +2.0 for any harbor, +0.5 extra for specialized harbor (e.g., ore harbor)
|
| 100 |
+
- **Overall score**: Combines all factors into single rating
|
| 101 |
+
|
| 102 |
+
**Interpretation:**
|
| 103 |
+
- 15+ = Excellent position (build here!)
|
| 104 |
+
- 10-14 = Good position
|
| 105 |
+
- 7-9 = Mediocre position
|
| 106 |
+
- <7 = Poor position (avoid if possible)
|
| 107 |
+
|
| 108 |
+
---
|
| 109 |
+
|
| 110 |
+
### Tool 3: Check Winning Path
|
| 111 |
+
|
| 112 |
+
**Purpose:** Analyze what the agent needs to do to reach 10 victory points and win.
|
| 113 |
+
|
| 114 |
+
**Use cases:**
|
| 115 |
+
- "How close am I to winning?"
|
| 116 |
+
- "Should I focus on cities or development cards?"
|
| 117 |
+
- "What's the fastest path to victory?"
|
| 118 |
+
|
| 119 |
+
**Example:**
|
| 120 |
+
```python
|
| 121 |
+
from pycatan.ai.agent_tools import AgentTools
|
| 122 |
+
|
| 123 |
+
tools = AgentTools()
|
| 124 |
+
result = tools.check_winning_path(
|
| 125 |
+
current_vp=7,
|
| 126 |
+
settlements=3,
|
| 127 |
+
cities=1,
|
| 128 |
+
longest_road_owned=True,
|
| 129 |
+
largest_army_owned=False
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
print(result)
|
| 133 |
+
# Output:
|
| 134 |
+
# {
|
| 135 |
+
# "current_vp": 7,
|
| 136 |
+
# "vp_needed": 3,
|
| 137 |
+
# "paths_to_victory": [
|
| 138 |
+
# "Build 2 more cities (3 VP needed, 2 cities = 4 VP gain)",
|
| 139 |
+
# "Build 3 settlements (if space available on board)",
|
| 140 |
+
# "Get Largest Army card (2 VP) - need 3+ knights played",
|
| 141 |
+
# ...
|
| 142 |
+
# ],
|
| 143 |
+
# "recommendations": [
|
| 144 |
+
# "Cities are efficient! You need 2 more.",
|
| 145 |
+
# "Buy development cards to try for knights"
|
| 146 |
+
# ],
|
| 147 |
+
# "breakdown": {
|
| 148 |
+
# "settlements": 3,
|
| 149 |
+
# "cities": 1,
|
| 150 |
+
# "longest_road": true,
|
| 151 |
+
# "largest_army": false
|
| 152 |
+
# }
|
| 153 |
+
# }
|
| 154 |
+
```
|
| 155 |
+
|
| 156 |
+
**Victory point sources:**
|
| 157 |
+
- Settlement = 1 VP (max 5)
|
| 158 |
+
- City = 2 VP (max 5, built on settlements)
|
| 159 |
+
- Longest Road = 2 VP (need 5+ connected roads)
|
| 160 |
+
- Largest Army = 2 VP (need 3+ knights played)
|
| 161 |
+
- Development cards = 1 VP each (University, Library, etc.)
|
| 162 |
+
|
| 163 |
+
---
|
| 164 |
+
|
| 165 |
+
## 🔌 Integration with LLM
|
| 166 |
+
|
| 167 |
+
### Option 1: Function Calling / Tool Use
|
| 168 |
+
|
| 169 |
+
Modern LLMs (GPT-4, Claude, Gemini) support function calling. You can pass the tool schemas directly:
|
| 170 |
+
|
| 171 |
+
```python
|
| 172 |
+
from pycatan.ai.agent_tools import AgentTools
|
| 173 |
+
|
| 174 |
+
tools = AgentTools()
|
| 175 |
+
|
| 176 |
+
# Get schemas for LLM
|
| 177 |
+
tool_schemas = tools.get_tools_schema()
|
| 178 |
+
|
| 179 |
+
# Send to LLM (example with OpenAI)
|
| 180 |
+
response = openai.ChatCompletion.create(
|
| 181 |
+
model="gpt-4",
|
| 182 |
+
messages=[{"role": "user", "content": "Should I build at position 14?"}],
|
| 183 |
+
functions=tool_schemas,
|
| 184 |
+
function_call="auto"
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
# If LLM calls a tool
|
| 188 |
+
if response.get("function_call"):
|
| 189 |
+
tool_name = response["function_call"]["name"]
|
| 190 |
+
parameters = json.loads(response["function_call"]["arguments"])
|
| 191 |
+
|
| 192 |
+
# Execute the tool
|
| 193 |
+
result = tools.execute_tool(tool_name, parameters)
|
| 194 |
+
print(result)
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
### Option 2: Include in Prompt
|
| 198 |
+
|
| 199 |
+
You can also describe the tools in the system prompt and have the LLM "call" them by outputting JSON:
|
| 200 |
+
|
| 201 |
+
```python
|
| 202 |
+
system_prompt = """
|
| 203 |
+
You have access to 3 helper tools:
|
| 204 |
+
|
| 205 |
+
1. calculate_building_costs(building_type, current_resources)
|
| 206 |
+
- Returns whether you can afford to build and what's missing
|
| 207 |
+
|
| 208 |
+
2. evaluate_node_value(node_tiles, include_harbor, harbor_type)
|
| 209 |
+
- Rates a settlement position by resource value
|
| 210 |
+
|
| 211 |
+
3. check_winning_path(current_vp, settlements, cities, ...)
|
| 212 |
+
- Shows paths to reach 10 VP and win
|
| 213 |
+
|
| 214 |
+
To use a tool, output: TOOL: tool_name(parameters)
|
| 215 |
+
"""
|
| 216 |
+
```
|
| 217 |
+
|
| 218 |
+
### Option 3: Pre-calculate and Include in State
|
| 219 |
+
|
| 220 |
+
For simpler integration, you can run the tools beforehand and include results in the game state:
|
| 221 |
+
|
| 222 |
+
```python
|
| 223 |
+
# Before sending state to LLM
|
| 224 |
+
game_state["tool_results"] = {
|
| 225 |
+
"can_afford_settlement": tools.calculate_building_costs(
|
| 226 |
+
"settlement", player.resources
|
| 227 |
+
),
|
| 228 |
+
"best_available_position": max(
|
| 229 |
+
available_positions,
|
| 230 |
+
key=lambda pos: tools.evaluate_node_value(pos["tiles"])["overall_score"]
|
| 231 |
+
),
|
| 232 |
+
"winning_strategy": tools.check_winning_path(
|
| 233 |
+
player.vp, player.settlements, player.cities
|
| 234 |
+
)
|
| 235 |
+
}
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
---
|
| 239 |
+
|
| 240 |
+
## 📊 Testing
|
| 241 |
+
|
| 242 |
+
Run the test suite to see all tools in action:
|
| 243 |
+
|
| 244 |
+
```bash
|
| 245 |
+
python examples/test_agent_tools.py
|
| 246 |
+
```
|
| 247 |
+
|
| 248 |
+
This will show:
|
| 249 |
+
- ✅ All 3 tools working with various scenarios
|
| 250 |
+
- ✅ Tool schema generation for LLM integration
|
| 251 |
+
- ✅ Tool dispatcher/executor functionality
|
| 252 |
+
|
| 253 |
+
---
|
| 254 |
+
|
| 255 |
+
## 🚀 Next Steps
|
| 256 |
+
|
| 257 |
+
### Immediate:
|
| 258 |
+
1. Integrate tools with `AIManager.process_agent_turn()`
|
| 259 |
+
2. Update prompts to mention available tools
|
| 260 |
+
3. Parse tool calls from LLM responses
|
| 261 |
+
|
| 262 |
+
### Future enhancements:
|
| 263 |
+
4. Add more tools:
|
| 264 |
+
- `analyze_trade_offer(give, receive)` - Evaluate trade fairness
|
| 265 |
+
- `predict_opponent_move(opponent_state)` - Anticipate opponent strategy
|
| 266 |
+
- `find_best_road_path(from_node, to_node)` - Pathfinding for Longest Road
|
| 267 |
+
- `calculate_robber_impact(tile)` - Evaluate where to place robber
|
| 268 |
+
5. Add tool usage to agent state/memory
|
| 269 |
+
6. Log which tools agent uses and why
|
| 270 |
+
|
| 271 |
+
---
|
| 272 |
+
|
| 273 |
+
## 🏗️ Architecture
|
| 274 |
+
|
| 275 |
+
```
|
| 276 |
+
┌─────────────────┐
|
| 277 |
+
│ AIManager │ <- Coordinates everything
|
| 278 |
+
└────────┬────────┘
|
| 279 |
+
│
|
| 280 |
+
├──> PromptManager (creates prompts)
|
| 281 |
+
├──> LLMClient (sends to LLM)
|
| 282 |
+
├──> ResponseParser (parses responses)
|
| 283 |
+
└──> AgentTools ✨ (helper tools - NEW!)
|
| 284 |
+
│
|
| 285 |
+
├─> calculate_building_costs()
|
| 286 |
+
├─> evaluate_node_value()
|
| 287 |
+
└─> check_winning_path()
|
| 288 |
+
```
|
| 289 |
+
|
| 290 |
+
**Design principles:**
|
| 291 |
+
- **Stateless tools** - Each tool call is independent
|
| 292 |
+
- **Pure functions** - No side effects, just calculations
|
| 293 |
+
- **Clear outputs** - Structured data the LLM can easily use
|
| 294 |
+
- **Extensible** - Easy to add new tools without breaking existing ones
|
| 295 |
+
|
| 296 |
+
---
|
| 297 |
+
|
| 298 |
+
## 📝 Notes
|
| 299 |
+
|
| 300 |
+
- Tools are **read-only** - They don't modify game state, only provide information
|
| 301 |
+
- Tools are **fast** - All calculations are lightweight (no AI calls)
|
| 302 |
+
- Tools are **accurate** - Use exact Catan rules for costs and scoring
|
| 303 |
+
- Tools provide **context** - Help LLM understand "why" not just "what"
|
| 304 |
+
|
| 305 |
+
---
|
| 306 |
+
|
| 307 |
+
## 🤝 Contributing
|
| 308 |
+
|
| 309 |
+
To add a new tool:
|
| 310 |
+
|
| 311 |
+
1. Add the function to `AgentTools` class
|
| 312 |
+
2. Add comprehensive docstring with examples
|
| 313 |
+
3. Add to `get_tools_schema()` for LLM integration
|
| 314 |
+
4. Add to `execute_tool()` dispatcher
|
| 315 |
+
5. Add tests in `examples/test_agent_tools.py`
|
| 316 |
+
|
| 317 |
+
**Tool naming convention:**
|
| 318 |
+
- Use verbs: `calculate_`, `evaluate_`, `check_`, `analyze_`, `find_`
|
| 319 |
+
- Be specific: `calculate_building_costs` not `get_costs`
|
| 320 |
+
- Stay focused: One clear purpose per tool
|
pycatan/ai/__init__.py
CHANGED
|
@@ -18,6 +18,7 @@ Supporting Components:
|
|
| 18 |
- response_parser: LLM response parsing and validation
|
| 19 |
- llm_client: LLM API abstraction and client
|
| 20 |
- schemas: JSON schemas for LLM responses
|
|
|
|
| 21 |
|
| 22 |
Architecture Overview:
|
| 23 |
┌─────────────────────────────────────────────────────────────────────┐
|
|
@@ -70,6 +71,9 @@ from pycatan.ai.llm_client import LLMResponse, GeminiClient, create_llm_client
|
|
| 70 |
from pycatan.ai.schemas import ResponseType, ACTIVE_TURN_RESPONSE_SCHEMA, OBSERVING_RESPONSE_SCHEMA
|
| 71 |
from pycatan.ai.state_optimizer import optimize_state_for_ai, format_with_legend, game_state_to_dict
|
| 72 |
|
|
|
|
|
|
|
|
|
|
| 73 |
__all__ = [
|
| 74 |
# Main classes
|
| 75 |
"AIManager",
|
|
@@ -99,4 +103,7 @@ __all__ = [
|
|
| 99 |
"optimize_state_for_ai",
|
| 100 |
"format_with_legend",
|
| 101 |
"game_state_to_dict",
|
|
|
|
|
|
|
|
|
|
| 102 |
]
|
|
|
|
| 18 |
- response_parser: LLM response parsing and validation
|
| 19 |
- llm_client: LLM API abstraction and client
|
| 20 |
- schemas: JSON schemas for LLM responses
|
| 21 |
+
- agent_tools: Helper tools for LLM decision-making (NEW!)
|
| 22 |
|
| 23 |
Architecture Overview:
|
| 24 |
┌─────────────────────────────────────────────────────────────────────┐
|
|
|
|
| 71 |
from pycatan.ai.schemas import ResponseType, ACTIVE_TURN_RESPONSE_SCHEMA, OBSERVING_RESPONSE_SCHEMA
|
| 72 |
from pycatan.ai.state_optimizer import optimize_state_for_ai, format_with_legend, game_state_to_dict
|
| 73 |
|
| 74 |
+
# Agent tools
|
| 75 |
+
from pycatan.ai.agent_tools import AgentTools
|
| 76 |
+
|
| 77 |
__all__ = [
|
| 78 |
# Main classes
|
| 79 |
"AIManager",
|
|
|
|
| 103 |
"optimize_state_for_ai",
|
| 104 |
"format_with_legend",
|
| 105 |
"game_state_to_dict",
|
| 106 |
+
|
| 107 |
+
# Agent tools
|
| 108 |
+
"AgentTools",
|
| 109 |
]
|
pycatan/ai/agent_tools.py
ADDED
|
@@ -0,0 +1,539 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Agent Tools - Helper functions for LLM AI Agents
|
| 3 |
+
|
| 4 |
+
This module provides tools that the LLM can use to help make better decisions.
|
| 5 |
+
These tools are designed to PREVENT HALLUCINATIONS by providing processed information
|
| 6 |
+
rather than forcing the LLM to interpret raw data structures.
|
| 7 |
+
|
| 8 |
+
Tools available:
|
| 9 |
+
1. inspect_node - Get detailed info about a specific node (prevents hallucinations)
|
| 10 |
+
2. find_best_nodes - Search for nodes by criteria (prevents missing opportunities)
|
| 11 |
+
3. analyze_path_potential - Analyze road building potential (helps with strategy)
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from typing import Dict, Any, List, Optional, Set, Tuple
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
# Pip values (dots on tiles): represents probability of each dice number
|
| 18 |
+
PIP_VALUES = {
|
| 19 |
+
2: 1, 3: 2, 4: 3, 5: 4, 6: 5,
|
| 20 |
+
8: 5, 9: 4, 10: 3, 11: 2, 12: 1
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class AgentTools:
|
| 25 |
+
"""
|
| 26 |
+
Collection of helper tools for AI agents.
|
| 27 |
+
|
| 28 |
+
These tools are designed to PREVENT HALLUCINATIONS by providing
|
| 29 |
+
the LLM with processed, accurate information rather than forcing
|
| 30 |
+
it to interpret complex data structures (Arrays N and H).
|
| 31 |
+
|
| 32 |
+
Key principle: The LLM asks questions, the tools provide answers.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
def __init__(self, game_state: Optional[Dict[str, Any]] = None):
|
| 36 |
+
"""
|
| 37 |
+
Initialize agent tools with game state.
|
| 38 |
+
|
| 39 |
+
Args:
|
| 40 |
+
game_state: Current game state dictionary (from state_optimizer)
|
| 41 |
+
"""
|
| 42 |
+
self.game_state = game_state or {}
|
| 43 |
+
self._update_internal_structures()
|
| 44 |
+
|
| 45 |
+
def update_game_state(self, game_state: Dict[str, Any]):
|
| 46 |
+
"""
|
| 47 |
+
Update the game state (called each turn).
|
| 48 |
+
|
| 49 |
+
Args:
|
| 50 |
+
game_state: New game state dictionary
|
| 51 |
+
"""
|
| 52 |
+
self.game_state = game_state
|
| 53 |
+
self._update_internal_structures()
|
| 54 |
+
|
| 55 |
+
def _update_internal_structures(self):
|
| 56 |
+
"""Build internal lookup structures from game state for fast queries."""
|
| 57 |
+
# Extract board data
|
| 58 |
+
self.board_data = self.game_state.get("board", {})
|
| 59 |
+
self.nodes = self.board_data.get("nodes", [])
|
| 60 |
+
self.tiles = self.board_data.get("tiles", [])
|
| 61 |
+
|
| 62 |
+
# Build node lookup: node_id -> node_data
|
| 63 |
+
self.node_lookup: Dict[int, Dict[str, Any]] = {}
|
| 64 |
+
for node in self.nodes:
|
| 65 |
+
node_id = node.get("id")
|
| 66 |
+
if node_id is not None:
|
| 67 |
+
self.node_lookup[node_id] = node
|
| 68 |
+
|
| 69 |
+
# Build tile lookup: tile_id -> tile_data
|
| 70 |
+
self.tile_lookup: Dict[int, Dict[str, Any]] = {}
|
| 71 |
+
for tile in self.tiles:
|
| 72 |
+
tile_id = tile.get("id")
|
| 73 |
+
if tile_id is not None:
|
| 74 |
+
self.tile_lookup[tile_id] = tile
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
# ========== TOOL 1: Inspect Node (Prevents Hallucinations) ==========
|
| 78 |
+
|
| 79 |
+
def inspect_node(self, node_id: int) -> Dict[str, Any]:
|
| 80 |
+
"""
|
| 81 |
+
Get detailed, processed information about a specific node.
|
| 82 |
+
|
| 83 |
+
CRITICAL: This prevents hallucinations!
|
| 84 |
+
Instead of the LLM trying to interpret Arrays N and H, it asks this tool
|
| 85 |
+
for accurate information about a specific node.
|
| 86 |
+
|
| 87 |
+
Args:
|
| 88 |
+
node_id: The node ID to inspect (e.g., 10, 18, 40)
|
| 89 |
+
|
| 90 |
+
Returns:
|
| 91 |
+
Dictionary with complete node information
|
| 92 |
+
"""
|
| 93 |
+
# Check if node exists
|
| 94 |
+
if node_id not in self.node_lookup:
|
| 95 |
+
return {
|
| 96 |
+
"node_id": node_id,
|
| 97 |
+
"exists": False,
|
| 98 |
+
"error": f"Node {node_id} does not exist on the board"
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
node = self.node_lookup[node_id]
|
| 102 |
+
|
| 103 |
+
# Extract resources and calculate pips
|
| 104 |
+
resources = {}
|
| 105 |
+
total_pips = 0
|
| 106 |
+
|
| 107 |
+
adjacent_tiles = node.get("adjacent_tiles", [])
|
| 108 |
+
for tile_id in adjacent_tiles:
|
| 109 |
+
if tile_id in self.tile_lookup:
|
| 110 |
+
tile = self.tile_lookup[tile_id]
|
| 111 |
+
resource = tile.get("type", "")
|
| 112 |
+
number = tile.get("number", 0)
|
| 113 |
+
|
| 114 |
+
# Skip desert
|
| 115 |
+
if resource.lower() != "desert" and number > 0:
|
| 116 |
+
resources[resource] = number
|
| 117 |
+
total_pips += PIP_VALUES.get(number, 0)
|
| 118 |
+
|
| 119 |
+
# Check for port
|
| 120 |
+
port = node.get("port")
|
| 121 |
+
if port:
|
| 122 |
+
# Normalize port format
|
| 123 |
+
if isinstance(port, dict):
|
| 124 |
+
port = port.get("type", None)
|
| 125 |
+
|
| 126 |
+
# Check if occupied
|
| 127 |
+
building = node.get("building")
|
| 128 |
+
occupied = building is not None
|
| 129 |
+
occupied_by = None
|
| 130 |
+
building_type = None
|
| 131 |
+
|
| 132 |
+
if building:
|
| 133 |
+
occupied_by = building.get("owner", "Unknown")
|
| 134 |
+
building_type = building.get("type", "settlement")
|
| 135 |
+
|
| 136 |
+
# Get neighbors
|
| 137 |
+
neighbors = node.get("neighbors", [])
|
| 138 |
+
|
| 139 |
+
# Check if can build here
|
| 140 |
+
can_build_here = not occupied
|
| 141 |
+
blocked_reason = None
|
| 142 |
+
|
| 143 |
+
if occupied:
|
| 144 |
+
blocked_reason = f"Occupied by {occupied_by}'s {building_type}"
|
| 145 |
+
else:
|
| 146 |
+
# Check distance rule (no buildings within 1 edge)
|
| 147 |
+
for neighbor_id in neighbors:
|
| 148 |
+
if neighbor_id in self.node_lookup:
|
| 149 |
+
neighbor_building = self.node_lookup[neighbor_id].get("building")
|
| 150 |
+
if neighbor_building:
|
| 151 |
+
can_build_here = False
|
| 152 |
+
neighbor_owner = neighbor_building.get("owner", "someone")
|
| 153 |
+
blocked_reason = f"Too close to {neighbor_owner}'s building at node {neighbor_id}"
|
| 154 |
+
break
|
| 155 |
+
|
| 156 |
+
return {
|
| 157 |
+
"node_id": node_id,
|
| 158 |
+
"exists": True,
|
| 159 |
+
"resources": resources,
|
| 160 |
+
"total_pips": total_pips,
|
| 161 |
+
"port": port,
|
| 162 |
+
"neighbors": neighbors,
|
| 163 |
+
"occupied": occupied,
|
| 164 |
+
"occupied_by": occupied_by,
|
| 165 |
+
"building_type": building_type,
|
| 166 |
+
"can_build_here": can_build_here,
|
| 167 |
+
"blocked_reason": blocked_reason
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# ========== TOOL 2: Find Best Nodes (Prevents Missing Opportunities) ==========
|
| 172 |
+
|
| 173 |
+
def find_best_nodes(
|
| 174 |
+
self,
|
| 175 |
+
min_pips: int = 0,
|
| 176 |
+
must_have_resource: Optional[str] = None,
|
| 177 |
+
exclude_blocked: bool = True,
|
| 178 |
+
prefer_port: bool = False,
|
| 179 |
+
limit: int = 10
|
| 180 |
+
) -> Dict[str, Any]:
|
| 181 |
+
"""
|
| 182 |
+
Search for the best nodes on the board based on criteria.
|
| 183 |
+
|
| 184 |
+
CRITICAL: This prevents missing opportunities!
|
| 185 |
+
Instead of the LLM trying to visually scan Arrays N and H, it asks this
|
| 186 |
+
tool to find the best positions that match specific criteria.
|
| 187 |
+
|
| 188 |
+
Args:
|
| 189 |
+
min_pips: Minimum total pip value (e.g., 10 for high-probability spots)
|
| 190 |
+
must_have_resource: Required resource type (e.g., "Wheat", "Ore")
|
| 191 |
+
exclude_blocked: Skip nodes that can't be built on
|
| 192 |
+
prefer_port: Sort port nodes higher
|
| 193 |
+
limit: Maximum number of results to return
|
| 194 |
+
|
| 195 |
+
Returns:
|
| 196 |
+
Dictionary with search results
|
| 197 |
+
"""
|
| 198 |
+
matching_nodes = []
|
| 199 |
+
|
| 200 |
+
# Search all nodes
|
| 201 |
+
for node_id, node in self.node_lookup.items():
|
| 202 |
+
# Get node info using inspect_node
|
| 203 |
+
info = self.inspect_node(node_id)
|
| 204 |
+
|
| 205 |
+
if not info.get("exists"):
|
| 206 |
+
continue
|
| 207 |
+
|
| 208 |
+
# Apply filters
|
| 209 |
+
if exclude_blocked and not info.get("can_build_here"):
|
| 210 |
+
continue
|
| 211 |
+
|
| 212 |
+
total_pips = info.get("total_pips", 0)
|
| 213 |
+
if total_pips < min_pips:
|
| 214 |
+
continue
|
| 215 |
+
|
| 216 |
+
resources = info.get("resources", {})
|
| 217 |
+
if must_have_resource:
|
| 218 |
+
# Case-insensitive resource check
|
| 219 |
+
has_resource = False
|
| 220 |
+
for res in resources.keys():
|
| 221 |
+
if res.lower() == must_have_resource.lower():
|
| 222 |
+
has_resource = True
|
| 223 |
+
break
|
| 224 |
+
if not has_resource:
|
| 225 |
+
continue
|
| 226 |
+
|
| 227 |
+
# Calculate score
|
| 228 |
+
score = total_pips
|
| 229 |
+
|
| 230 |
+
# Resource diversity bonus
|
| 231 |
+
score += len(resources) * 0.5
|
| 232 |
+
|
| 233 |
+
# Port bonus
|
| 234 |
+
if info.get("port"):
|
| 235 |
+
score += 2.0
|
| 236 |
+
if info.get("port") != "3:1":
|
| 237 |
+
score += 0.5 # Specialized port
|
| 238 |
+
|
| 239 |
+
matching_nodes.append({
|
| 240 |
+
"node_id": node_id,
|
| 241 |
+
"resources": resources,
|
| 242 |
+
"total_pips": total_pips,
|
| 243 |
+
"port": info.get("port"),
|
| 244 |
+
"neighbors": info.get("neighbors", []),
|
| 245 |
+
"score": round(score, 1),
|
| 246 |
+
"can_build": info.get("can_build_here", False),
|
| 247 |
+
"occupied": info.get("occupied", False)
|
| 248 |
+
})
|
| 249 |
+
|
| 250 |
+
# Sort by score (and prefer ports if requested)
|
| 251 |
+
if prefer_port:
|
| 252 |
+
matching_nodes.sort(
|
| 253 |
+
key=lambda n: (n["port"] is not None, n["score"]),
|
| 254 |
+
reverse=True
|
| 255 |
+
)
|
| 256 |
+
else:
|
| 257 |
+
matching_nodes.sort(key=lambda n: n["score"], reverse=True)
|
| 258 |
+
|
| 259 |
+
# Limit results
|
| 260 |
+
total_found = len(matching_nodes)
|
| 261 |
+
matching_nodes = matching_nodes[:limit]
|
| 262 |
+
|
| 263 |
+
return {
|
| 264 |
+
"query": {
|
| 265 |
+
"min_pips": min_pips,
|
| 266 |
+
"must_have_resource": must_have_resource,
|
| 267 |
+
"exclude_blocked": exclude_blocked,
|
| 268 |
+
"prefer_port": prefer_port
|
| 269 |
+
},
|
| 270 |
+
"total_found": total_found,
|
| 271 |
+
"nodes": matching_nodes
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
# ========== TOOL 3: Analyze Path Potential (Helps with Road Strategy) ==========
|
| 276 |
+
|
| 277 |
+
def analyze_path_potential(
|
| 278 |
+
self,
|
| 279 |
+
from_node: int,
|
| 280 |
+
direction_node: Optional[int] = None,
|
| 281 |
+
max_depth: int = 2
|
| 282 |
+
) -> Dict[str, Any]:
|
| 283 |
+
"""
|
| 284 |
+
Analyze the potential of building roads in a direction.
|
| 285 |
+
|
| 286 |
+
CRITICAL: This helps with road-building strategy!
|
| 287 |
+
Instead of the LLM guessing where roads lead, this tool shows exactly
|
| 288 |
+
what opportunities exist 1-2 steps ahead (ports, high-value nodes, etc.)
|
| 289 |
+
|
| 290 |
+
Args:
|
| 291 |
+
from_node: Starting node ID
|
| 292 |
+
direction_node: Target direction (neighbor node ID), or None to check all directions
|
| 293 |
+
max_depth: How many steps ahead to look (1 or 2)
|
| 294 |
+
|
| 295 |
+
Returns:
|
| 296 |
+
Dictionary with path analysis
|
| 297 |
+
"""
|
| 298 |
+
# Get starting node info
|
| 299 |
+
from_info = self.inspect_node(from_node)
|
| 300 |
+
if not from_info.get("exists"):
|
| 301 |
+
return {
|
| 302 |
+
"error": f"Starting node {from_node} does not exist",
|
| 303 |
+
"from_node": from_node
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
neighbors = from_info.get("neighbors", [])
|
| 307 |
+
|
| 308 |
+
# If specific direction given, only analyze that one
|
| 309 |
+
if direction_node is not None:
|
| 310 |
+
if direction_node not in neighbors:
|
| 311 |
+
return {
|
| 312 |
+
"error": f"Node {direction_node} is not a neighbor of {from_node}",
|
| 313 |
+
"from_node": from_node,
|
| 314 |
+
"neighbors": neighbors
|
| 315 |
+
}
|
| 316 |
+
neighbors = [direction_node]
|
| 317 |
+
|
| 318 |
+
paths = []
|
| 319 |
+
|
| 320 |
+
for neighbor_id in neighbors:
|
| 321 |
+
path_analysis = {
|
| 322 |
+
"direction": neighbor_id,
|
| 323 |
+
"depth_1": None,
|
| 324 |
+
"depth_2": None,
|
| 325 |
+
"highlights": [],
|
| 326 |
+
"score": 0.0
|
| 327 |
+
}
|
| 328 |
+
|
| 329 |
+
# Depth 1: Immediate neighbor
|
| 330 |
+
depth_1_info = self.inspect_node(neighbor_id)
|
| 331 |
+
if depth_1_info.get("exists"):
|
| 332 |
+
path_analysis["depth_1"] = {
|
| 333 |
+
"node_id": neighbor_id,
|
| 334 |
+
"resources": depth_1_info.get("resources", {}),
|
| 335 |
+
"total_pips": depth_1_info.get("total_pips", 0),
|
| 336 |
+
"port": depth_1_info.get("port"),
|
| 337 |
+
"can_build": depth_1_info.get("can_build_here", False),
|
| 338 |
+
"occupied": depth_1_info.get("occupied", False)
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
# Score based on depth 1
|
| 342 |
+
score = depth_1_info.get("total_pips", 0) * 1.0 # Full weight
|
| 343 |
+
|
| 344 |
+
# Highlights
|
| 345 |
+
if depth_1_info.get("port"):
|
| 346 |
+
path_analysis["highlights"].append(
|
| 347 |
+
f"Port ({depth_1_info['port']}) at depth 1"
|
| 348 |
+
)
|
| 349 |
+
score += 3.0
|
| 350 |
+
|
| 351 |
+
if depth_1_info.get("total_pips", 0) >= 12:
|
| 352 |
+
path_analysis["highlights"].append("High-value node at depth 1")
|
| 353 |
+
|
| 354 |
+
if depth_1_info.get("can_build_here"):
|
| 355 |
+
path_analysis["highlights"].append("Can build settlement at depth 1")
|
| 356 |
+
score += 1.0
|
| 357 |
+
|
| 358 |
+
# Depth 2: Look ahead one more step
|
| 359 |
+
if max_depth >= 2:
|
| 360 |
+
depth_2_neighbors = depth_1_info.get("neighbors", [])
|
| 361 |
+
reachable_nodes = []
|
| 362 |
+
best_node = None
|
| 363 |
+
best_pips = 0
|
| 364 |
+
|
| 365 |
+
for depth_2_id in depth_2_neighbors:
|
| 366 |
+
if depth_2_id == from_node: # Don't go back
|
| 367 |
+
continue
|
| 368 |
+
|
| 369 |
+
depth_2_info = self.inspect_node(depth_2_id)
|
| 370 |
+
if depth_2_info.get("exists"):
|
| 371 |
+
node_data = {
|
| 372 |
+
"node_id": depth_2_id,
|
| 373 |
+
"total_pips": depth_2_info.get("total_pips", 0),
|
| 374 |
+
"port": depth_2_info.get("port"),
|
| 375 |
+
"can_build": depth_2_info.get("can_build_here", False)
|
| 376 |
+
}
|
| 377 |
+
reachable_nodes.append(node_data)
|
| 378 |
+
|
| 379 |
+
# Track best
|
| 380 |
+
pips = depth_2_info.get("total_pips", 0)
|
| 381 |
+
if pips > best_pips:
|
| 382 |
+
best_pips = pips
|
| 383 |
+
best_node = depth_2_id
|
| 384 |
+
|
| 385 |
+
# Highlights at depth 2
|
| 386 |
+
if depth_2_info.get("port"):
|
| 387 |
+
path_analysis["highlights"].append(
|
| 388 |
+
f"Port ({depth_2_info['port']}) at depth 2 (node {depth_2_id})"
|
| 389 |
+
)
|
| 390 |
+
score += 1.5
|
| 391 |
+
|
| 392 |
+
if pips >= 12:
|
| 393 |
+
path_analysis["highlights"].append(
|
| 394 |
+
f"High-value node at depth 2 (node {depth_2_id}, {pips} pips)"
|
| 395 |
+
)
|
| 396 |
+
|
| 397 |
+
path_analysis["depth_2"] = {
|
| 398 |
+
"reachable_nodes": reachable_nodes,
|
| 399 |
+
"best_node": best_node,
|
| 400 |
+
"best_pips": best_pips
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
# Add partial score for depth 2 (50% weight)
|
| 404 |
+
score += best_pips * 0.5
|
| 405 |
+
|
| 406 |
+
path_analysis["score"] = round(score, 1)
|
| 407 |
+
|
| 408 |
+
paths.append(path_analysis)
|
| 409 |
+
|
| 410 |
+
# Sort by score
|
| 411 |
+
paths.sort(key=lambda p: p["score"], reverse=True)
|
| 412 |
+
|
| 413 |
+
return {
|
| 414 |
+
"from_node": from_node,
|
| 415 |
+
"total_directions": len(paths),
|
| 416 |
+
"paths": paths
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
# ========== Tool Registration for LLM ==========
|
| 421 |
+
|
| 422 |
+
def get_tools_schema(self) -> List[Dict[str, Any]]:
|
| 423 |
+
"""
|
| 424 |
+
Get tool definitions in a format suitable for LLM function calling.
|
| 425 |
+
|
| 426 |
+
These schemas can be passed to LLM APIs like OpenAI's function calling,
|
| 427 |
+
Anthropic's tool use, or Google Gemini's function declarations.
|
| 428 |
+
|
| 429 |
+
Returns:
|
| 430 |
+
List of tool definition dictionaries
|
| 431 |
+
"""
|
| 432 |
+
return [
|
| 433 |
+
{
|
| 434 |
+
"name": "inspect_node",
|
| 435 |
+
"description": "Get detailed information about a specific node on the board. USE THIS to verify node data instead of trying to interpret Arrays N and H yourself - this prevents hallucinations!",
|
| 436 |
+
"parameters": {
|
| 437 |
+
"type": "object",
|
| 438 |
+
"properties": {
|
| 439 |
+
"node_id": {
|
| 440 |
+
"type": "integer",
|
| 441 |
+
"description": "The node ID to inspect (e.g., 10, 18, 40)"
|
| 442 |
+
}
|
| 443 |
+
},
|
| 444 |
+
"required": ["node_id"]
|
| 445 |
+
}
|
| 446 |
+
},
|
| 447 |
+
{
|
| 448 |
+
"name": "find_best_nodes",
|
| 449 |
+
"description": "Search for the best available nodes matching specific criteria. USE THIS instead of manually scanning the board - prevents missing opportunities!",
|
| 450 |
+
"parameters": {
|
| 451 |
+
"type": "object",
|
| 452 |
+
"properties": {
|
| 453 |
+
"min_pips": {
|
| 454 |
+
"type": "integer",
|
| 455 |
+
"description": "Minimum total pip value (probability). Good nodes have 10+, excellent have 12+",
|
| 456 |
+
"default": 0
|
| 457 |
+
},
|
| 458 |
+
"must_have_resource": {
|
| 459 |
+
"type": "string",
|
| 460 |
+
"description": "Required resource type (e.g., 'Wheat', 'Ore', 'Brick', 'Wood', 'Sheep')",
|
| 461 |
+
"nullable": True
|
| 462 |
+
},
|
| 463 |
+
"exclude_blocked": {
|
| 464 |
+
"type": "boolean",
|
| 465 |
+
"description": "Skip nodes that cannot be built on (occupied or too close to other buildings)",
|
| 466 |
+
"default": True
|
| 467 |
+
},
|
| 468 |
+
"prefer_port": {
|
| 469 |
+
"type": "boolean",
|
| 470 |
+
"description": "Prioritize nodes with port access",
|
| 471 |
+
"default": False
|
| 472 |
+
},
|
| 473 |
+
"limit": {
|
| 474 |
+
"type": "integer",
|
| 475 |
+
"description": "Maximum number of results to return",
|
| 476 |
+
"default": 10
|
| 477 |
+
}
|
| 478 |
+
},
|
| 479 |
+
"required": []
|
| 480 |
+
}
|
| 481 |
+
},
|
| 482 |
+
{
|
| 483 |
+
"name": "analyze_path_potential",
|
| 484 |
+
"description": "Analyze where a road path leads and what opportunities exist ahead. USE THIS to plan road building - shows ports and valuable nodes 1-2 steps away!",
|
| 485 |
+
"parameters": {
|
| 486 |
+
"type": "object",
|
| 487 |
+
"properties": {
|
| 488 |
+
"from_node": {
|
| 489 |
+
"type": "integer",
|
| 490 |
+
"description": "Starting node ID (where you currently have a settlement/road)"
|
| 491 |
+
},
|
| 492 |
+
"direction_node": {
|
| 493 |
+
"type": "integer",
|
| 494 |
+
"description": "Specific neighbor to analyze, or omit to see all directions",
|
| 495 |
+
"nullable": True
|
| 496 |
+
},
|
| 497 |
+
"max_depth": {
|
| 498 |
+
"type": "integer",
|
| 499 |
+
"description": "How many steps ahead to look (1 or 2)",
|
| 500 |
+
"default": 2
|
| 501 |
+
}
|
| 502 |
+
},
|
| 503 |
+
"required": ["from_node"]
|
| 504 |
+
}
|
| 505 |
+
}
|
| 506 |
+
]
|
| 507 |
+
|
| 508 |
+
|
| 509 |
+
def execute_tool(
|
| 510 |
+
self,
|
| 511 |
+
tool_name: str,
|
| 512 |
+
parameters: Dict[str, Any]
|
| 513 |
+
) -> Dict[str, Any]:
|
| 514 |
+
"""
|
| 515 |
+
Execute a tool by name with given parameters.
|
| 516 |
+
|
| 517 |
+
This is the dispatcher for when the LLM calls a tool.
|
| 518 |
+
|
| 519 |
+
Args:
|
| 520 |
+
tool_name: Name of the tool to execute
|
| 521 |
+
parameters: Parameters for the tool (as a dictionary)
|
| 522 |
+
|
| 523 |
+
Returns:
|
| 524 |
+
Tool execution result
|
| 525 |
+
|
| 526 |
+
Raises:
|
| 527 |
+
ValueError: If tool name is unknown
|
| 528 |
+
"""
|
| 529 |
+
if tool_name == "inspect_node":
|
| 530 |
+
return self.inspect_node(**parameters)
|
| 531 |
+
elif tool_name == "find_best_nodes":
|
| 532 |
+
return self.find_best_nodes(**parameters)
|
| 533 |
+
elif tool_name == "analyze_path_potential":
|
| 534 |
+
return self.analyze_path_potential(**parameters)
|
| 535 |
+
else:
|
| 536 |
+
raise ValueError(
|
| 537 |
+
f"Unknown tool: {tool_name}. "
|
| 538 |
+
f"Available tools: inspect_node, find_best_nodes, analyze_path_potential"
|
| 539 |
+
)
|
pycatan/ai/ai_manager.py
CHANGED
|
@@ -281,9 +281,22 @@ class AIManager:
|
|
| 281 |
tokens=response.total_tokens if response else 0
|
| 282 |
)
|
| 283 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
if parsed:
|
| 285 |
# Update memory
|
| 286 |
-
|
|
|
|
|
|
|
|
|
|
| 287 |
|
| 288 |
# Clear events since they've been processed
|
| 289 |
agent.clear_events()
|
|
@@ -441,6 +454,9 @@ class AIManager:
|
|
| 441 |
if agent.memory:
|
| 442 |
agent_memory = {"note_from_last_turn": agent.memory}
|
| 443 |
|
|
|
|
|
|
|
|
|
|
| 444 |
# Create prompt through PromptManager
|
| 445 |
prompt = self.prompt_manager.create_prompt(
|
| 446 |
player_num=agent.player_id,
|
|
|
|
| 281 |
tokens=response.total_tokens if response else 0
|
| 282 |
)
|
| 283 |
|
| 284 |
+
# Merge LLM's memory/chat with the action (even if action was manual)
|
| 285 |
+
# This ensures note_to_self and say_outloud are preserved from LLM
|
| 286 |
+
if llm_suggestion and parsed:
|
| 287 |
+
# If parsed doesn't have note_to_self, use LLM's
|
| 288 |
+
if not parsed.get("note_to_self") and llm_suggestion.get("note_to_self"):
|
| 289 |
+
parsed["note_to_self"] = llm_suggestion["note_to_self"]
|
| 290 |
+
# If parsed doesn't have say_outloud, use LLM's
|
| 291 |
+
if not parsed.get("say_outloud") and llm_suggestion.get("say_outloud"):
|
| 292 |
+
parsed["say_outloud"] = llm_suggestion["say_outloud"]
|
| 293 |
+
|
| 294 |
if parsed:
|
| 295 |
# Update memory
|
| 296 |
+
note_to_self = parsed.get("note_to_self")
|
| 297 |
+
agent.update_memory(note_to_self)
|
| 298 |
+
print(f"[DEBUG SAVE] Saved note_to_self: {note_to_self[:50] if note_to_self else 'None'}...")
|
| 299 |
+
print(f"[DEBUG SAVE] Agent.memory now: {agent.memory[:50] if agent.memory else 'None'}...")
|
| 300 |
|
| 301 |
# Clear events since they've been processed
|
| 302 |
agent.clear_events()
|
|
|
|
| 454 |
if agent.memory:
|
| 455 |
agent_memory = {"note_from_last_turn": agent.memory}
|
| 456 |
|
| 457 |
+
# DEBUG: Print memory state
|
| 458 |
+
print(f"[DEBUG MEMORY] Agent: {agent.player_name}, Memory: {agent.memory[:50] if agent.memory else 'None'}...")
|
| 459 |
+
|
| 460 |
# Create prompt through PromptManager
|
| 461 |
prompt = self.prompt_manager.create_prompt(
|
| 462 |
player_num=agent.player_id,
|
pycatan/ai/ai_user.py
CHANGED
|
@@ -110,11 +110,15 @@ class AIUser(User):
|
|
| 110 |
try:
|
| 111 |
user_input = input(f" {self.name} (AI) > ").strip()
|
| 112 |
|
|
|
|
|
|
|
|
|
|
| 113 |
# If empty and we have LLM suggestion, use it
|
| 114 |
if not user_input:
|
| 115 |
-
llm_response = self.ai_manager._last_llm_response
|
| 116 |
if llm_response:
|
| 117 |
print(f" [OK] Using LLM suggestion")
|
|
|
|
|
|
|
| 118 |
return self._decision_to_action(llm_response, allowed_actions)
|
| 119 |
else:
|
| 120 |
print(f" [!] No input and no LLM suggestion")
|
|
@@ -134,6 +138,10 @@ class AIUser(User):
|
|
| 134 |
print(f" Allowed: {allowed_actions}")
|
| 135 |
continue
|
| 136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
return action
|
| 138 |
|
| 139 |
except ValueError as e:
|
|
@@ -142,6 +150,29 @@ class AIUser(User):
|
|
| 142 |
print("\n Game interrupted.")
|
| 143 |
return Action(ActionType.END_TURN, self.user_id)
|
| 144 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
def _show_help(self):
|
| 146 |
"""Show help for manual input."""
|
| 147 |
print("""
|
|
|
|
| 110 |
try:
|
| 111 |
user_input = input(f" {self.name} (AI) > ").strip()
|
| 112 |
|
| 113 |
+
# Get LLM response (for memory/chat even if action is manual)
|
| 114 |
+
llm_response = self.ai_manager._last_llm_response
|
| 115 |
+
|
| 116 |
# If empty and we have LLM suggestion, use it
|
| 117 |
if not user_input:
|
|
|
|
| 118 |
if llm_response:
|
| 119 |
print(f" [OK] Using LLM suggestion")
|
| 120 |
+
# Save memory and chat from LLM response
|
| 121 |
+
self._save_llm_memory_and_chat(llm_response)
|
| 122 |
return self._decision_to_action(llm_response, allowed_actions)
|
| 123 |
else:
|
| 124 |
print(f" [!] No input and no LLM suggestion")
|
|
|
|
| 138 |
print(f" Allowed: {allowed_actions}")
|
| 139 |
continue
|
| 140 |
|
| 141 |
+
# Even with manual action, save LLM's memory and chat if available
|
| 142 |
+
if llm_response:
|
| 143 |
+
self._save_llm_memory_and_chat(llm_response)
|
| 144 |
+
|
| 145 |
return action
|
| 146 |
|
| 147 |
except ValueError as e:
|
|
|
|
| 150 |
print("\n Game interrupted.")
|
| 151 |
return Action(ActionType.END_TURN, self.user_id)
|
| 152 |
|
| 153 |
+
def _save_llm_memory_and_chat(self, llm_response: Dict[str, Any]) -> None:
|
| 154 |
+
"""
|
| 155 |
+
Save memory and chat from LLM response to agent state.
|
| 156 |
+
|
| 157 |
+
This ensures note_to_self and say_outloud are preserved even when
|
| 158 |
+
the user provides a manual action override.
|
| 159 |
+
"""
|
| 160 |
+
agent = self.ai_manager.get_agent(self.name)
|
| 161 |
+
if not agent:
|
| 162 |
+
return
|
| 163 |
+
|
| 164 |
+
# Save note_to_self to agent memory
|
| 165 |
+
note_to_self = llm_response.get("note_to_self")
|
| 166 |
+
if note_to_self:
|
| 167 |
+
agent.update_memory(note_to_self)
|
| 168 |
+
print(f"[DEBUG SAVE] Saved note_to_self: {note_to_self[:50] if note_to_self else 'None'}...")
|
| 169 |
+
print(f"[DEBUG SAVE] Agent.memory now: {agent.memory[:50] if agent.memory else 'None'}...")
|
| 170 |
+
|
| 171 |
+
# Broadcast say_outloud to chat
|
| 172 |
+
say_outloud = llm_response.get("say_outloud")
|
| 173 |
+
if say_outloud:
|
| 174 |
+
self.ai_manager._broadcast_chat(self.name, say_outloud)
|
| 175 |
+
|
| 176 |
def _show_help(self):
|
| 177 |
"""Show help for manual input."""
|
| 178 |
print("""
|