EZTIME2025 commited on
Commit
c7f3125
·
1 Parent(s): 9ecc8c1

create tools, fix promts

Browse files
Files changed (31) hide show
  1. examples/ai_testing/my_games/current_session.txt +1 -1
  2. examples/ai_testing/my_games/session_20260109_020107/A/A.md +35 -0
  3. examples/ai_testing/my_games/session_20260109_020107/A/prompts/prompt_2.json +95 -0
  4. examples/ai_testing/my_games/session_20260109_020107/A/prompts/prompt_2.txt +88 -0
  5. examples/ai_testing/my_games/session_20260109_020107/A/responses/response_1.json +24 -0
  6. examples/ai_testing/my_games/session_20260109_020107/A/responses/response_2.json +25 -0
  7. examples/ai_testing/my_games/session_20260109_022638/A/A.md +51 -0
  8. examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_1.json +95 -0
  9. examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_1.txt +87 -0
  10. examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_2.json +95 -0
  11. examples/ai_testing/my_games/session_20260109_022638/A/prompts/prompt_2.txt +88 -0
  12. examples/ai_testing/my_games/session_20260109_022638/A/responses/response_1.json +24 -0
  13. examples/ai_testing/my_games/session_20260109_022638/A/responses/response_2.json +25 -0
  14. examples/ai_testing/my_games/session_20260109_022638/session_metadata.json +5 -0
  15. examples/ai_testing/my_games/session_20260109_023135/A/A.md +55 -0
  16. examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_1.json +95 -0
  17. examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_1.txt +87 -0
  18. examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_2.json +107 -0
  19. examples/ai_testing/my_games/session_20260109_023135/A/prompts/prompt_2.txt +100 -0
  20. examples/ai_testing/my_games/session_20260109_023135/A/responses/response_1.json +25 -0
  21. examples/ai_testing/my_games/session_20260109_023135/A/responses/response_2.json +25 -0
  22. examples/ai_testing/my_games/session_20260109_023135/chat_history.json +8 -0
  23. examples/ai_testing/my_games/session_20260109_023135/session_metadata.json +5 -0
  24. examples/example_agent_tools_integration.py +273 -0
  25. examples/test_agent_tools.py +222 -0
  26. examples/test_agent_tools_v2.py +301 -0
  27. pycatan/ai/AGENT_TOOLS_README.md +320 -0
  28. pycatan/ai/__init__.py +7 -0
  29. pycatan/ai/agent_tools.py +539 -0
  30. pycatan/ai/ai_manager.py +17 -1
  31. pycatan/ai/ai_user.py +32 -1
examples/ai_testing/my_games/current_session.txt CHANGED
@@ -1 +1 @@
1
- session_20260109_020107
 
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
- agent.update_memory(parsed.get("note_to_self"))
 
 
 
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("""