Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

349 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
斗地主三方对战测试
使用 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())