194 lines
5.7 KiB
Python
194 lines
5.7 KiB
Python
|
|
"""石头剪刀布游戏"""
|
|||
|
|
import random
|
|||
|
|
import logging
|
|||
|
|
from games.base import BaseGame
|
|||
|
|
from utils.parser import CommandParser
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RPSGame(BaseGame):
|
|||
|
|
"""石头剪刀布游戏"""
|
|||
|
|
|
|||
|
|
# 选择列表
|
|||
|
|
CHOICES = ["石头", "剪刀", "布"]
|
|||
|
|
|
|||
|
|
# 胜负关系:key 击败 value
|
|||
|
|
WINS_AGAINST = {
|
|||
|
|
"石头": "剪刀",
|
|||
|
|
"剪刀": "布",
|
|||
|
|
"布": "石头"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 英文/表情符号映射
|
|||
|
|
CHOICE_MAP = {
|
|||
|
|
"石头": "石头", "rock": "石头", "🪨": "石头", "👊": "石头",
|
|||
|
|
"剪刀": "剪刀", "scissors": "剪刀", "✂️": "剪刀", "✌️": "剪刀",
|
|||
|
|
"布": "布", "paper": "布", "📄": "布", "✋": "布"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async def handle(self, command: str, chat_id: int, user_id: int) -> str:
|
|||
|
|
"""处理石头剪刀布指令
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
command: 指令,如 ".rps 石头" 或 ".rps stats"
|
|||
|
|
chat_id: 会话ID
|
|||
|
|
user_id: 用户ID
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
回复消息
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
# 提取参数
|
|||
|
|
_, args = CommandParser.extract_command_args(command)
|
|||
|
|
args = args.strip()
|
|||
|
|
|
|||
|
|
# 查看战绩
|
|||
|
|
if args in ['stats', '战绩', '统计']:
|
|||
|
|
return self._get_stats(user_id)
|
|||
|
|
|
|||
|
|
# 没有参数,显示帮助
|
|||
|
|
if not args:
|
|||
|
|
return self.get_help()
|
|||
|
|
|
|||
|
|
# 解析用户选择
|
|||
|
|
player_choice = self._parse_choice(args)
|
|||
|
|
if not player_choice:
|
|||
|
|
return f"❌ 无效的选择:{args}\n\n{self.get_help()}"
|
|||
|
|
|
|||
|
|
# 机器人随机选择
|
|||
|
|
bot_choice = random.choice(self.CHOICES)
|
|||
|
|
|
|||
|
|
# 判定胜负
|
|||
|
|
result = self._judge(player_choice, bot_choice)
|
|||
|
|
|
|||
|
|
# 更新统计
|
|||
|
|
if result == 'win':
|
|||
|
|
self.db.update_game_stats(user_id, 'rps', win=True)
|
|||
|
|
elif result == 'loss':
|
|||
|
|
self.db.update_game_stats(user_id, 'rps', loss=True)
|
|||
|
|
elif result == 'draw':
|
|||
|
|
self.db.update_game_stats(user_id, 'rps', draw=True)
|
|||
|
|
|
|||
|
|
# 格式化输出
|
|||
|
|
return self._format_result(player_choice, bot_choice, result)
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"处理石头剪刀布指令错误: {e}", exc_info=True)
|
|||
|
|
return f"❌ 处理指令出错: {str(e)}"
|
|||
|
|
|
|||
|
|
def _parse_choice(self, choice_str: str) -> str:
|
|||
|
|
"""解析用户选择
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
choice_str: 用户输入的选择
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
标准化的选择(石头/剪刀/布)或空字符串
|
|||
|
|
"""
|
|||
|
|
choice_str = choice_str.lower().strip()
|
|||
|
|
return self.CHOICE_MAP.get(choice_str, "")
|
|||
|
|
|
|||
|
|
def _judge(self, player: str, bot: str) -> str:
|
|||
|
|
"""判定胜负
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
player: 玩家选择
|
|||
|
|
bot: 机器人选择
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
'win', 'loss', 或 'draw'
|
|||
|
|
"""
|
|||
|
|
if player == bot:
|
|||
|
|
return 'draw'
|
|||
|
|
elif self.WINS_AGAINST[player] == bot:
|
|||
|
|
return 'win'
|
|||
|
|
else:
|
|||
|
|
return 'loss'
|
|||
|
|
|
|||
|
|
def _format_result(self, player_choice: str, bot_choice: str, result: str) -> str:
|
|||
|
|
"""格式化游戏结果
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
player_choice: 玩家选择
|
|||
|
|
bot_choice: 机器人选择
|
|||
|
|
result: 游戏结果
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
格式化的Markdown消息
|
|||
|
|
"""
|
|||
|
|
# 表情符号映射
|
|||
|
|
emoji_map = {
|
|||
|
|
"石头": "🪨",
|
|||
|
|
"剪刀": "✂️",
|
|||
|
|
"布": "📄"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
text = f"## ✊ 石头剪刀布\n\n"
|
|||
|
|
text += f"**你出**:{emoji_map[player_choice]} {player_choice}\n\n"
|
|||
|
|
text += f"**我出**:{emoji_map[bot_choice]} {bot_choice}\n\n"
|
|||
|
|
|
|||
|
|
if result == 'win':
|
|||
|
|
text += "**结果**:<font color='#4CAF50'>🎉 你赢了!</font>\n"
|
|||
|
|
elif result == 'loss':
|
|||
|
|
text += "**结果**:<font color='#F44336'>😢 你输了!</font>\n"
|
|||
|
|
else:
|
|||
|
|
text += "**结果**:<font color='#FFC107'>🤝 平局!</font>\n"
|
|||
|
|
|
|||
|
|
return text
|
|||
|
|
|
|||
|
|
def _get_stats(self, user_id: int) -> str:
|
|||
|
|
"""获取用户战绩
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
user_id: 用户ID
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
战绩信息
|
|||
|
|
"""
|
|||
|
|
stats = self.db.get_game_stats(user_id, 'rps')
|
|||
|
|
|
|||
|
|
total = stats['total_plays']
|
|||
|
|
if total == 0:
|
|||
|
|
return "📊 你还没有玩过石头剪刀布呢~\n\n快来试试吧!输入 `.rps 石头/剪刀/布` 开始游戏"
|
|||
|
|
|
|||
|
|
wins = stats['wins']
|
|||
|
|
losses = stats['losses']
|
|||
|
|
draws = stats['draws']
|
|||
|
|
win_rate = (wins / total * 100) if total > 0 else 0
|
|||
|
|
|
|||
|
|
text = f"## 📊 石头剪刀布战绩\n\n"
|
|||
|
|
text += f"**总局数**:{total} 局\n\n"
|
|||
|
|
text += f"**胜利**:{wins} 次 🎉\n\n"
|
|||
|
|
text += f"**失败**:{losses} 次 😢\n\n"
|
|||
|
|
text += f"**平局**:{draws} 次 🤝\n\n"
|
|||
|
|
text += f"**胜率**:<font color='#4CAF50'>{win_rate:.1f}%</font>\n"
|
|||
|
|
|
|||
|
|
return text
|
|||
|
|
|
|||
|
|
def get_help(self) -> str:
|
|||
|
|
"""获取帮助信息"""
|
|||
|
|
return """## ✊ 石头剪刀布
|
|||
|
|
|
|||
|
|
### 基础用法
|
|||
|
|
- `.rps 石头` - 出石头
|
|||
|
|
- `.rps 剪刀` - 出剪刀
|
|||
|
|
- `.rps 布` - 出布
|
|||
|
|
|
|||
|
|
### 其他指令
|
|||
|
|
- `.rps stats` - 查看战绩
|
|||
|
|
|
|||
|
|
### 支持的输入
|
|||
|
|
- 中文:石头、剪刀、布
|
|||
|
|
- 英文:rock、scissors、paper
|
|||
|
|
- 表情:🪨 ✂️ 📄
|
|||
|
|
|
|||
|
|
### 示例
|
|||
|
|
```
|
|||
|
|
.rps 石头
|
|||
|
|
.rps rock
|
|||
|
|
.rps 🪨
|
|||
|
|
```
|
|||
|
|
"""
|
|||
|
|
|