21点游戏规则更新
This commit is contained in:
289
games/casino.py
289
games/casino.py
@@ -767,7 +767,9 @@ class CasinoGame(BaseGame):
|
||||
|
||||
if action in ['open', '开启', '开始']:
|
||||
return await self._open_blackjack(sub_args, chat_id, user_id)
|
||||
elif action in ['bet', '下注', '押']:
|
||||
elif action in ['join', '加入']:
|
||||
return await self._join_blackjack(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)
|
||||
@@ -791,23 +793,19 @@ class CasinoGame(BaseGame):
|
||||
try:
|
||||
parts = args.split()
|
||||
if len(parts) < 2:
|
||||
return "❌ 参数格式错误!\n\n正确格式:`.赌场 21点 open <最小下注> <最大下注> [黑杰克倍数]`\n\n示例:`.赌场 21点 open 10 100 1.5`"
|
||||
return "❌ 参数格式错误!\n\n正确格式:`.赌场 21点 open <底注> <黑杰克倍数>`\n\n示例:`.赌场 21点 open 50 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
|
||||
base_bet = int(parts[0])
|
||||
blackjack_multiplier = float(parts[1])
|
||||
except ValueError:
|
||||
return "❌ 参数必须是数字!"
|
||||
|
||||
if min_bet <= 0 or max_bet <= 0:
|
||||
return "❌ 下注金额必须大于0!"
|
||||
if base_bet <= 0:
|
||||
return "❌ 底注必须大于0!"
|
||||
|
||||
if min_bet > max_bet:
|
||||
return "❌ 最小下注不能大于最大下注!"
|
||||
|
||||
if max_bet > 10000:
|
||||
return "❌ 最大下注不能超过10000分!"
|
||||
if base_bet > 10000:
|
||||
return "❌ 底注不能超过10000分!"
|
||||
|
||||
if blackjack_multiplier <= 0:
|
||||
return "❌ 黑杰克倍数必须大于0!"
|
||||
@@ -820,8 +818,8 @@ class CasinoGame(BaseGame):
|
||||
chat_id=chat_id,
|
||||
game_type='21点',
|
||||
banker_id=user_id,
|
||||
min_bet=min_bet,
|
||||
max_bet=max_bet,
|
||||
min_bet=base_bet, # 底注作为最小下注
|
||||
max_bet=base_bet * 10, # 最大下注设为底注的10倍
|
||||
multiplier=1.0, # 21点标准赔率1:1
|
||||
house_fee=0.05,
|
||||
current_phase='betting',
|
||||
@@ -831,12 +829,11 @@ class CasinoGame(BaseGame):
|
||||
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"**底注**:{base_bet} 分\n\n"
|
||||
text += f"**黑杰克倍数**:{blackjack_multiplier} 倍\n\n"
|
||||
text += f"**抽水率**:5%\n\n"
|
||||
text += "---\n\n"
|
||||
text += "💡 提示:玩家下注后,庄家使用 `.赌场 21点 deal` 发牌"
|
||||
text += f"💡 提示:玩家使用 `.赌场 21点 join` 加入游戏(需要至少{base_bet}分积分)"
|
||||
|
||||
return text
|
||||
|
||||
@@ -844,47 +841,49 @@ class CasinoGame(BaseGame):
|
||||
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点"""
|
||||
async def _join_blackjack(self, 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 "❌ 当前不是下注阶段,无法下注。"
|
||||
return "❌ 当前不是加入阶段,游戏已开始。"
|
||||
|
||||
if amount < session['min_bet']:
|
||||
return f"❌ 下注金额太小!最小下注:{session['min_bet']} 分"
|
||||
# 检查是否已经加入
|
||||
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']} 分"
|
||||
|
||||
if amount > session['max_bet']:
|
||||
return f"❌ 下注金额太大!最大下注:{session['max_bet']} 分"
|
||||
base_bet = session['min_bet']
|
||||
|
||||
# 检查积分是否足够底注
|
||||
user_points = self.db.get_user_points(user_id)
|
||||
if user_points['points'] < amount:
|
||||
return f"❌ 积分不足!需要 {amount} 分,当前可用 {user_points['points']} 分"
|
||||
if user_points['points'] < base_bet:
|
||||
return f"❌ 积分不足!需要至少 {base_bet} 分才能加入,当前可用 {user_points['points']} 分"
|
||||
|
||||
if not self.db.consume_points(user_id, amount, 'casino_bet', "21点游戏下注"):
|
||||
# 扣除底注
|
||||
if not self.db.consume_points(user_id, base_bet, '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,
|
||||
amount=base_bet,
|
||||
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"
|
||||
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"
|
||||
text += f"**剩余积分**:{updated_points['points']} 分\n\n"
|
||||
text += "---\n\n"
|
||||
text += "💡 等待庄家发牌..."
|
||||
@@ -892,8 +891,74 @@ class CasinoGame(BaseGame):
|
||||
return text
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"21点下注失败: {e}", exc_info=True)
|
||||
return f"❌ 下注失败: {str(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:
|
||||
"""玩家加注(游戏中加注)"""
|
||||
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)}"
|
||||
|
||||
def _draw_card(self) -> int:
|
||||
"""抽取一张牌(1-13,其中1=A,11=J,12=Q,13=K)"""
|
||||
@@ -930,17 +995,28 @@ class CasinoGame(BaseGame):
|
||||
|
||||
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'
|
||||
# 标准发牌顺序:
|
||||
# 1. 先给每个玩家发一张牌
|
||||
# 2. 再给庄家发一张明牌
|
||||
# 3. 再给每个玩家发第二张牌
|
||||
# 4. 最后给庄家发第二张暗牌
|
||||
|
||||
self.db.create_blackjack_hand(session_id, 0, banker_cards, banker_status)
|
||||
|
||||
# 为每个下注的玩家发两张牌
|
||||
player_hands = {}
|
||||
player_first_cards = {}
|
||||
|
||||
# 第一轮:给每个玩家发一张牌
|
||||
for bet in bets:
|
||||
player_cards = [self._draw_card(), self._draw_card()]
|
||||
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]
|
||||
player_points = self._calculate_points(player_cards)
|
||||
player_status = 'blackjack' if self._check_blackjack(player_cards) else 'playing'
|
||||
|
||||
@@ -951,6 +1027,15 @@ class CasinoGame(BaseGame):
|
||||
'status': player_status
|
||||
}
|
||||
|
||||
# 给庄家发第二张暗牌
|
||||
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)
|
||||
|
||||
# 更新session阶段
|
||||
cursor = self.db.conn.cursor()
|
||||
cursor.execute("""
|
||||
@@ -959,9 +1044,9 @@ class CasinoGame(BaseGame):
|
||||
WHERE id = ?
|
||||
""", (session_id,))
|
||||
|
||||
# 构建发牌结果
|
||||
# 构建发牌结果(庄家只显示明牌)
|
||||
text = f"## 🎰 发牌完成\n\n"
|
||||
text += f"**庄家手牌**:{self._format_cards(banker_cards)} ({banker_points}点)\n\n"
|
||||
text += f"**庄家手牌**:{self._format_cards([banker_first_card])} ? (隐藏一张暗牌)\n\n"
|
||||
|
||||
if banker_status == 'blackjack':
|
||||
text += "**庄家黑杰克!**\n\n"
|
||||
@@ -976,7 +1061,7 @@ class CasinoGame(BaseGame):
|
||||
text += "\n"
|
||||
|
||||
text += "\n---\n\n"
|
||||
text += "💡 玩家可以使用 `.赌场 21点 hit` 要牌或 `.赌场 21点 stand` 停牌"
|
||||
text += "💡 玩家可以使用 `.赌场 21点 hit` 要牌、`.赌场 21点 stand` 停牌或 `.赌场 21点 bet <金额>` 加注"
|
||||
|
||||
return text
|
||||
|
||||
@@ -1051,6 +1136,30 @@ class CasinoGame(BaseGame):
|
||||
elif points == 21:
|
||||
text += "**21点!** ✅\n\n"
|
||||
|
||||
# 如果爆牌,检查是否所有玩家都已完成
|
||||
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
|
||||
|
||||
text += "---\n\n"
|
||||
if status == 'playing':
|
||||
text += "💡 可以继续要牌或停牌"
|
||||
@@ -1088,11 +1197,37 @@ class CasinoGame(BaseGame):
|
||||
|
||||
points = self._calculate_points(hand['hand_data'])
|
||||
player_name = self.db.get_user_display_name(user_id)
|
||||
|
||||
# 检查是否所有玩家都已停牌或爆牌
|
||||
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
|
||||
|
||||
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 += "💡 已停牌,等待其他玩家操作或庄家结算"
|
||||
|
||||
# 如果所有玩家都已完成,自动结算
|
||||
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 += "💡 已停牌,等待其他玩家操作"
|
||||
|
||||
return text
|
||||
|
||||
@@ -1118,13 +1253,20 @@ class CasinoGame(BaseGame):
|
||||
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 += " **黑杰克!**"
|
||||
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 += " **黑杰克!**"
|
||||
text += "\n\n"
|
||||
|
||||
text += "---\n\n"
|
||||
@@ -1293,28 +1435,39 @@ class CasinoGame(BaseGame):
|
||||
"""获取21点游戏帮助信息"""
|
||||
return """## 🎰 21点游戏帮助
|
||||
|
||||
### 游戏流程
|
||||
1. **庄家开启**:`.赌场 21点 open <底注> <黑杰克倍数>`
|
||||
- 示例:`.赌场 21点 open 50 1.5`
|
||||
2. **玩家加入**:`.赌场 21点 join`(需要至少底注的积分)
|
||||
3. **庄家发牌**:`.赌场 21点 deal`
|
||||
4. **玩家操作**:要牌、停牌或加注
|
||||
5. **自动结算**:所有玩家停牌后自动结算
|
||||
|
||||
### 庄家命令
|
||||
- `.赌场 21点 open <最小> <最大> [黑杰克倍数]` - 开启游戏
|
||||
- 示例:`.赌场 21点 open 10 100 1.5`
|
||||
- `.赌场 21点 deal` - 发牌(所有玩家下注后)
|
||||
- `.赌场 21点 settle` - 结算游戏(自动要牌到17点以上后结算)
|
||||
- `.赌场 21点 open <底注> <黑杰克倍数>` - 开启游戏
|
||||
- 示例:`.赌场 21点 open 50 1.5`
|
||||
- `.赌场 21点 deal` - 发牌(玩家加入后)
|
||||
- `.赌场 21点 settle` - 手动结算(可选,会自动结算)
|
||||
- `.赌场 21点 cancel` - 放弃游戏(返还所有下注)
|
||||
|
||||
### 玩家命令
|
||||
- `.赌场 21点 bet <金额>` - 下注
|
||||
- `.赌场 21点 join` - 加入游戏(扣除底注,需要至少底注的积分)
|
||||
- `.赌场 21点 bet <金额>` - 加注(游戏中,不低于底注)
|
||||
- 示例:`.赌场 21点 bet 50`
|
||||
- `.赌场 21点 hit` - 要牌
|
||||
- `.赌场 21点 stand` - 停牌
|
||||
- `.赌场 21点 stand` - 停牌(所有玩家停牌后自动结算)
|
||||
|
||||
### 通用命令
|
||||
- `.赌场 21点 status` - 查看当前状态
|
||||
|
||||
### 游戏规则
|
||||
- 目标:手牌点数尽可能接近21点但不能超过
|
||||
- 黑杰克(A+10/J/Q/K):赢得1.5倍(默认)
|
||||
- 玩家点数>庄家点数且未爆牌:赢得1:1
|
||||
- 平局:返还下注
|
||||
- 爆牌:失去下注
|
||||
- 庄家自动要牌到17点以上
|
||||
- 系统抽水5%"""
|
||||
- **目标**:手牌点数尽可能接近21点但不能超过
|
||||
- **发牌**:标准发牌顺序,庄家隐藏一张暗牌
|
||||
- **黑杰克**(A+10/J/Q/K):赢得设定倍数(默认1.5倍)
|
||||
- **玩家点数>庄家点数且未爆牌**:赢得1:1
|
||||
- **平局**:返还下注
|
||||
- **爆牌**:失去下注
|
||||
- **庄家规则**:自动要牌到17点以上
|
||||
- **自动结算**:所有玩家停牌后,最后一名玩家停牌时立即结算
|
||||
- **系统抽水**:5%"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user