"""赌场游戏模块""" import logging import random from typing import List 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) 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) elif game_type in ['help', '帮助']: return self.get_help() else: return f"❌ 暂不支持的游戏类型: {game_type}\n\n支持的类型:大小、轮盘、21点" 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) elif action in ['cancel', '放弃', '关闭']: return await self._cancel_bigsmall(chat_id, user_id) 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分!" # 检查是否已有活跃游戏(单场限制) 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=multiplier, house_fee=0.05 # 固定5%抽水 ) 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"**赔率**:{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'] == '小') 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 += 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" text += "💡 庄家可以使用 `.赌场 大小 settle` 结算" return text async def _settle_bigsmall(self, args: str, chat_id: int, user_id: int) -> str: """庄家结算游戏(系统随机生成结果) Args: args: 无参数 chat_id: 会话ID user_id: 用户ID Returns: 结算结果消息 """ try: # 不需要参数,系统自动随机生成结果 # 使用系统随机数决定大小(50%概率) result = '大' if random.random() > 0.5 else '小' # 结算 settlement = self.db.settle_casino_bets(chat_id, '大小', result, user_id) # 构建结算报告 text = f"## 🎰 大小游戏结算\n\n" text += f"**结算结果**:🎲 {result}\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_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, '大小')) # 构建报告 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_bigsmall_help(self) -> str: """获取大小游戏帮助信息""" return """## 🎰 大小游戏帮助 ### 庄家命令 - `.赌场 大小 open <最小> <最大> <赔率>` - 开启游戏 - 示例:`.赌场 大小 open 10 100 2.0` - `.赌场 大小 settle` - 结算游戏(系统随机生成结果) - `.赌场 大小 cancel` - 放弃游戏(返还所有下注) ### 玩家命令 - `.赌场 大小 bet <大/小> <金额>` - 下注 - 示例:`.赌场 大小 bet 大 50` - 示例:`.赌场 大小 bet 小 30` ### 通用命令 - `.赌场 大小 status` - 查看当前状态 ### 游戏规则 - 玩家下注后积分立即扣除 - 结算后根据结果发放奖励 - 庄家放弃游戏时,所有下注全额返还 - 系统抽水5% - 赔率由庄家设置 """ def get_help(self) -> str: """获取帮助信息""" 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)}" 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` (押数字17,100分,35倍赔率) - 颜色:`.赌场 轮盘 bet 红色 50` (押红色,50分,2倍赔率) - 奇偶:`.赌场 轮盘 bet 奇数 30` (押奇数,30分,2倍赔率) - 大小:`.赌场 轮盘 bet 大 40` (押大19-36,40分,2倍赔率) - 区间:`.赌场 轮盘 bet 1-12 60` (押1-12,60分,3倍赔率) ### 通用命令 - `.赌场 轮盘 status` - 查看当前状态 ### 游戏规则 - 数字0-36,0为绿色,其他数字为红色或黑色 - 数字下注:35倍赔率 - 颜色/奇偶/大小:2倍赔率 - 区间(1-12/13-24/25-36):3倍赔率 - 系统抽水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) elif action in ['bet', '下注', '押']: 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: return "❌ 参数格式错误!\n\n正确格式:`.赌场 21点 open <最小下注> <最大下注> [黑杰克倍数]`\n\n示例:`.赌场 21点 open 10 100 1.5`" try: min_bet = int(parts[0]) max_bet = int(parts[1]) blackjack_multiplier = float(parts[2]) if len(parts) > 2 else 1.5 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分!" 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, min_bet=min_bet, max_bet=max_bet, 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" text += f"**最小下注**:{min_bet} 分\n\n" text += f"**最大下注**:{max_bet} 分\n\n" text += f"**黑杰克倍数**:{blackjack_multiplier} 倍\n\n" text += f"**抽水率**:5%\n\n" text += "---\n\n" text += "💡 提示:玩家下注后,庄家使用 `.赌场 21点 deal` 发牌" return text except Exception as e: 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: """玩家下注21点""" try: try: amount = int(args.strip()) except ValueError: return "❌ 下注金额必须是数字!\n\n正确格式:`.赌场 21点 bet <金额>`" session = self.db.get_active_casino_session(chat_id, '21点') if not session: return "❌ 当前没有进行中的游戏,请等待庄家开启游戏。" if session['current_phase'] != 'betting': 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', "21点游戏下注"): return "❌ 扣除积分失败!" bet_id = self.db.create_casino_bet( chat_id=chat_id, game_type='21点', user_id=user_id, bet_type='标准', amount=amount, multiplier=1.0, # 标准赔率1:1 hand_status='playing' ) updated_points = self.db.get_user_points(user_id) text = f"## 🎲 下注成功\n\n" text += f"**下注金额**:{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)}" def _draw_card(self) -> int: """抽取一张牌(1-13,其中1=A,11=J,12=Q,13=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'] # 为庄家发两张牌 banker_cards = [self._draw_card(), self._draw_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) # 为每个下注的玩家发两张牌 player_hands = {} for bet in bets: player_cards = [self._draw_card(), self._draw_card()] 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 } # 更新session阶段 cursor = self.db.conn.cursor() cursor.execute(""" UPDATE casino_sessions SET current_phase = 'playing' WHERE id = ? """, (session_id,)) # 构建发牌结果 text = f"## 🎰 发牌完成\n\n" text += f"**庄家手牌**:{self._format_cards(banker_cards)} ({banker_points}点)\n\n" 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" text += "💡 玩家可以使用 `.赌场 21点 hit` 要牌或 `.赌场 21点 stand` 停牌" 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" 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) text = f"## 🎲 停牌\n\n" text += f"**玩家**:{player_name}\n\n" text += f"**最终手牌**:{self._format_cards(hand['hand_data'])} ({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)}" 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" # 显示庄家手牌 banker_hand = hands.get(0) if banker_hand: banker_points = self._calculate_points(banker_hand['cards']) text += f"**庄家手牌**:{self._format_cards(banker_hand['cards'])} ({banker_points}点)" if banker_hand['status'] == 'blackjack': text += " **黑杰克!**" 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点游戏帮助 ### 庄家命令 - `.赌场 21点 open <最小> <最大> [黑杰克倍数]` - 开启游戏 - 示例:`.赌场 21点 open 10 100 1.5` - `.赌场 21点 deal` - 发牌(所有玩家下注后) - `.赌场 21点 settle` - 结算游戏(自动要牌到17点以上后结算) - `.赌场 21点 cancel` - 放弃游戏(返还所有下注) ### 玩家命令 - `.赌场 21点 bet <金额>` - 下注 - 示例:`.赌场 21点 bet 50` - `.赌场 21点 hit` - 要牌 - `.赌场 21点 stand` - 停牌 ### 通用命令 - `.赌场 21点 status` - 查看当前状态 ### 游戏规则 - 目标:手牌点数尽可能接近21点但不能超过 - 黑杰克(A+10/J/Q/K):赢得1.5倍(默认) - 玩家点数>庄家点数且未爆牌:赢得1:1 - 平局:返还下注 - 爆牌:失去下注 - 庄家自动要牌到17点以上 - 系统抽水5%"""