Files
nanocode/net-games/test_doudizhu_api.py
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

283 lines
9.2 KiB
Python

"""
斗地主 API 测试
测试三方对战:人类 + 规则AI + DeepSeek AI
"""
import asyncio
import json
import time
import sys
import os
import socketio
# 添加路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from config import DEEPSEEK_API_KEY
SERVER_URL = "http://localhost:8900"
class DoudizhuAPITest:
"""斗地主API测试"""
def __init__(self):
self.sio = socketio.Client()
self.room_code = None
self.player_id = None
self.game_ended = False
self.turn_count = 0
self.results = []
# 注册事件处理器
self.sio.on('connected', self.on_connected)
self.sio.on('room_created', self.on_room_created)
self.sio.on('game_started', self.on_game_started)
self.sio.on('state_update', self.on_state_update)
self.sio.on('game_ended', self.on_game_ended)
self.sio.on('error', self.on_error)
def on_connected(self, data):
print(f"✅ 连接到服务器: {data.get('message')}")
def on_room_created(self, data):
if data.get('success'):
room = data.get('room', {})
self.room_code = room.get('code')
player = data.get('player', {})
self.player_id = player.get('id')
print(f"✅ 房间创建成功: {self.room_code}")
print(f"👤 玩家ID: {self.player_id[:8] if self.player_id else 'N/A'}")
# 显示房间玩家
players = room.get('players', [])
print(f"\n👥 房间玩家 ({len(players)}/3):")
for p in players:
ai_tag = "🤖 AI" if p.get('is_ai') else "👤 人类"
print(f" {ai_tag}: {p.get('name', 'Unknown')}")
else:
print(f"❌ 创建房间失败: {data}")
def on_game_started(self, data):
print(f"\n🎲 游戏开始!")
room = data.get('room', {})
game = room.get('game', {})
doudizhu = game.get('doudizhu', {})
print(f" 底牌: {len(doudizhu.get('bottom_cards', []))}张")
print(f" 当前回合: {doudizhu.get('current_turn', 'N/A')[:8] if doudizhu.get('current_turn') else 'N/A'}")
# 检查是否是叫地主阶段
phase = doudizhu.get('phase')
if phase == 'calling':
print(f" 阶段: 叫地主")
def on_state_update(self, data):
self.turn_count += 1
action = data.get('action')
player_id = data.get('player_id', '')
result = data.get('result', {})
game_state = data.get('game_state', {})
doudizhu = game_state.get('doudizhu', {})
player_name = player_id[:8] if player_id else 'N/A'
ai_reasoning = data.get('ai_reasoning', '')
print(f"\n📍 第 {self.turn_count} 步")
print(f" 玩家: {player_name}")
print(f" 操作: {action}")
if result.get('success'):
if action == 'play':
card_type = result.get('card_type', {})
print(f" 出牌: {card_type.get('name', 'Unknown')}")
remaining = result.get('remaining_cards', 0)
print(f" 剩余: {remaining}张")
elif action == 'call':
want = result.get('data', {}).get('want', False)
print(f" 叫地主: {'是' if want else '否'}")
if result.get('lord_id'):
print(f" 🎩 地主确定!")
elif action == 'pass':
print(f" 不出")
if ai_reasoning:
print(f" 🤖 AI思考: {ai_reasoning[:60]}...")
next_player = doudizhu.get('current_turn', '')
if next_player:
print(f" 下家: {next_player[:8]}")
else:
print(f" ❌ 失败: {result.get('message', 'Unknown')}")
def on_game_ended(self, data):
self.game_ended = True
winner = data.get('winner', '')
room = data.get('room', {})
game = room.get('game', {})
print(f"\n" + "=" * 50)
print(f"🏆 游戏结束!")
print(f" 获胜者: {winner[:8] if winner else 'N/A'}")
print(f" 总步数: {self.turn_count}")
print("=" * 50)
def on_error(self, data):
print(f"❌ 错误: {data.get('message', 'Unknown')}")
def run(self):
"""运行测试"""
print("=" * 60)
print(" 斗地主三方对战 API 测试")
print("=" * 60)
print(f"🤖 AI配置: 规则AI + DeepSeek AI")
print(f"🔑 DeepSeek Key: {DEEPSEEK_API_KEY[:10]}..." if DEEPSEEK_API_KEY else "❌ 未配置")
try:
# 连接服务器
print(f"\n📡 连接服务器: {SERVER_URL}")
self.sio.connect(SERVER_URL)
# 等待连接
time.sleep(1)
# 创建房间
print("\n📝 创建房间...")
self.sio.emit('create_room', {
'game_type': 'doudizhu',
'player_name': '人类玩家',
'config': {'max_players': 3},
'ai_config': {
'ai_count': 2,
'ai_types': ['rule', 'deepseek']
}
})
# 等待房间创建
for _ in range(10):
if self.room_code:
break
time.sleep(0.5)
if not self.room_code:
print("❌ 房间创建超时")
return
# 准备并开始
time.sleep(1)
print("\n✅ 准备...")
self.sio.emit('set_ready', {'ready': True})
time.sleep(0.5)
print("🚀 开始游戏...")
self.sio.emit('start_game')
# 等待游戏结束或超时
print("\n⏳ 等待游戏完成...")
start_time = time.time()
timeout = 90 # 90秒超时
while not self.game_ended and time.time() - start_time < timeout:
time.sleep(0.5)
if not self.game_ended:
print(f"\n⏱️ 测试超时 ({timeout}秒)")
# 断开连接
self.sio.disconnect()
except Exception as e:
print(f"❌ 测试出错: {e}")
import traceback
traceback.print_exc()
def print_summary(self):
"""打印总结"""
print("\n" + "=" * 60)
print("📊 测试总结")
print("=" * 60)
if self.game_ended:
print("✅ 游戏正常完成")
else:
print("⚠️ 游戏未完成")
print(f"📈 总回合数: {self.turn_count}")
print(f"\n🎮 三方配置:")
print(" 👤 人类玩家")
print(" 🤖 规则AI (本地算法)")
print(" 🧠 DeepSeek AI (大模型)")
if self.game_ended:
print("\n✅ Phase 2 测试通过!")
else:
print("\n⚠️ 部分功能正常,游戏可能因超时而未结束")
def test_ai_only():
"""仅测试AI适配器"""
print("=" * 60)
print(" AI适配器单元测试")
print("=" * 60)
from doudizhu.ai_adapters import DoudizhuRuleAI, DoudizhuDeepSeekAI
from doudizhu.game_logic import Deck
async def test_rule_ai():
print("\n🧪 测试规则AI...")
# 创建手牌
deck = Deck.shuffle(Deck.create_full_deck())
hand = deck[:17]
rule_ai = DoudizhuRuleAI()
# 测试叫地主
decision = await rule_ai.make_decision_calling(hand)
print(f" 叫地主: {decision['action']} (want={decision['data'].get('want')})")
print(f" 理由: {decision['reasoning']}")
# 测试出牌
decision = await rule_ai.make_decision_playing(hand, None, False, 17)
print(f" 出牌决策: {decision['action']}")
print(f" 理由: {decision['reasoning']}")
print("✅ 规则AI测试通过")
async def test_deepseek_ai():
if not DEEPSEEK_API_KEY:
print("\n⏭️ 跳过DeepSeek AI测试 (未配置API Key)")
return
print("\n🧠 测试DeepSeek AI...")
deck = Deck.shuffle(Deck.create_full_deck())
hand = deck[:17]
deepseek_ai = DoudizhuDeepSeekAI(api_key=DEEPSEEK_API_KEY)
# 测试叫地主
try:
decision = await deepseek_ai.make_decision_calling(hand)
print(f" 叫地主: {decision['action']}")
print(f" 理由: {decision['reasoning'][:80]}...")
print("✅ DeepSeek AI测试通过")
except Exception as e:
print(f" ⚠️ DeepSeek API调用失败: {e}")
print(" (这是正常的,API可能暂时不可用)")
# 运行异步测试
asyncio.run(test_rule_ai())
asyncio.run(test_deepseek_ai())
if __name__ == '__main__':
# 先测试AI适配器
test_ai_only()
# 然后运行完整测试
print("\n")
test = DoudizhuAPITest()
test.run()
test.print_summary()