From 1f10799562cde35025af4b8375d42ca2c2021d50 Mon Sep 17 00:00:00 2001 From: ninemine <1371605831@qq.com> Date: Fri, 31 Oct 2025 12:09:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B021=E7=82=B9=E4=B8=8E=E8=BD=AE?= =?UTF-8?q?=E7=9B=98=E6=B8=B8=E6=88=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- games/base.py | 18 ++ games/casino.py | 713 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 731 insertions(+) diff --git a/games/base.py b/games/base.py index 9c213a5..13714c5 100644 --- a/games/base.py +++ b/games/base.py @@ -114,12 +114,30 @@ def get_help_message() -> str: - `.aiconfig host=xxx port=xxx model=xxx` - 配置Ollama服务地址和模型 ### 🎰 赌场系统 +**大小游戏**: - `.赌场 大小 open <最小> <最大> <赔率>` - 庄家开启大小游戏 - `.赌场 大小 bet <大/小> <金额>` - 下注 - `.赌场 大小 status` - 查看状态 - `.赌场 大小 settle` - 庄家结算(系统随机) - `.赌场 大小 cancel` - 庄家放弃游戏(返还下注) +**轮盘游戏**: +- `.赌场 轮盘 open <最小> <最大>` - 庄家开启轮盘游戏 +- `.赌场 轮盘 bet <类型> <选项> <金额>` - 下注(数字/颜色/奇偶/大小/区间) +- `.赌场 轮盘 status` - 查看状态 +- `.赌场 轮盘 settle` - 庄家结算(系统随机0-36) +- `.赌场 轮盘 cancel` - 庄家放弃游戏(返还下注) + +**21点游戏**: +- `.赌场 21点 open <最小> <最大> [黑杰克倍数]` - 庄家开启21点游戏 +- `.赌场 21点 bet <金额>` - 下注 +- `.赌场 21点 deal` - 庄家发牌 +- `.赌场 21点 hit` - 玩家要牌 +- `.赌场 21点 stand` - 玩家停牌 +- `.赌场 21点 status` - 查看状态 +- `.赌场 21点 settle` - 庄家结算 +- `.赌场 21点 cancel` - 庄家放弃游戏(返还下注) + ### 其他 - `.help` - 显示帮助 - `.stats` - 查看个人统计 diff --git a/games/casino.py b/games/casino.py index 3d270ad..29ff089 100644 --- a/games/casino.py +++ b/games/casino.py @@ -1,6 +1,7 @@ """赌场游戏模块""" import logging import random +from typing import List from games.base import BaseGame from utils.parser import CommandParser from core.database import get_db @@ -604,4 +605,716 @@ class CasinoGame(BaseGame): 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%"""