# 背景 文件名:2025-10-30_1_add_casino_games.md 创建于:2025-10-30_15:16:56 创建者:admin 主分支:main 任务分支:task/add_casino_games_2025-01-14_1 Yolo模式:Off # 任务描述 在项目中新增赌场常见游戏,支持多个玩家下注等功能,使用积分系统模拟真实的赌场环境。 要求: - 指令格式:`.赌场 <游戏类型> <参数>` - 采用模块化设计,便于扩展多种赌场游戏 - 支持多人同时下注 - 集成现有积分系统 - 记录下注和收益数据 # 项目概览 基于WPS协作开放平台的自定义机器人游戏系统。使用FastAPI + SQLite架构,已有完善的积分系统和多款游戏(五子棋、成语接龙等)。需要通过模块化设计添加赌场游戏功能。 # 分析 ## 现有系统分析 1. **积分系统**:已实现 `add_points()` 和 `consume_points()` 方法 2. **游戏基类**:`BaseGame` 提供统一的接口 3. **路由系统**:通过 `CommandParser` 解析指令,在 `callback.py` 中路由 4. **数据库**:SQLite,已有用户表、游戏状态表、统计表 ## 需要新增的内容 1. **数据库表**: - `casino_bets`:记录所有下注 - `casino_results`:记录游戏结果和结算 - `casino_games`:记录游戏房间(可选) 2. **游戏模块**: - `games/casino.py`:主赌场模块 - 第一期支持:大小游戏 - 第二期计划:轮盘、二十一点等 3. **指令映射**: - `.赌场` -> casino 游戏类型 - 子指令:`轮盘`、`大小`、`21点` 等 ## 设计要点 1. **模块化设计**:每种赌场游戏作为独立类 2. **下注流程**:创建房间 -> 玩家下注 -> 结算 -> 分发奖励 3. **安全性**:下注前检查积分,结算时原子性操作 4. **多玩家支持**:以 chat_id 为单位创建游戏房间 # 提议的解决方案 ## 指令设计 采用 `.赌场 <游戏类型> <操作> <参数>` 的模块化结构: ### 大小游戏 - **庄家开启游戏**:`.赌场 大小 open <最小下注> <最大下注> <赔率>` - 示例:`.赌场 大小 open 10 100 2.0` (最小10分,最大100分,赔率2.0倍) - **玩家下注**:`.赌场 大小 bet <大小/小> <下注金额>` - 示例:`.赌场 大小 bet 大 50` (下注50分压大) - 示例:`.赌场 大小 bet 小 30` (下注30分压小) - **查看状态**:`.赌场 大小 status` - **庄家结算**:`.赌场 大小 settle <结果>` - 示例:`.赌场 大小 settle 大` (开大) - 示例:`.赌场 大小 settle 小` (开小) ### 轮盘游戏(二期实现) - 暂不实现,等大小游戏完善后再扩展 ### 21点游戏(二期实现) - 暂不实现,等大小游戏完善后再扩展 ## 游戏流程 1. 庄家开启游戏(指定下注限额和赔率参数) 2. 玩家下注(可多人同时参与) 3. 庄家确认结算(手动触发结果) 4. 系统自动分发奖励 ## 数据库设计 ### 新增表:casino_bets(下注记录表) ```sql CREATE TABLE IF NOT EXISTS casino_bets ( id INTEGER PRIMARY KEY AUTOINCREMENT, chat_id INTEGER NOT NULL, game_type TEXT NOT NULL, user_id INTEGER NOT NULL, bet_type TEXT NOT NULL, -- '大' 或 '小' amount INTEGER NOT NULL, multiplier REAL NOT NULL, -- 赔率 status TEXT DEFAULT 'pending', -- pending/settled/cancelled result TEXT, -- 游戏结果 win_amount INTEGER, -- 赢得金额 created_at INTEGER NOT NULL, settled_at INTEGER, FOREIGN KEY (user_id) REFERENCES users(user_id) ) ``` ### 新增表:casino_sessions(游戏会话表) ```sql CREATE TABLE IF NOT EXISTS casino_sessions ( id INTEGER PRIMARY KEY AUTOINCREMENT, chat_id INTEGER NOT NULL, game_type TEXT NOT NULL, banker_id INTEGER NOT NULL, -- 庄家ID min_bet INTEGER NOT NULL, max_bet INTEGER NOT NULL, multiplier REAL NOT NULL, house_fee REAL DEFAULT 0.05, -- 抽水率,默认5% status TEXT DEFAULT 'open', -- open/settling/closed created_at INTEGER NOT NULL, settled_at INTEGER, UNIQUE(chat_id, game_type, status) ) ``` ### 索引 - `casino_bets(chat_id, game_type, status)` - 快速查询待结算下注 - `casino_sessions(chat_id, game_type, status)` - 快速查询活跃游戏 ## 方案对比 ### 方案A:纯数据库表方案(推荐) **优点**: - 数据结构清晰,便于查询统计 - 支持历史记录追踪 - 并发安全,利用数据库事务 - 易于扩展复杂查询 **缺点**: - 需要维护额外的表结构 - 稍微复杂一些 **决策**:采用此方案 ### 方案B:game_states + JSON方案 **优点**: - 复用现有系统 - 实现简单 **缺点**: - 难以进行复杂统计查询 - JSON解析性能较差 - 数据格式不够规范化 ## 核心实现细节 ### 1. 游戏流程控制 - **开启游戏**:检查是否已有活跃游戏,同一chat_id只能有一个进行中的游戏 - **下注限制**:检查session状态、下注金额范围、玩家积分 - **结算控制**:只有庄家可以结算,结算后自动关闭session ### 2. 下注流程 1. 检查是否有活跃的session 2. 检查下注金额是否符合min/max限制 3. 检查用户积分是否充足 4. 扣除下注金额(consume_points) 5. 记录下注到casino_bets表 ### 3. 结算流程 1. 验证是否为庄家操作 2. 查询所有pending状态的下注 3. 计算每个玩家的输赢 4. 使用数据库事务确保原子性: - 更新bets状态 - 发放/扣除积分 - 更新session状态 5. 返回结算报告 ### 4. 抽水机制 - **抽水率**:5%(可配置,存储在session.house_fee中) - **抽水时机**:从玩家的赢得金额中扣除 - **抽水归属**:归系统所有(不返还给庄家) - **计算方式**: - 玩家赢得 = 下注金额 × 赔率 - 实际发放 = 赢得金额 × (1 - 抽水率) - 抽水金额 = 赢得金额 × 抽水率 ### 5. 错误处理 - 下注时积分不足:给出明确提示 - 重复下注:允许(可下多注) - 非法下注金额:给出范围提示 - 非庄家尝试结算:拒绝 ## 安全性 - 下注前检查积分 - 结算时使用数据库事务保证原子性 - 抽水机制保护庄家(虽然抽水归系统) - 验证庄家身份 - 防止重复结算 # 详细实施计划 ## 文件1: core/database.py ### 修改函数: init_tables() 在现有表创建之后(约第130行),添加赌场相关表的创建: 位置:在 `user_points` 表创建之后(约第130行)添加 ```python # 赌场下注记录表 cursor.execute(""" CREATE TABLE IF NOT EXISTS casino_bets ( id INTEGER PRIMARY KEY AUTOINCREMENT, chat_id INTEGER NOT NULL, game_type TEXT NOT NULL, user_id INTEGER NOT NULL, bet_type TEXT NOT NULL, amount INTEGER NOT NULL, multiplier REAL NOT NULL, status TEXT DEFAULT 'pending', result TEXT, win_amount INTEGER, created_at INTEGER NOT NULL, settled_at INTEGER, FOREIGN KEY (user_id) REFERENCES users (user_id) ) """) # 赌场游戏会话表 cursor.execute(""" CREATE TABLE IF NOT EXISTS casino_sessions ( id INTEGER PRIMARY KEY AUTOINCREMENT, chat_id INTEGER NOT NULL, game_type TEXT NOT NULL, banker_id INTEGER NOT NULL, min_bet INTEGER NOT NULL, max_bet INTEGER NOT NULL, multiplier REAL NOT NULL, house_fee REAL DEFAULT 0.05, status TEXT DEFAULT 'open', created_at INTEGER NOT NULL, settled_at INTEGER, UNIQUE(chat_id, game_type, status) ) """) # 创建索引 cursor.execute(""" CREATE INDEX IF NOT EXISTS idx_casino_bets ON casino_bets(chat_id, game_type, status) """) cursor.execute(""" CREATE INDEX IF NOT EXISTS idx_casino_sessions ON casino_sessions(chat_id, game_type, status) """) ``` ### 新增函数: create_casino_session() 函数签名: ```python def create_casino_session(self, chat_id: int, game_type: str, banker_id: int, min_bet: int, max_bet: int, multiplier: float, house_fee: float = 0.05) -> int: ``` 功能:创建新的赌场游戏会话,返回session_id ### 新增函数: get_active_casino_session() 函数签名: ```python def get_active_casino_session(self, chat_id: int, game_type: str) -> Optional[Dict]: ``` 功能:获取活跃的游戏会话 ### 新增函数: create_casino_bet() 函数签名: ```python def create_casino_bet(self, chat_id: int, game_type: str, user_id: int, bet_type: str, amount: int, multiplier: float) -> int: ``` 功能:创建下注记录,返回bet_id ### 新增函数: get_pending_bets() 函数签名: ```python def get_pending_bets(self, chat_id: int, game_type: str) -> List[Dict]: ``` 功能:获取待结算的下注列表 ### 新增函数: settle_casino_bets() 函数签名: ```python def settle_casino_bets(self, chat_id: int, game_type: str, result: str, banker_id: int) -> Dict: ``` 功能:结算所有下注,返回结算详情字典(winners, losers, total_win等) ### 新增函数: close_casino_session() 函数签名: ```python def close_casino_session(self, chat_id: int, game_type: str): ``` 功能:关闭游戏会话 ## 文件2: games/casino.py(新建) ### 类: CasinoGame 继承自 `BaseGame` ### 方法: __init__() 初始化数据库连接 ### 方法: async handle(command, chat_id, user_id) -> str 主处理函数,解析指令并调用相应的处理方法 解析逻辑: - 提取命令参数,格式:`.赌场 <游戏类型> <操作> <参数>` - 识别游戏类型(第一期只支持"大小") - 分发到相应的处理方法 ### 方法: async _handle_bigsmall(command, args, chat_id, user_id) -> str 处理大小游戏的各种操作 支持的操作: - open: 开启游戏 - bet: 下注 - status: 查看状态 - settle: 结算 ### 方法: async _open_bigsmall(args, chat_id, user_id) -> str 庄家开启大小游戏 参数解析:`<最小下注> <最大下注> <赔率>` 参数验证和限制 ### 方法: async _bet_bigsmall(args, chat_id, user_id) -> str 玩家下注 参数解析:`<大小/小> <下注金额>` 检查session、金额范围、用户积分 ### 方法: async _status_bigsmall(chat_id, game_type) -> str 查看当前游戏状态 ### 方法: async _settle_bigsmall(args, chat_id, user_id) -> str 庄家结算游戏 参数解析:`<大/小>` 验证庄家身份,结算所有下注 ### 方法: get_help() -> str 返回帮助信息 ## 文件3: utils/parser.py ### 修改: COMMAND_MAP 添加赌场指令映射: ```python # 赌场系统 '.赌场': 'casino', '.casino': 'casino', ``` ## 文件4: routers/callback.py ### 修改: async handle_command() 在AI对话系统之后(约第209行)添加: ```python # 赌场系统 if game_type == 'casino': from games.casino import CasinoGame game = CasinoGame() return await game.handle(command, chat_id, user_id) ``` ## 文件5: games/base.py ### 修改: get_help_message() 在积分赠送系统之后添加赌场游戏帮助: ```python ### 🎰 赌场系统 - `.赌场 大小 open <最小> <最大> <赔率>` - 庄家开启大小游戏 - `.赌场 大小 bet <大/小> <金额>` - 下注 - `.赌场 大小 status` - 查看状态 - `.赌场 大小 settle <大/小>` - 庄家结算 ``` ## 实施清单 1. 修改 `core/database.py` 的 `init_tables()` 方法,添加赌场表创建和索引 2. 在 `core/database.py` 中添加 `create_casino_session()` 方法 3. 在 `core/database.py` 中添加 `get_active_casino_session()` 方法 4. 在 `core/database.py` 中添加 `create_casino_bet()` 方法 5. 在 `core/database.py` 中添加 `get_pending_bets()` 方法 6. 在 `core/database.py` 中添加 `settle_casino_bets()` 方法 7. 在 `core/database.py` 中添加 `close_casino_session()` 方法 8. 创建文件 `games/casino.py`,定义 `CasinoGame` 类 9. 在 `games/casino.py` 中实现 `__init__()` 方法 10. 在 `games/casino.py` 中实现 `async handle()` 方法 11. 在 `games/casino.py` 中实现 `async _handle_bigsmall()` 方法 12. 在 `games/casino.py` 中实现 `async _open_bigsmall()` 方法 13. 在 `games/casino.py` 中实现 `async _bet_bigsmall()` 方法 14. 在 `games/casino.py` 中实现 `async _status_bigsmall()` 方法 15. 在 `games/casino.py` 中实现 `async _settle_bigsmall()` 方法 16. 在 `games/casino.py` 中实现 `get_help()` 方法 17. 修改 `utils/parser.py`,在 COMMAND_MAP 中添加赌场指令映射 18. 修改 `routers/callback.py`,在 `handle_command()` 中添加赌场路由 19. 修改 `games/base.py`,在 `get_help_message()` 中添加赌场帮助信息 20. 测试所有功能点,确保无错误 # 当前执行步骤:"2. 详细技术规划完成,等待进入实现阶段" # 任务进度 [2025-10-30_15:16:56] - 已修改:创建任务文件 `.tasks/2025-10-30_1_add_casino_games.md` - 更改:创建任务分支 `task/add_casino_games_2025-01-14_1` 和任务文件 - 原因:按照RIPER-5协议建立工作基础 - 阻碍因素:无 - 状态:成功 [2025-10-30_16:30:00](预估时间) - 已修改:完成详细技术规划 - 更改:设计数据库表结构、游戏流程、抽水机制等细节 - 原因:为实施阶段提供详细技术规范 - 阻碍因素:无 - 状态:成功 [2025-10-30_16:07:57] - 已修改:core/database.py, games/casino.py, utils/parser.py, routers/callback.py, games/base.py - 更改:完成所有实施步骤1-19 - 添加赌场表创建和索引 - 实现6个数据库方法(create_casino_session, get_active_casino_session, create_casino_bet, get_pending_bets, settle_casino_bets, close_casino_session) - 创建完整的CasinoGame类,实现大小游戏所有功能 - 注册指令映射和路由 - 添加帮助信息 - 原因:按照详细实施计划完成全部功能开发 - 阻碍因素:无 - 状态:成功 [2025-10-30_17:20:00](预估时间) - 已修改:games/casino.py - 更改:修改结算逻辑,从庄家指定结果改为系统随机生成 - 移除庄家输入种子/结果的参数 - 使用random.random()生成随机结果(50%大/50%小) - 更新帮助信息,settle命令不再需要参数 - 原因:用户反馈庄家不应该能够操控游戏结果,庄家也是玩家 - 阻碍因素:无 - 状态:成功 [2025-10-30_17:26:19] - 已修改:games/casino.py, games/base.py - 更改:添加庄家放弃游戏功能 - 新增_cancel_bigsmall()方法处理放弃逻辑 - 放弃时返还所有玩家下注 - 关闭会话并标记下注为cancelled - 添加cancel命令支持(cancel/放弃/关闭) - 更新帮助信息和base.py中的帮助 - 原因:用户要求庄家可以放弃本轮游戏 - 阻碍因素:无 - 状态:成功 [2025-10-31_11:35:18] - 已修改:core/database.py - 更改:扩展数据库支持轮盘和21点游戏 - 添加列存在性检查辅助方法(_column_exists, _add_column_if_not_exists) - 扩展casino_sessions表:添加current_phase和blackjack_multiplier字段(兼容性检查) - 扩展casino_bets表:添加bet_category、bet_number、bet_value、hand_status字段(兼容性检查) - 创建casino_blackjack_hands表:存储21点游戏手牌数据 - 修改create_casino_session():支持单场限制检查(get_any_active_casino_session)和新字段 - 扩展create_casino_bet():支持轮盘和21点专用字段参数 - 添加21点手牌管理方法:create_blackjack_hand、get_blackjack_hand、update_blackjack_hand、get_all_blackjack_hands - 原因:为轮盘和21点游戏提供数据库支持,确保字段分离和向后兼容 - 阻碍因素:无 - 状态:成功 [2025-10-31_15:15:08] - 已修改:core/database.py - 更改:修复大小游戏结算时的UNIQUE约束冲突问题 - 移除casino_sessions表的UNIQUE(chat_id, game_type, status)约束 - 原因:status='closed'时需要允许多条历史记录,UNIQUE约束阻止了结算时更新status - 添加兼容性迁移逻辑:检测旧版本表结构,自动重建表以移除UNIQUE约束 - 迁移时复制所有历史数据,处理外键关系(临时禁用/启用外键检查) - 单场限制通过应用层逻辑(get_any_active_casino_session)保证 - 原因:用户测试大小游戏结算时遇到"UNIQUE constraint failed"错误 - 阻碍因素:无 - 状态:成功 [2025-10-31_15:15:08] - 已修改:core/database.py - 更改:修复21点游戏结算逻辑问题 - 修正losers统计逻辑:将条件从`not player_is_busted and player_points != banker_points`改为`player_points != banker_points` - 原因:原条件排除了爆牌玩家,导致爆牌玩家未被统计到losers列表 - 修正数据库更新逻辑:明确区分三种情况 - 赢家:发放奖励并更新数据库 - 平局(player_points == banker_points):已返还下注,更新数据库 - 输家(else分支,包括爆牌和点数小于庄家):更新数据库 - 改进结果字符串显示:包含玩家和庄家的状态信息(爆牌、黑杰克等) - 例如:"庄家19点 vs 玩家爆牌" 或 "庄家19点 vs 玩家20点(黑杰克)" - 原因:用户测试21点游戏时发现3人游戏中只有1个赢家被结算,1个爆牌玩家和1个平局玩家未被结算 - 阻碍因素:无 - 状态:成功 [2025-10-31_17:24:08] - 已修改:games/casino.py - 更改:重构21点游戏指令流程,改为更符合标准的玩法 - 修改_open_blackjack:改为`.赌场 21点 open <底注> <黑杰克倍数>`,移除max_bet参数 - 新增_join_blackjack:添加`.赌场 21点 join`指令,玩家加入游戏时扣除底注,检查积分是否足够 - 修改_bet_blackjack:改为加注功能,仅在playing阶段可用,加注金额必须不低于底注 - 修改_deal_blackjack:实现标准发牌顺序(先玩家1张→庄家明牌→玩家第2张→庄家暗牌),庄家隐藏一张暗牌 - 修改_status_blackjack:游戏阶段隐藏庄家暗牌,只显示明牌,结算后显示完整手牌 - 修改_stand_blackjack:检查所有玩家是否都已完成(停牌或爆牌),如果所有玩家都完成则自动触发结算 - 修改_hit_blackjack:如果爆牌后所有玩家都完成,也自动触发结算 - 更新_get_blackjack_help:反映新的指令流程和规则 - 原因:用户要求新的指令流程:启动(open)→加入(join)→发牌(deal)→操作(hit/stand/bet加注)→自动结算 - 阻碍因素:无 - 状态:成功 [2025-10-31_17:24:08] - 已修改:games/casino.py - 更改:修复停牌和要牌功能中的字典键访问错误 - 修复_hit_blackjack中自动结算检查:将`player_hand['hand_status']`改为`player_hand['status']` - 修复_stand_blackjack中自动结算检查:将`player_hand['hand_status']`改为`player_hand['status']` - 原因:`get_all_blackjack_hands`返回的字典结构为`{user_id: {'cards': [...], 'status': ...}}`,应使用`status`而不是`hand_status` - 原因:用户测试停牌功能时遇到KeyError: 'hand_status'错误 - 阻碍因素:无 - 状态:成功 # 最终审查 待完成