若烹小鲜

基于python+html的桂林跑牌

桂林跑牌

一个使用 Flask 编写的桂林跑牌网页游戏。项目包含大厅、多人房间、牌桌界面、
出牌规则、局内轮转、自动过牌、跨小局积分和整局结束结算。

功能概览

  • 创建 4 位房间号,其他玩家可输入房间号加入。
  • 支持 2 到 4 人开局,每名玩家每小局发 13 张牌。
  • 支持单张、对子、三张、四张、四连张牌型。
  • 按桂林跑牌规则实现“有牌可管必须出”,无牌可管时允许过牌。
  • 当前玩家无牌可管时,前端 5 秒后自动过牌,避免牌局卡住。
  • 记录最近出牌历史,并展示每名玩家最近出的牌堆。
  • 每小局结束后结算剩余牌分数,累计达到 100 分结束整局。
  • 房主退出会释放房间;整局结束后房间保留短暂倒计时再释放。

技术栈

  • 后端:Python + Flask
  • 前端:原生 HTML/CSS/JavaScript
  • 存储:单进程内存存储,无数据库依赖
  • 依赖:见 requirements.txt

目录结构

.
├── app/
│ ├── init.py # Flask 应用工厂
│ ├── cardgame/
│ │ ├── routes.py # 页面路由和 JSON API
│ │ ├── store.py # 内存房间、成员、积分和继续投票
│ │ ├── engine.py # 单局牌局状态机
│ │ ├── rules.py # 牌、牌型、大小比较和可出牌枚举
│ │ └── crypto.py # MD4 摘要工具,用于生成短房间号
│ ├── static/
│ │ ├── css/guilin_paopai.css # 大厅、牌桌和响应式样式
│ │ └── js/
│ │ ├── guilin_paopai_lobby.js
│ │ └── guilin_paopai.js
│ └── templates/
│ ├── guilin_paopai_lobby.html
│ └── guilin_paopai.html
├── docs/
│ ├── API.md # 接口说明
│ └── GAME_RULES.md # 详细游戏规则
├── config.py # Flask 配置
├── run.py # 本地开发启动入口
└── requirements.txt

本地运行

建议使用虚拟环境运行:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python run.py

默认监听地址:

http://127.0.0.1:5001/cardgame/guilin-paopai

run.py 使用 host="0.0.0.0",同一局域网设备也可以通过主机 IP 访问:

http://<你的主机IP>:5001/cardgame/guilin-paopai

基本玩法流程

  1. 打开大厅页,点击“建房”创建房间。
  2. 把页面中的 4 位房间号告诉其他玩家。
  3. 其他玩家在大厅输入房间号并加入。
  4. 房主点击“开始”后进入第一小局。
  5. 轮到自己时选择手牌并点击“出牌”;如果无牌可管,可以点击“过牌”。
  6. 某名玩家先出完手牌后,本小局结束并展示结算。
  7. 所有人点击“继续”后开始下一小局。
  8. 任一玩家累计分达到 100 分,整局结束并展示最终积分。

规则摘要

  • 人数:2 到 4 人。
  • 发牌:每人 13 张,剩余牌保留但当前不会摸牌。
  • 首出:每小局随机一名玩家首出。
  • 牌型:单张、对子、三张、四张、四连张。
  • 大小:单张/对子/三张/四张按 3 < 4 < ... < K < A < 2
  • 四连张:最大 AKQJ,最小 432AKA2 不算连续。
  • 管牌:必须同牌型且点数更大;当前实现中四张不跨牌型压制其他牌型。
  • 过牌:首出不能过;有牌可管时必须出,只有无牌可管时可以过。
  • 直接胜利:起手拿到 4 个 2,或起手全小,立即赢得本小局。
  • 小局结束:任一玩家先出完手牌。
  • 计分:胜者本局不加分,其他玩家按剩余牌数量和倍率加分。

完整规则见 docs/GAME_RULES.md

计分方式

剩余张数 倍率
0 到 7 张 x1
8 到 9 张 x2
10 到 12 张 x3
13 张 x4

本局加分:

胜者:0
其他玩家:剩余张数 * 倍率

当前实现把累计分视为剩余牌惩罚分;任一玩家累计达到 100 分时整局结束,界面
保留积分列表供玩家查看最终结果。

API 文档

接口说明见 docs/API.md。常用接口包括:

  • POST /cardgame/api/guilin-paopai/rooms:创建房间
  • POST /cardgame/api/guilin-paopai/rooms//join:加入房间
  • GET /cardgame/api/guilin-paopai/rooms/:轮询房间状态
  • POST /cardgame/api/guilin-paopai/rooms//start:房主开始
  • POST /cardgame/api/guilin-paopai/rooms//play:出牌
  • POST /cardgame/api/guilin-paopai/rooms//pass:过牌
  • POST /cardgame/api/guilin-paopai/rooms//continue:继续下一小局

设计说明

  • rules.py 是纯规则层,不依赖 Flask,也不保存房间状态。
  • engine.py 管一小局内的发牌、出牌、过牌、轮转和胜负。
  • store.py 管多人房间、成员、积分、继续投票和房间释放。
  • routes.py 只做 HTTP 请求解析和错误响应转换。
  • 前端通过 localStorage 保存 playerId,刷新页面后可回到原座位。
  • 房间状态通过轮询同步,默认约 1.6 秒请求一次。

开发注意事项

  • 当前房间和牌局都存放在 Python 进程内存中,服务重启后全部丢失。
  • Flask 开发服务器适合本地调试,不建议直接作为生产服务。
  • 如果要多进程或多实例部署,需要把 _ROOMS_GAMES 和锁替换为共享存储。
  • config.py 中的 SECRET_KEY 是开发值,正式部署应改为环境变量注入。
  • 房主开局当前只要求人数不少于 2 人,不强制所有玩家准备。

基础检查

修改 Python 代码后可以先运行语法检查:

python -m compileall app config.py run.py

目前项目没有自动化测试套件;规则层和引擎层已经拆开,后续可优先补充
rules.pyengine.py 的单元测试。

桂林跑牌规则说明

本文档描述当前代码实现的桂林跑牌规则。不同地区规则可能有差异,以下内容以
本项目代码为准。

人数与发牌

  • 支持 2 到 4 名玩家。
  • 每名玩家每小局发 13 张牌。
  • 2 人或 3 人局会有剩余牌,当前实现中剩余牌不参与摸牌,只在状态中保留数量。
  • 每小局重新洗牌和发牌,积分跨小局累计。

点数大小

单张、对子、三张、四张都使用同一套点数顺序:

3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < J < Q < K < A < 2

花色不参与大小比较,只用于展示和稳定排序。

合法牌型

单张

任意 1 张牌。

示例:

A

对子

2 张相同点数的牌。

示例:

88

三张

3 张相同点数的牌。

示例:

KKK

四张

4 张相同点数的牌。

示例:

2222

当前实现中四张只是一个普通牌型,只能管更小的四张,不会跨牌型压制单张、对子、
三张或四连张。

四连张

4 个不同点数组成的连续牌型。当前实现支持的四连张从大到小为:

AKQJ
KQJ10
QJ109
J1098
10987
9876
8765
7654
6543
5432
432A

注意:

  • AKQJ 最大。
  • 432A 最小。
  • KA2 不算连续。
  • 四连张之间只比较上表顺序,不按普通扑克牌顺子规则推导。

出牌与管牌

  • 每小局随机一个玩家首出。
  • 本轮首出时可以选择任意合法牌型。
  • 后续玩家必须出相同牌型,并且大小严格大于桌面当前牌。
  • 如果没有牌能管,可以过牌。
  • 如果有牌能管,必须出牌,不能主动过牌。
  • 当其他玩家都无法管住当前牌时,本轮结束,最后成功出牌的玩家获得下一轮首出权。

起手直接胜利

发牌后会先检查两个直接胜利条件:

  • 玩家起手拿到 4 个 2
  • 玩家起手全小,即手牌中没有 2AKQJ

如果满足条件,本小局立即结束并进入计分。若多人同时满足,当前实现按座位顺序
先检查到的玩家获胜。

小局结束

任意玩家出完最后一张手牌后,本小局结束。界面会展示:

  • 本局胜者。
  • 每名玩家剩余张数。
  • 每名玩家倍率。
  • 本局加分。
  • 累计总分。

计分

胜者本局加 0 分。其他玩家按剩余牌数量计算惩罚分:

本局加分 = 剩余张数 * 倍率

倍率规则:

剩余张数 倍率
0 到 7 张 x1
8 到 9 张 x2
10 到 12 张 x3
13 张 x4

任一玩家累计达到 100 分时整局结束。当前实现没有额外的排名算法,最终以积分板
展示的累计分作为结果参考;分数来自剩余牌惩罚,通常越低越好。

房间规则

  • 房间号为 4 位十六进制字符。
  • 房主创建房间后自动进入等待区。
  • 玩家加入房间时会自动获得昵称。
  • 同一浏览器刷新页面会复用本地 playerId,不会重复占座。
  • 房主退出会释放房间。
  • 普通玩家只能在等待区退出;牌局开始后暂不支持中途退出。
  • 每小局结束后,所有玩家都点击“继续”才会开始下一小局。
  • 整局结束后房间会保留短暂倒计时,方便查看结算,然后释放。

前端交互规则

  • “提示”会选择后端返回的第一手可出牌。
  • “出牌”按钮只在轮到自己且已选择手牌时可用。
  • “过牌”按钮只在轮到自己且无牌可管时可用。
  • 无牌可管时,前端会提示并在 5 秒后自动过牌。
  • 手牌和桌面状态通过轮询同步,其他玩家出牌后会自动刷新界面。