Files
WPSBot/.tasks/2025-10-30_1_add_casino_games.md
2025-10-31 17:26:30 +08:00

532 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 背景
文件名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纯数据库表方案推荐
**优点**
- 数据结构清晰,便于查询统计
- 支持历史记录追踪
- 并发安全,利用数据库事务
- 易于扩展复杂查询
**缺点**
- 需要维护额外的表结构
- 稍微复杂一些
**决策**:采用此方案
### 方案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行添加
```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'错误
- 阻碍因素:无
- 状态:成功
# 最终审查
待完成