Skip to content

Latest commit

 

History

History
160 lines (114 loc) · 5.82 KB

File metadata and controls

160 lines (114 loc) · 5.82 KB

Phase 1 展开设计:信息过滤、ELO 结算、客户端联网改造

本文档仅作设计,不修改代码。是 PHASE1_DESIGN.md 的细化和补充。


1. 信息过滤规则(关键安全设计)

现状 GameModel::json 输出的是全量状态(双方舰船的坐标/血量/朝向全都在)。联网后绝不能把全量状态下发客户端,否则就是「全图外挂」。服务端必须按玩家视角过滤。

1.1 过滤原则

  • 己方舰船:全量下发(坐标、血量、朝向、体积)——本人永远可见。
  • 敌方舰船:只下发「已揭示」的格子;未揭示格子一律以 fog 呈现,不给坐标/朝向/血量。
  • 敌方血量:仅当某敌舰全部格子都被揭示时才下发其当前血量(与现有 GUI 的血量圆点显示一致)。可配置为「永不显示敌方精确血量」以增强策略性。
  • 行/列提示:公开(当前规则即双方合计)。
  • 种子 seed:不下发(防止客户端据此反推布局);仅在「结算后回放」中由服务端提供。

1.2 过滤后的状态结构(服务端下发给玩家 P)

{
  "currentPlayer": 0, "winner": -1, "rows": 9, "cols": 9,
  "rowHints": [5,5,4,5,3,4,3,0,1],
  "colHints": [5,5,4,5,3,4,3,0,1],
  "myShips": [
    { "index":0,"size":1,"hp":1,"dir":1,"cells":[[2,8]] },
    { "index":1,"size":2,"hp":2,"dir":2,"cells":[[2,5],[3,5]] }
  ],
  "enemyShips": [                       // 仅含“已揭示”信息
    { "size":5,"hp":5,"cells":[[1,3],[2,3],[3,3]] }  // 部分揭示:cells 只给已揭示格,hp 默认省略
  ],
  "view": [                             // 每格:fog / water / own / enemy
    ["fog","water","own","enemy", ...],
    ...
  ]
}

1.3 服务端过滤实现要点

  • 以 GameModel 的 isRevealed(P, r, c) 和 ship(i).owner == P 为依据逐格/逐船过滤。
  • 建议新增纯函数:QJsonObject buildFilteredState(int player) const(或独立于 GameModel 的 StateFilter 工具类),供服务端调用;json 命令保留为「调试用全量输出」,不进入网络协议。

2. ELO 结算算法

采用标准 Elo,胜负二值(无平局),投降按负处理。

2.1 公式

期望胜率   E_a = 1 / (1 + 10^((R_b - R_a) / 400))
新分数     R_a' = R_a + K * (S_a - E_a)
  其中 S_a = 1(胜)、0(负)

2.2 参数建议

参数 值 说明
初始分 R₀ 1200 新玩家
K 值 32(前 20 局)→ 16(之后) 新号收敛快,老号波动小
投降 S=0 投降方判负

2.3 计算示例

场景 A 分 B 分 结果 变化
均分对战 1200 1200 A 胜 A→1216,B→1184
高打低 1400 1000 A 胜 A→1403,B→997
低打高(爆冷) 1000 1400 A 胜 A→1029,B→1371

2.4 段位映射(可选)

段位 分数区间
青铜 < 1100
白银 1100–1250
黄金 1250–1400
铂金 1400–1550
钻石 ≥ 1550

3. 客户端联网改造方案

目标:不改玩法,把「本地热座」流程替换为「网络会话」流程,保留本地模式用于测试/离线。

3.1 新增模块

类 职责
NetClient WebSocket 连接、鉴权、消息收发、心跳、断线重连
GameSession 客户端会话:持有服务端下发的 state、提交动作、驱动界面
StateSnapshot 解码 §1.2 的过滤状态为可渲染结构(或直接复用 JSON)

3.2 渲染层解耦(关键改动)

当前 BoardWidget 直接查询 GameModel。改造方案二选一:

  • 方案 A(推荐):BoardWidget 改为渲染 StateSnapshot(从服务端 state 解码),彻底与 GameModel 解耦;本地热座模式也统一走 StateSnapshot(由本地 GameModel 生成)。
  • 方案 B(改动小):客户端保留 GameModel 作为「镜像」,收到 state 后重放/重建镜像再渲染。缺点:镜像与权威可能漂移,需频繁全量同步。

3.3 流程改造

现状:菜单 → 玩家1观察 → 玩家2观察 → 轮流行动 → 结束(热座)
改后:菜单 → 登录 → 匹配 → 对局(网络) → 结算 → 回放/再来一局

MainWindow 的阶段状态机把「观察/回合」的本地切换替换为「等待服务端 state」;动作按钮改为向服务端发 action 并等待 action_result。

3.4 兼容与回退

  • 保留 CLI 与本地热座模式(离线、测试、AI 对战)。
  • 网络模式作为「房间模式」并列存在,用同一套 BoardWidget 渲染。

4. 房间生命周期、超时与断线

4.1 状态机

WAITING → MATCHED → PLAYING → FINISHED
   ↑          │(超时/掉线)
   └──────────┘

4.2 规则

事件 处理
回合超时(如 60s) 服务端自动 pass,推进回合
玩家掉线 保留房间(如 120s),期间可 request_state 重连
掉线超时 判负(ELO 按负结算),对手 opponent_left
连续超时 N 次 判负
投降 立即 FINISHED,按负结算

5. 匹配队列(简略)

  • Redis 有序集合,按 ELO 排序;入队写入 score=ELO。
  • 匹配窗口随等待时间放宽(如 0s±50 → 30s±200)。
  • 产出 (userA, userB, seed),交房间服务创建权威 GameModel。

6. 与现有代码的改动点汇总

改动 位置 性质
buildFilteredState(player) 新增(服务端侧) 新增纯函数
StateSnapshot 渲染 boardwidget 改造
NetClient / GameSession 新增 新增
MainWindow 流程 mainwindow 改造(保留本地模式)
json 命令 cli 保留(调试用,不进网络协议)