17 KiB
背景
文件名: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架构,已有完善的积分系统和多款游戏(五子棋、成语接龙等)。需要通过模块化设计添加赌场游戏功能。
分析
现有系统分析
- 积分系统:已实现
add_points()和consume_points()方法 - 游戏基类:
BaseGame提供统一的接口 - 路由系统:通过
CommandParser解析指令,在callback.py中路由 - 数据库:SQLite,已有用户表、游戏状态表、统计表
需要新增的内容
-
数据库表:
casino_bets:记录所有下注casino_results:记录游戏结果和结算casino_games:记录游戏房间(可选)
-
游戏模块:
games/casino.py:主赌场模块- 第一期支持:大小游戏
- 第二期计划:轮盘、二十一点等
-
指令映射:
.赌场-> casino 游戏类型- 子指令:
轮盘、大小、21点等
设计要点
- 模块化设计:每种赌场游戏作为独立类
- 下注流程:创建房间 -> 玩家下注 -> 结算 -> 分发奖励
- 安全性:下注前检查积分,结算时原子性操作
- 多玩家支持:以 chat_id 为单位创建游戏房间
提议的解决方案
指令设计
采用 .赌场 <游戏类型> <操作> <参数> 的模块化结构:
大小游戏
- 庄家开启游戏:
.赌场 大小 open <最小下注> <最大下注> <赔率>- 示例:
.赌场 大小 open 10 100 2.0(最小10分,最大100分,赔率2.0倍)
- 示例:
- 玩家下注:
.赌场 大小 bet <大小/小> <下注金额>- 示例:
.赌场 大小 bet 大 50(下注50分压大) - 示例:
.赌场 大小 bet 小 30(下注30分压小)
- 示例:
- 查看状态:
.赌场 大小 status - 庄家结算:
.赌场 大小 settle <结果>- 示例:
.赌场 大小 settle 大(开大) - 示例:
.赌场 大小 settle 小(开小)
- 示例:
轮盘游戏(二期实现)
- 暂不实现,等大小游戏完善后再扩展
21点游戏(二期实现)
- 暂不实现,等大小游戏完善后再扩展
游戏流程
- 庄家开启游戏(指定下注限额和赔率参数)
- 玩家下注(可多人同时参与)
- 庄家确认结算(手动触发结果)
- 系统自动分发奖励
数据库设计
新增表: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:纯数据库表方案(推荐)
优点:
- 数据结构清晰,便于查询统计
- 支持历史记录追踪
- 并发安全,利用数据库事务
- 易于扩展复杂查询
缺点:
- 需要维护额外的表结构
- 稍微复杂一些
决策:采用此方案
方案B:game_states + JSON方案
优点:
- 复用现有系统
- 实现简单
缺点:
- 难以进行复杂统计查询
- JSON解析性能较差
- 数据格式不够规范化
核心实现细节
1. 游戏流程控制
- 开启游戏:检查是否已有活跃游戏,同一chat_id只能有一个进行中的游戏
- 下注限制:检查session状态、下注金额范围、玩家积分
- 结算控制:只有庄家可以结算,结算后自动关闭session
2. 下注流程
- 检查是否有活跃的session
- 检查下注金额是否符合min/max限制
- 检查用户积分是否充足
- 扣除下注金额(consume_points)
- 记录下注到casino_bets表
3. 结算流程
- 验证是否为庄家操作
- 查询所有pending状态的下注
- 计算每个玩家的输赢
- 使用数据库事务确保原子性:
- 更新bets状态
- 发放/扣除积分
- 更新session状态
- 返回结算报告
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 <大/小>` - 庄家结算
实施清单
- 修改
core/database.py的init_tables()方法,添加赌场表创建和索引 - 在
core/database.py中添加create_casino_session()方法 - 在
core/database.py中添加get_active_casino_session()方法 - 在
core/database.py中添加create_casino_bet()方法 - 在
core/database.py中添加get_pending_bets()方法 - 在
core/database.py中添加settle_casino_bets()方法 - 在
core/database.py中添加close_casino_session()方法 - 创建文件
games/casino.py,定义CasinoGame类 - 在
games/casino.py中实现__init__()方法 - 在
games/casino.py中实现async handle()方法 - 在
games/casino.py中实现async _handle_bigsmall()方法 - 在
games/casino.py中实现async _open_bigsmall()方法 - 在
games/casino.py中实现async _bet_bigsmall()方法 - 在
games/casino.py中实现async _status_bigsmall()方法 - 在
games/casino.py中实现async _settle_bigsmall()方法 - 在
games/casino.py中实现get_help()方法 - 修改
utils/parser.py,在 COMMAND_MAP 中添加赌场指令映射 - 修改
routers/callback.py,在handle_command()中添加赌场路由 - 修改
games/base.py,在get_help_message()中添加赌场帮助信息 - 测试所有功能点,确保无错误
当前执行步骤:"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点(黑杰克)"
- 修正losers统计逻辑:将条件从
- 原因:用户测试21点游戏时发现3人游戏中只有1个赢家被结算,1个爆牌玩家和1个平局玩家未被结算
- 阻碍因素:无
- 状态:成功
最终审查
待完成