Files
WPSBot/.tasks/2025-10-30_1_add_casino_games.md

17 KiB
Raw Blame History

背景

文件名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下注记录表

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游戏会话表

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纯数据库表方案推荐

优点

  • 数据结构清晰,便于查询统计
  • 支持历史记录追踪
  • 并发安全,利用数据库事务
  • 易于扩展复杂查询

缺点

  • 需要维护额外的表结构
  • 稍微复杂一些

决策:采用此方案

方案Bgame_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行添加

        # 赌场下注记录表
        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()

函数签名:

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()

函数签名:

def get_active_casino_session(self, chat_id: int, game_type: str) -> Optional[Dict]:

功能:获取活跃的游戏会话

新增函数: create_casino_bet()

函数签名:

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()

函数签名:

def get_pending_bets(self, chat_id: int, game_type: str) -> List[Dict]:

功能:获取待结算的下注列表

新增函数: settle_casino_bets()

函数签名:

def settle_casino_bets(self, chat_id: int, game_type: str, result: str, 
                       banker_id: int) -> Dict:

功能结算所有下注返回结算详情字典winners, losers, total_win等

新增函数: close_casino_session()

函数签名:

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

添加赌场指令映射:

# 赌场系统
'.赌场': 'casino',
'.casino': 'casino',

文件4: routers/callback.py

修改: async handle_command()

在AI对话系统之后约第209行添加

# 赌场系统
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()

在积分赠送系统之后添加赌场游戏帮助:

### 🎰 赌场系统
- `.赌场 大小 open <最小> <最大> <赔率>` - 庄家开启大小游戏
- `.赌场 大小 bet </> <金额>` - 下注
- `.赌场 大小 status` - 查看状态
- `.赌场 大小 settle </>` - 庄家结算

实施清单

  1. 修改 core/database.pyinit_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个平局玩家未被结算
  • 阻碍因素:无
  • 状态:成功

最终审查

待完成