Files
WPSBot/games/casino.py

1474 lines
61 KiB
Python
Raw Normal View History

2025-10-30 16:11:50 +08:00
"""赌场游戏模块"""
import logging
2025-10-30 17:27:15 +08:00
import random
2025-10-31 12:09:07 +08:00
from typing import List
2025-10-30 16:11:50 +08:00
from games.base import BaseGame
from utils.parser import CommandParser
from core.database import get_db
logger = logging.getLogger(__name__)
class CasinoGame(BaseGame):
"""赌场游戏"""
def __init__(self):
"""初始化游戏"""
super().__init__()
self.db = get_db()
async def handle(self, command: str, chat_id: int, user_id: int) -> str:
"""处理赌场指令
Args:
command: 完整指令
chat_id: 会话ID
user_id: 用户ID
Returns:
回复消息
"""
try:
# 提取参数
_, args = CommandParser.extract_command_args(command)
args = args.strip().lower()
# 没有参数,显示帮助
if not args:
return self.get_help()
# 解析第一个参数为游戏类型
parts = args.split(maxsplit=1)
game_type = parts[0]
sub_args = parts[1] if len(parts) > 1 else ""
# 根据游戏类型分发
if game_type == '大小':
return await self._handle_bigsmall(sub_args, chat_id, user_id)
2025-10-31 11:58:35 +08:00
elif game_type == '轮盘':
return await self._handle_roulette(sub_args, chat_id, user_id)
elif game_type in ['21点', 'blackjack', '21']:
return await self._handle_blackjack(sub_args, chat_id, user_id)
2025-10-30 16:11:50 +08:00
elif game_type in ['help', '帮助']:
return self.get_help()
else:
2025-10-31 11:58:35 +08:00
return f"❌ 暂不支持的游戏类型: {game_type}\n\n支持的类型大小、轮盘、21点"
2025-10-30 16:11:50 +08:00
except Exception as e:
logger.error(f"处理赌场指令错误: {e}", exc_info=True)
return f"❌ 处理指令出错: {str(e)}"
async def _handle_bigsmall(self, args: str, chat_id: int, user_id: int) -> str:
"""处理大小游戏
Args:
args: 子命令和参数
chat_id: 会话ID
user_id: 用户ID
Returns:
回复消息
"""
if not args:
return self._get_bigsmall_help()
# 解析子命令
parts = args.split(maxsplit=1)
action = parts[0].lower()
sub_args = parts[1] if len(parts) > 1 else ""
if action in ['open', '开启', '开始']:
return await self._open_bigsmall(sub_args, chat_id, user_id)
elif action in ['bet', '下注', '']:
return await self._bet_bigsmall(sub_args, chat_id, user_id)
elif action in ['status', '状态', '查看']:
return await self._status_bigsmall(chat_id)
elif action in ['settle', '结算', '开奖']:
return await self._settle_bigsmall(sub_args, chat_id, user_id)
2025-10-30 17:27:15 +08:00
elif action in ['cancel', '放弃', '关闭']:
return await self._cancel_bigsmall(chat_id, user_id)
2025-10-30 16:11:50 +08:00
elif action in ['help', '帮助']:
return self._get_bigsmall_help()
else:
return f"❌ 未知命令: {action}\n\n{self._get_bigsmall_help()}"
async def _open_bigsmall(self, args: str, chat_id: int, user_id: int) -> str:
"""庄家开启大小游戏
Args:
args: 参数字符串 "<最小下注> <最大下注> <赔率>"
chat_id: 会话ID
user_id: 用户ID
Returns:
回复消息
"""
try:
# 解析参数
parts = args.split()
if len(parts) != 3:
return "❌ 参数格式错误!\n\n正确格式:`.赌场 大小 open <最小下注> <最大下注> <赔率>`\n\n示例:`.赌场 大小 open 10 100 2.0`"
try:
min_bet = int(parts[0])
max_bet = int(parts[1])
multiplier = float(parts[2])
except ValueError:
return "❌ 参数必须是数字!"
# 参数验证
if min_bet <= 0 or max_bet <= 0:
return "❌ 下注金额必须大于0"
if min_bet > max_bet:
return "❌ 最小下注不能大于最大下注!"
if multiplier <= 0:
return "❌ 赔率必须大于0"
if max_bet > 10000:
return "❌ 最大下注不能超过10000分"
2025-10-31 11:58:35 +08:00
# 检查是否已有活跃游戏(单场限制)
existing = self.db.get_any_active_casino_session(chat_id)
2025-10-30 16:11:50 +08:00
if existing:
2025-10-31 11:58:35 +08:00
return f"⚠️ 当前已有进行中的游戏({existing['game_type']}),请先结算后再开启新游戏。"
2025-10-30 16:11:50 +08:00
# 创建游戏会话
session_id = self.db.create_casino_session(
chat_id=chat_id,
game_type='大小',
banker_id=user_id,
min_bet=min_bet,
max_bet=max_bet,
multiplier=multiplier,
house_fee=0.05 # 固定5%抽水
)
2025-10-31 11:16:38 +08:00
banker_display_name = self.db.get_user_display_name(user_id)
2025-10-30 16:11:50 +08:00
text = f"## 🎰 大小游戏已开启\n\n"
2025-10-31 11:16:38 +08:00
text += f"**庄家**{banker_display_name}\n\n"
2025-10-30 16:11:50 +08:00
text += f"**最小下注**{min_bet}\n\n"
text += f"**最大下注**{max_bet}\n\n"
text += f"**赔率**{multiplier}\n\n"
text += f"**抽水率**5%\n\n"
text += "---\n\n"
text += "💡 提示:玩家可以使用 `.赌场 大小 bet <大/小> <金额>` 进行下注"
return text
except Exception as e:
logger.error(f"开启游戏失败: {e}", exc_info=True)
return f"❌ 开启游戏失败: {str(e)}"
async def _bet_bigsmall(self, args: str, chat_id: int, user_id: int) -> str:
"""玩家下注
Args:
args: 参数字符串 "<大/小> <下注金额>"
chat_id: 会话ID
user_id: 用户ID
Returns:
回复消息
"""
try:
# 解析参数
parts = args.split()
if len(parts) != 2:
return "❌ 参数格式错误!\n\n正确格式:`.赌场 大小 bet <大/小> <下注金额>`\n\n示例:`.赌场 大小 bet 大 50`"
bet_type = parts[0]
try:
amount = int(parts[1])
except ValueError:
return "❌ 下注金额必须是数字!"
# 验证下注类型
if bet_type not in ['', '']:
return f"❌ 下注类型错误!只支持'''',您输入的是:{bet_type}"
# 检查是否有活跃的会话
session = self.db.get_active_casino_session(chat_id, '大小')
if not session:
return "❌ 当前没有进行中的游戏,请等待庄家开启游戏。"
# 验证下注金额
if amount < session['min_bet']:
return f"❌ 下注金额太小!最小下注:{session['min_bet']}"
if amount > session['max_bet']:
return f"❌ 下注金额太大!最大下注:{session['max_bet']}"
# 检查用户积分
user_points = self.db.get_user_points(user_id)
if user_points['points'] < amount:
return f"❌ 积分不足!需要 {amount} 分,当前可用 {user_points['points']}"
# 扣除积分
if not self.db.consume_points(user_id, amount, 'casino_bet', f"大小游戏下注{bet_type}"):
return "❌ 扣除积分失败!"
# 记录下注
bet_id = self.db.create_casino_bet(
chat_id=chat_id,
game_type='大小',
user_id=user_id,
bet_type=bet_type,
amount=amount,
multiplier=session['multiplier']
)
# 获取更新后的积分
updated_points = self.db.get_user_points(user_id)
text = f"## 🎲 下注成功\n\n"
text += f"**下注类型**{bet_type}\n\n"
text += f"**下注金额**{amount}\n\n"
text += f"**赔率**{session['multiplier']}\n\n"
text += f"**剩余积分**{updated_points['points']}\n\n"
text += "---\n\n"
text += "💡 等待庄家结算结果..."
return text
except Exception as e:
logger.error(f"下注失败: {e}", exc_info=True)
return f"❌ 下注失败: {str(e)}"
async def _status_bigsmall(self, chat_id: int) -> str:
"""查看当前游戏状态
Args:
chat_id: 会话ID
Returns:
状态消息
"""
session = self.db.get_active_casino_session(chat_id, '大小')
if not session:
return "❌ 当前没有进行中的大小游戏"
# 获取所有待结算下注
bets = self.db.get_pending_bets(chat_id, '大小')
# 统计下注情况
bet_big = sum(b['amount'] for b in bets if b['bet_type'] == '')
bet_small = sum(b['amount'] for b in bets if b['bet_type'] == '')
2025-10-31 11:16:38 +08:00
banker_display_name = self.db.get_user_display_name(session['banker_id'])
2025-10-30 16:11:50 +08:00
text = f"## 🎰 大小游戏状态\n\n"
2025-10-31 11:16:38 +08:00
text += f"**庄家**{banker_display_name}\n\n"
2025-10-30 16:11:50 +08:00
text += f"**最小下注**{session['min_bet']}\n\n"
text += f"**最大下注**{session['max_bet']}\n\n"
text += f"**赔率**{session['multiplier']}\n\n"
text += "---\n\n"
text += f"**当前下注统计**\n"
text += f"- 压大:{bet_big} 分({len([b for b in bets if b['bet_type'] == ''])}注)\n"
text += f"- 压小:{bet_small} 分({len([b for b in bets if b['bet_type'] == ''])}注)\n"
text += f"- 总下注:{len(bets)}\n\n"
text += "---\n\n"
2025-10-30 17:27:15 +08:00
text += "💡 庄家可以使用 `.赌场 大小 settle` 结算"
2025-10-30 16:11:50 +08:00
return text
async def _settle_bigsmall(self, args: str, chat_id: int, user_id: int) -> str:
2025-10-30 17:27:15 +08:00
"""庄家结算游戏(系统随机生成结果)
2025-10-30 16:11:50 +08:00
Args:
2025-10-30 17:27:15 +08:00
args: 无参数
2025-10-30 16:11:50 +08:00
chat_id: 会话ID
user_id: 用户ID
Returns:
结算结果消息
"""
try:
2025-10-30 17:27:15 +08:00
# 不需要参数,系统自动随机生成结果
# 使用系统随机数决定大小50%概率)
result = '' if random.random() > 0.5 else ''
2025-10-30 16:11:50 +08:00
# 结算
settlement = self.db.settle_casino_bets(chat_id, '大小', result, user_id)
# 构建结算报告
text = f"## 🎰 大小游戏结算\n\n"
2025-10-30 17:27:15 +08:00
text += f"**结算结果**:🎲 {result}\n\n"
2025-10-30 16:11:50 +08:00
text += "---\n\n"
text += f"**赢家**{len(settlement['winners'])}\n"
text += f"**输家**{len(settlement['losers'])}\n\n"
# 显示赢家详情
if settlement['winners']:
text += "**赢家明细**\n"
for winner in settlement['winners']:
2025-10-31 11:16:38 +08:00
winner_display_name = self.db.get_user_display_name(winner['user_id'])
text += f"- {winner_display_name}: 下注{winner['amount']}分,赢得{winner['win_amount']}\n"
2025-10-30 16:11:50 +08:00
text += "\n"
# 显示输家详情
if settlement['losers']:
text += "**输家明细**\n"
for loser in settlement['losers']:
2025-10-31 11:16:38 +08:00
loser_display_name = self.db.get_user_display_name(loser['user_id'])
text += f"- {loser_display_name}: 下注{loser['amount']}\n"
2025-10-30 16:11:50 +08:00
text += "\n"
text += "---\n\n"
text += "✅ 游戏已结束,欢迎再次游戏!"
return text
except ValueError as e:
return f"{str(e)}"
except Exception as e:
logger.error(f"结算失败: {e}", exc_info=True)
return f"❌ 结算失败: {str(e)}"
2025-10-30 17:27:15 +08:00
async def _cancel_bigsmall(self, chat_id: int, user_id: int) -> str:
"""庄家放弃本轮游戏
Args:
chat_id: 会话ID
user_id: 用户ID
Returns:
放弃结果消息
"""
try:
# 检查是否有活跃的会话
session = self.db.get_active_casino_session(chat_id, '大小')
if not session:
return "❌ 当前没有进行中的游戏"
# 验证是否为庄家
if session['banker_id'] != user_id:
return "❌ 只有庄家可以放弃游戏"
# 获取所有待结算下注
bets = self.db.get_pending_bets(chat_id, '大小')
# 返还所有下注的积分
return_amount = 0
for bet in bets:
# 返还积分
self.db.add_points(bet['user_id'], bet['amount'], 'casino_cancel',
"庄家放弃游戏,返还下注")
return_amount += bet['amount']
# 关闭会话(不结算,返还积分)
self.db.close_casino_session(chat_id, '大小')
# 标记下注为已取消
cursor = self.db.conn.cursor()
cursor.execute("""
UPDATE casino_bets
SET status = 'cancelled'
WHERE chat_id = ? AND game_type = ? AND status = 'pending'
""", (chat_id, '大小'))
# 构建报告
2025-10-31 11:16:38 +08:00
banker_display_name = self.db.get_user_display_name(user_id)
2025-10-30 17:27:15 +08:00
text = f"## 🎰 游戏已放弃\n\n"
2025-10-31 11:16:38 +08:00
text += f"**庄家**{banker_display_name}\n\n"
2025-10-30 17:27:15 +08:00
text += f"**总返还积分**{return_amount}\n\n"
text += f"**返还人数**{len(bets)}\n\n"
text += "---\n\n"
text += "✅ 所有下注已返还,游戏已关闭"
return text
except Exception as e:
logger.error(f"放弃游戏失败: {e}", exc_info=True)
return f"❌ 放弃游戏失败: {str(e)}"
2025-10-30 16:11:50 +08:00
def _get_bigsmall_help(self) -> str:
"""获取大小游戏帮助信息"""
return """## 🎰 大小游戏帮助
### 庄家命令
- `.赌场 大小 open <最小> <最大> <赔率>` - 开启游戏
- 示例`.赌场 大小 open 10 100 2.0`
2025-10-30 17:27:15 +08:00
- `.赌场 大小 settle` - 结算游戏系统随机生成结果
- `.赌场 大小 cancel` - 放弃游戏返还所有下注
2025-10-30 16:11:50 +08:00
### 玩家命令
- `.赌场 大小 bet </> <金额>` - 下注
- 示例`.赌场 大小 bet 50`
- 示例`.赌场 大小 bet 30`
### 通用命令
- `.赌场 大小 status` - 查看当前状态
### 游戏规则
- 玩家下注后积分立即扣除
- 结算后根据结果发放奖励
2025-10-30 17:27:15 +08:00
- 庄家放弃游戏时所有下注全额返还
2025-10-30 16:11:50 +08:00
- 系统抽水5%
- 赔率由庄家设置
"""
def get_help(self) -> str:
"""获取帮助信息"""
2025-10-31 11:58:35 +08:00
text = "# 🎰 赌场游戏帮助\n\n"
text += self._get_bigsmall_help() + "\n\n"
text += self._get_roulette_help() + "\n\n"
text += self._get_blackjack_help()
return text
# ===== 轮盘游戏 =====
async def _handle_roulette(self, args: str, chat_id: int, user_id: int) -> str:
"""处理轮盘游戏
Args:
args: 子命令和参数
chat_id: 会话ID
user_id: 用户ID
Returns:
回复消息
"""
if not args:
return self._get_roulette_help()
parts = args.split(maxsplit=1)
action = parts[0].lower()
sub_args = parts[1] if len(parts) > 1 else ""
if action in ['open', '开启', '开始']:
return await self._open_roulette(sub_args, chat_id, user_id)
elif action in ['bet', '下注', '']:
return await self._bet_roulette(sub_args, chat_id, user_id)
elif action in ['status', '状态', '查看']:
return await self._status_roulette(chat_id)
elif action in ['settle', '结算', '开奖']:
return await self._settle_roulette(chat_id, user_id)
elif action in ['cancel', '放弃', '关闭']:
return await self._cancel_roulette(chat_id, user_id)
elif action in ['help', '帮助']:
return self._get_roulette_help()
else:
return f"❌ 未知命令: {action}\n\n{self._get_roulette_help()}"
async def _open_roulette(self, args: str, chat_id: int, user_id: int) -> str:
"""庄家开启轮盘游戏"""
try:
parts = args.split()
if len(parts) != 2:
return "❌ 参数格式错误!\n\n正确格式:`.赌场 轮盘 open <最小下注> <最大下注>`\n\n示例:`.赌场 轮盘 open 10 100`"
try:
min_bet = int(parts[0])
max_bet = int(parts[1])
except ValueError:
return "❌ 参数必须是数字!"
if min_bet <= 0 or max_bet <= 0:
return "❌ 下注金额必须大于0"
if min_bet > max_bet:
return "❌ 最小下注不能大于最大下注!"
if max_bet > 10000:
return "❌ 最大下注不能超过10000分"
existing = self.db.get_any_active_casino_session(chat_id)
if existing:
return f"⚠️ 当前已有进行中的游戏({existing['game_type']}),请先结算后再开启新游戏。"
session_id = self.db.create_casino_session(
chat_id=chat_id,
game_type='轮盘',
banker_id=user_id,
min_bet=min_bet,
max_bet=max_bet,
multiplier=1.0, # 轮盘赔率根据下注类型自动计算
house_fee=0.05
)
banker_display_name = self.db.get_user_display_name(user_id)
text = f"## 🎰 轮盘游戏已开启\n\n"
text += f"**庄家**{banker_display_name}\n\n"
text += f"**最小下注**{min_bet}\n\n"
text += f"**最大下注**{max_bet}\n\n"
text += f"**抽水率**5%\n\n"
text += "**赔率**\n"
text += "- 数字35倍\n"
text += "- 颜色/奇偶/大小2倍\n"
text += "- 区间3倍\n\n"
text += "---\n\n"
text += "💡 提示:玩家可以使用 `.赌场 轮盘 bet <类型> <选项> <金额>` 进行下注"
return text
except Exception as e:
logger.error(f"开启轮盘游戏失败: {e}", exc_info=True)
return f"❌ 开启游戏失败: {str(e)}"
async def _bet_roulette(self, args: str, chat_id: int, user_id: int) -> str:
"""玩家下注轮盘"""
try:
parts = args.split()
if len(parts) < 2:
return "❌ 参数格式错误!\n\n正确格式:`.赌场 轮盘 bet <类型> <选项> <金额>`\n\n示例:`.赌场 轮盘 bet 数字 17 100`"
bet_category = parts[0]
if len(parts) == 2:
# 颜色/奇偶/大小/区间下注bet <类型> <金额>
bet_value = bet_category
bet_category_map = {
'红色': '颜色', '黑色': '颜色', '绿色': '颜色',
'奇数': '奇偶', '偶数': '奇偶',
'': '大小', '': '大小',
'1-12': '区间', '13-24': '区间', '25-36': '区间'
}
bet_category = bet_category_map.get(bet_value)
if not bet_category:
return f"❌ 未知的下注类型: {bet_value}"
try:
amount = int(parts[1])
except ValueError:
return "❌ 下注金额必须是数字!"
bet_number = None
elif len(parts) == 3:
# 数字下注bet 数字 <数字> <金额>
if bet_category != '数字':
return "❌ 数字下注格式:`.赌场 轮盘 bet 数字 <0-36> <金额>`"
try:
bet_number = int(parts[1])
amount = int(parts[2])
except ValueError:
return "❌ 数字和金额必须是数字!"
if bet_number < 0 or bet_number > 36:
return "❌ 数字必须在0-36之间"
bet_value = None
else:
return "❌ 参数格式错误!"
session = self.db.get_active_casino_session(chat_id, '轮盘')
if not session:
return "❌ 当前没有进行中的游戏,请等待庄家开启游戏。"
if amount < session['min_bet']:
return f"❌ 下注金额太小!最小下注:{session['min_bet']}"
if amount > session['max_bet']:
return f"❌ 下注金额太大!最大下注:{session['max_bet']}"
user_points = self.db.get_user_points(user_id)
if user_points['points'] < amount:
return f"❌ 积分不足!需要 {amount} 分,当前可用 {user_points['points']}"
# 根据下注类型计算赔率
if bet_category == '数字':
multiplier = 36.0 # 35:1赔率返回36倍包含本金
elif bet_category in ['颜色', '奇偶', '大小']:
multiplier = 2.0
elif bet_category == '区间':
multiplier = 3.0
else:
return f"❌ 未知的下注类别: {bet_category}"
if not self.db.consume_points(user_id, amount, 'casino_bet', f"轮盘游戏下注{bet_category}"):
return "❌ 扣除积分失败!"
bet_id = self.db.create_casino_bet(
chat_id=chat_id,
game_type='轮盘',
user_id=user_id,
bet_type='轮盘',
amount=amount,
multiplier=multiplier,
bet_category=bet_category,
bet_number=bet_number,
bet_value=bet_value
)
updated_points = self.db.get_user_points(user_id)
text = f"## 🎲 下注成功\n\n"
text += f"**下注类型**{bet_category}"
if bet_number is not None:
text += f" - {bet_number}"
elif bet_value:
text += f" - {bet_value}"
text += f"\n\n**下注金额**{amount}\n\n"
text += f"**赔率**{multiplier}\n\n"
text += f"**剩余积分**{updated_points['points']}\n\n"
text += "---\n\n"
text += "💡 等待庄家结算结果..."
return text
except Exception as e:
logger.error(f"轮盘下注失败: {e}", exc_info=True)
return f"❌ 下注失败: {str(e)}"
2025-10-31 12:09:07 +08:00
async def _status_roulette(self, chat_id: int) -> str:
"""查看轮盘游戏状态"""
session = self.db.get_active_casino_session(chat_id, '轮盘')
if not session:
return "❌ 当前没有进行中的轮盘游戏"
bets = self.db.get_pending_bets(chat_id, '轮盘')
# 统计下注情况
bet_stats = {}
for bet in bets:
category = bet.get('bet_category', '未知')
value = bet.get('bet_value') or str(bet.get('bet_number', ''))
key = f"{category}-{value}"
if key not in bet_stats:
bet_stats[key] = {'amount': 0, 'count': 0}
bet_stats[key]['amount'] += bet['amount']
bet_stats[key]['count'] += 1
banker_display_name = self.db.get_user_display_name(session['banker_id'])
text = f"## 🎰 轮盘游戏状态\n\n"
text += f"**庄家**{banker_display_name}\n\n"
text += f"**最小下注**{session['min_bet']}\n\n"
text += f"**最大下注**{session['max_bet']}\n\n"
text += "---\n\n"
text += f"**当前下注统计**\n"
for key, stat in bet_stats.items():
parts = key.split('-', 1)
text += f"- {parts[0]}{' - ' + parts[1] if len(parts) > 1 else ''}{stat['amount']} 分({stat['count']}注)\n"
text += f"\n**总下注**{len(bets)}\n\n"
text += "---\n\n"
text += "💡 庄家可以使用 `.赌场 轮盘 settle` 结算"
return text
async def _settle_roulette(self, chat_id: int, user_id: int) -> str:
"""庄家结算轮盘游戏(系统随机生成结果)"""
try:
# 系统随机生成0-36的数字
result_number = random.randint(0, 36)
# 结算
settlement = self.db.settle_casino_bets(
chat_id, '轮盘', str(result_number), user_id,
result_number=result_number
)
# 构建结算报告
text = f"## 🎰 轮盘游戏结算\n\n"
text += f"**结算结果**:🎲 **{result_number}**\n\n"
text += "---\n\n"
text += f"**赢家**{len(settlement['winners'])}\n"
text += f"**输家**{len(settlement['losers'])}\n\n"
if settlement['winners']:
text += "**赢家明细**\n"
for winner in settlement['winners']:
winner_display_name = self.db.get_user_display_name(winner['user_id'])
text += f"- {winner_display_name}: 下注{winner['amount']}分,赢得{winner['win_amount']}\n"
text += "\n"
if settlement['losers']:
text += "**输家明细**\n"
for loser in settlement['losers']:
loser_display_name = self.db.get_user_display_name(loser['user_id'])
text += f"- {loser_display_name}: 下注{loser['amount']}\n"
text += "\n"
text += "---\n\n"
text += "✅ 游戏已结束,欢迎再次游戏!"
return text
except ValueError as e:
return f"{str(e)}"
except Exception as e:
logger.error(f"轮盘结算失败: {e}", exc_info=True)
return f"❌ 结算失败: {str(e)}"
async def _cancel_roulette(self, chat_id: int, user_id: int) -> str:
"""庄家放弃轮盘游戏"""
try:
session = self.db.get_active_casino_session(chat_id, '轮盘')
if not session:
return "❌ 当前没有进行中的游戏"
if session['banker_id'] != user_id:
return "❌ 只有庄家可以放弃游戏"
bets = self.db.get_pending_bets(chat_id, '轮盘')
return_amount = 0
for bet in bets:
self.db.add_points(bet['user_id'], bet['amount'], 'casino_cancel',
"庄家放弃游戏,返还下注")
return_amount += bet['amount']
self.db.close_casino_session(chat_id, '轮盘')
cursor = self.db.conn.cursor()
cursor.execute("""
UPDATE casino_bets
SET status = 'cancelled'
WHERE chat_id = ? AND game_type = ? AND status = 'pending'
""", (chat_id, '轮盘'))
banker_display_name = self.db.get_user_display_name(user_id)
text = f"## 🎰 游戏已放弃\n\n"
text += f"**庄家**{banker_display_name}\n\n"
text += f"**总返还积分**{return_amount}\n\n"
text += f"**返还人数**{len(bets)}\n\n"
text += "---\n\n"
text += "✅ 所有下注已返还,游戏已关闭"
return text
except Exception as e:
logger.error(f"放弃轮盘游戏失败: {e}", exc_info=True)
return f"❌ 放弃游戏失败: {str(e)}"
def _get_roulette_help(self) -> str:
"""获取轮盘游戏帮助信息"""
return """## 🎰 轮盘游戏帮助
### 庄家命令
- `.赌场 轮盘 open <最小> <最大>` - 开启游戏
- 示例`.赌场 轮盘 open 10 100`
- `.赌场 轮盘 settle` - 结算游戏系统随机生成0-36
- `.赌场 轮盘 cancel` - 放弃游戏返还所有下注
### 玩家命令
- `.赌场 轮盘 bet <类型> <选项> <金额>` - 下注
- 数字`.赌场 轮盘 bet 数字 17 100` 押数字1710035倍赔率
- 颜色`.赌场 轮盘 bet 红色 50` 押红色502倍赔率
- 奇偶`.赌场 轮盘 bet 奇数 30` 押奇数302倍赔率
- 大小`.赌场 轮盘 bet 40` 押大19-36402倍赔率
- 区间`.赌场 轮盘 bet 1-12 60` 押1-12603倍赔率
### 通用命令
- `.赌场 轮盘 status` - 查看当前状态
### 游戏规则
- 数字0-360为绿色其他数字为红色或黑色
- 数字下注35倍赔率
- 颜色/奇偶/大小2倍赔率
- 区间1-12/13-24/25-363倍赔率
- 系统抽水5%"""
# ===== 21点游戏 =====
async def _handle_blackjack(self, args: str, chat_id: int, user_id: int) -> str:
"""处理21点游戏"""
if not args:
return self._get_blackjack_help()
parts = args.split(maxsplit=1)
action = parts[0].lower()
sub_args = parts[1] if len(parts) > 1 else ""
if action in ['open', '开启', '开始']:
return await self._open_blackjack(sub_args, chat_id, user_id)
2025-10-31 15:57:26 +08:00
elif action in ['join', '加入']:
return await self._join_blackjack(chat_id, user_id)
elif action in ['bet', '下注', '', '加注']:
2025-10-31 12:09:07 +08:00
return await self._bet_blackjack(sub_args, chat_id, user_id)
elif action in ['deal', '发牌']:
return await self._deal_blackjack(chat_id, user_id)
elif action in ['hit', '要牌']:
return await self._hit_blackjack(chat_id, user_id)
elif action in ['stand', '停牌']:
return await self._stand_blackjack(chat_id, user_id)
elif action in ['status', '状态', '查看']:
return await self._status_blackjack(chat_id)
elif action in ['settle', '结算']:
return await self._settle_blackjack(chat_id, user_id)
elif action in ['cancel', '放弃', '关闭']:
return await self._cancel_blackjack(chat_id, user_id)
elif action in ['help', '帮助']:
return self._get_blackjack_help()
else:
return f"❌ 未知命令: {action}\n\n{self._get_blackjack_help()}"
async def _open_blackjack(self, args: str, chat_id: int, user_id: int) -> str:
"""庄家开启21点游戏"""
try:
parts = args.split()
if len(parts) < 2:
2025-10-31 15:57:26 +08:00
return "❌ 参数格式错误!\n\n正确格式:`.赌场 21点 open <底注> <黑杰克倍数>`\n\n示例:`.赌场 21点 open 50 1.5`"
2025-10-31 12:09:07 +08:00
try:
2025-10-31 15:57:26 +08:00
base_bet = int(parts[0])
blackjack_multiplier = float(parts[1])
2025-10-31 12:09:07 +08:00
except ValueError:
return "❌ 参数必须是数字!"
2025-10-31 15:57:26 +08:00
if base_bet <= 0:
return "❌ 底注必须大于0"
2025-10-31 12:09:07 +08:00
2025-10-31 15:57:26 +08:00
if base_bet > 10000:
return "❌ 底注不能超过10000分"
2025-10-31 12:09:07 +08:00
if blackjack_multiplier <= 0:
return "❌ 黑杰克倍数必须大于0"
existing = self.db.get_any_active_casino_session(chat_id)
if existing:
return f"⚠️ 当前已有进行中的游戏({existing['game_type']}),请先结算后再开启新游戏。"
session_id = self.db.create_casino_session(
chat_id=chat_id,
game_type='21点',
banker_id=user_id,
2025-10-31 15:57:26 +08:00
min_bet=base_bet, # 底注作为最小下注
max_bet=base_bet * 10, # 最大下注设为底注的10倍
2025-10-31 12:09:07 +08:00
multiplier=1.0, # 21点标准赔率1:1
house_fee=0.05,
current_phase='betting',
blackjack_multiplier=blackjack_multiplier
)
banker_display_name = self.db.get_user_display_name(user_id)
text = f"## 🎰 21点游戏已开启\n\n"
text += f"**庄家**{banker_display_name}\n\n"
2025-10-31 15:57:26 +08:00
text += f"**底注**{base_bet}\n\n"
2025-10-31 12:09:07 +08:00
text += f"**黑杰克倍数**{blackjack_multiplier}\n\n"
text += f"**抽水率**5%\n\n"
text += "---\n\n"
2025-10-31 15:57:26 +08:00
text += f"💡 提示:玩家使用 `.赌场 21点 join` 加入游戏(需要至少{base_bet}分积分)"
2025-10-31 12:09:07 +08:00
return text
except Exception as e:
logger.error(f"开启21点游戏失败: {e}", exc_info=True)
return f"❌ 开启游戏失败: {str(e)}"
2025-10-31 15:57:26 +08:00
async def _join_blackjack(self, chat_id: int, user_id: int) -> str:
"""玩家加入21点游戏"""
2025-10-31 12:09:07 +08:00
try:
session = self.db.get_active_casino_session(chat_id, '21点')
if not session:
return "❌ 当前没有进行中的游戏,请等待庄家开启游戏。"
if session['current_phase'] != 'betting':
2025-10-31 15:57:26 +08:00
return "❌ 当前不是加入阶段,游戏已开始。"
2025-10-31 12:09:07 +08:00
2025-10-31 15:57:26 +08:00
# 检查是否已经加入
bets = self.db.get_pending_bets(chat_id, '21点')
existing_bet = next((b for b in bets if b['user_id'] == user_id), None)
if existing_bet:
return f"❌ 您已经加入游戏,当前下注:{existing_bet['amount']}"
2025-10-31 12:09:07 +08:00
2025-10-31 15:57:26 +08:00
base_bet = session['min_bet']
2025-10-31 12:09:07 +08:00
2025-10-31 15:57:26 +08:00
# 检查积分是否足够底注
2025-10-31 12:09:07 +08:00
user_points = self.db.get_user_points(user_id)
2025-10-31 15:57:26 +08:00
if user_points['points'] < base_bet:
return f"❌ 积分不足!需要至少 {base_bet} 分才能加入,当前可用 {user_points['points']}"
2025-10-31 12:09:07 +08:00
2025-10-31 15:57:26 +08:00
# 扣除底注
if not self.db.consume_points(user_id, base_bet, 'casino_bet', "21点游戏加入底注"):
2025-10-31 12:09:07 +08:00
return "❌ 扣除积分失败!"
2025-10-31 15:57:26 +08:00
# 创建下注记录(使用底注)
2025-10-31 12:09:07 +08:00
bet_id = self.db.create_casino_bet(
chat_id=chat_id,
game_type='21点',
user_id=user_id,
bet_type='标准',
2025-10-31 15:57:26 +08:00
amount=base_bet,
2025-10-31 12:09:07 +08:00
multiplier=1.0, # 标准赔率1:1
hand_status='playing'
)
updated_points = self.db.get_user_points(user_id)
2025-10-31 15:57:26 +08:00
player_name = self.db.get_user_display_name(user_id)
text = f"## 🎲 加入成功\n\n"
text += f"**玩家**{player_name}\n\n"
text += f"**底注**{base_bet}\n\n"
2025-10-31 12:09:07 +08:00
text += f"**剩余积分**{updated_points['points']}\n\n"
text += "---\n\n"
text += "💡 等待庄家发牌..."
return text
except Exception as e:
2025-10-31 15:57:26 +08:00
logger.error(f"21点加入失败: {e}", exc_info=True)
return f"❌ 加入失败: {str(e)}"
async def _bet_blackjack(self, args: str, chat_id: int, user_id: int) -> str:
"""玩家加注(游戏中加注)"""
try:
try:
amount = int(args.strip())
except ValueError:
return "❌ 加注金额必须是数字!\n\n正确格式:`.赌场 21点 bet <加注金额>`\n\n加注金额必须不低于底注"
session = self.db.get_active_casino_session(chat_id, '21点')
if not session:
return "❌ 当前没有进行中的游戏。"
if session['current_phase'] != 'playing':
return "❌ 当前不是游戏阶段,无法加注。"
base_bet = session['min_bet']
if amount < base_bet:
return f"❌ 加注金额太小!必须不低于底注:{base_bet}"
# 检查玩家是否有下注
bets = self.db.get_pending_bets(chat_id, '21点')
user_bet = next((b for b in bets if b['user_id'] == user_id), None)
if not user_bet:
return "❌ 您没有加入游戏,无法加注"
# 检查玩家手牌状态
session_id = session['id']
hand = self.db.get_blackjack_hand(session_id, user_id)
if not hand or hand['hand_status'] != 'playing':
return f"❌ 当前手牌状态不允许加注(状态:{hand['hand_status'] if hand else '未找到'}"
# 检查积分是否足够
user_points = self.db.get_user_points(user_id)
if user_points['points'] < amount:
return f"❌ 积分不足!需要 {amount} 分,当前可用 {user_points['points']}"
# 扣除加注金额
if not self.db.consume_points(user_id, amount, 'casino_bet', "21点游戏加注"):
return "❌ 扣除积分失败!"
# 更新下注金额
cursor = self.db.conn.cursor()
cursor.execute("""
UPDATE casino_bets
SET amount = amount + ?
WHERE id = ?
""", (amount, user_bet['id']))
updated_points = self.db.get_user_points(user_id)
player_name = self.db.get_user_display_name(user_id)
new_total = user_bet['amount'] + amount
text = f"## 🎲 加注成功\n\n"
text += f"**玩家**{player_name}\n\n"
text += f"**加注金额**{amount}\n\n"
text += f"**总下注**{new_total} 分(原底注 {user_bet['amount']} + 加注 {amount}\n\n"
text += f"**剩余积分**{updated_points['points']}\n\n"
text += "---\n\n"
text += "💡 可以继续要牌或停牌"
return text
except Exception as e:
logger.error(f"21点加注失败: {e}", exc_info=True)
return f"❌ 加注失败: {str(e)}"
2025-10-31 12:09:07 +08:00
def _draw_card(self) -> int:
"""抽取一张牌1-13其中1=A11=J12=Q13=K"""
return random.randint(1, 13)
def _calculate_points(self, cards: List[int]) -> int:
"""计算21点手牌点数复用数据库方法逻辑"""
return self.db._calculate_blackjack_points(cards)
def _check_blackjack(self, cards: List[int]) -> bool:
"""检查是否为黑杰克A+10/J/Q/K"""
if len(cards) != 2:
return False
has_ace = 1 in cards
has_ten = any(card >= 11 for card in cards) or 10 in cards
return has_ace and has_ten
async def _deal_blackjack(self, chat_id: int, user_id: int) -> str:
"""庄家发初始牌"""
try:
session = self.db.get_active_casino_session(chat_id, '21点')
if not session:
return "❌ 当前没有进行中的游戏"
if session['banker_id'] != user_id:
return "❌ 只有庄家可以发牌"
if session['current_phase'] != 'betting':
return "❌ 当前阶段不是下注阶段,无法发牌"
bets = self.db.get_pending_bets(chat_id, '21点')
if not bets:
return "❌ 还没有玩家下注,无法发牌"
session_id = session['id']
2025-10-31 15:57:26 +08:00
# 标准发牌顺序:
# 1. 先给每个玩家发一张牌
# 2. 再给庄家发一张明牌
# 3. 再给每个玩家发第二张牌
# 4. 最后给庄家发第二张暗牌
2025-10-31 12:09:07 +08:00
player_hands = {}
2025-10-31 15:57:26 +08:00
player_first_cards = {}
# 第一轮:给每个玩家发一张牌
2025-10-31 12:09:07 +08:00
for bet in bets:
2025-10-31 15:57:26 +08:00
first_card = self._draw_card()
player_first_cards[bet['user_id']] = [first_card]
# 给庄家发第一张明牌
banker_first_card = self._draw_card()
banker_cards = [banker_first_card]
# 第二轮:给每个玩家发第二张牌
for bet in bets:
second_card = self._draw_card()
player_cards = player_first_cards[bet['user_id']] + [second_card]
2025-10-31 12:09:07 +08:00
player_points = self._calculate_points(player_cards)
player_status = 'blackjack' if self._check_blackjack(player_cards) else 'playing'
self.db.create_blackjack_hand(session_id, bet['user_id'], player_cards, player_status)
player_hands[bet['user_id']] = {
'cards': player_cards,
'points': player_points,
'status': player_status
}
2025-10-31 15:57:26 +08:00
# 给庄家发第二张暗牌
banker_second_card = self._draw_card()
banker_cards.append(banker_second_card)
banker_points = self._calculate_points(banker_cards)
banker_status = 'blackjack' if self._check_blackjack(banker_cards) else 'playing'
# 存储庄家完整手牌(包含暗牌)
self.db.create_blackjack_hand(session_id, 0, banker_cards, banker_status)
2025-10-31 12:09:07 +08:00
# 更新session阶段
cursor = self.db.conn.cursor()
cursor.execute("""
UPDATE casino_sessions
SET current_phase = 'playing'
WHERE id = ?
""", (session_id,))
2025-10-31 15:57:26 +08:00
# 构建发牌结果(庄家只显示明牌)
2025-10-31 12:09:07 +08:00
text = f"## 🎰 发牌完成\n\n"
2025-10-31 15:57:26 +08:00
text += f"**庄家手牌**{self._format_cards([banker_first_card])} ? (隐藏一张暗牌)\n\n"
2025-10-31 12:09:07 +08:00
if banker_status == 'blackjack':
text += "**庄家黑杰克!**\n\n"
text += "---\n\n"
text += "**玩家手牌**\n"
for user_id, hand in player_hands.items():
player_name = self.db.get_user_display_name(user_id)
text += f"- {player_name}{self._format_cards(hand['cards'])} ({hand['points']}点)"
if hand['status'] == 'blackjack':
text += " **黑杰克!**"
text += "\n"
text += "\n---\n\n"
2025-10-31 15:57:26 +08:00
text += "💡 玩家可以使用 `.赌场 21点 hit` 要牌、`.赌场 21点 stand` 停牌或 `.赌场 21点 bet <金额>` 加注"
2025-10-31 12:09:07 +08:00
return text
except Exception as e:
logger.error(f"21点发牌失败: {e}", exc_info=True)
return f"❌ 发牌失败: {str(e)}"
def _format_cards(self, cards: List[int]) -> str:
"""格式化手牌显示"""
card_names = {
1: 'A',
11: 'J',
12: 'Q',
13: 'K'
}
formatted = []
for card in cards:
if card in card_names:
formatted.append(card_names[card])
else:
formatted.append(str(card))
return ' '.join(formatted)
async def _hit_blackjack(self, chat_id: int, user_id: int) -> str:
"""玩家要牌"""
try:
session = self.db.get_active_casino_session(chat_id, '21点')
if not session:
return "❌ 当前没有进行中的游戏"
if session['current_phase'] != 'playing':
return "❌ 当前不是游戏阶段,无法要牌"
# 检查玩家是否有下注
bets = self.db.get_pending_bets(chat_id, '21点')
user_bet = next((b for b in bets if b['user_id'] == user_id), None)
if not user_bet:
return "❌ 您没有下注,无法要牌"
# 获取玩家手牌
session_id = session['id']
hand = self.db.get_blackjack_hand(session_id, user_id)
if not hand:
return "❌ 未找到手牌信息"
if hand['hand_status'] != 'playing':
return f"❌ 当前手牌状态为:{hand['hand_status']},无法要牌"
# 抽一张新牌
new_card = self._draw_card()
cards = hand['hand_data'] + [new_card]
points = self._calculate_points(cards)
# 判断状态
if points > 21:
status = 'busted'
elif points == 21:
status = 'stood'
else:
status = 'playing'
# 更新手牌
self.db.update_blackjack_hand(session_id, user_id, cards, status)
player_name = self.db.get_user_display_name(user_id)
text = f"## 🎲 要牌\n\n"
text += f"**玩家**{player_name}\n\n"
text += f"**新手牌**{self._format_cards(cards)} ({points}点)\n\n"
if status == 'busted':
text += "**爆牌!** ❌\n\n"
elif points == 21:
text += "**21点** ✅\n\n"
2025-10-31 15:57:26 +08:00
# 如果爆牌,检查是否所有玩家都已完成
if status == 'busted':
all_hands = self.db.get_all_blackjack_hands(session_id)
bets = self.db.get_pending_bets(chat_id, '21点')
all_players_done = True
for bet in bets:
player_hand = all_hands.get(bet['user_id'])
if player_hand and player_hand['hand_status'] == 'playing':
all_players_done = False
break
if all_players_done:
text += "---\n\n"
text += "✅ 所有玩家已完成操作,自动结算中...\n\n"
# 调用结算方法使用庄家ID
try:
settlement_result = await self._settle_blackjack(chat_id, session['banker_id'])
return settlement_result
except Exception as e:
logger.error(f"自动结算失败: {e}", exc_info=True)
text += f"❌ 自动结算失败: {str(e)}"
return text
2025-10-31 12:09:07 +08:00
text += "---\n\n"
if status == 'playing':
text += "💡 可以继续要牌或停牌"
elif status == 'stood':
text += "💡 21点自动停牌"
else:
text += "💡 已爆牌,等待结算"
return text
except Exception as e:
logger.error(f"21点要牌失败: {e}", exc_info=True)
return f"❌ 要牌失败: {str(e)}"
async def _stand_blackjack(self, chat_id: int, user_id: int) -> str:
"""玩家停牌"""
try:
session = self.db.get_active_casino_session(chat_id, '21点')
if not session:
return "❌ 当前没有进行中的游戏"
if session['current_phase'] != 'playing':
return "❌ 当前不是游戏阶段,无法停牌"
session_id = session['id']
hand = self.db.get_blackjack_hand(session_id, user_id)
if not hand:
return "❌ 未找到手牌信息"
if hand['hand_status'] != 'playing':
return f"❌ 当前手牌状态为:{hand['hand_status']},无法停牌"
# 更新状态为停牌
self.db.update_blackjack_hand(session_id, user_id, hand['hand_data'], 'stood')
points = self._calculate_points(hand['hand_data'])
player_name = self.db.get_user_display_name(user_id)
2025-10-31 15:57:26 +08:00
# 检查是否所有玩家都已停牌或爆牌
all_hands = self.db.get_all_blackjack_hands(session_id)
bets = self.db.get_pending_bets(chat_id, '21点')
all_players_done = True
for bet in bets:
player_hand = all_hands.get(bet['user_id'])
if player_hand and player_hand['hand_status'] == 'playing':
all_players_done = False
break
2025-10-31 12:09:07 +08:00
text = f"## 🎲 停牌\n\n"
text += f"**玩家**{player_name}\n\n"
text += f"**最终手牌**{self._format_cards(hand['hand_data'])} ({points}点)\n\n"
2025-10-31 15:57:26 +08:00
# 如果所有玩家都已完成,自动结算
if all_players_done:
text += "---\n\n"
text += "✅ 所有玩家已完成操作,自动结算中...\n\n"
# 调用结算方法使用庄家ID
try:
settlement_result = await self._settle_blackjack(chat_id, session['banker_id'])
return settlement_result
except Exception as e:
logger.error(f"自动结算失败: {e}", exc_info=True)
text += f"❌ 自动结算失败: {str(e)}"
return text
else:
text += "---\n\n"
text += "💡 已停牌,等待其他玩家操作"
2025-10-31 12:09:07 +08:00
return text
except Exception as e:
logger.error(f"21点停牌失败: {e}", exc_info=True)
return f"❌ 停牌失败: {str(e)}"
async def _status_blackjack(self, chat_id: int) -> str:
"""查看21点游戏状态"""
session = self.db.get_active_casino_session(chat_id, '21点')
if not session:
return "❌ 当前没有进行中的21点游戏"
session_id = session['id']
bets = self.db.get_pending_bets(chat_id, '21点')
hands = self.db.get_all_blackjack_hands(session_id)
banker_display_name = self.db.get_user_display_name(session['banker_id'])
text = f"## 🎰 21点游戏状态\n\n"
text += f"**庄家**{banker_display_name}\n\n"
text += f"**当前阶段**{session['current_phase']}\n\n"
text += f"**最小下注**{session['min_bet']}\n\n"
text += f"**最大下注**{session['max_bet']}\n\n"
text += f"**黑杰克倍数**{session.get('blackjack_multiplier', 1.5)}\n\n"
2025-10-31 15:57:26 +08:00
# 显示庄家手牌(隐藏暗牌)
2025-10-31 12:09:07 +08:00
banker_hand = hands.get(0)
if banker_hand:
2025-10-31 15:57:26 +08:00
banker_cards = banker_hand['cards']
if session['current_phase'] == 'playing' and len(banker_cards) >= 2:
# 游戏阶段:只显示第一张明牌,隐藏暗牌
visible_card = banker_cards[0]
text += f"**庄家手牌**{self._format_cards([visible_card])} ? (隐藏一张暗牌)"
else:
# 已结算或发牌前:显示完整手牌
banker_points = self._calculate_points(banker_cards)
text += f"**庄家手牌**{self._format_cards(banker_cards)} ({banker_points}点)"
if banker_hand['status'] == 'blackjack':
text += " **黑杰克!**"
2025-10-31 12:09:07 +08:00
text += "\n\n"
text += "---\n\n"
text += f"**玩家下注**{len(bets)}\n\n"
# 显示玩家手牌
if hands:
text += "**玩家手牌**\n"
for user_id, hand in hands.items():
if user_id == 0: # 跳过庄家
continue
player_name = self.db.get_user_display_name(user_id)
player_bet = next((b for b in bets if b['user_id'] == user_id), None)
if player_bet:
points = self._calculate_points(hand['cards'])
text += f"- {player_name}{self._format_cards(hand['cards'])} ({points}点) - 下注{player_bet['amount']}"
if hand['status'] == 'blackjack':
text += " **黑杰克!**"
elif hand['status'] == 'busted':
text += " **爆牌**"
elif hand['status'] == 'stood':
text += " **已停牌**"
text += "\n"
text += "\n"
text += "---\n\n"
if session['current_phase'] == 'betting':
text += "💡 庄家可以使用 `.赌场 21点 deal` 发牌"
elif session['current_phase'] == 'playing':
text += "💡 玩家可以使用 `.赌场 21点 hit` 要牌或 `.赌场 21点 stand` 停牌\n"
text += "💡 庄家可以使用 `.赌场 21点 settle` 结算"
else:
text += "💡 庄家可以使用 `.赌场 21点 settle` 结算"
return text
async def _settle_blackjack(self, chat_id: int, user_id: int) -> str:
"""庄家结算21点游戏"""
try:
session = self.db.get_active_casino_session(chat_id, '21点')
if not session:
return "❌ 当前没有进行中的游戏"
if session['banker_id'] != user_id:
return "❌ 只有庄家可以结算游戏"
if session['current_phase'] == 'betting':
return "❌ 还未发牌,无法结算"
session_id = session['id']
hands = self.db.get_all_blackjack_hands(session_id)
# 获取庄家手牌
banker_hand = hands.get(0, {})
banker_cards = banker_hand.get('cards', [])
banker_points = self._calculate_points(banker_cards)
banker_status = banker_hand.get('status', 'stood')
# 如果庄家还在playing状态自动要牌到17点以上
while banker_status == 'playing' and banker_points < 17:
new_card = self._draw_card()
banker_cards.append(new_card)
banker_points = self._calculate_points(banker_cards)
if banker_points > 21:
banker_status = 'busted'
elif banker_points >= 17:
banker_status = 'stood'
# 更新庄家手牌
self.db.update_blackjack_hand(session_id, 0, banker_cards, banker_status)
# 更新hands字典
hands[0] = {'cards': banker_cards, 'status': banker_status}
#進行结算
settlement = self.db.settle_casino_bets(
chat_id, '21点', f"庄家{banker_points}", user_id,
hands_dict=hands
)
# 构建结算报告
text = f"## 🎰 21点游戏结算\n\n"
text += f"**庄家最终手牌**{self._format_cards(banker_cards)} ({banker_points}点)\n\n"
if banker_status == 'blackjack':
text += "**庄家黑杰克!**\n\n"
elif banker_status == 'busted':
text += "**庄家爆牌!**\n\n"
text += "---\n\n"
text += f"**赢家**{len(settlement['winners'])}\n"
text += f"**输家**{len(settlement['losers'])}\n\n"
if settlement['winners']:
text += "**赢家明细**\n"
for winner in settlement['winners']:
winner_display_name = self.db.get_user_display_name(winner['user_id'])
player_hand = hands.get(winner['user_id'], {})
player_points = self._calculate_points(player_hand.get('cards', []))
text += f"- {winner_display_name}: {self._format_cards(player_hand.get('cards', []))} ({player_points}点) - 下注{winner['amount']}分,赢得{winner['win_amount']}\n"
text += "\n"
if settlement['losers']:
text += "**输家明细**\n"
for loser in settlement['losers']:
loser_display_name = self.db.get_user_display_name(loser['user_id'])
player_hand = hands.get(loser['user_id'], {})
player_points = self._calculate_points(player_hand.get('cards', []))
text += f"- {loser_display_name}: {self._format_cards(player_hand.get('cards', []))} ({player_points}点) - 下注{loser['amount']}\n"
text += "\n"
text += "---\n\n"
text += "✅ 游戏已结束,欢迎再次游戏!"
return text
except ValueError as e:
return f"{str(e)}"
except Exception as e:
logger.error(f"21点结算失败: {e}", exc_info=True)
return f"❌ 结算失败: {str(e)}"
async def _cancel_blackjack(self, chat_id: int, user_id: int) -> str:
"""庄家放弃21点游戏"""
try:
session = self.db.get_active_casino_session(chat_id, '21点')
if not session:
return "❌ 当前没有进行中的游戏"
if session['banker_id'] != user_id:
return "❌ 只有庄家可以放弃游戏"
bets = self.db.get_pending_bets(chat_id, '21点')
return_amount = 0
for bet in bets:
self.db.add_points(bet['user_id'], bet['amount'], 'casino_cancel',
"庄家放弃游戏,返还下注")
return_amount += bet['amount']
self.db.close_casino_session(chat_id, '21点')
cursor = self.db.conn.cursor()
cursor.execute("""
UPDATE casino_bets
SET status = 'cancelled'
WHERE chat_id = ? AND game_type = ? AND status = 'pending'
""", (chat_id, '21点'))
banker_display_name = self.db.get_user_display_name(user_id)
text = f"## 🎰 游戏已放弃\n\n"
text += f"**庄家**{banker_display_name}\n\n"
text += f"**总返还积分**{return_amount}\n\n"
text += f"**返还人数**{len(bets)}\n\n"
text += "---\n\n"
text += "✅ 所有下注已返还,游戏已关闭"
return text
except Exception as e:
logger.error(f"放弃21点游戏失败: {e}", exc_info=True)
return f"❌ 放弃游戏失败: {str(e)}"
def _get_blackjack_help(self) -> str:
"""获取21点游戏帮助信息"""
return """## 🎰 21点游戏帮助
2025-10-31 15:57:26 +08:00
### 游戏流程
1. **庄家开启**`.赌场 21 open <底注> <黑杰克倍数>`
- 示例`.赌场 21 open 50 1.5`
2. **玩家加入**`.赌场 21 join`需要至少底注的积分
3. **庄家发牌**`.赌场 21 deal`
4. **玩家操作**要牌停牌或加注
5. **自动结算**所有玩家停牌后自动结算
2025-10-31 12:09:07 +08:00
### 庄家命令
2025-10-31 15:57:26 +08:00
- `.赌场 21 open <底注> <黑杰克倍数>` - 开启游戏
- 示例`.赌场 21 open 50 1.5`
- `.赌场 21 deal` - 发牌玩家加入后
- `.赌场 21 settle` - 手动结算可选会自动结算
2025-10-31 12:09:07 +08:00
- `.赌场 21 cancel` - 放弃游戏返还所有下注
### 玩家命令
2025-10-31 15:57:26 +08:00
- `.赌场 21 join` - 加入游戏扣除底注需要至少底注的积分
- `.赌场 21 bet <金额>` - 加注游戏中不低于底注
2025-10-31 12:09:07 +08:00
- 示例`.赌场 21 bet 50`
- `.赌场 21 hit` - 要牌
2025-10-31 15:57:26 +08:00
- `.赌场 21 stand` - 停牌所有玩家停牌后自动结算
2025-10-31 12:09:07 +08:00
### 通用命令
- `.赌场 21 status` - 查看当前状态
### 游戏规则
2025-10-31 15:57:26 +08:00
- **目标**手牌点数尽可能接近21点但不能超过
- **发牌**标准发牌顺序庄家隐藏一张暗牌
- **黑杰克**A+10/J/Q/K赢得设定倍数默认1.5
- **玩家点数>庄家点数且未爆牌**赢得1:1
- **平局**返还下注
- **爆牌**失去下注
- **庄家规则**自动要牌到17点以上
- **自动结算**所有玩家停牌后最后一名玩家停牌时立即结算
- **系统抽水**5%"""
2025-10-30 16:11:50 +08:00