~cytrogen/blog-public

blog-public/posts/9408.html -rw-r--r-- 24.0 KiB
88eebf3dCytrogen Deploy 2026-02-19 08:34:27 3 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
<!DOCTYPE html><html lang="zh" data-theme="dark"><head><meta charset="utf-8"><meta name="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>用笑脸玩扫雷 · Cytrogen 的个人博客</title><meta name="description" content="想必大家都有玩过「扫雷」这款游戏,尤其是经历过 Windows 早年版本时代的人们,在计算机的开始菜单里找了半天,好不容易找到「游戏」文件夹,里面赫然躺着一个应用程序。双击打开,看到一个黄黄的微笑,心想这游戏肯定很简单吧!然后便被炸弹炸死。"><link rel="icon" href="../favicon.png"><link rel="canonical" href="https://cytrogen.icu/posts/9408.html"><link rel="webmention" href="https://webmention.io/cytrogen.icu/webmention"><link rel="me" href="https://m.otter.homes/@Cytrogen"><link rel="me" href="https://github.com/cytrogen"><meta name="fediverse:creator" content="@Cytrogen@m.otter.homes"><link rel="preload" href="../fonts/opensans-regular-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous"><style>@font-face {
  font-family: 'Open Sans';
  src: url('../fonts/opensans-regular-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  size-adjust: 107%;
  ascent-override: 97%;
  descent-override: 25%;
  line-gap-override: 0%;
}
</style><script>(function() {
  try {
    // 优先级:用户选择 > 系统偏好 > 默认浅色
    const saved = localStorage.getItem('theme');
    const theme = saved || 
      (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
    
    document.documentElement.setAttribute('data-theme', theme);
    document.documentElement.style.colorScheme = theme;
  } catch (error) {
    // 失败时使用默认主题,不阻塞渲染
    document.documentElement.setAttribute('data-theme', 'light');
  }
})();
</script><link rel="stylesheet" href="../css/ares.css"><script data-netlify-skip-bundle="true">(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const theme = document.documentElement.getAttribute('data-theme');
    const pageWrapper = document.getElementById('page-wrapper');
    if (pageWrapper && theme) {
      pageWrapper.setAttribute('data-theme', theme);
    }
  });
})();

</script><!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 8.1.1"><link rel="alternate" href="atom.xml" title="Cytrogen 的个人博客" type="application/atom+xml">
</head><body><div id="page-wrapper"><a class="skip-link" href="#main-content">跳到主要内容</a><div class="wrap"><header><a class="logo-link" href="../index.html"><img src="../favicon.png" alt="logo"></a><div class="h-card visually-hidden"><img class="u-photo" src="https://cytrogen.icu/favicon.png" alt="Cytrogen"><a class="p-name u-url u-uid" href="https://cytrogen.icu">Cytrogen</a><p class="p-note">Cytrogen 的个人博客,Cytrogen's Blog</p><a class="u-url" rel="me noopener" target="_blank" href="https://m.otter.homes/@Cytrogen">Mastodon</a><a class="u-url" rel="me noopener" target="_blank" href="https://github.com/cytrogen">GitHub</a></div><nav class="site-nav"><div class="nav-main"><div class="nav-primary"><ul class="nav-list hidden-mobile"><li class="nav-item"><a class="nav-link" href="../index.html">首页</a></li></ul><div class="nav-tools"><div class="language-menu"><button class="language-toggle" type="button"><svg class="icon icon-globe" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855A7.97 7.97 0 0 0 5.145 4H7.5V1.077zM4.09 4a9.267 9.267 0 0 1 .64-1.539 6.7 6.7 0 0 1 .597-.933A7.025 7.025 0 0 0 2.255 4H4.09zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a6.958 6.958 0 0 0-.656 2.5h2.49zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5H4.847zM8.5 5v2.5h2.99a12.495 12.495 0 0 0-.337-2.5H8.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5H4.51zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5H8.5zM5.145 12c.138.386.295.744.468 1.068.552 1.035 1.218 1.65 1.887 1.855V12H5.145zm.182 2.472a6.696 6.696 0 0 1-.597-.933A9.268 9.268 0 0 1 4.09 12H2.255a7.024 7.024 0 0 0 3.072 2.472zM3.82 11a13.652 13.652 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5H3.82zm6.853 3.472A7.024 7.024 0 0 0 13.745 12H11.91a9.27 9.27 0 0 1-.64 1.539 6.688 6.688 0 0 1-.597.933zM8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855A7.97 7.97 0 0 0 10.855 12H8.5zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.65 13.65 0 0 1-.312 2.5zm2.802-3.5a6.959 6.959 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5h2.49zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7.024 7.024 0 0 0-3.072-2.472c.218.284.418.598.597.933zM10.855 4a7.966 7.966 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4h2.355z"></path></svg><span>中文</span></button><div class="language-dropdown"></div></div></div><div class="nav-controls"><div class="more-menu hidden-mobile"><button class="more-toggle" type="button"><span>更多</span><svg class="icon icon-chevron-down" width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 8.825c-.2 0-.4-.1-.5-.2l-3.3-3.3c-.3-.3-.3-.8 0-1.1s.8-.3 1.1 0l2.7 2.7 2.7-2.7c.3-.3.8-.3 1.1 0s.3.8 0 1.1l-3.3 3.3c-.1.1-.3.2-.5.2z"></path></svg></button><div class="more-dropdown"><ul class="dropdown-list"><li class="dropdown-item"><a class="nav-link" href="../archives/index.html">归档</a></li><li class="dropdown-item"><a class="nav-link" href="../categories/index.html">分类</a></li><li class="dropdown-item"><a class="nav-link" href="../tags/index.html">标签</a></li><li class="dropdown-item"><a class="nav-link" href="../about/index.html">关于</a></li><li class="dropdown-item"><a class="nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></div><div class="theme-switcher"><button class="theme-toggle" type="button" role="switch" aria-pressed="false" aria-label="切换主题"><div class="theme-icon moon-icon"><svg class="icon icon-moon" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"></path></svg></div><div class="theme-icon sun-icon"><svg class="icon icon-sun" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"></path></svg></div></button></div><details class="mobile-menu-details hidden-desktop"><summary class="hamburger-menu" aria-label="nav.menu"><svg class="icon icon-bars" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"></path></svg><span class="menu-text">nav.menu</span></summary><div class="mobile-menu-dropdown"><ul class="mobile-nav-list"><li class="mobile-nav-item"><a class="mobile-nav-link" href="../index.html">首页</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../archives/index.html">归档</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../categories/index.html">分类</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../tags/index.html">标签</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../about/index.html">关于</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></details></div></div></div></nav></header><main class="container" id="main-content" tabindex="-1"><div class="post"><article class="post-block h-entry"><div class="post-meta p-author h-card visually-hidden"><img class="author-avatar u-photo" src="../favicon.png" alt="Cytrogen"><span class="p-name">Cytrogen</span><a class="u-url" href="https://cytrogen.icu">https://cytrogen.icu</a></div><a class="post-permalink u-url u-uid visually-hidden" href="https://cytrogen.icu/posts/9408.html">永久链接</a><div class="p-summary visually-hidden"><p>想必大家都有玩过「扫雷」这款游戏,尤其是经历过 Windows 早年版本时代的人们,在计算机的开始菜单里找了半天,好不容易找到「游戏」文件夹,里面赫然躺着一个应用程序。双击打开,看到一个黄黄的微笑,心想这游戏肯定很简单吧!然后便被炸弹炸死。</p></div><div class="visually-hidden"><a class="p-category" href="../categories/%E6%B8%B8%E6%88%8F%E8%AF%B4%E6%98%8E/">游戏说明</a><a class="p-category" href="../tags/%E6%89%AB%E9%9B%B7/">扫雷</a></div><a class="u-syndication visually-hidden" target="_blank" rel="noopener" href="https://m.otter.homes/@Cytrogen/116076986398086371">https://m.otter.homes/@Cytrogen/116076986398086371</a><h1 class="post-title p-name">用笑脸玩扫雷</h1><div class="post-info"><time class="post-date dt-published" datetime="2026-02-14T05:00:00.000Z">2/14/2026</time><time class="dt-updated visually-hidden" datetime="2026-02-19T07:42:07.357Z"></time></div><div class="post-content e-content"><html><head></head><body><p>想必大家都有玩过「扫雷」这款游戏,尤其是经历过 Windows 早年版本时代的人们,在计算机的开始菜单里找了半天,好不容易找到「游戏」文件夹,里面赫然躺着一个应用程序。双击打开,看到一个黄黄的微笑,心想这游戏肯定很简单吧!然后便被炸弹炸死。</p>
<span id="more"></span>
<p><img src="/posts/9408/start.png" alt=""></p>
<p>好吧,这游戏到底该怎么玩呢?首先,你可以点开一个网格,看看下面有没有雷,就像是现实生活中挖雷一般。你可以右键给一个网格插上旗子,标记该网格,假设它底下有雷。</p>
<p>那就点开一个网格看看吧!</p>
<p><img src="/posts/9408/first_click.png" alt=""></p>
<p>看来我们挖出了一个大区域。其中没有数字的网格,代表它的周围(即相邻的八个格子)没有雷;有数字的网格,上面的数字代表周围存在多少个雷,例如 1 就是相邻的格子里有 1 个雷、2 就是相邻的格子里有 2 个雷……</p>
<p>知道了这一条规则后,我们可以快速锁定这些 1 们:因为它们的周围只有 1 个格子,也只有 1 个雷,那么雷到底在哪里,很显而易见了吧~</p>
<p><img src="/posts/9408/eight_ones.png" alt=""></p>
<p>给这些定有雷的格子插上旗后,我们可以进一步推测出,旁边的 1 的雷已经被找到了,那么它们的周围就不可能再有其他雷 —— 全部挖开!</p>
<p>持续做下去后,很快你便会发现,已经没有可以用这种推理方法挖开或者插旗的格子了。当然,这不意味着扫雷无法再继续进行下去,只是我们还没有明白该游戏的本质。</p>
<p>扫雷的核心逻辑在于「公共区域的控制权」。什么是公共区域?当两个数字相邻时,它们周围的一圈格子在中间部分一定是重叠的。除了这片公共区域,它们又各自拥有属于自己的一小块独占区域。</p>
<p>假设我们决定观察这两个相邻的数字:M 和 N。M 周围有 M 个雷,N 周围也有 N 个雷,它们中间夹着的几个格子是它们共同拥有的。无论这些公共格子通过什么方式排列雷,它们对 M 和 N 的贡献是一样的。如果 M 是 1,N 也是 1,意味着 M 需要在它的视野里找到 1 颗雷,N 也需要找到 1 颗雷 —— 因为 M 通过公共区域得到了满足,那么 N 作为邻居,自然也得到了满足。如果此时 N 还有额外的独占区域格子,那么格子必然不能有雷,否则 N 周围的雷数就会超标。</p>
<p>明白了这些后,恭喜你,你理解了扫雷游戏里的「1-1」定式!</p>
<p>接着我们用这个逻辑去理解「1-2」定式。想象这么一个场景,M 是 1,N 是 2。这意味着公共区域内最多只能有 1 颗雷,绝对不能再多。既然最多只能有 1 颗雷,那么 N 的最后那颗雷哪里来?答案是在 N 的独占区域内。也就是说,我们可以通过减法公式 N - M = 1,来得出答案 1 颗雷必然存在于大数字的独占区域内。</p>
<p>再来看「1-2-1」定式。我们这里使用 M = 1、N = 2、O = 1 来代表这个场景。假设我们把雷放在 N 的正下方,这颗雷会同时被 M、N、O 看到,且同时满足 M 和 O 的条件。可是,中间的 N 还差 1 颗雷。但这颗雷没有地方放了,因为如果再在任何地方放雷,左右两边的 1 就会变成 2,也就违背了已知条件。所以,结论只有一个:中间那个公共格子绝对不能有雷。既然中间不能有雷,为了满足中间的 2,雷只能被迫分布在两个 1 的下方。也就是说,「1-2-1」是一个硬性定式,只要看到了就可以断定两个 1 下面有雷,而 2 的下面没有。</p>
<p><img src="/posts/9408/one-two-one.png" alt=""></p>
<p>如果将两个「1-2」叠加起来,我们能得到「1-2-2-1」定式。左边的「1-2」结构意味着,为了补足 2 的雷数,最左边的雷必须在 2 的独占区域。同理,右边的「2-1」结构意味着最右边的雷也必须在 2 的独占区域。这样一来,两个 2 外侧的雷都确定了。现在只需要找到公共区域的雷。由于它们原本都是 2,现在各减去确定的 1 颗,剩下的需求便变成了「1-1」的局面。</p>
<p>根据前面提到的「1-1」逻辑,中间往往是安全的,或者雷的分布是对称的。结果便是,两头的 1 下面有雷,中间的两个 2 下面往往可以挖开。不过该定式需要取决于雷的具体排列,不过逻辑核心依然是先确定外侧的必然雷。</p>
<p>其实,这些定式本质上都是数个「1-1」或者「1-2」组成的,只要理解底层逻辑,都可以通过这些减法公式来判断雷的位置。但是呢,在实际的中盘博弈中,数字周围通常已经插上了旗帜。如果不扣除这些已知的雷,直接套用减法公式,新手容易将实质上的「1-2」误判为「2-2」或者「3-3」等对称局面,从而得出完全相反的安全结论并导致踩雷。</p>
<p>过去我为作业写过 <a target="_blank" rel="noopener" href="https://github.com/Cytrogen/CS50AI_2023/blob/main/minesweeper/minesweeper.py">一个简单的扫雷 AI</a>。因为对该游戏的不熟悉,代码里并没有赋予 AI 处理任何需要综合判断的局面的能力。这对我们而言也是同样的,如果只是随便将定式套在数字上,而没有考虑到更为综合的情况的话,得到的可能就是一个爆炸的雷,和一个死掉的小黄脸了。</p>
<p>该文章仅讲述扫雷游戏的入门玩法,参考 <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/27550382">该文章</a>,想要更多了解的也请阅读该文章。</p>
</body></html></div></article></div></main><footer><div class="paginator"><a class="prev" href="cc52.html">上一篇</a><a class="next" href="df9e.html">下一篇</a></div><!-- Webmention 显示区域--><div class="webmention-section" data-page-url="posts/9408.html" data-full-url="https://cytrogen.icu/posts/9408.html" data-mode="static">
                <h3 class="webmention-title">Webmentions (<span class="webmention-count">1</span>)</h3><div class="webmention-group webmention-group-likes"><h4 class="webmention-group-title">喜欢 (1)</h4><div class="webmention-compact-list"><div class="webmention-compact-item"><img class="webmention-compact-photo" src="https://avatars.webmention.io/media.otter.homes/b3a5ebdc7764c92d0a28a39ee05451f98685bd5f10185dc981856443952aa13c.png" alt="KipJayChou ⁂" loading="lazy"><a class="webmention-compact-name" href="https://mstdn.feddit.social/@admin" target="_blank" rel="noopener ugc">KipJayChou ⁂</a></div></div></div></div><div class="copyright"><p class="footer-links"><a href="../friends/index.html">友链</a><span class="footer-separator"> ·</span><a href="../links/index.html">邻邦</a><span class="footer-separator"> ·</span><a href="../contact/index.html">联络</a><span class="footer-separator"> ·</span><a href="../colophon/index.html">营造记</a><span class="footer-separator"> ·</span><a href="../atom.xml">RSS订阅</a></p><p>© 2025 - 2026 <a href="https://cytrogen.icu">Cytrogen</a>, powered by <a href="https://hexo.io/" target="_blank">Hexo</a> and <a href="https://github.com/cytrogen/hexo-theme-ares" target="_blank">hexo-theme-ares</a>.</p><p><a href="https://blogscn.fun" target="_blank" rel="noopener">BLOGS·CN</a></p></div></footer></div></div><a class="back-to-top" href="#top" aria-label="返回顶部"><svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M3.293 9.707a1 1 0 010-1.414L9.586 2a2 2 0 012.828 0l6.293 6.293a1 1 0 01-1.414 1.414L11 3.414V17a1 1 0 11-2 0V3.414L2.707 9.707a1 1 0 01-1.414 0z"></path></svg></a><script>document.addEventListener('DOMContentLoaded', function() {
  const codeBlocks = document.querySelectorAll('figure.highlight');
  
  codeBlocks.forEach(block => {
    let caption = block.querySelector('figcaption');
    if (!caption) {
      caption = document.createElement('figcaption');
      block.insertBefore(caption, block.firstChild);
    }

    const info = document.createElement('div');
    info.className = 'info';
    
    const filename = caption.querySelector('span');
    if (filename) {
      filename.className = 'filename';
      info.appendChild(filename);
    }
    
    const lang = block.className.split(' ')[1];
    if (lang) {
      const langSpan = document.createElement('span');
      langSpan.className = 'lang-name';
      langSpan.textContent = lang;
      info.appendChild(langSpan);
    }

    const sourceLink = caption.querySelector('a');
    if (sourceLink) {
      sourceLink.className = 'source-link';
      info.appendChild(sourceLink);
    }

    const actions = document.createElement('div');
    actions.className = 'actions';

    const codeHeight = block.scrollHeight;
    const threshold = 300;

    if (codeHeight > threshold) {
      block.classList.add('folded');
      
      const toggleBtn = document.createElement('button');
      toggleBtn.textContent = '展开';
      toggleBtn.addEventListener('click', () => {
        block.classList.toggle('folded');
        toggleBtn.textContent = block.classList.contains('folded') ? '展开' : '折叠';
      });
      actions.appendChild(toggleBtn);
    }

    const copyBtn = document.createElement('button');
    copyBtn.textContent = '复制';
    copyBtn.addEventListener('click', async () => {
      const codeLines = block.querySelectorAll('.code .line');
      const code = Array.from(codeLines)
        .map(line => line.textContent)
        .join('\n')
        .replace(/\n\n/g, '\n');
      
      try {
        await navigator.clipboard.writeText(code);
        copyBtn.textContent = '已复制';
        copyBtn.classList.add('copied');
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
          copyBtn.classList.remove('copied');
        }, 3000);
      } catch (err) {
        console.error('复制失败:', err);
        copyBtn.textContent = '复制失败';
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
        }, 3000);
      }
    });
    actions.appendChild(copyBtn);

    caption.innerHTML = '';
    caption.appendChild(info);
    caption.appendChild(actions);

    const markedLines = block.getAttribute('data-marked-lines');
    if (markedLines) {
      const lines = markedLines.split(',');
      lines.forEach(range => {
        if (range.includes('-')) {
          const [start, end] = range.split('-').map(Number);
          for (let i = start; i <= end; i++) {
            const line = block.querySelector(`.line-${i}`);
            if (line) line.classList.add('marked');
          }
        } else {
          const line = block.querySelector(`.line-${range}`);
          if (line) line.classList.add('marked');
        }
      });
    }
  });
});</script><script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script"></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const themeToggle = document.querySelector('.theme-toggle');
    
    if (!themeToggle) return;
    
    const getCurrentTheme = () => {
      return document.documentElement.getAttribute('data-theme') || 'light';
    };
    
    const updateUI = (theme) => {
      const isDark = theme === 'dark';
      themeToggle.setAttribute('aria-pressed', isDark.toString());
    };
    
    const setTheme = (theme) => {
      document.documentElement.setAttribute('data-theme', theme);
      document.documentElement.style.colorScheme = theme;
      
      const pageWrapper = document.getElementById('page-wrapper');
      if (pageWrapper) {
        pageWrapper.setAttribute('data-theme', theme);
      }
      
      // Find and remove the temporary anti-flicker style tag if it exists.
      // This ensures the main stylesheet takes full control after the initial load.
      const antiFlickerStyle = document.getElementById('anti-flicker-style');
      if (antiFlickerStyle) {
        antiFlickerStyle.remove();
      }
      
      localStorage.setItem('theme', theme);
      updateUI(theme);
    };
    
    const toggleTheme = () => {
      const current = getCurrentTheme();
      const newTheme = current === 'light' ? 'dark' : 'light';
      setTheme(newTheme);
    };
    
    updateUI(getCurrentTheme());
    
    themeToggle.addEventListener('click', toggleTheme);
    
    if (window.matchMedia) {
      const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
      mediaQuery.addEventListener('change', function(e) {
        if (!localStorage.getItem('theme')) {
          const theme = e.matches ? 'dark' : 'light';
          setTheme(theme);
        }
      });
    }
  });
})();
</script><script src="../js/details-toggle.js" defer></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const backToTopBtn = document.querySelector('.back-to-top');
    
    if (!backToTopBtn) return;
    
    const toggleButtonVisibility = () => {
      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      const shouldShow = scrollTop > 200;
      
      if (shouldShow) {
        backToTopBtn.classList.add('is-visible');
      } else {
        backToTopBtn.classList.remove('is-visible');
      }
    };
    
    let ticking = false;
    const handleScroll = () => {
      if (!ticking) {
        requestAnimationFrame(() => {
          toggleButtonVisibility();
          ticking = false;
        });
        ticking = true;
      }
    };
    
    const scrollToTop = (event) => {
      event.preventDefault();
      window.scrollTo({
        top: 0,
        behavior: 'smooth'
      });
    };
    
    window.addEventListener('scroll', handleScroll);
    backToTopBtn.addEventListener('click', scrollToTop);
    
    toggleButtonVisibility();
  });
})();</script></body></html>