File size: 10,119 Bytes
85c616d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24784e0
 
 
 
 
 
85c616d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24784e0
 
 
85c616d
24784e0
85c616d
 
 
24784e0
85c616d
 
 
 
 
 
 
 
 
 
 
 
 
24784e0
 
 
 
85c616d
 
24784e0
 
 
 
 
85c616d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
from playwright.sync_api import sync_playwright, expect, Page
import time
import random
import re
from pathlib import Path
import sys
import os
import subprocess
import signal

# Add project root to path
sys.path.insert(0, str(Path(__file__).parent.parent))

from e2e_tests.playwright_helpers import (
    wait_for_gradio,
    load_character,
    send_message,
    click_tab,
    kill_port, # Import kill_port
    get_hp_from_sheet, # Import get_hp_from_sheet
    extract_creatures, # Import extract_creatures
    get_last_bot_message
)

def test_adventure_simulation_playwright():
    """
    Automated Adventure Simulation - Random Travel & Combat Until Death (Playwright)
    """
    print("=" * 80)
    print("๐ŸŽฎ AUTOMATED ADVENTURE SIMULATION (Playwright)")
    print("=" * 80)
    
    # Ensure port 7860 is free
    kill_port(7860)
    
    # Start Gradio server
    print("\n๐Ÿš€ Starting Gradio server...")
    gradio_process = subprocess.Popen(
        ['python3', 'web/app_gradio.py'],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
    time.sleep(10) # Give server time to start
    
    with sync_playwright() as p:
        # Use headless for automation, set to False to watch
        browser = p.chromium.launch(headless=True) 
        page = browser.new_page()
        page.set_viewport_size({"width": 1280, "height": 720}) # Set a reasonable viewport
        
        try:
            # Navigate to app
            print("\n๐Ÿ“ฑ Opening browser...")
            page.goto("http://localhost:7860")
            wait_for_gradio(page)
            
            # Click "Play Game" tab
            click_tab(page, "๐ŸŽฎ Play Game")
            
            # Load character
            print("โš”๏ธ  Loading Thorin Stormshield...")
            load_character(page, "Thorin Stormshield")
            
            print("โœ… Character loaded!\n")
            
            # Start adventure
            print("=" * 80)
            print("๐Ÿ—บ๏ธ  ADVENTURE BEGINS!")
            print("=" * 80)
            
            turn = 0
            max_turns = 20  # Safety limit
            in_combat = False
            current_enemy = None
            combat_started_for_current_enemy = False  # Track if we've called /start_combat

            # Stuck detection (from GRADIO_FREEZE_BUG_FIX.md)
            last_response_text = ""
            stuck_count = 0

            while turn < max_turns:
                turn += 1
                
                # Check HP
                hp = get_hp_from_sheet(page)
                print(f"\n๐Ÿ’š Turn {turn} - HP: {hp if hp is not None else '???'}")
                
                if hp is not None and hp <= 0:
                    print("\n" + "=" * 80)
                    print("๐Ÿ’€ GAME OVER - CHARACTER DIED!")
                    print("=" * 80)
                    break
                
                # Decide action
                if not in_combat:
                    # Exploration mode - Ask GM what we see/encounter
                    action_roll = random.random()
                    
                    if action_roll < 0.3:
                        # Venture into wilderness/danger
                        actions = [
                            "I venture deeper into the wilderness, looking for adventure",
                            "I follow the path ahead, seeking what dangers or treasures await",
                            "I explore the area thoroughly, searching for anything unusual",
                            "I venture into the darker parts of this place",
                            "I head toward the sounds of potential danger",
                            "I search for signs of monsters or bandits in the area"
                        ]
                        action = random.choice(actions)
                        print(f"\n๐Ÿ” Turn {turn}: {action}")
                        send_message(page, action)
                        response = get_last_bot_message(page)
                        print(f"GM: {response[:300]}...")
                        
                        # Check for creatures
                        creatures = extract_creatures(response)
                        if creatures:
                            print(f"โš ๏ธ  CREATURE SPOTTED: {creatures[0]}!")
                            current_enemy = creatures[0]
                            in_combat = True
                    
                    elif action_roll < 0.6:
                        # Ask GM about surroundings
                        actions = [
                            "I look around carefully. What do I see?",
                            "I listen for any sounds nearby. What do I hear?",
                            "What dangers might be lurking here?",
                            "I investigate the area. Is there anything interesting?",
                            "Are there any creatures or people nearby?"
                        ]
                        action = random.choice(actions)
                        print(f"\n๐Ÿ‘€ Turn {turn}: {action}")
                        send_message(page, action)
                        response = get_last_bot_message(page)
                        print(f"GM: {response[:300]}...")
                        
                        creatures = extract_creatures(response)
                        if creatures:
                            print(f"โš ๏ธ  CREATURE SPOTTED: {creatures[0]}!")
                            current_enemy = creatures[0]
                            in_combat = True
                    
                    else:
                        # Move to a new area (use travel or natural language)
                        destinations = ['Dark Forest', 'Mountain Road', 'Old Ruins', 
                                      'Temple District', 'Market Square', 'Forest Path',
                                      'Town Gates', 'Town Square']
                        dest = random.choice(destinations)
                        
                        print(f"\n๐Ÿšถ Turn {turn}: Traveling to {dest}...")
                        send_message(page, f"I travel to {dest}")
                        response = get_last_bot_message(page)
                        
                        print(f"GM: {response[:300]}...")
                        
                        # Check for creatures
                        creatures = extract_creatures(response)
                        if creatures:
                            print(f"โš ๏ธ  CREATURE SPOTTED: {creatures[0]}!")
                            current_enemy = creatures[0]
                            in_combat = True
                
                else:
                    # Combat mode - attack until someone dies
                    print(f"\nโš”๏ธ  Turn {turn}: COMBAT - Attacking {current_enemy}!")

                    # Start combat only if we haven't started it for this enemy yet
                    if not combat_started_for_current_enemy:
                        send_message(page, f"/start_combat {current_enemy}")
                        combat_started_for_current_enemy = True
                        time.sleep(2) # Give a moment for combat to init
                        response = get_last_bot_message(page)
                        print(f"Combat started: {response[:200]}...")

                    # Attack
                    attack_variations = [
                        f"I attack the {current_enemy} with my battleaxe",
                        f"I swing at the {current_enemy}",
                        f"I strike the {current_enemy}",
                        f"I attack {current_enemy}"
                    ]
                    attack = random.choice(attack_variations)
                    
                    send_message(page, attack)
                    response = get_last_bot_message(page)
                    print(f"Attack: {response[:250]}...")
                    
                    # Check if enemy died OR combat ended
                    if ('dead' in response.lower() or 'dies' in response.lower() or 'falls' in response.lower() or
                        'combat has ended' in response.lower() or 'combat ended' in response.lower()):
                        print(f"โœ… {current_enemy} defeated! Combat ended.")
                        in_combat = False
                        current_enemy = None
                        combat_started_for_current_enemy = False  # Reset flag for next combat

                        # End combat if not already ended
                        if 'combat has ended' not in response.lower():
                            send_message(page, "/end_combat")
                    
                    # Check our HP
                    hp = get_hp_from_sheet(page)
                    if hp is not None and hp <= 0:
                        print(f"\n๐Ÿ’€ Thorin has fallen in battle against {current_enemy}!")
                        break
                
                # Pause between turns
                time.sleep(1) # Keep it running slower to better observe interactions if headless=False
            
            if turn >= max_turns:
                print("\nโฑ๏ธ  Adventure ended - max turns reached")
            
            print("\n" + "=" * 80)
            print("๐Ÿ ADVENTURE COMPLETE")
            print("=" * 80)
            print(f"\nTotal turns: {turn}")
            print(f"Final HP: {get_hp_from_sheet(page)}")
            
            # Keep browser open on error for inspection
            if "PYTEST_CURRENT_TEST" not in os.environ: # Only if run directly, not by pytest
                print("\n๐Ÿ‘€ Browser will stay open for 10 seconds...")
                time.sleep(10)
            
        except Exception as e:
            print(f"\nโŒ Test Failed: {e}")
            page.screenshot(path="adventure_failure.png")
            raise e
        
        finally:
            browser.close()
            print("๐Ÿ›‘ Stopping Gradio...")
            gradio_process.terminate()
            gradio_process.wait()

if __name__ == "__main__":
    test_adventure_simulation_playwright()