dou-cl 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/PUBLISH.md +322 -0
- package/README.md +229 -2
- package/bin/dou-cl.js +215 -0
- package/package.json +61 -4
- package/src/app.js +356 -0
- package/src/bot.js +263 -0
- package/src/color.js +26 -0
- package/src/core/cards.js +152 -0
- package/src/core/combo.js +569 -0
- package/src/core/rules.js +92 -0
- package/src/exercise.js +682 -0
- package/src/games/base.js +280 -0
- package/src/games/doudizhu.js +146 -0
- package/src/games/fivek.js +77 -0
- package/src/games/index.js +25 -0
- package/src/games/paodekuai.js +48 -0
- package/src/net/client.js +262 -0
- package/src/net/server.js +432 -0
- package/src/runner.js +61 -0
- package/src/skins/diff.js +147 -0
- package/src/skins/hex.js +174 -0
- package/src/skins/index.js +116 -0
- package/src/skins/json.js +151 -0
- package/src/skins/log.js +111 -0
- package/src/skins/packet.js +144 -0
- package/src/ui.js +395 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 iguangyu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/PUBLISH.md
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# 发布到 npm —— 让别人的机器 `npx dou-cl` 直接开玩
|
|
2
|
+
|
|
3
|
+
> 本文只讲怎么把这个包发上 npm。玩法说明看 [README.md](README.md)。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 0. 先说名字:`dou-cli` 已经被占了
|
|
8
|
+
|
|
9
|
+
你要的 `npx dou-cli` 用不了,因为 npm 上已经有这个包:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
dou-cli@1.0.12
|
|
13
|
+
author: Andoni Arbulu Lozano <andoni.arbulu@digitalonus.com>
|
|
14
|
+
maintainers: andoni-arb, richardhern
|
|
15
|
+
last modified: 2022-04-29
|
|
16
|
+
description: A simple cli for creating and setting up projects ... [Digital On Us]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
是家美国公司 2022 年发的脚手架工具,虽然早就没人维护了,但包名还是人家的。
|
|
20
|
+
npm 没有"抢名字"的正规途径(除非走商标争议,不值当)。
|
|
21
|
+
|
|
22
|
+
**所以用 `dou-cl`**,这名字是空的,而且正好是这个仓库的名字,一样短:
|
|
23
|
+
|
|
24
|
+
| 方案 | 别人怎么运行 | 说明 |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| **`dou-cl`(推荐)** | `npx dou-cl` | 名字已确认空闲,和仓库同名 |
|
|
27
|
+
| 带 scope | `npx @iguangyu/dou-cli` | 一定能用,但每次要多敲一截 |
|
|
28
|
+
| `doucli` | `npx doucli` | 也空闲,但少个连字符看着怪 |
|
|
29
|
+
|
|
30
|
+
`package.json` 里现在就是 `"name": "dou-cl"`,`bin` 里也有 `dou-cl`,所以发上去就是 `npx dou-cl`。
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 1. 一次性准备
|
|
35
|
+
|
|
36
|
+
### 1.1 注册 npm 账号
|
|
37
|
+
|
|
38
|
+
没有账号就去 <https://www.npmjs.com/signup> 注册。注册完**去邮箱点验证链接**,
|
|
39
|
+
没验证的账号发布时会报 `E403`。
|
|
40
|
+
|
|
41
|
+
### 1.2 登录(注意要指向官方源)
|
|
42
|
+
|
|
43
|
+
你这台机器默认 registry 是淘宝镜像,所以**登录必须显式指定官方源**:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm login --registry=https://registry.npmjs.org/
|
|
47
|
+
# Username / Password / Email / 一次性验证码(如果开了 2FA)
|
|
48
|
+
npm whoami --registry=https://registry.npmjs.org/ # 能打印出用户名就成功了
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> token 会写进 `~/.npmrc`,并且**只对 npmjs 生效** —— 你平时 `npm i` 还是走淘宝镜像,不受影响。
|
|
52
|
+
|
|
53
|
+
**发布目标已经在 `package.json` 里锁死了**,不用你每次加参数:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
"publishConfig": { "registry": "https://registry.npmjs.org/", "access": "public" }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 2. 发布(两条命令)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cd C:\Users\lgy\WorkBuddy\Dou_CL
|
|
65
|
+
|
|
66
|
+
npm publish --dry-run # 先干跑一遍:列文件、算体积,不会真发
|
|
67
|
+
npm publish # 真发
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`--dry-run` 应该看到这样的输出(已经验证过):
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
npm notice name: dou-cl
|
|
74
|
+
npm notice version: 1.0.0
|
|
75
|
+
npm notice package size: 46.3 kB
|
|
76
|
+
npm notice unpacked size: 150.3 kB
|
|
77
|
+
npm notice total files: 25
|
|
78
|
+
npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access (dry-run)
|
|
79
|
+
+ dou-cl@1.0.0
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
注意 `Publishing to https://registry.npmjs.org/` 这行 —— 说明 `publishConfig` 生效了,
|
|
83
|
+
没跑到淘宝镜像上去。
|
|
84
|
+
|
|
85
|
+
真发的时候会自动先跑测试(`prepublishOnly` → `npm test`,97 条断言)。
|
|
86
|
+
**测试挂了就不会发出去**,这是故意的。
|
|
87
|
+
|
|
88
|
+
### 2.1 账号开了 2FA 的话(大概率会撞上)
|
|
89
|
+
|
|
90
|
+
现在 npm 强制 2FA,很多人都开了。症状是发布直接 403:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
npm error code E403
|
|
94
|
+
npm error 403 403 Forbidden - PUT https://registry.npmjs.org/dou-cl - Two-factor
|
|
95
|
+
authentication or granular access token with bypass 2fa enabled is required to publish packages.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
意思是:**你手上这个 token 没有「Bypass 2FA」权限**。
|
|
99
|
+
|
|
100
|
+
> 顺带一个坑:2025 年 11 月起 npm 只支持 **Granular access token**,老的 Legacy token 已经下线。
|
|
101
|
+
> Granular token 长这样:`npm_` + 36 位(共 40 字符)。
|
|
102
|
+
> 如果你早先建的 token 没勾 Bypass 2FA(**该选项默认不勾**),就会正好撞上这个 403。
|
|
103
|
+
|
|
104
|
+
两条路,任选:
|
|
105
|
+
|
|
106
|
+
#### 路 A:发布时补一次性验证码(最快)
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm publish --otp=123456
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
验证码来自你绑定 2FA 的验证器 App,**30 秒失效**,过期就重新跑一次。
|
|
113
|
+
缺点:每次发布都要掏手机。
|
|
114
|
+
|
|
115
|
+
> 如果你的 2FA 是 **passkey / 安全密钥**(不是 6 位数字),那没码可输,只能走路 B。
|
|
116
|
+
|
|
117
|
+
#### 路 B:重建一个勾了 Bypass 2FA 的 token(一劳永逸)
|
|
118
|
+
|
|
119
|
+
1. 打开 <https://www.npmjs.com/settings/~/tokens> → **Generate New Token → Granular Access Token**
|
|
120
|
+
2. 逐项这么选(**这四项选错任何一个都会继续 403**):
|
|
121
|
+
|
|
122
|
+
| 项目 | 选什么 | 为什么 |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| Expiration | 自定,比如 90 天 | 到期要重建,记得加提醒 |
|
|
125
|
+
| Packages and scopes | **All packages** | 第一次发新包时 `dou-cl` 还不存在,列表里根本勾不到它 |
|
|
126
|
+
| Permission | **Read and write** → 再选 **Publish and stage** | 选成 *Stage only* 会报 `E_STAGE_REQUIRED`,那是另一套审核流程 |
|
|
127
|
+
| **Bypass 2FA** | **☑ 必须勾上** | 就是这次 403 的根因 |
|
|
128
|
+
|
|
129
|
+
> 「Bypass 2FA」这个勾选框**只在 token 有写权限时才出现**,默认是**不勾**的。
|
|
130
|
+
> 它一旦勾上,优先级高于账号级/包级的 2FA 设置 —— 也就是说这个 token 能做发布这类"自动化动作"而不用再掏验证码。
|
|
131
|
+
> 但注意:**账号安全类操作(改密码、改 2FA、管 token、加维护者)永远要交互式 2FA**,token 绕不过去。
|
|
132
|
+
|
|
133
|
+
3. 复制 token(**只显示这一次**),替换掉本地那个:
|
|
134
|
+
|
|
135
|
+
直接编辑 `C:\Users\lgy\.npmrc`,把这一行
|
|
136
|
+
```
|
|
137
|
+
//registry.npmjs.org/:_authToken=npm_旧token
|
|
138
|
+
```
|
|
139
|
+
换成新的。或者:
|
|
140
|
+
```bash
|
|
141
|
+
npm config set //registry.npmjs.org/:_authToken npm_新的token
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
4. 验证一下换成功了、然后发:
|
|
145
|
+
```bash
|
|
146
|
+
npm whoami --registry=https://registry.npmjs.org/
|
|
147
|
+
npm publish
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> ⚠️ token 是密码级别的凭证:只放在 `~/.npmrc`,**别写进项目里的任何文件**,别提交,别贴聊天里。
|
|
151
|
+
> 泄露了就回上面的页面 Revoke 掉重建。
|
|
152
|
+
|
|
153
|
+
#### 路 C:将来换 trusted publishing(推荐,但要 CI)
|
|
154
|
+
|
|
155
|
+
npm 已经公告:**Bypass 2FA token 的"直接发布"能力会在 2027 年 1 月移除**。
|
|
156
|
+
长期方案是 trusted publishing(OIDC),**完全不需要 token**,也就没有 2FA 这档事 —— 见 §7。
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 3. 别人怎么玩
|
|
161
|
+
|
|
162
|
+
发完之后,**任何装了 Node 16+ 的机器**,一行就够:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npx dou-cl
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
第一次会下载(46 kB,加上 `ws` 依赖也就 100 KB 出头),之后走 npx 缓存。
|
|
169
|
+
|
|
170
|
+
常用几种:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
npx dou-cl -g doudizhu # 跳过菜单,直接开一局(最隐蔽)
|
|
174
|
+
npx dou-cl -g fivek # 510K
|
|
175
|
+
npx dou-cl --skin hex # 换个伪装皮肤
|
|
176
|
+
npx dou-cl --serve --port 8080 # 当服务器,别人连你
|
|
177
|
+
npx dou-cl --join 192.168.1.10:8080 --code ABCD
|
|
178
|
+
npx dou-cl --match doudizhu --server 1.2.3.4:8080
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### ⚠️ 刚发完可能拉不到
|
|
182
|
+
|
|
183
|
+
如果对方的 npm 也配了淘宝镜像(npmmirror),**镜像同步有几分钟到几十分钟延迟**,
|
|
184
|
+
刚发布立刻 `npx dou-cl` 可能 404。两个办法:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# 立刻能用的写法,绕开镜像
|
|
188
|
+
npx --registry=https://registry.npmjs.org/ dou-cl
|
|
189
|
+
|
|
190
|
+
# 或者直接等几分钟再试
|
|
191
|
+
npx dou-cl
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 4. 以后更新版本
|
|
197
|
+
|
|
198
|
+
改完代码,**必须升版本号**再发,否则 npm 会报 `EPUBLISHCONFLICT` / `E403`:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npm version patch # 1.0.0 → 1.0.1 修 bug
|
|
202
|
+
npm version minor # 1.0.0 → 1.1.0 加功能
|
|
203
|
+
npm version major # 1.0.0 → 2.0.0 不兼容改动
|
|
204
|
+
|
|
205
|
+
npm publish
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`npm version` 会自动改 `package.json`、打一个 git tag(`v1.0.1`)、并提交。
|
|
209
|
+
顺手把 tag 推上去,GitHub 上就能看到版本:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
git push --follow-tags
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 5. 常见错误对照
|
|
218
|
+
|
|
219
|
+
| 报错 | 原因 / 怎么办 |
|
|
220
|
+
| --- | --- |
|
|
221
|
+
| `E403 ... you must be logged in` | 没登录。`npm login --registry=https://registry.npmjs.org/` |
|
|
222
|
+
| `E403 ... verify your email` | 账号邮箱没验证,去邮箱点链接 |
|
|
223
|
+
| `E403 ... Two-factor authentication or granular access token with bypass 2fa enabled is required` | **token 没勾 Bypass 2FA** —— 最常撞的一个。`npm publish --otp=123456`,或按 §2.1 重建 token |
|
|
224
|
+
| `E403 You do not have permission to publish "dou-cli"` | 名字被占用(就是本文第 0 节那事)。换名或改 `package.json` 的 `name` |
|
|
225
|
+
| `E403 You do not have permission to publish "dou-cl"` | token 的 Packages and scopes 没勾「All packages」,勾不到还不存在的包 |
|
|
226
|
+
| `E_STAGE_REQUIRED` | token 建成了 *Stage only*,重建时改选 **Read and write → Publish and stage** |
|
|
227
|
+
| `EPUBLISHCONFLICT` / `cannot publish over the previously published versions` | 版本号没升。`npm version patch` |
|
|
228
|
+
| `EOTP` | 开了 2FA,命令会提示你输一次性验证码 |
|
|
229
|
+
| `npm notice Publishing to https://registry.npmmirror.com` | 说明 `publishConfig` 被改掉了,**千万别发到镜像**,改回来 |
|
|
230
|
+
| 发布成功但 `npx dou-cl` 找不到 | 镜像同步延迟,见第 3 节 |
|
|
231
|
+
| `402 Payment Required` | 想发到别人的 scope 下,或 scope 未授权 |
|
|
232
|
+
|
|
233
|
+
### 发错了想撤
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
npm unpublish dou-cl@1.0.0 --force
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**限制很严**:只有发布后 **72 小时内**、且没有别的包依赖它、且不是被大量下载过的版本才能撤。
|
|
240
|
+
所以发布前务必先用 `--dry-run` 看一眼文件列表。
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 6. 发布前检查清单
|
|
245
|
+
|
|
246
|
+
- [ ] `npm whoami --registry=https://registry.npmjs.org/` 能打印用户名
|
|
247
|
+
- [ ] `npm test` 全绿(97 条断言)
|
|
248
|
+
- [ ] `npm publish --dry-run` 输出的 `Publishing to` 是 **registry.npmjs.org**
|
|
249
|
+
- [ ] `--dry-run` 的文件列表里**没有** `test/`、`预览.html`、`.workbuddy/`、`node_modules/`
|
|
250
|
+
(`package.json` 的 `files` 白名单控制,现在只有 `bin` / `src` / `README.md` / `PUBLISH.md` / `LICENSE`)
|
|
251
|
+
- [ ] **token 勾了 Bypass 2FA,或者你准备好输 `--otp=`**(见 §2.1,这是最容易卡住的一步)
|
|
252
|
+
- [ ] 升过版本号了(重复发同一个版本会被拒)
|
|
253
|
+
- [ ] `git status` 干净、改动的代码都提交了
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 7. 可选:推 tag 自动发布(GitHub Actions + trusted publishing)
|
|
258
|
+
|
|
259
|
+
npm 已经公告 **2027 年 1 月会移除 bypass-2FA token 的"直接发布"能力**,
|
|
260
|
+
所以 CI 这条路现在推荐走 **trusted publishing(OIDC)**:**完全不用 token**,
|
|
261
|
+
也就没有 2FA 那档子事,而且顺带拿到 provenance 签名。
|
|
262
|
+
|
|
263
|
+
在仓库里建 `.github/workflows/publish.yml`:
|
|
264
|
+
|
|
265
|
+
```yaml
|
|
266
|
+
name: publish
|
|
267
|
+
on:
|
|
268
|
+
push:
|
|
269
|
+
tags: ['v*'] # 只有推 v1.2.3 这种 tag 才触发,平时 push 不会跑
|
|
270
|
+
jobs:
|
|
271
|
+
npm:
|
|
272
|
+
runs-on: ubuntu-latest
|
|
273
|
+
permissions:
|
|
274
|
+
contents: read
|
|
275
|
+
id-token: write # trusted publishing / provenance 都要这个
|
|
276
|
+
steps:
|
|
277
|
+
- uses: actions/checkout@v4
|
|
278
|
+
- uses: actions/setup-node@v4
|
|
279
|
+
with:
|
|
280
|
+
node-version: 20
|
|
281
|
+
registry-url: https://registry.npmjs.org/
|
|
282
|
+
- run: npm ci
|
|
283
|
+
- run: npm publish --provenance --access public
|
|
284
|
+
# 注意:这里没有 NODE_AUTH_TOKEN,靠 OIDC 换临时凭证
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
用之前要到 npm 上把仓库挂上去:
|
|
288
|
+
|
|
289
|
+
1. 打开 <https://www.npmjs.com/package/dou-cl/access>(包发出来之后才有这一页)
|
|
290
|
+
→ **Trusted Publisher** → 选 **GitHub Actions**
|
|
291
|
+
2. 填:Organization/user = `iguangyu`,Repository = `dou-cl`,
|
|
292
|
+
Workflow filename = `publish.yml`(**只写文件名,不带路径**),Environment 留空
|
|
293
|
+
3. 保存。之后 `npm version patch && git push --follow-tags`,tag 一推就自动发。
|
|
294
|
+
|
|
295
|
+
> 这套的前提是包**已经存在**(第一次还是得用 `npm publish` 手动发一次)。
|
|
296
|
+
> 所以顺序是:先按 §2 手动发布成功 → 再去配 trusted publisher。
|
|
297
|
+
|
|
298
|
+
> 注意:`publish.yml` 我**没有**替你建。因为没配 trusted publisher 之前,
|
|
299
|
+
> 一旦有 tag 推上去 CI 就会红。你要用的话自己建,或者让我建。
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## 8. 附:这个包现在的发布配置
|
|
304
|
+
|
|
305
|
+
```jsonc
|
|
306
|
+
{
|
|
307
|
+
"name": "dou-cl", // 发布后的包名 → npx dou-cl
|
|
308
|
+
"version": "1.0.0",
|
|
309
|
+
"bin": {
|
|
310
|
+
"dou-cl": "bin/dou-cl.js", // npx 找的就是这个
|
|
311
|
+
"doucl": "bin/dou-cl.js" // 顺手给的短别名(本地安装可用)
|
|
312
|
+
},
|
|
313
|
+
"files": ["bin", "src", "README.md", "PUBLISH.md", "LICENSE"], // 只发这些
|
|
314
|
+
"engines": { "node": ">=16" },
|
|
315
|
+
"dependencies": { "ws": "^8.18.0" }, // 联网用的,就这一个
|
|
316
|
+
"scripts": { "prepublishOnly": "npm test" }, // 发布前自动跑测试
|
|
317
|
+
"publishConfig": { "registry": "https://registry.npmjs.org/", "access": "public" }
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
`ws` 这个依赖已经在 tarball 里装好验证过了:本地 `npm i dou-cl-1.0.0.tgz`
|
|
322
|
+
之后 `npx ../dou-cl-1.0.0.tgz` 能正常起菜单、开对局、出牌,所以发出去别人也一样能跑。
|
package/README.md
CHANGED
|
@@ -1,3 +1,230 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dou-cl 🃏
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
命令行牌桌 —— **斗地主 / 跑得快 / 510K**,纯终端操作,单机可玩,也能联网对战。
|
|
4
|
+
|
|
5
|
+
**摸鱼模式**:整个牌桌伪装成看起来正常的开发输出(日志流 / xxd 转储 / JSON 帧 / git diff),
|
|
6
|
+
信息一条不删,只是换了层壳 —— 工位上扫一眼屏幕的人只会觉得你在看构建日志。
|
|
7
|
+
菜单上只有两件事要选:**玩什么** 和 **单机还是联机**。
|
|
8
|
+
详见 [三、牌桌藏在开发输出里](#三牌桌藏在开发输出里默认)。
|
|
9
|
+
|
|
10
|
+
牌面只用一个字符表示点数,不搞花哨的 ASCII 画图:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
点数: 3 4 5 6 7 8 9 0(=10) J Q K A 2 w=小王 W=大王
|
|
14
|
+
花色: ♠ ♥ ♣ ♦ (--ascii 就会变成 S H C D)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 一、跑起来
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 进菜单:只选玩法和是否联机
|
|
23
|
+
npx dou-cl
|
|
24
|
+
|
|
25
|
+
# 跳过菜单直接开一局(最隐蔽,屏幕上不会闪出任何“游戏”字样)
|
|
26
|
+
npx dou-cl -g doudizhu # 斗地主
|
|
27
|
+
npx dou-cl -g paodekuai # 跑得快
|
|
28
|
+
npx dou-cl -g fivek # 510K
|
|
29
|
+
|
|
30
|
+
# 本地源码方式(没发布 npm 也照样用)
|
|
31
|
+
node bin/dou-cl.js
|
|
32
|
+
npm link # 之后直接敲 dou-cl
|
|
33
|
+
npx . # 在项目目录里用 npx 跑
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> 从别人那里拿到这个目录时,先 `npm install`(只有 `ws` 一个依赖,联网用;不装也能单机玩)。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 二、操作
|
|
41
|
+
|
|
42
|
+
| 操作 | 写法 |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| 出牌 | 直接敲点数:`34567` 或 `3 4 5 6 7` |
|
|
45
|
+
| 指定花色 | 点数后面接花色:`5s 5h 5c`、`10h`、`Wh` |
|
|
46
|
+
| 不要 | `p` 或 `pass` |
|
|
47
|
+
| 提示 | `h` 或 `hint` |
|
|
48
|
+
| 看真身 | `reveal`(临时打印一次原生牌桌) |
|
|
49
|
+
| 换皮肤 | `skin log` / `skin hex` / `skin json` / `skin diff` / `skin term` |
|
|
50
|
+
| 重置画面 | `redraw` |
|
|
51
|
+
| 帮助 | `?` |
|
|
52
|
+
| 退出 | `quit` |
|
|
53
|
+
|
|
54
|
+
几个小细节:
|
|
55
|
+
|
|
56
|
+
- 想要同花顺或五十K 时,**不用管花色**,直接敲 `56789` / `50K`,程序会自动挑同一花色那组。
|
|
57
|
+
- 花色字母:`s`=♠ `h`=♥ `c`=♣ `d`=♦。
|
|
58
|
+
- 电脑出牌有约 0.7 秒间隔,用 `--speed 0` 可以飞快跳过。
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 三、牌桌藏在开发输出里(默认)
|
|
63
|
+
|
|
64
|
+
给工位上玩的人准备的:**牌局信息一条都不删,只换一层壳**。
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx dou-cl # 默认皮肤就是 log(日志流)
|
|
68
|
+
npx dou-cl --skin hex # 换皮肤: log / hex / json / diff
|
|
69
|
+
npx dou-cl --skin term # 关掉伪装,显示原生牌桌
|
|
70
|
+
npx dou-cl --skins # 列皮肤
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
游戏里敲 `skin <名>` 可以随时换,`reveal` 可以临时看一眼真身。
|
|
74
|
+
|
|
75
|
+
### 四种壳
|
|
76
|
+
|
|
77
|
+
| 皮肤 | 长什么样 | 自洽性 |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `log`(默认) | 服务端结构化日志(`tail -f` 的感觉),牌就是日志字段 | 时间戳单调递增,混了无关噪声行 |
|
|
80
|
+
| `hex` | `xxd -c 24` 十六进制转储,右边 ASCII 列就是牌 | **左边 hex 与右边字符严格逐字节对应** |
|
|
81
|
+
| `json` | WS 帧 + jq 风格配色 | **去掉颜色就是合法 JSON,能直接 `JSON.parse`** |
|
|
82
|
+
| `diff` | YAML 的统一 diff,绿色 `+` 行是当前牌局 | **是真 diff,能 apply 回去得到当前状态** |
|
|
83
|
+
|
|
84
|
+
关于「信息没丢」这件事 —— 四种壳里都有:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
你的手牌 hand[] / "hand": [...] / hand: [...]
|
|
88
|
+
各家剩几张 peers=[mei:12 fei:8] / "peers": [{ "hold": 12 }]
|
|
89
|
+
谁出的什么 history 里的 play/pass 行
|
|
90
|
+
场上最大 table=mei->[5s 5h 5d 6c] trio1
|
|
91
|
+
轮到谁 turn=self
|
|
92
|
+
底牌/分数 bottom= [...] base= / 结果在 result= 行
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
其它细节:
|
|
96
|
+
|
|
97
|
+
- **不清屏**,像真的日志流一样往下追加(`--skin term` 才清屏)。
|
|
98
|
+
- 输出**纯 ASCII**,不出现中文,`hex` 的 ASCII 列也不会变成一串 `.`。
|
|
99
|
+
- 玩家名会 ASCII 化(你 → `self`,小美 → `mei`),角色是 `landlord` / `farmer`。
|
|
100
|
+
- 输入提示符也是伪装的(`dev@ws:~/svc$ `、`node > `、`[tail -f] $ `),你敲的 `34567` 看起来就是条命令。
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 四、联网
|
|
105
|
+
|
|
106
|
+
三种玩法都支持联网。房主开服务器,其他人连进来;人数不够的位置由电脑补上。
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# 1. 一个人当服务器(局域网内直连)
|
|
110
|
+
npx dou-cl --serve --port 8080
|
|
111
|
+
|
|
112
|
+
# 2. 开房间(连到自己或别人的服务器)
|
|
113
|
+
npx dou-cl --create doudizhu --server 192.168.1.10:8080
|
|
114
|
+
|
|
115
|
+
# 3. 别人加进来
|
|
116
|
+
npx dou-cl --join 192.168.1.10:8080 --code ABCD
|
|
117
|
+
|
|
118
|
+
# 或者快速匹配(服务器自动凑桌)
|
|
119
|
+
npx dou-cl --match doudizhu --server 192.168.1.10:8080
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
参数说明:
|
|
123
|
+
|
|
124
|
+
| 参数 | 作用 |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `--serve [--port N]` | 在本机起服务器,默认端口 8080 |
|
|
127
|
+
| `--create <玩法>` | 连服务器并创建一个房间(会打印 4 位房间号) |
|
|
128
|
+
| `--join <地址> [--code XXXX]` | 加入房间 |
|
|
129
|
+
| `--match <玩法>` | 快速匹配 |
|
|
130
|
+
| `--server <地址>` | 服务器地址,也可用环境变量 `DOUCL_SERVER` |
|
|
131
|
+
| `--name <昵称>` | 显示的名字 |
|
|
132
|
+
| `--skin <名>` | 换伪装皮肤,`term` = 原生牌桌(也可用 `DOUCL_SKIN`) |
|
|
133
|
+
|
|
134
|
+
**局域网**:直接 `--serve`,把本机 IP(`ipconfig` 里那个 192.168.x.x)告诉朋友即可。
|
|
135
|
+
|
|
136
|
+
**公网**:把服务器放到有公网 IP 的机器 / 云主机上跑 `npx dou-cl --serve`,大家用 `--server <公网IP>:8080`。服务端是**权威端**,所有牌局逻辑在服务器上跑,客户端只收自己的手牌,改前端看不到别人的牌。
|
|
137
|
+
|
|
138
|
+
几个实现上的说明:
|
|
139
|
+
|
|
140
|
+
- 房间 4 位房间号不会重复,房间空了自动回收。
|
|
141
|
+
- 中途断线会被电脑托管,不会卡住别人。
|
|
142
|
+
- 轮到你了不动手,90 秒后自动帮你出一张(可在代码里改 `turnTimeout`)。
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 五、玩法
|
|
147
|
+
|
|
148
|
+
### 斗地主
|
|
149
|
+
- 3 人,每人 17 张,留 3 张底牌。
|
|
150
|
+
- 依次叫分 1/2/3 或不叫,最高分当地主并拿走底牌;三家都不叫则流局重发。
|
|
151
|
+
- 牌型:单张、对子、三张、三带一、三带二、顺子(5 张起)、连对(3 对起)、飞机(带单/带对)、四带二、四带两对、炸弹、王炸。
|
|
152
|
+
- 胜负:地主先出完 → 地主赢;任一农民先出完 → 农民赢。
|
|
153
|
+
- 计分:底分 × 倍数,每个炸弹/王炸 ×2,春天 ×2。
|
|
154
|
+
|
|
155
|
+
### 跑得快
|
|
156
|
+
- 3 人,一副牌**去掉大小王**共 52 张,每人 17 张,剩 1 张废弃。
|
|
157
|
+
- 首手:持有 ♠3 的玩家先出,且第一手必须带上 ♠3。
|
|
158
|
+
- 牌型:和斗地主一样,但**没有王炸**,连对 2 对即可。
|
|
159
|
+
- 胜负:谁先出完谁赢,其他人按剩余张数扣分。
|
|
160
|
+
|
|
161
|
+
### 510K
|
|
162
|
+
- 4 人,**两副牌**共 108 张,每人 27 张,没有底牌。
|
|
163
|
+
- 牌型:单张、对子、三张、顺子(5 张起)、连对(2 对起)、飞机、**同花顺**、**五十K**、炸弹(4 张及以上同点)、王炸。
|
|
164
|
+
- **五十K**(同花色的 5·10·K 各一张)是通吃牌型:除炸弹和王炸外,**可以压任何牌型**;同花五十K 之间比花色(♠ > ♥ > ♣ > ♦)。
|
|
165
|
+
- **同花顺**可以压同长度的普通顺子。
|
|
166
|
+
- 计分:5 = 5 分,10 = 10 分,K = 10 分,两副共 200 分。**每一轮结束时,本轮桌面上的分牌全部归本轮最后出牌的人。**
|
|
167
|
+
- 胜负:谁先出完手牌谁赢,分数作为副榜。
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 六、目录结构
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
bin/dou-cl.js 命令行入口(参数解析)
|
|
175
|
+
src/core/cards.js 牌模型 / 发牌 / 排序
|
|
176
|
+
src/core/combo.js 牌型识别、比大小、候选生成
|
|
177
|
+
src/core/rules.js 三种玩法的规则开关
|
|
178
|
+
src/games/base.js 通用回合引擎(校验与执行分离)
|
|
179
|
+
src/games/doudizhu.js 斗地主(叫分 / 春天 / 计分)
|
|
180
|
+
src/games/paodekuai.js 跑得快(♠3 先手)
|
|
181
|
+
src/games/fivek.js 510K(收分 / 五十K)
|
|
182
|
+
src/bot.js 电脑 AI
|
|
183
|
+
src/ui.js 渲染 + 输入解析
|
|
184
|
+
src/color.js 颜色(原生与伪装皮肤共用)
|
|
185
|
+
src/runner.js 单局循环
|
|
186
|
+
src/skins/packet.js 把视野压成与渲染方式无关的状态包
|
|
187
|
+
src/skins/log.js 伪装皮肤:日志流
|
|
188
|
+
src/skins/hex.js 伪装皮肤:xxd 十六进制转储
|
|
189
|
+
src/skins/json.js 伪装皮肤:JSON 帧
|
|
190
|
+
src/skins/diff.js 伪装皮肤:git diff
|
|
191
|
+
src/skins/index.js 皮肤注册表 + 会话状态
|
|
192
|
+
src/net/server.js 房间服务器(权威端)
|
|
193
|
+
src/net/client.js 联网客户端
|
|
194
|
+
test/sim.js 电脑互打自测
|
|
195
|
+
test/smoke.js 渲染 / 输入解析 / 联网全流程
|
|
196
|
+
test/local-test.js 本地视角回归(人类手牌不能被看错)
|
|
197
|
+
test/skins-test.js 伪装皮肤自洽性(hex 对字节 / JSON 可解析 / diff 可 apply / 纯 ASCII)
|
|
198
|
+
test/make-preview.js 生成 预览.html
|
|
199
|
+
test/show.js 单看某个皮肤的效果: node test/show.js fivek hex 8
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
自测:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
node test/sim.js 300 # 每种玩法各打 300 局电脑互打,校验规则与牌数守恒
|
|
206
|
+
node test/smoke.js # 渲染 / 输入解析 / 服务器+客户端联网打完整一局
|
|
207
|
+
node test/local-test.js # 本地对局:渲染视角必须固定在人类座位上
|
|
208
|
+
node test/skins-test.js # 伪装皮肤:hex 逐字节、JSON 可解析、diff 可 apply、输出纯 ASCII
|
|
209
|
+
npm test # 以上全部
|
|
210
|
+
npm run preview # 生成 预览.html,先看看跑起来长什么样
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 七、加新玩法 / 加新皮肤
|
|
216
|
+
|
|
217
|
+
**加玩法**:在 `src/core/rules.js` 里加一份规则(座位数、牌堆、最小顺子长度、有没有王炸/五十K……),
|
|
218
|
+
再写一个继承 `src/games/base.js` 的类实现 `setupPhase` / `checkOver` / `onGameEnd`,
|
|
219
|
+
最后在 `src/games/index.js` 注册即可 —— 牌型识别、AI、联网、伪装皮肤全都不用动。
|
|
220
|
+
|
|
221
|
+
**加伪装皮肤**:在 `src/skins/` 下加一个模块,导出
|
|
222
|
+
`{ key, name, desc, render(p, st), prompt(), help(lines), message(text) }`,
|
|
223
|
+
其中 `p` 是 `packet.build()` 出来的标准状态包(手牌 / 各家张数 / 桌上最大 / 历史 / 结果都已经在里面),
|
|
224
|
+
在 `src/skins/index.js` 的 `LIST` 里注册一行就完事了。
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT
|
|
229
|
+
|
|
230
|
+
想把它发到 npm 让别的机器 `npx dou-cl` 直接用?看 [PUBLISH.md](PUBLISH.md)。
|