koishi-plugin-monetary-bourse 3.0.0 → 3.0.1

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.
@@ -0,0 +1,10 @@
1
+ Place local SVG icon files for monetary-bourse templates here.
2
+
3
+ Current icons:
4
+
5
+ - stock.svg
6
+ - user.svg
7
+ - empty.svg
8
+ - clock.svg
9
+ - trend-up.svg
10
+ - trend-down.svg
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="32" cy="32" r="22" fill="#1F242E" stroke="#30363D" stroke-width="4"/>
3
+ <path d="M32 20V33L40 38" stroke="#F0F3F5" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M19 28H27L31 34H33L37 28H45" stroke="#8B949E" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M19 21L32 32L45 21" stroke="#58A6FF" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M18 40L28 30L34 36L46 22" stroke="white" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M39 22H46V29" stroke="white" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M20 24L29 33L34 28L44 40" stroke="#089981" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M38 40H44V34" stroke="#089981" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M20 40L29 31L34 36L44 24" stroke="#F23645" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M38 24H44V30" stroke="#F23645" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="32" cy="24" r="8" fill="white"/>
3
+ <path d="M20 46C21.9587 39.9249 26.1789 37 32 37C37.8211 37 42.0413 39.9249 44 46" stroke="white" stroke-width="4.5" stroke-linecap="round"/>
4
+ </svg>
package/lib/index.js CHANGED
@@ -30,17 +30,36 @@ module.exports = __toCommonJS(src_exports);
30
30
  var import_koishi2 = require("koishi");
31
31
 
32
32
  // src/render.ts
33
+ var import_fs = require("fs");
33
34
  var import_koishi = require("koishi");
34
35
  var import_path = require("path");
35
36
  var import_url = require("url");
36
- var import_fs = require("fs");
37
+ var import_fs2 = require("fs");
37
38
  var templatesDir = (0, import_path.resolve)(__dirname, "templates");
38
39
  var assetsDir = (0, import_path.resolve)(__dirname, "..", "assets");
39
40
  var assetsBaseUrl = (0, import_url.pathToFileURL)(assetsDir).toString().replace(/\/$/, "");
41
+ var iconMap = {
42
+ stock: "icons/stock.svg",
43
+ user: "icons/user.svg",
44
+ empty: "icons/empty.svg",
45
+ clock: "icons/clock.svg",
46
+ trendUp: "icons/trend-up.svg",
47
+ trendDown: "icons/trend-down.svg"
48
+ };
49
+ var iconDataUrls = {};
40
50
  function getAssetUrl(relativePath) {
41
51
  return `${assetsBaseUrl}/${relativePath}`;
42
52
  }
43
53
  __name(getAssetUrl, "getAssetUrl");
54
+ for (const [key, relativePath] of Object.entries(iconMap)) {
55
+ try {
56
+ const absolutePath = (0, import_path.resolve)(assetsDir, relativePath);
57
+ const buffer = (0, import_fs.readFileSync)(absolutePath);
58
+ iconDataUrls[key] = `data:image/svg+xml;base64,${buffer.toString("base64")}`;
59
+ } catch {
60
+ iconDataUrls[key] = getAssetUrl(relativePath);
61
+ }
62
+ }
44
63
  function getFontFaceCss() {
45
64
  const fontRegularUrl = getAssetUrl("fonts/RobotoMono-Regular.ttf");
46
65
  const fontBoldUrl = getAssetUrl("fonts/RobotoMono-Bold.ttf");
@@ -67,11 +86,34 @@ function injectFontFace(template) {
67
86
  return template.replace("{{FONT_FACE}}", getFontFaceCss());
68
87
  }
69
88
  __name(injectFontFace, "injectFontFace");
89
+ function getIconUrl(iconName) {
90
+ return iconDataUrls[iconName] || "";
91
+ }
92
+ __name(getIconUrl, "getIconUrl");
93
+ function replaceTemplateTokens(template, replacements) {
94
+ let result = template;
95
+ for (const [key, value] of Object.entries(replacements)) {
96
+ result = result.replace(new RegExp(key, "g"), () => value);
97
+ }
98
+ return result;
99
+ }
100
+ __name(replaceTemplateTokens, "replaceTemplateTokens");
101
+ function injectStaticAssets(template) {
102
+ return replaceTemplateTokens(injectFontFace(template), {
103
+ "{{ICON_STOCK}}": getIconUrl("stock"),
104
+ "{{ICON_USER}}": getIconUrl("user"),
105
+ "{{ICON_EMPTY}}": getIconUrl("empty"),
106
+ "{{ICON_CLOCK}}": getIconUrl("clock"),
107
+ "{{ICON_TREND_UP}}": getIconUrl("trendUp"),
108
+ "{{ICON_TREND_DOWN}}": getIconUrl("trendDown")
109
+ });
110
+ }
111
+ __name(injectStaticAssets, "injectStaticAssets");
70
112
  async function renderHoldingImage(ctx, logger2, username, holding, pending, currency) {
71
113
  try {
72
114
  const templatePath = (0, import_path.resolve)(templatesDir, "holding-card.html");
73
- let template = await import_fs.promises.readFile(templatePath, "utf-8");
74
- template = injectFontFace(template);
115
+ let template = await import_fs2.promises.readFile(templatePath, "utf-8");
116
+ template = injectStaticAssets(template);
75
117
  const data = {
76
118
  username,
77
119
  holding,
@@ -99,8 +141,8 @@ __name(renderHoldingImage, "renderHoldingImage");
99
141
  async function renderTradeResultImage(ctx, logger2, tradeType, stockName, amount, tradePrice, totalCost, currency, priceHistory, sellInfo, newHolding, tradeMeta) {
100
142
  try {
101
143
  const templatePath = (0, import_path.resolve)(templatesDir, "trade-result.html");
102
- let template = await import_fs.promises.readFile(templatePath, "utf-8");
103
- template = injectFontFace(template);
144
+ let template = await import_fs2.promises.readFile(templatePath, "utf-8");
145
+ template = injectStaticAssets(template);
104
146
  const tradeIndex = priceHistory.length - 1;
105
147
  const status = tradeMeta?.status ?? "settled";
106
148
  const pendingMinutes = tradeMeta?.pendingMinutes ?? 0;
@@ -161,8 +203,8 @@ async function renderStockImage(ctx, logger2, data, name2, viewLabel, current, h
161
203
  includeEmpty: klineOptions?.includeEmpty
162
204
  }) : [];
163
205
  const templatePath = (0, import_path.resolve)(templatesDir, "stock-chart.html");
164
- let html = await import_fs.promises.readFile(templatePath, "utf-8");
165
- html = injectFontFace(html);
206
+ let html = await import_fs2.promises.readFile(templatePath, "utf-8");
207
+ html = injectStaticAssets(html);
166
208
  const colorScheme = {
167
209
  mainColor: isUp ? "#f23645" : "#089981",
168
210
  gradientStart: isUp ? "rgba(242, 54, 69, 0.25)" : "rgba(8, 153, 129, 0.25)",
@@ -190,7 +232,7 @@ async function renderStockImage(ctx, logger2, data, name2, viewLabel, current, h
190
232
  }),
191
233
  "{{CURRENT_PRICE}}": current.toFixed(2),
192
234
  "{{CHANGE_VALUE}}": `${change >= 0 ? "+" : ""}${change.toFixed(2)}`,
193
- "{{CHANGE_ICON}}": change >= 0 ? "" : "",
235
+ "{{CHANGE_ICON}}": change >= 0 ? getIconUrl("trendUp") : getIconUrl("trendDown"),
194
236
  "{{CHANGE_PERCENT}}": Math.abs(changePercent).toFixed(2),
195
237
  "{{CHART_TYPE}}": chartType,
196
238
  "{{HIGH_PRICE}}": high.toFixed(2),
@@ -204,9 +246,7 @@ async function renderStockImage(ctx, logger2, data, name2, viewLabel, current, h
204
246
  "{{KLINE_DATA}}": JSON.stringify(klineData),
205
247
  "{{G2_SCRIPT}}": ""
206
248
  };
207
- for (const [key, value] of Object.entries(replacements)) {
208
- html = html.replace(new RegExp(key, "g"), () => value);
209
- }
249
+ html = replaceTemplateTokens(html, replacements);
210
250
  const page = await ctx.puppeteer.page();
211
251
  try {
212
252
  await page.setContent(html);
package/lib/render.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Context, h, Logger } from "koishi";
2
+ export declare function injectStaticAssets(template: string): string;
2
3
  export type PricePoint = {
3
4
  time: string;
4
5
  price: number;
@@ -27,7 +27,7 @@
27
27
  position: relative;
28
28
  margin: 0;
29
29
  padding: 32px;
30
- font-family: 'Roboto Mono', 'Trebuchet MS', 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
30
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
31
31
  background-color: var(--bg-color);
32
32
  width: 480px;
33
33
  box-sizing: border-box;
@@ -60,12 +60,15 @@
60
60
  display: flex;
61
61
  align-items: center;
62
62
  justify-content: center;
63
- color: white;
64
- font-size: 24px;
65
- font-weight: 700;
66
63
  box-shadow: 0 8px 16px rgba(99, 102, 241, 0.3);
67
64
  }
68
65
 
66
+ .avatar-icon {
67
+ width: 28px;
68
+ height: 28px;
69
+ display: block;
70
+ }
71
+
69
72
  .user-info {
70
73
  flex: 1;
71
74
  }
@@ -159,7 +162,7 @@
159
162
  color: var(--text-primary);
160
163
  padding: 4px 10px;
161
164
  border-radius: 6px;
162
- font-family: 'Roboto Mono', 'Trebuchet MS', monospace;
165
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
163
166
  }
164
167
 
165
168
  .stock-body {
@@ -187,7 +190,7 @@
187
190
  font-size: 18px;
188
191
  font-weight: 450;
189
192
  color: var(--text-primary);
190
- font-family: 'Roboto Mono', 'Trebuchet MS', monospace;
193
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
191
194
  letter-spacing: -0.3px;
192
195
  }
193
196
 
@@ -249,7 +252,10 @@
249
252
  }
250
253
 
251
254
  .empty-icon {
252
- font-size: 32px;
255
+ width: 36px;
256
+ height: 36px;
257
+ display: block;
258
+ margin: 0 auto 12px;
253
259
  margin-bottom: 12px;
254
260
  opacity: 0.5;
255
261
  }
@@ -361,10 +367,26 @@
361
367
  font-size: 18px;
362
368
  font-weight: 600;
363
369
  color: var(--text-primary);
364
- font-family: 'Roboto Mono', monospace;
370
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
365
371
  letter-spacing: -0.5px;
366
372
  }
367
373
 
374
+ .inline-icon {
375
+ width: 16px;
376
+ height: 16px;
377
+ display: inline-block;
378
+ vertical-align: middle;
379
+ flex-shrink: 0;
380
+ }
381
+
382
+ .stock-name-icon {
383
+ width: 18px;
384
+ height: 18px;
385
+ display: inline-block;
386
+ vertical-align: middle;
387
+ flex-shrink: 0;
388
+ }
389
+
368
390
  /* Bottom Row */
369
391
  .pi-footer {
370
392
  display: flex;
@@ -403,9 +425,9 @@
403
425
  </head>
404
426
 
405
427
  <body>
406
- <div class="card">
407
- <div class="header">
408
- <div class="avatar" id="avatar"></div>
428
+ <div class="card">
429
+ <div class="header">
430
+ <div class="avatar"><img class="avatar-icon" src="{{ICON_USER}}" alt="" /></div>
409
431
  <div class="user-info">
410
432
  <div class="username" id="username"></div>
411
433
  <div class="account-label">股票账户</div>
@@ -424,7 +446,6 @@
424
446
  const DATA = JSON.parse(document.getElementById('data-source').textContent);
425
447
 
426
448
  // 渲染用户信息
427
- document.getElementById('avatar').textContent = DATA.username.charAt(0).toUpperCase();
428
449
  document.getElementById('username').textContent = DATA.username;
429
450
  document.getElementById('footer').textContent = `数据更新于 ${DATA.updateTime}`;
430
451
 
@@ -454,7 +475,7 @@
454
475
  <div class="stock-card">
455
476
  <div class="stock-header">
456
477
  <div class="stock-name">
457
- <span>⚡</span>
478
+ <img class="stock-name-icon" src="{{ICON_STOCK}}" alt="" />
458
479
  ${h.stockName}
459
480
  </div>
460
481
  <div class="stock-amount">${h.amount} 股</div>
@@ -488,7 +509,7 @@
488
509
  <div class="section">
489
510
  <div class="section-title">持仓详情 Holding</div>
490
511
  <div class="empty-state">
491
- <div class="empty-icon">📭</div>
512
+ <img class="empty-icon" src="{{ICON_EMPTY}}" alt="" />
492
513
  <div class="empty-text">暂无持仓</div>
493
514
  </div>
494
515
  </div>
@@ -502,7 +523,7 @@
502
523
  <div class="pending-item">
503
524
  <div class="pi-header">
504
525
  <span class="pi-badge ${p.typeClass}">${p.type}</span>
505
- <span class="pi-time">⏱ ${p.timeLeft}</span>
526
+ <span class="pi-time"><img class="inline-icon" src="{{ICON_CLOCK}}" alt="" />${p.timeLeft}</span>
506
527
  </div>
507
528
  <div class="pi-amount">${p.amount} 股</div>
508
529
  <div class="pi-footer">
@@ -25,7 +25,7 @@
25
25
 
26
26
  body {
27
27
  padding: 64px;
28
- font-family: 'Roboto Mono', 'Trebuchet MS', 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
28
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
29
29
  background-color: var(--bg-color);
30
30
  width: 1024px;
31
31
  height: 768px;
@@ -79,10 +79,15 @@
79
79
  display: flex;
80
80
  align-items: center;
81
81
  justify-content: center;
82
- font-size: 24px;
83
82
  box-shadow: 0 8px 20px var(--icon-shadow);
84
83
  }
85
84
 
85
+ .stock-icon img {
86
+ width: 24px;
87
+ height: 24px;
88
+ display: block;
89
+ }
90
+
86
91
  .meta-info {
87
92
  display: flex;
88
93
  align-items: center;
@@ -99,7 +104,7 @@
99
104
  .price-group { text-align: right; }
100
105
 
101
106
  .current-price {
102
- font-family: 'Roboto Mono', 'Trebuchet MS', monospace;
107
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
103
108
  font-size: 64px;
104
109
  font-weight: normal;
105
110
  color: var(--main-color);
@@ -127,6 +132,12 @@
127
132
  gap: 4px;
128
133
  }
129
134
 
135
+ .change-icon {
136
+ width: 14px;
137
+ height: 14px;
138
+ display: block;
139
+ }
140
+
130
141
  .chart-wrapper {
131
142
  position: relative;
132
143
  flex: 1;
@@ -184,7 +195,7 @@
184
195
  font-size: 20px;
185
196
  font-weight: 600;
186
197
  color: var(--text-primary);
187
- font-family: 'Roboto Mono', monospace;
198
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
188
199
  letter-spacing: 0;
189
200
  }
190
201
 
@@ -204,7 +215,7 @@
204
215
  <div class="header">
205
216
  <div class="title-group">
206
217
  <div class="stock-name">
207
- <div class="stock-icon">¥</div>
218
+ <div class="stock-icon"><img src="{{ICON_STOCK}}" alt="" /></div>
208
219
  {{STOCK_NAME}}
209
220
  </div>
210
221
  <div class="meta-info">
@@ -228,9 +239,10 @@
228
239
  "
229
240
  >{{CHANGE_VALUE}}</span
230
241
  >
231
- <span class="change-badge"
232
- >{{CHANGE_ICON}} {{CHANGE_PERCENT}}%</span
233
- >
242
+ <span class="change-badge">
243
+ <img class="change-icon" src="{{CHANGE_ICON}}" alt="" />
244
+ {{CHANGE_PERCENT}}%
245
+ </span>
234
246
  </div>
235
247
  </div>
236
248
  </div>
@@ -331,7 +343,7 @@
331
343
 
332
344
  const val = yMin + (i / 4) * yRange;
333
345
  ctx.fillStyle = "#64748b";
334
- ctx.font = '12px "Trebuchet MS", monospace';
346
+ ctx.font = '12px "Roboto Mono"';
335
347
  ctx.textAlign = "left";
336
348
  ctx.textBaseline = "middle";
337
349
  ctx.fillText(val.toFixed(2), W - padding.right + 10, y);
@@ -395,7 +407,7 @@
395
407
  ctx.setLineDash([]);
396
408
 
397
409
  const currentPriceStr = prices[prices.length - 1].toFixed(2);
398
- ctx.font = 'bold 13px "Trebuchet MS", monospace';
410
+ ctx.font = 'bold 13px "Roboto Mono"';
399
411
  const labelWidth = ctx.measureText(currentPriceStr).width + 18;
400
412
  const labelHeight = 24;
401
413
  const labelX = W - padding.right;
@@ -423,8 +435,7 @@
423
435
  ctx.textAlign = "center";
424
436
  ctx.textBaseline = "top";
425
437
  ctx.fillStyle = "#64748b";
426
- ctx.font =
427
- '12px -apple-system, BlinkMacSystemFont, "Inter", sans-serif';
438
+ ctx.font = '12px "Roboto Mono"';
428
439
  const labelIndexes = [
429
440
  0,
430
441
  Math.floor((times.length - 1) / 2),
@@ -39,7 +39,7 @@
39
39
  /* 修改 3: 增大页面外间距 (32px -> 64px) */
40
40
  padding: 64px;
41
41
  /* 统一字体组合 */
42
- font-family: 'Roboto Mono', 'Trebuchet MS', 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
42
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
43
43
  background-color: var(--bg-color);
44
44
  width: 960px;
45
45
  height: 720px;
@@ -87,8 +87,12 @@
87
87
  display: flex;
88
88
  align-items: center;
89
89
  justify-content: center;
90
- font-size: 32px;
91
- color: var(--text-primary);
90
+ }
91
+
92
+ .stock-icon img {
93
+ width: 32px;
94
+ height: 32px;
95
+ display: block;
92
96
  }
93
97
 
94
98
  .stock-meta {
@@ -211,7 +215,7 @@
211
215
  color: var(--text-primary);
212
216
  font-weight: 600;
213
217
  /* 修改 2: 强制使用数字字体 */
214
- font-family: 'Roboto Mono', monospace;
218
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
215
219
  }
216
220
 
217
221
  /* --- 图表区域 --- */
@@ -306,7 +310,7 @@
306
310
  opacity: 0.8;
307
311
  margin-top: 6px;
308
312
  /* 百分比也属于数字,用一下 Roboto Mono 更好看 */
309
- font-family: 'Roboto Mono', sans-serif;
313
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
310
314
  }
311
315
 
312
316
  /* 底部时间戳 */
@@ -324,7 +328,7 @@
324
328
  color: var(--text-secondary);
325
329
  padding: 6px 8px;
326
330
  border-radius: 6px;
327
- font-family: 'Roboto Mono', sans-serif;
331
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
328
332
  /* 包含时间数字 */
329
333
  }
330
334
  </style>
@@ -335,7 +339,7 @@
335
339
  <!-- 顶部信息栏 -->
336
340
  <div class="top-section">
337
341
  <div class="stock-group">
338
- <div class="stock-icon">⚡</div>
342
+ <div class="stock-icon"><img src="{{ICON_STOCK}}" alt="" /></div>
339
343
  <div class="stock-meta">
340
344
  <div class="stock-name" id="stock-name">--</div>
341
345
  <!-- 这里会动态插入徽标 -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-monetary-bourse",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "main": "lib/index.js",
5
5
  "typings": "lib/index.d.ts",
6
6
  "files": [
package/readme.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  本插件模拟了一个具备自动宏观调控、25种经典K线形态、智能概率博弈和可视化交割单的深度拟真股票市场。用户可以使用机器人通用的货币(如信用点)进行股票买卖、炒股理财。
24
24
 
25
- > 版本:**3.0.0**
25
+ > 版本:**3.0.1**
26
26
 
27
27
  ## ✨ 特性
28
28
 
@@ -35,6 +35,7 @@
35
35
  - **持仓资产卡片**:精美渲染个人持仓、成本分析、浮动盈亏比及排队中的挂单详情。
36
36
  - **交易交割单**:买卖成交瞬间生成**交易回单图片**,在 K 线图上精确标记买卖点位,直观展示单笔盈亏与买入成本线。
37
37
  - **内置字体**:使用 Roboto Mono 字体,确保图表中的数字和文本清晰易读。
38
+ - **内置图标**:使用内置的箭头和标记图标,增强视觉效果,无需外部资源。
38
39
  - **❄️ 资金冻结与挂单排队**:
39
40
  - 交易采用 T+0 机制,但大额资金/股票会根据金额计算**动态冻结时间**。
40
41
  - 挂单采用**串行排队模式**,同一用户的多个挂单需依次读秒,防止通过拆单绕过冻结机制,增加博弈深度。