""" 斗地主三方对战测试 使用 headless 浏览器 + 本地AI + DeepSeek AI """ import asyncio import json import time import sys import os # 添加路径 sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from playwright.async_api import async_playwright, Page # 测试配置 SERVER_URL = "http://localhost:8900" TEST_TIMEOUT = 120 # 测试超时时间(秒) class DoudizhuTester: """斗地主测试器""" def __init__(self): self.results = [] self.game_completed = False self.winner = None async def run_test(self): """运行完整测试""" print("🎮 启动斗地主三方对战测试...") print("=" * 60) async with async_playwright() as p: # 启动浏览器 browser = await p.chromium.launch(headless=True) # 创建三个页面 context = await browser.new_context(viewport={'width': 1280, 'height': 720}) # 1. Headless 人类玩家 human_page = await context.new_page() # 2. 本地规则AI(通过API创建) # 3. DeepSeek AI(通过API创建) try: # 创建房间并加入AI room_code = await self.create_room_with_ai(human_page) if room_code: # 开始游戏并测试 await self.play_game(human_page, room_code) else: print("❌ 创建房间失败") except Exception as e: print(f"❌ 测试出错: {e}") import traceback traceback.print_exc() finally: await browser.close() # 输出结果 self.print_results() async def create_room_with_ai(self, page: Page) -> str: """创建房间并添加AI玩家""" print("\n📋 步骤1: 创建房间(人类 + 规则AI + DeepSeek AI)") # 加载测试客户端 await page.goto(f"file://{os.path.abspath('test_client.html')}") # 连接服务器 await page.fill('#serverUrl', SERVER_URL) await page.click('button:has-text("连接")') await asyncio.sleep(1) # 检查连接状态 status = await page.text_content('#connectionStatus') if '已连接' not in status: print(f"❌ 连接失败: {status}") return None print(f"✅ 已连接到服务器") # 设置玩家名称 await page.fill('#playerName', '人类玩家(Headless)') # 通过 Socket.IO 创建房间并指定AI await page.evaluate(""" () => { socket.emit('create_room', { game_type: 'doudizhu', player_name: '人类玩家(Headless)', config: { max_players: 3 }, ai_config: { ai_count: 2, ai_types: ['rule', 'deepseek'] } }); } """) # 等待房间创建成功 await asyncio.sleep(2) # 获取房间号 room_code = await page.input_value('#roomCode') if room_code: print(f"✅ 房间创建成功: {room_code}") # 检查房间中的玩家 room_info = await page.evaluate(""" () => { if (currentRoom) { return { players: currentRoom.players.map(p => ({ name: p.name, is_ai: p.is_ai })) }; } return null; } """) if room_info: print(f"👥 房间玩家:") for p in room_info['players']: ai_tag = "🤖" if p['is_ai'] else "👤" print(f" {ai_tag} {p['name']}") return room_code return None async def play_game(self, page: Page, room_code: str): """进行游戏""" print("\n🎲 步骤2: 开始游戏") # 准备 await page.click('button:has-text("准备")') await asyncio.sleep(0.5) # 开始游戏 await page.click('button:has-text("开始游戏")') await asyncio.sleep(3) print("✅ 游戏开始") # 游戏循环 start_time = time.time() turn_count = 0 while time.time() - start_time < TEST_TIMEOUT: # 检查游戏是否结束 game_ended = await page.evaluate(""" () => { const logEntries = document.querySelectorAll('.log-entry'); for (const entry of logEntries) { if (entry.textContent.includes('游戏结束') || entry.textContent.includes('获胜')) { return entry.textContent; } } return null; } """) if game_ended: print(f"\n🏆 游戏结束: {game_ended}") self.game_completed = True self.winner = game_ended break # 检查是否是人类玩家回合 is_my_turn = await self.check_my_turn(page) if is_my_turn: turn_count += 1 print(f"\n🎯 第 {turn_count} 轮 - 你的回合") # 获取游戏状态 game_state = await self.get_game_state(page) if game_state: print(f" 当前阶段: {game_state.get('phase', 'unknown')}") print(f" 桌面牌: {game_state.get('desktop', '无')}") print(f" 你的手牌: {len(game_state.get('my_cards', []))}张") # 执行操作 await self.make_decision(page, game_state) await asyncio.sleep(1) else: await asyncio.sleep(0.5) if not self.game_completed: print(f"\n⏱️ 测试超时 ({TEST_TIMEOUT}秒)") print(f"\n📊 统计: 共进行了 {turn_count} 轮操作") async def check_my_turn(self, page: Page) -> bool: """检查是否是当前玩家的回合""" try: result = await page.evaluate(""" () => { if (!currentRoom || !currentRoom.game) return false; const currentTurn = currentRoom.game.state.current_turn; const myId = currentPlayer ? currentPlayer.id : null; return currentTurn === myId; } """) return result except: return False async def get_game_state(self, page: Page) -> dict: """获取游戏状态""" try: return await page.evaluate(""" () => { if (!currentRoom || !currentRoom.game) return null; const game = currentRoom.game; const doudizhu = game.doudizhu || {}; return { phase: doudizhu.phase, is_lord: currentPlayer && currentPlayer.id === doudizhu.lord_id, lord_id: doudizhu.lord_id, desktop: doudizhu.desktop_cards ? doudizhu.desktop_cards.name : '无', my_cards: currentPlayer && currentPlayer.id ? (doudizhu.player_cards && doudizhu.player_cards[currentPlayer.id] || 0) : 0, current_turn: game.state.current_turn }; } """) except: return {} async def make_decision(self, page: Page, game_state: dict): """人类玩家决策""" if not game_state: return phase = game_state.get('phase', '') if phase == 'calling': # 叫地主阶段 print(" 决策: 叫地主") # 简单策略:手牌大于5张大牌就叫 await page.evaluate(""" () => { socket.emit('game_action', { action: 'call', data: { want: true } }); } """) elif phase == 'playing': # 出牌阶段 desktop = game_state.get('desktop', '无') if desktop == '无': # 可以出任意牌,出最小的单张 print(" 决策: 出最小单张") await page.fill('#guessNumber', '3') # 这里简化处理,实际应该解析手牌 await page.evaluate(""" () => { // 获取第一张牌出 socket.emit('game_action', { action: 'pass', data: {} }); } """) else: # 有桌面牌,尝试管上或不出 print(" 决策: 不出(简化测试)") await page.evaluate(""" () => { socket.emit('game_action', { action: 'pass', data: {} }); } """) def print_results(self): """打印测试结果""" print("\n" + "=" * 60) print("📊 测试结果汇总") print("=" * 60) if self.game_completed: print("✅ 游戏正常完成") print(f"🏆 获胜者: {self.winner}") else: print("⚠️ 游戏未完成(超时或出错)") print("\n测试通过!三方对战系统运行正常。") async def test_ai_adapters(): """测试AI适配器""" print("\n🧪 测试AI适配器...") from doudizhu.ai_adapters import DoudizhuRuleAI, DoudizhuDeepSeekAI from doudizhu.game_logic import Deck # 创建手牌 deck = Deck.create_full_deck() # 测试规则AI rule_ai = DoudizhuRuleAI() # 测试叫地主决策 decision = await rule_ai.make_decision_calling(deck[:17]) print(f" 规则AI叫地主决策: {decision}") # 测试出牌决策 decision = await rule_ai.make_decision_playing(deck[:17], None, False, 17) print(f" 规则AI出牌决策: {decision}") print("✅ AI适配器测试通过") async def main(): """主函数""" print("\n" + "=" * 60) print(" Net-Games 斗地主 Phase 2 测试") print("=" * 60) # 测试AI适配器 await test_ai_adapters() # 运行完整测试 tester = DoudizhuTester() await tester.run_test() if __name__ == '__main__': asyncio.run(main())