<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Lonako</title><description>A personal blog about technology, anime, and more.</description><link>https://lonako-blog.bocchi0708.workers.dev/</link><templateTheme>Firefly</templateTheme><templateThemeVersion>6.16.3</templateThemeVersion><templateThemeUrl>https://github.com/CuteLeaf/Firefly</templateThemeUrl><lastBuildDate>2026年8月23日 17:31:21</lastBuildDate><item><title>关于我。</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-5-17-2/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-5-17-2/</guid><description>一些互联网角落的坐标。</description><pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;关于我&lt;a href=&quot;#关于我&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;您好！这里是 Lonako！&lt;/p&gt;&lt;p&gt;一个正在学习中的普通大学生，&lt;br /&gt;
会一点剪辑，会一点后期，会一点代码，想要继续点亮没有探索过的技能树！&lt;br /&gt;
懂一点动漫，懂一点游戏，懂一点虚拟歌手，喜欢互联网文化，还有各种新奇的东西！&lt;/p&gt;&lt;p&gt;最近正在尝试搭建自己的个人博客，&lt;br /&gt;
希望以后这里能慢慢变成我的赛博小屋。&lt;/p&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;社交平台&lt;a href=&quot;#社交平台&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://space.bilibili.com/37880274&quot; target=&quot;_blank&quot;&gt;Bilibili&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://steamcommunity.com/profiles/76561198347690780/&quot; target=&quot;_blank&quot;&gt;Steam&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://bangumi.tv/user/741148&quot; target=&quot;_blank&quot;&gt;Bangumi&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/LonakoBc&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://music.163.com/#/user/home?id=359688479&quot; target=&quot;_blank&quot;&gt;网易云音乐&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://lonako-blog.bocchi0708.workers.dev&quot;&gt;个人博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;就是这里啦！等以后会搞一个专属域名的！&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;最近在做什么？&lt;a href=&quot;#最近在做什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;学习 Vibe Coding 、AE 、 Unity&lt;/li&gt;
&lt;li&gt;折腾个人博客&lt;/li&gt;
&lt;li&gt;研究 AI 生图、RP聊天&lt;/li&gt;
&lt;li&gt;准备实习和秋招&lt;/li&gt;
&lt;li&gt;想做一些自己的小项目&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;游戏&lt;a href=&quot;#游戏&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最近常玩的游戏：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;鸣潮（许愿不歪）&lt;/li&gt;
&lt;li&gt;异环（严肃攒钱买房买车）&lt;/li&gt;
&lt;li&gt;街头霸王6（欢迎所有人来切磋！）&lt;/li&gt;
&lt;li&gt;卡厄斯梦境&lt;/li&gt;
&lt;li&gt;一些steam上的新游戏！&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;最后&lt;a href=&quot;#最后&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果你恰好也喜欢：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;洛天依&lt;/li&gt;
&lt;li&gt;ACGN文化&lt;/li&gt;
&lt;li&gt;PC游戏&lt;/li&gt;
&lt;li&gt;AI&lt;/li&gt;
&lt;li&gt;互联网文化&lt;/li&gt;
&lt;li&gt;虚拟主播 / 虚拟偶像&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;那么也许我们会聊得来。&lt;/p&gt;&lt;p&gt;欢迎来到我的博客。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>Markdown 常用写法速查：博客编辑入门</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-23-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-23-1/</guid><description>整理一些写博客时最常用的 Markdown 语法，方便以后随写随查。</description><pubDate>Sat, 22 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Markdown 是一种轻量级标记语言。写文章时只需要在文字前后加上一些简单符号，就能排出标题、列表、链接、代码块等常见格式。&lt;/p&gt;
&lt;p&gt;这篇文章整理了一份够日常写博客使用的 Markdown 小抄，也顺便作为博客升级后的第一篇测试文章。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;标题&lt;a href=&quot;#标题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;标题使用 &lt;code&gt;#&lt;/code&gt; 表示，&lt;code&gt;#&lt;/code&gt; 越多，标题层级越低：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 一级标题&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;## 二级标题&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;### 三级标题&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;通常一篇文章只需要从二级标题开始组织正文，因为文章标题已经由 frontmatter 中的 &lt;code&gt;title&lt;/code&gt; 提供了。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;粗体、斜体与删除线&lt;a href=&quot;#粗体斜体与删除线&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;**这是粗体**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;*这是斜体*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;~~这是删除线~~&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;这是一小段行内代码&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;显示效果：&lt;strong&gt;这是粗体&lt;/strong&gt;、&lt;em&gt;这是斜体&lt;/em&gt;、这是删除线，以及 &lt;code&gt;行内代码&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;列表&lt;a href=&quot;#列表&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;无序列表可以使用 &lt;code&gt;-&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 第一项&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 第二项&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 第二项的子项目&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;有序列表则使用数字加英文句点：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; 打开文章文件&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; 编辑正文&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;3.&lt;/span&gt;&lt;span&gt; 保存并预览&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;引用&lt;a href=&quot;#引用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在一行开头添加 &lt;code&gt;&amp;gt;&lt;/code&gt; 就能创建引用：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 写作是思考留下的脚印。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;写作是思考留下的脚印。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;链接与图片&lt;a href=&quot;#链接与图片&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;链接的写法是 &lt;code&gt;[显示文字](网址)&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;访问我的博客&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;https://lonako-blog.bocchi0708.workers.dev/&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;图片只比链接多一个感叹号：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;![&lt;/span&gt;&lt;span&gt;图片说明&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;./images/example.avif&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;博客文章自己的图片可以放进 &lt;code&gt;src/content/posts/images/&lt;/code&gt;，然后使用相对路径引用。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;代码块&lt;a href=&quot;#代码块&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;短代码可以用一对反引号包住。较长的代码则放在三对反引号之间，并在开头注明语言以启用语法高亮：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;```javascript&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Hello, Markdown!&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;```&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;效果如下：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Hello, Markdown!&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;表格&lt;a href=&quot;#表格&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| 语法 | 用途 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| --- | --- |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span&gt; | 标题 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span&gt; | 列表 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span&gt; | 引用 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;语法&lt;/th&gt;&lt;th&gt;用途&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;#&lt;/code&gt;&lt;/td&gt;&lt;td&gt;标题&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;-&lt;/code&gt;&lt;/td&gt;&lt;td&gt;列表&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;引用&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;分割线与待办事项&lt;a href=&quot;#分割线与待办事项&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;单独输入三个短横线可以添加分割线：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;待办列表则这样写：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; 写完文章&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; 本地预览&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 继续更新博客&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt; 写完文章&lt;/li&gt;
&lt;li&gt; 本地预览&lt;/li&gt;
&lt;li&gt; 继续更新博客&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;文章开头的 frontmatter&lt;a href=&quot;#文章开头的-frontmatter&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这个博客的每篇文章都要以一段 frontmatter 开头，用来设置标题、日期、分类等信息：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;文章标题&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;published&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2026-08-22&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;一句话介绍文章内容&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;标签一&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;标签二&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;category&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;学习笔记&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;draft&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;其中 &lt;code&gt;draft: true&lt;/code&gt; 表示草稿，不会正常公开；准备发布时记得改为 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;最后&lt;a href=&quot;#最后&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Markdown 的核心不是记住所有语法，而是让注意力留在内容本身。标题、列表、链接、图片和代码块已经足够完成大多数博客文章；忘记写法时，再回来翻一下这份小抄就好。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>从铁人三项到正式上线：Toy 构建、故障复盘与阿里云迁移</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-16-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-16-1/</guid><description>总结“洛一把”如何组合三项联机玩法，并通过 Toy 独立构建、线上故障复盘和公网验收完成正式发布。</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;当猜曲、歌曲排序和老资历都能独立联机以后，我们开始制作铁人三项：三项固定顺序、每项三轮，共九轮累计积分。&lt;/p&gt;
&lt;p&gt;真正困难的不是把三个入口放在一起，而是让三个状态机共享同一个房间、同一组玩家和一张总分表，并在项目切换时不留下旧状态。与此同时，同一个 React 项目还要发布到普通网页和 B 站 Toy，测试也必须覆盖真实公网环境。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;铁人三项需要两套轮次编号&lt;a href=&quot;#铁人三项需要两套轮次编号&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;服务端同时记录：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;总轮次 &lt;code&gt;overallRoundNumber&lt;/code&gt;：1—9；&lt;/li&gt;
&lt;li&gt;当前项目轮次 &lt;code&gt;stageRound&lt;/code&gt;：每项重新从 1—3；&lt;/li&gt;
&lt;li&gt;当前玩法 &lt;code&gt;activeMode&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;赛程固定为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;总第 1—3 轮：猜曲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;总第 4—6 轮：歌曲排序&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;总第 7—9 轮：谁是老资历&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;前端用项目轮次显示“第 2/3 轮”，用总轮次点亮赛程进度；服务端用总轮次判断何时切换处理器和最终结算。&lt;/p&gt;&lt;p&gt;每轮开始时只初始化当前玩法需要的字段，并重置玩家的本轮状态。总分、玩家身份、颜色和连接保持不变。曲目优先从整场尚未使用的歌曲中选择；曲库不足时允许合理重复，避免小曲库无法开局。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;线上故障一：倒计时显示不等于权威时间&lt;a href=&quot;#线上故障一倒计时显示不等于权威时间&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;早期版本中，多个倒计时看起来会提前约三秒跳转。用户看到“剩余 3 秒”，页面却已经进入下一轮。&lt;/p&gt;&lt;p&gt;原因不是服务端真的少计时，而是客户端使用自己的接收时刻显示剩余时间，没有正确消化状态在网络上传输的延迟。显示值、状态广播和服务端推进受三套时刻共同影响。&lt;/p&gt;&lt;p&gt;修复后，每个房间投影都携带 &lt;code&gt;serverNow&lt;/code&gt;。客户端收到消息时计算本地与服务器的偏移，并用校准后的时间显示倒计时。服务端仍然只认自己的 &lt;code&gt;endsAt&lt;/code&gt;，客户端不会因为动画到零就擅自切换状态。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;线上故障二：短暂的不完整状态也会让 React 白屏&lt;a href=&quot;#线上故障二短暂的不完整状态也会让-react-白屏&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;铁人三项的排序环节曾在每轮结束时突然白屏，刷新后又恢复正常。项目切换和轮间结算涉及 &lt;code&gt;phase&lt;/code&gt;、&lt;code&gt;activeMode&lt;/code&gt;、题目对象和下一轮时间；如果先改变玩法标识，再准备好对应题目，React 就可能按新玩法渲染旧数据。&lt;/p&gt;&lt;p&gt;修复思路有两层：服务端尽量以一次对象更新提交完整状态，前端只在阶段和题目对象同时满足条件时渲染玩法组件。弹窗、答案和排行榜也都加入空值保护，不能假设 WebSocket 的每个快照都处于理想状态。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;线上故障三：部分答对时的超时卡死&lt;a href=&quot;#线上故障三部分答对时的超时卡死&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最隐蔽的问题出现在四人猜曲：两人答对、两人未答，等待倒计时结束后没有弹出答案，刷新也可能继续卡住。双人测试往往因为两人都答对而提前结算，恰好绕开了超时路径。&lt;/p&gt;&lt;p&gt;最终采用两层兜底：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;客户端在校准时间超过截止时间 500 毫秒后，只发送一次 &lt;code&gt;sync&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;服务端处理 &lt;code&gt;sync&lt;/code&gt; 前先执行 &lt;code&gt;tick(Date.now())&lt;/code&gt;，补做所有已经到期的状态转换。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;计时器仍然是主要触发器，在线玩家的同步或重连则是恢复触发器。客户端不能决定结果，但房间不会因为一次漏唤醒永远停在过去。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;从 Cloudflare 迁移到阿里云&lt;a href=&quot;#从-cloudflare-迁移到阿里云&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最初方案使用 Cloudflare Worker 与 Durable Object。它适合用单个强一致对象协调一个房间，并用 Alarm 和休眠 WebSocket 降低空闲成本。&lt;/p&gt;&lt;p&gt;后来为了让服务部署、题库同步和运行环境更可控，线上权威服务迁移到阿里云香港，同时保留原有协议和玩法处理器：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;B站 Toy / Cloudflare Pages&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│ HTTPS + WSS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Nginx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Node.js RoomManager&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;房间 JSON 持久化目录&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;systemd 负责开机启动和异常重启，Nginx 负责 HTTPS、WebSocket Upgrade 和证书入口。服务提供健康检查、题库版本、房间 REST API 与 WebSocket。&lt;/p&gt;&lt;p&gt;迁移能够保持前端改动很小，是因为房间协议没有绑定到 Durable Object 的内部 API，前端只依赖 REST 路径、WebSocket 消息和协议版本。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;Toy 需要独立构建，但不需要复制源码&lt;a href=&quot;#toy-需要独立构建但不需要复制源码&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;普通构建继续使用默认 Vite 配置，Toy 构建使用单独配置，主要差异包括：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;base: &apos;./&apos;&lt;/code&gt;，确保资源使用相对路径；&lt;/li&gt;
&lt;li&gt;使用 Hash 路由，让页面留在一个 &lt;code&gt;index.html&lt;/code&gt; 内；&lt;/li&gt;
&lt;li&gt;注入 Toy JavaScript SDK；&lt;/li&gt;
&lt;li&gt;写入阿里云多人 API 地址；&lt;/li&gt;
&lt;li&gt;只保留 Toy 所需的 BGM；&lt;/li&gt;
&lt;li&gt;输出到独立的 &lt;code&gt;toy-dist&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;路由最终表现为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;index.html#/multiplayer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;index.html#/seniority/preset/all&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Toy 包的文件全部平铺在根目录，并使用 ASCII 文件名：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;index.html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;app-xxxx.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;asset-xxxx.css&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;asset-xxxx.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;asset-xxxx.mp3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这样可以避免平台更新后找不到嵌套资源。构建校验器会拒绝子目录文件、根绝对资源路径、错误 API 地址和超过 20MB 目标的包体。完整网站的 BGM 不全部放入 Toy，只保留三首，最终包体约 13MB。&lt;/p&gt;&lt;p&gt;Toy 发布分成预览和审核两步：先生成并校验 &lt;code&gt;toy-dist&lt;/code&gt;，上传预览版本，在 iframe 中检查单机路由和多人入口，确认无误后才提交审核。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;四层测试覆盖从规则到公网&lt;a href=&quot;#四层测试覆盖从规则到公网&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;第一层是纯规则函数测试，验证人数、积分、截止边界、提示解锁、排序相对顺序、同分排名以及曲库不足等情况。&lt;/p&gt;&lt;p&gt;第二层是房间集成测试，用临时持久化目录和模拟 WebSocket 执行完整命令流程，覆盖创建、加入、颜色唯一、隐私投影、刷新同步、超时追赶和九轮累计积分。&lt;/p&gt;&lt;p&gt;一条关键回归测试专门构造四人猜曲：前两人猜对，后两人未答，把截止时间移到过去，再通过第三名玩家的 &lt;code&gt;sync&lt;/code&gt; 推动状态机。预期结果必须是完整答案公开、得分 &lt;code&gt;5、3、0、0&lt;/code&gt;，且服务端没有异常。&lt;/p&gt;&lt;p&gt;第三层是 React 交互与响应式测试，检查创建和加入房间、颜色选择、对手模糊反馈、答案弹窗、排序拖动、老资历选择和最终排名。移动端还要实际检查卡片高度、长标题和页面滚动。&lt;/p&gt;&lt;p&gt;第四层是公网冒烟验收，直接访问生产地址检查：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/health&lt;/code&gt; 返回正确协议和题库版本；&lt;/li&gt;
&lt;li&gt;Toy Origin 请求返回 200；&lt;/li&gt;
&lt;li&gt;CORS 预检返回 204；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; 精确匹配；&lt;/li&gt;
&lt;li&gt;WSS 能建立并完成回声；&lt;/li&gt;
&lt;li&gt;四种模式可以创建房间并推进到预期阶段。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;上线的定义应该是验证完成&lt;a href=&quot;#上线的定义应该是验证完成&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;服务重启成功不等于上线完成。每次更新后，都要检查健康接口、题库版本、CORS、WebSocket 回声，并跑完猜曲、老资历、排序和铁人三项的公网流程。&lt;/p&gt;&lt;p&gt;多人游戏的错误往往只在特定人数、特定阶段和特定时间边界出现。规则测试解释结果，房间测试验证状态机，组件测试保护交互，公网测试确认部署环境；四层合起来，才接近玩家真正使用的系统。&lt;/p&gt;&lt;p&gt;对于实时服务，部署动作只占最后阶段。真正让版本可用的是：能发现旧状态、能从延迟中恢复、能解释跨域来源，并且能在真实公网入口上跑完一场游戏。&lt;/p&gt;&lt;p&gt;项目仓库：&lt;a href=&quot;https://github.com/LonakoBc/luo-yi-ba&quot; target=&quot;_blank&quot;&gt;LonakoBc/luo-yi-ba&lt;/a&gt;&lt;br /&gt;
在线试玩：&lt;a href=&quot;https://luo-yi-ba.pages.dev/&quot; target=&quot;_blank&quot;&gt;luo-yi-ba.pages.dev&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>没有账号系统，如何实现房间、身份与断线重连</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-15-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-15-1/</guid><description>介绍“洛一把”如何用房间码、恢复令牌、WebSocket 状态同步和房主转移实现轻量多人房间。</description><pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;“洛一把”没有用户账号，却仍然需要知道刷新后的玩家是谁、该回到哪个座位，以及掉线前有多少分。&lt;/p&gt;
&lt;p&gt;解决办法不是把昵称当身份，而是在加入房间时签发一组临时凭据，让“方便人类识别的名字”和“机器可验证的身份”分开。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;房间码只负责定位房间&lt;a href=&quot;#房间码只负责定位房间&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;房间码由六位大写易读字符组成，并排除容易混淆的 &lt;code&gt;0/O/1/I&lt;/code&gt;。创建时随机生成，如果已经存在就重新生成。&lt;/p&gt;&lt;p&gt;房间码适合口头分享，但它不是密码。知道房间码只能尝试加入等待中的房间，不能冒充已有玩家。&lt;/p&gt;&lt;p&gt;创建或加入成功后，服务端返回：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;playerId&lt;/code&gt;：房间内部的随机玩家 ID；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;resumeToken&lt;/code&gt;：高熵恢复令牌；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;code&lt;/code&gt;：房间码。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;客户端把这些信息保存在本机。之后建立 WebSocket 或刷新重连时携带恢复令牌，服务端才能把新连接重新绑定到原玩家。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;昵称不能承担身份职责&lt;a href=&quot;#昵称不能承担身份职责&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;昵称限制为 1—12 个字符，并且房间内不能重复。这个限制是为了显示和交流，不是为了安全。&lt;/p&gt;&lt;p&gt;如果重连只提交昵称，任何知道房间码的人都可以在原玩家掉线后抢占其积分。恢复令牌则把“用户友好的名字”和“机器可验证的身份”分开了。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;等待阶段和比赛阶段采用不同离线规则&lt;a href=&quot;#等待阶段和比赛阶段采用不同离线规则&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;等待阶段的目标是顺利坐满。普通玩家主动离开，或者断线超过十秒，席位就可以释放，让其他人加入。&lt;/p&gt;&lt;p&gt;比赛开始后房间锁定。掉线玩家的席位、得分和答题记录会保留到整场结束，新玩家不能替换参赛者。否则一次网络波动就可能让陌生人继承原玩家的位置。&lt;/p&gt;&lt;p&gt;房主掉线时先等待十秒；仍未恢复，则把房主权限转给当前在线且加入最早的玩家。比赛进行中不依赖房主推进，但等待房间必须始终有人能够开始游戏。&lt;/p&gt;&lt;p&gt;所有玩家都离线后，房间继续保留三十分钟，给移动网络切换、页面刷新和设备短暂休眠留下恢复窗口。超过保留时间才清理持久状态。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;一个 WebSocket 不等于一个玩家&lt;a href=&quot;#一个-websocket-不等于一个玩家&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;浏览器可能重复连接，也可能旧连接尚未完全关闭，新连接已经建立。因此服务端维护的是“连接到玩家 ID”的映射，而不是把连接对象直接当玩家。&lt;/p&gt;&lt;p&gt;玩家是否在线，取决于是否仍有任何有效连接指向该 ID。断开一条旧连接时，如果同一玩家已经建立新连接，不能误把玩家标记成离线。&lt;/p&gt;&lt;p&gt;客户端连接流程保持简单：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;根据房间码读取本地身份；&lt;/li&gt;
&lt;li&gt;使用恢复令牌建立 WebSocket；&lt;/li&gt;
&lt;li&gt;连接成功后发送 &lt;code&gt;sync&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;收到服务端状态后校准时钟；&lt;/li&gt;
&lt;li&gt;断开时显示“正在重连”；&lt;/li&gt;
&lt;li&gt;等待 1.5 秒后重新连接。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;页面不自行合并增量事件，而是反复接收服务端投影的房间快照。房间人数最多四人，完整快照体积可控，却能显著降低客户端状态错乱的概率。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;状态同步也承担超时恢复&lt;a href=&quot;#状态同步也承担超时恢复&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;线上运行后，曾经出现过房间停在 0 秒却没有结算的情况。仅仅重连并重新发送旧状态不能解决问题，因为房间仍然认为自己处于游戏中。&lt;/p&gt;&lt;p&gt;后来 &lt;code&gt;sync&lt;/code&gt; 被升级成状态机的恢复入口：服务端收到同步请求时，会先使用当前时间执行一次 &lt;code&gt;tick&lt;/code&gt;，补做已经到期的提示、结算或下一轮，再广播新状态。&lt;/p&gt;&lt;p&gt;客户端也会在校准时间超过截止时间 500 毫秒后发送一次兜底同步。它不能在本地直接结算，但可以唤醒权威状态机。&lt;/p&gt;&lt;p&gt;这条机制非常实用：重连不应该只恢复网络连接，还应该推动已经落后的服务端状态追上现实时间。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;这套房间系统解决了什么&lt;a href=&quot;#这套房间系统解决了什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在没有账号和数据库用户表的前提下，系统仍然实现了：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;房间内唯一玩家；&lt;/li&gt;
&lt;li&gt;刷新后恢复原席位；&lt;/li&gt;
&lt;li&gt;断线不丢分；&lt;/li&gt;
&lt;li&gt;不重复创建玩家；&lt;/li&gt;
&lt;li&gt;房主自动转移；&lt;/li&gt;
&lt;li&gt;游戏开始后禁止替补；&lt;/li&gt;
&lt;li&gt;有期限的房间恢复。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;它不适合长期社交关系和跨设备账号同步，但非常适合一次性、低门槛、通过链接邀请朋友加入的小型网页游戏。&lt;/p&gt;&lt;p&gt;项目仓库：&lt;a href=&quot;https://github.com/LonakoBc/luo-yi-ba&quot; target=&quot;_blank&quot;&gt;LonakoBc/luo-yi-ba&lt;/a&gt;&lt;br /&gt;
在线试玩：&lt;a href=&quot;https://luo-yi-ba.pages.dev/&quot; target=&quot;_blank&quot;&gt;luo-yi-ba.pages.dev&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>从单机到多人：可扩展联机架构与公平猜曲</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-12-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-12-1/</guid><description>记录“洛一把”如何在保留单机玩法的基础上加入多人房间、服务端权威判分和按玩家投影的实时状态。</description><pubDate>Wed, 12 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;给一个已经能玩的单机项目加入多人联机，最危险的做法不是代码写得慢，而是为了联机把原有玩法全部推倒重写。&lt;/p&gt;
&lt;p&gt;“洛一把”原本已经有 React/Vite 前端、静态歌曲题库和本地游戏服务。因此多人版本首先确定了一条边界：单机玩法继续按原来的方式运行，多人玩法作为一条独立路径加入。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;首版范围为什么很小&lt;a href=&quot;#首版范围为什么很小&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;多人首版只支持：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;创建 2—4 人房间；&lt;/li&gt;
&lt;li&gt;房主选择目标人数、轮数和曲库；&lt;/li&gt;
&lt;li&gt;生成六位房间码和邀请链接；&lt;/li&gt;
&lt;li&gt;玩家输入昵称后加入；&lt;/li&gt;
&lt;li&gt;坐满后由房主开始；&lt;/li&gt;
&lt;li&gt;完成所有轮次后显示排名。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;大厅、自动匹配、账号、聊天、旁观、踢人和房间重赛都暂时不做。这些功能并非没有价值，但会迅速把一个玩法项目变成社交平台项目。先限制范围，才能集中验证同一道题如何同步、如何公平判分，以及玩家掉线后如何回来。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;保留单机服务，新增多人状态&lt;a href=&quot;#保留单机服务新增多人状态&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;原有的 &lt;code&gt;gameService&lt;/code&gt;、歌曲搜索、字段标准化和反馈算法继续保留。多人客户端仍然复用联想搜索和表格展示，但不再拥有最终决定权。&lt;/p&gt;&lt;p&gt;多人系统分成三层：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;React 页面&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ WebSocket / REST 客户端&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ 通用房间会话 RoomSession&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ 具体玩法处理器&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ GuessSongMode&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ SeniorityMode&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ SortingMode&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ TriathlonMode&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;通用房间层只处理成员、房主、连接、阶段、持久化和广播；玩法处理器只处理出题、命令、结算，以及不同玩家可以看到哪些状态。&lt;/p&gt;&lt;p&gt;每个处理器遵循相近的职责：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;handleCommand&lt;/code&gt;：处理经过身份校验的玩家命令；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tick&lt;/code&gt;：根据服务端时间推进超时和下一轮；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;addScheduleTimes&lt;/code&gt;：告诉房间下一次应该何时唤醒；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;project&lt;/code&gt;：按查看者身份生成可公开状态。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;消息中保留 &lt;code&gt;mode&lt;/code&gt; 和协议版本。这样客户端可以拒绝不兼容的旧协议，服务端也能在不改变房间外壳的情况下增加玩法。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;服务端必须是比赛的唯一裁判&lt;a href=&quot;#服务端必须是比赛的唯一裁判&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;单机猜曲只需要回答“玩家猜对了吗”，多人猜曲还必须回答谁先猜对、倒计时以谁为准，以及对手可以看到多少信息。&lt;/p&gt;&lt;p&gt;多人猜曲采用服务端权威模型：客户端负责输入、动画和倒计时显示，服务端负责选题、时间、提示、判分和阶段推进。&lt;/p&gt;&lt;p&gt;每轮开始时，服务端记录：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;startedAt&lt;/code&gt;：开始时间；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;endsAt&lt;/code&gt;：截止时间；&lt;/li&gt;
&lt;li&gt;当前答案；&lt;/li&gt;
&lt;li&gt;已解锁提示等级；&lt;/li&gt;
&lt;li&gt;每位玩家的猜测与本轮得分。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;四人局正确顺序的积分依次为 &lt;code&gt;5、3、2、1&lt;/code&gt;，少于四人时只使用前面的分数档。未猜出者得 0 分；全员提前猜出时立即结算，否则等到截止时间。&lt;/p&gt;&lt;p&gt;客户端提交只包含歌曲 ID。服务端从自己的题库查出歌曲，重新计算字段反馈，并以请求进入房间串行队列的顺序确定名次。即使两个请求几乎同时到达，也不会由客户端时间戳或网络往返时间自行决定先后。&lt;/p&gt;&lt;p&gt;截止边界明确为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;receivedAt &amp;lt; endsAt   接受&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;receivedAt &amp;gt;= endsAt  拒绝&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;如果没有这条规则，同一个“剩余 0 秒”的答案就可能在不同机器上得到不同结果。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;提示和隐私也属于服务端状态&lt;a href=&quot;#提示和隐私也属于服务端状态&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;三分钟内依次解锁：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;开始 60 秒后公开发布时间与歌姬；&lt;/li&gt;
&lt;li&gt;开始 120 秒后公开 STAFF；&lt;/li&gt;
&lt;li&gt;开始 150 秒后公开歌词；&lt;/li&gt;
&lt;li&gt;开始 180 秒后强制结算。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;客户端可以根据服务器校准时间显示倒计时，但不能自行宣布提示已解锁。真正公开哪些字段，由服务端投影决定。玩家修改本地时钟，只能改变自己的动画，无法提前拿到后面的提示。&lt;/p&gt;&lt;p&gt;同一个房间状态会按照查看者生成不同版本。玩家本人可以收到自己的歌曲、字段反馈和方向提示；对手只能收到猜测次数、发生时间、是否正确、公开反馈的颜色状态和得分。&lt;/p&gt;&lt;p&gt;对手状态中不包含歌曲 ID、曲名和完整字段值。前端把它绘制成与真实表格相同的模糊单元格，让玩家能感受到对手正在接近答案，却无法直接抄写。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;客户端仍然可以复用单机体验&lt;a href=&quot;#客户端仍然可以复用单机体验&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;服务端权威并不意味着前端原有逻辑全部作废。歌曲联想搜索、输入标准化、表格组件和字段颜色都可以继续复用。区别在于：搜索结果只是准备提交的候选，最终反馈必须来自服务端。&lt;/p&gt;&lt;p&gt;这种分工既保留了单机版本成熟的输入体验，也避免客户端自行判分。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;从猜曲扩展到更多玩法&lt;a href=&quot;#从猜曲扩展到更多玩法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;多人猜曲稳定后，我们先实现了独立的“谁是老资历”和“歌曲大排序”，再考虑组合成铁人三项。只有每个玩法都能独立完成出题、同步、结算和状态投影，综合赛才只是组合处理器，而不是互相调用的页面逻辑。&lt;/p&gt;&lt;p&gt;歌曲排序就是一个典型例子：玩家把五首歌按发布时间排成时间线，拖拽发生在本地，但完整排列属于私有答案，不能在每次移动时广播给对手。服务端只公开是否提交，提交后再按照十组歌曲对的相对顺序计算正确数量。&lt;/p&gt;&lt;p&gt;相比只检查五个绝对位置，相对顺序计分能给“整体方向正确但有两首互换”的答案合理的部分分数。同一轮还要保证五首歌拥有不同的发布时间，否则先后关系没有定义。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;轻量重构带来的收益&lt;a href=&quot;#轻量重构带来的收益&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这次重构没有把单机玩法改造成联网玩法，而是把题库、搜索和判定能力留在公共服务中，把“谁决定状态”明确交给服务端。&lt;/p&gt;&lt;p&gt;最终得到的不只是几个多人页面，而是一套可以继续容纳新玩法的结构。以后新增填字、抢答或团队模式时，首先要实现的是新的玩法处理器和隐私投影，而不是重新编写房间码、连接、成员和重连系统。&lt;/p&gt;&lt;p&gt;对于已有项目来说，先保护已经工作的部分，再为真正变化的部分建立清晰边界，通常比追求一次性完美的“大一统架构”更可靠。&lt;/p&gt;&lt;p&gt;项目仓库：&lt;a href=&quot;https://github.com/LonakoBc/luo-yi-ba&quot; target=&quot;_blank&quot;&gt;LonakoBc/luo-yi-ba&lt;/a&gt;&lt;br /&gt;
在线试玩：&lt;a href=&quot;https://luo-yi-ba.pages.dev/&quot; target=&quot;_blank&quot;&gt;luo-yi-ba.pages.dev&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>从本地原型到在线小游戏合集：前端架构、体验优化与 Cloudflare Pages 发布</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-9-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-9-1/</guid><description>总结“洛一把”从本地原型走向在线小游戏合集时的架构拆分、体验打磨、测试与部署实践。</description><pubDate>Sun, 09 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;一个小游戏如何逐渐变成合集&lt;a href=&quot;#一个小游戏如何逐渐变成合集&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;“洛一把”最初只有一个功能：随机选择一首洛天依传说曲，让玩家根据线索不断猜测。随着曲库扩展和玩法增加，主页逐渐变成了一个中文虚拟歌声主题的小游戏合集。&lt;/p&gt;&lt;p&gt;目前项目包含曲目猜猜看、闪耀的 Producer、歌曲大排序、谁是老资历／小资历、曲名填字和歌曲数据库等模块，也为多人猜曲保留了测试入口。&lt;/p&gt;&lt;p&gt;这种变化带来的问题并不只是“多写几个页面”。路由、共享数据、音乐播放、移动端布局、构建脚本和部署规则都必须能够支持不断增加的模块。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;React + Vite 的单页应用结构&lt;a href=&quot;#react--vite-的单页应用结构&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;网站使用 React 和 Vite 构建。主页只是整个应用的入口，主要页面使用固定路由区分，例如：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/modes&lt;/code&gt;：曲目猜猜看的曲库范围；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/play/...&lt;/code&gt;：曲目猜猜看；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/crossword&lt;/code&gt;：曲名填字；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/sorting&lt;/code&gt;：歌曲大排序；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/seniority&lt;/code&gt;：老资历／小资历；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/producer&lt;/code&gt;：闪耀的 Producer；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/database&lt;/code&gt;：数据库选择与浏览。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;全局播放器挂载在应用最外层，因此页面切换只改变路由内容，不会卸载音频组件。刷新页面会重新开始当前游戏，但普通的站内导航不会打断音乐。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;页面不直接读取原始资料&lt;a href=&quot;#页面不直接读取原始资料&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;项目的数据来源经历过 Markdown、Excel 和 JSON 的多次扩展。如果每个页面分别解析这些文件，字段规则很快就会失控。&lt;/p&gt;&lt;p&gt;当前做法是在开发、测试和生产构建前执行数据生成脚本：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;人工审核 Excel / 歌曲 Markdown / P 主 Excel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;数据生成与校验&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;前端可直接读取的 generated JSON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;各玩法服务与页面&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;生成阶段负责字段命名、成员拆分、URL 规范化、跨歌姬作品去重、预设构建和错误检查。页面只面对稳定的数据结构。&lt;/p&gt;&lt;p&gt;这种方式还有一个很实际的好处：如果数据缺字段、月份格式错误、作品重复或预设引用了不存在的曲名，构建会直接失败并指出记录，而不是等到玩家打开某一题时才出现空白。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;游戏服务层与 UI 分离&lt;a href=&quot;#游戏服务层与-ui-分离&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;每个玩法都有独立的状态与规则，但 React 组件不直接承担选题和判定。例如曲目猜猜看服务负责随机答案、提交反馈和提示状态；老资历服务负责时间候选、生命值和承接歌曲；排序服务负责得分和年份分配。&lt;/p&gt;&lt;p&gt;页面组件主要完成三件事：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;展示服务返回的状态；&lt;/li&gt;
&lt;li&gt;把玩家操作传给服务；&lt;/li&gt;
&lt;li&gt;根据屏幕宽度选择表格、卡片或抽屉布局。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;这使随机逻辑可以在测试中替换为固定随机源，规则也可以脱离浏览器界面单独验证。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;数据库从附属页面成长为独立模块&lt;a href=&quot;#数据库从附属页面成长为独立模块&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;随着收录歌姬和歌曲增加，数据库不再只是开发时检查资料的工具，而成为网站中的一个正式入口。&lt;/p&gt;&lt;p&gt;数据库选择页包含“全曲库”、各歌姬曲库和 P 主数据库。全曲库按照规范化 VCPedia 页面 URL 合并共享作品，歌姬页面则保留各自的收录关系，因此一首合唱曲可以出现在多个歌姬页面中，但在全站玩法题库中只保留一份。&lt;/p&gt;&lt;p&gt;歌曲表格支持关键词搜索、歌姬、声库、特殊标注、年份范围、表头排序和详情抽屉。桌面端保持类似电子表格的横向浏览体验，移动端允许横向滚动，并把右侧抽屉切换为底部详情面板。&lt;/p&gt;&lt;p&gt;这一模块还验证了一件事：经过人工审核的数据不应该只服务于猜题，也可以直接成为方便浏览和查错的资料产品。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;全局 BGM 播放器&lt;a href=&quot;#全局-bgm-播放器&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;网站早期只有一首《一花依世界》伴奏。随后曲目增加，播放器被重构为独立播放列表。&lt;/p&gt;&lt;p&gt;当前播放器会：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;首次进入网站时随机选择一首；&lt;/li&gt;
&lt;li&gt;播放结束后按照列表顺序自动切换；&lt;/li&gt;
&lt;li&gt;提供播放／暂停、下一首和音量控制；&lt;/li&gt;
&lt;li&gt;允许玩家主动选择要播放的曲目；&lt;/li&gt;
&lt;li&gt;在路由切换时保持播放状态；&lt;/li&gt;
&lt;li&gt;记忆音量设置；&lt;/li&gt;
&lt;li&gt;在浏览器阻止自动播放时，等待首次用户交互后恢复。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;音频配置与播放器组件分离，文件名称会转换成用户可读的歌曲标题。新增 BGM 时不需要改动游戏页面。&lt;/p&gt;&lt;p&gt;浏览器的自动播放策略是实现中绕不开的限制。所谓“默认播放”只能主动尝试，不能保证在所有设备上无条件成功。因此界面必须明确显示当前状态，并为首次点击后的恢复播放做好准备。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;用应援色建立模块识别&lt;a href=&quot;#用应援色建立模块识别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;首页不同玩法使用不同歌姬应援色，让功能卡片在保持同一结构的同时具备辨识度。卡片右上角使用统一编号，数据库则作为资料入口不显示玩法编号。&lt;/p&gt;&lt;p&gt;颜色从来不是唯一反馈手段。猜测结果、排序状态、输入错误和固定格子还会配合边框、文字、图标或标签，避免绿色和黄色成为玩家理解页面的唯一依据。&lt;/p&gt;&lt;p&gt;开发过程中也处理了许多看似细小、实际很影响体验的问题：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;搜索联想框被表格覆盖，需要提升层级并调整定位；&lt;/li&gt;
&lt;li&gt;长曲名在手机卡片中换行后与 STAFF 重叠；&lt;/li&gt;
&lt;li&gt;两张比较卡片在移动端留下过多空白，或超出首屏；&lt;/li&gt;
&lt;li&gt;结算说明在窄弹窗中出现“第二行只剩两个字”；&lt;/li&gt;
&lt;li&gt;新题替换旧题时缺少视觉反馈，需要加入过渡动画；&lt;/li&gt;
&lt;li&gt;标签内容过长时必须允许合理换行，而不能撑破表格。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这些调整很难在最初设计图中一次完成，通常都来自真实游玩后的观察。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;开发者工具只在本地出现&lt;a href=&quot;#开发者工具只在本地出现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;随机题目不利于测试指定歌曲。因此多个玩法在开发环境提供了指定下一题或查看谜底的工具，但入口使用 &lt;code&gt;import.meta.env.DEV&lt;/code&gt; 控制，生产构建中不会显示。&lt;/p&gt;&lt;p&gt;这是一种简单但非常有效的测试辅助：它既能快速复现特定长标题、多歌姬、多声库或特殊 STAFF 的边界情况，又不会把作弊入口带到线上版本。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;自动测试与构建检查&lt;a href=&quot;#自动测试与构建检查&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;项目的验证不只依赖手动打开页面。测试大致分为三层：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;数据测试：检查字段、数量、URL、重复作品、歌姬归属和预设；&lt;/li&gt;
&lt;li&gt;服务层测试：检查随机选题、反馈规则、提示顺序、计分、生命值与投降状态；&lt;/li&gt;
&lt;li&gt;React 交互测试：检查输入、按钮、弹窗、路由和移动端可用流程。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;生产构建本身也是最后一道数据校验。只要生成脚本发现非法记录，Cloudflare Pages 就不会发布一份数据不完整的版本。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;Git 与持续迭代&lt;a href=&quot;#git-与持续迭代&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;项目使用 Git 管理版本并同步到 GitHub。更新通常包含数据、页面、测试、README 和更新日志，完成本地验证后再提交和推送。&lt;/p&gt;&lt;p&gt;在 Windows 环境中，自动化工具创建的仓库曾触发 Git 的 &lt;code&gt;dubious ownership&lt;/code&gt; 检查。通过把明确的项目路径加入 &lt;code&gt;safe.directory&lt;/code&gt;，可以告诉 Git 当前用户信任这个仓库。这个设置应当只针对具体目录，而不是关闭所有权保护。&lt;/p&gt;&lt;p&gt;曲库项目还有一个额外挑战：音频和审核表会明显增加仓库体积，推送时也更容易受到网络中断影响。长期来看，可以进一步评估 Git LFS、外部对象存储或发布阶段下载资源，但首版仍以一个可完整构建的仓库为目标。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;从 Netlify 迁移到 Cloudflare Pages&lt;a href=&quot;#从-netlify-迁移到-cloudflare-pages&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;网站最初部署在 Netlify。免费额度到期后，项目迁移到 Cloudflare Pages，目前的试玩地址是：&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://luo-yi-ba.pages.dev/&quot; target=&quot;_blank&quot;&gt;https://luo-yi-ba.pages.dev/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Cloudflare Pages 直接连接 GitHub 仓库，每次推送后自动安装依赖、生成数据并执行 Vite 生产构建，最终发布 &lt;code&gt;web/dist&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;旧的 &lt;code&gt;netlify.toml&lt;/code&gt; 已经删除，但 &lt;code&gt;web/public/_redirects&lt;/code&gt; 继续保留。原因是这是 SPA 子路由回退规则：用户直接访问 &lt;code&gt;/database/all&lt;/code&gt; 或 &lt;code&gt;/seniority&lt;/code&gt; 时，服务器仍需返回应用入口，再由前端路由决定显示哪个页面。&lt;/p&gt;&lt;p&gt;迁移并不要求购买域名。&lt;code&gt;pages.dev&lt;/code&gt; 地址已经可以公开访问；自定义域名只是后续品牌化选择，不是网站上线的必要条件。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;README、更新日志与项目说明&lt;a href=&quot;#readme更新日志与项目说明&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;README 负责向第一次打开仓库的用户说明项目内容、启动方法、数据来源和在线试玩地址。更新日志则记录歌姬曲库、新玩法、BGM 和界面调整。&lt;/p&gt;&lt;p&gt;网站底部也保留参考项目和数据来源链接，并提供作者联系方式：&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://space.bilibili.com/37880274&quot; target=&quot;_blank&quot;&gt;洛奈lonako 的 Bilibili 个人空间&lt;/a&gt;&lt;/p&gt;&lt;p&gt;对于依赖社区资料的网站，清楚写明来源、参考项目和纠错入口，比只展示成品更重要。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;目前仍然存在的边界&lt;a href=&quot;#目前仍然存在的边界&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;“洛一把”目前仍是纯前端应用，因此存在一些明确限制：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;完整题库和答案会下载到浏览器，无法阻止开发者工具查看答案；&lt;/li&gt;
&lt;li&gt;Excel 和数据网站信息仍需人工审核，自动解析不能替代事实核对；&lt;/li&gt;
&lt;li&gt;远程图片可能失效，所以必须保留占位图；&lt;/li&gt;
&lt;li&gt;BGM 会增加加载与仓库体积，需要注意版权、带宽和缓存；&lt;/li&gt;
&lt;li&gt;没有账号、云存档、排行榜或真正的多人房间。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;如果未来实现多人猜曲，Cloudflare Workers、Durable Objects 或其他实时后端可以负责房间状态、答案保密和 WebSocket 通信。但这应当作为独立阶段开发，而不是把服务端复杂度提前带入所有单机玩法。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;回头看这段开发过程&lt;a href=&quot;#回头看这段开发过程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;项目真正困难的部分并不是写出第一个输入框，而是让数据、玩法和维护方式能够一起增长。&lt;/p&gt;&lt;p&gt;从一个洛天依传说曲猜谜，到多歌姬曲库、时间游戏、填字、P 主资料和数据库浏览，许多早期结构都被重写过。每次重构的目标不是追求抽象本身，而是解决下一个真实需求：更可靠的数据、更清楚的反馈、更方便的测试，以及更稳定的发布。&lt;/p&gt;&lt;p&gt;这也让我逐渐确认，一个个人项目即使从很小的点子开始，也值得建立数据校验、测试、版本记录和部署流程。它们不是大型团队的专属工具，而是让兴趣项目能够持续做下去的基础。&lt;/p&gt;&lt;p&gt;项目仓库：&lt;a href=&quot;https://github.com/LonakoBc/luo-yi-ba&quot; target=&quot;_blank&quot;&gt;LonakoBc/luo-yi-ba&lt;/a&gt;&lt;br /&gt;
在线试玩：&lt;a href=&quot;https://luo-yi-ba.pages.dev/&quot; target=&quot;_blank&quot;&gt;luo-yi-ba.pages.dev&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>用时间与创作者扩展玩法：老资历、歌曲大排序与“闪耀的 Producer”</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-8-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-8-1/</guid><description>记录“洛一把”如何用发布时间、排序和 P 主资料，把曲库继续扩展成不同类型的游戏。</description><pubDate>Sat, 08 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;从“猜出歌名”走向“理解曲库”&lt;a href=&quot;#从猜出歌名走向理解曲库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;完成曲目猜猜看和曲名填字后，网站已经能够利用曲名、STAFF、发布时间、歌姬和声库等字段进行游戏。但这两个玩法的核心仍然是“认出一首歌”。&lt;/p&gt;&lt;p&gt;下一阶段，我想做一些不要求玩家立刻记住答案，却能让人逐渐建立曲库印象的玩法。发布时间是最适合的切入口：它容易比较，又能自然体现中文虚拟歌声文化的发展阶段。另一方面，歌曲 STAFF 中积累的大量 P 主信息，也为另一种人物猜谜玩法提供了基础。&lt;/p&gt;&lt;p&gt;于是，这一阶段形成了三个模块：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;“谁是老资历？”与反向的“谁是小资历？”；&lt;/li&gt;
&lt;li&gt;“歌曲大排序”；&lt;/li&gt;
&lt;li&gt;“闪耀的 Producer”。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;谁是老资历：用连续比较建立时间感&lt;a href=&quot;#谁是老资历用连续比较建立时间感&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;“谁是老资历？”的规则很直接：每轮展示两首发布时间不同的歌曲，玩家选择其中发布时间更早的一首。答对加一分，答错扣除一点生命值，三点生命耗尽后结束。&lt;/p&gt;&lt;p&gt;页面使用两张大卡片展示歌曲。曲名位于视觉中心，同时显示 STAFF、演唱歌姬、特殊标注和演唱会／生日会次数。发布时间在作答前隐藏，选择后再以醒目的 &lt;code&gt;YYYY-MM&lt;/code&gt; 形式揭晓。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;承接歌曲，而不是每轮完全重抽&lt;a href=&quot;#承接歌曲而不是每轮完全重抽&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;这个玩法没有在每一轮都随机抽取两首新歌。玩家选择的歌曲会保留到下一轮，再与一首新歌比较。这样一来，玩家会逐渐记住一首歌在整个时间轴上的相对位置，而不是只完成彼此独立的选择题。&lt;/p&gt;&lt;p&gt;但这种设计也有一个问题：如果出现一首非常早期的歌曲，玩家可能连续多轮都选择它，后续判断会变得过于机械。为此，我增加了一条承接规则：当同一首较早歌曲连续三次成为正确选择时，下一轮改为保留上一组中较新的歌曲，让时间锚点向前移动。&lt;/p&gt;&lt;p&gt;如果上一轮已经揭晓过承接歌曲的发布时间，那么新一轮不会再次把它隐藏。玩家可以利用已经获得的信息判断新出现的歌曲，这让连续回合真正形成了知识积累。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;难度随得分递进&lt;a href=&quot;#难度随得分递进&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;题目不会从一开始就要求玩家区分同一年内的月份，而是按照得分逐步缩小时间差：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;0—4 分：优先选择相差 2—3 年的歌曲；&lt;/li&gt;
&lt;li&gt;5—9 分：优先选择相差 1—2 年的歌曲；&lt;/li&gt;
&lt;li&gt;10—14 分：固定比较相差 1 年的歌曲；&lt;/li&gt;
&lt;li&gt;15 分以上：进入同年、不同月的比较。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;新歌曲还会参考演唱会／生日会次数进行加权，权重为 &lt;code&gt;1 + min(次数, 3)&lt;/code&gt;。这让更为人熟知的现场曲目更容易在前期出现，但又不会完全垄断题库。&lt;/p&gt;&lt;p&gt;当目标时间范围内没有合适候选时，服务层会选择与当前难度最接近的合法歌曲，同时始终保证两首歌不是同一作品，也不会拥有完全相同的发布时间。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;谁是小资历：复用规则，而不是复制一套游戏&lt;a href=&quot;#谁是小资历复用规则而不是复制一套游戏&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;后来，我为这一模块加入了“谁是小资历？”：页面和生命值规则不变，但玩家需要选择发布时间更新的歌曲。&lt;/p&gt;&lt;p&gt;实现时没有复制整个游戏服务，而是增加 &lt;code&gt;older&lt;/code&gt; 和 &lt;code&gt;newer&lt;/code&gt; 两种比较方向。正确答案、按钮文案、历史记录、结算说明和承接逻辑都根据方向计算。&lt;/p&gt;&lt;p&gt;“连续三次”的规则也随之反转：小资历模式中，如果同一首较新的歌曲连续三次成为正确答案，下一轮就改为保留较早的另一首。这样两个模式共享相同的状态机，却保持各自完整的玩法语义。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;图片增强与移动端布局&lt;a href=&quot;#图片增强与移动端布局&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;为了让卡片更有识别度，项目增加了一份独立的图片清单，通过 VCPedia 的 MediaWiki &lt;code&gt;pageimages&lt;/code&gt; 接口批量查询页面缩略图。查询结果会被缓存，开发和生产构建不会临时访问数据网站；没有主图或远程图片加载失败时，则显示主题色占位图。&lt;/p&gt;&lt;p&gt;图片只是增强信息，不是游戏成立的前提。歌曲数据、图片 URL 和游戏规则彼此分离，避免外部资源失效导致玩法无法运行。&lt;/p&gt;&lt;p&gt;移动端是这一页面调整最多的地方之一。两张卡片需要在常见手机视口内同时出现，因此图片、留白和信息字号都要响应式变化。长曲名还需要限制行高和可用高度，避免多行标题与 STAFF 重叠。&lt;/p&gt;&lt;p&gt;每张卡片还会从一组具有可靠文字对比度的颜色中随机取得主题色。正确、错误和选中状态仍然使用边框、图标与文字表达，避免只依赖背景颜色。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;结算与整局回顾&lt;a href=&quot;#结算与整局回顾&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;玩家可以随时点击结算，确认后立即结束游戏；生命值归零也会自动结算。弹窗会展示最终得分、完成轮数、正确数、错误数和评价：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;0—4 分：初来乍到；&lt;/li&gt;
&lt;li&gt;5—9 分：小有资历；&lt;/li&gt;
&lt;li&gt;10—14 分：资深听众；&lt;/li&gt;
&lt;li&gt;15—24 分：曲库考古家；&lt;/li&gt;
&lt;li&gt;25 分以上：活化石级资历。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;结算页会保留所有出现过的题目，包括两首歌曲、实际发布时间、玩家选择与正误。尚未作答时手动结算的题目则标记为“未作答”。与单纯显示分数相比，这份回顾更适合玩家复盘自己不熟悉的年代。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;歌曲大排序：从单次比较走向整体时间线&lt;a href=&quot;#歌曲大排序从单次比较走向整体时间线&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果老资历玩法是在两首歌之间作出判断，那么“歌曲大排序”就是一次观察整段时间线。&lt;/p&gt;&lt;p&gt;玩法提供两种模式：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;时间线排序：系统抽取 5 首或 10 首歌曲，玩家拖动卡片，将它们按照发布时间排列；&lt;/li&gt;
&lt;li&gt;年份归位：系统提供年份，将每首歌曲放到正确年份。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;两个模式都可以使用与曲目猜猜看相同的曲库范围和快速预设。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;相对顺序计分&lt;a href=&quot;#相对顺序计分&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;时间线模式最初只按照完全归位的歌曲数量计分。这个算法对局部正确的排列并不友好：玩家可能只交换了相邻两首歌，却得到很低的成绩。&lt;/p&gt;&lt;p&gt;后来计分方式改为“两两相对顺序”。五首歌曲共有 10 对关系，十首歌曲共有 45 对关系。只要任意两首歌曲的前后关系正确，就获得一分。&lt;/p&gt;&lt;p&gt;这种方式能够更准确地体现玩家对整体年代的判断。结算时除了显示“正确关系数／总关系数”和百分比，还会在每张卡片上标记正确名次以及与正确位置相差几位。只有完全归位的卡片才显示绿色完成状态。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;年份备选池与唯一分配&lt;a href=&quot;#年份备选池与唯一分配&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;年份归位模式要求本局歌曲来自互不相同的年份。每张卡片保留一个下拉框，但某个年份一旦分配给歌曲 A，就会从其他卡片的选项中消失；清空或更换后，旧年份自动回到候选池。&lt;/p&gt;&lt;p&gt;卡片下方还有一个按时间排序的年份备选池。桌面端可以把年份拖到歌曲卡片，移动端则可以先点选年份，再点选目标卡片。下拉、拖拽、触控点击和键盘操作最终都修改同一份分配状态，不会产生彼此不一致的答案。&lt;/p&gt;&lt;p&gt;两种模式均支持投降。确认投降后锁定操作并揭晓完整答案，但不计算成绩，也不生成正常评价。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;闪耀的 Producer：从歌曲 STAFF 中看见创作者&lt;a href=&quot;#闪耀的-producer从歌曲-staff-中看见创作者&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;曲库越来越大后，歌曲背后的创作者也逐渐形成了另一套值得整理的数据。项目目录中的 P 主统计表最终成为“闪耀的 Producer”的事实来源。&lt;/p&gt;&lt;p&gt;首版收录 104 位 P 主，其中 44 位被标记为“名 P”。每条记录包含：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;P 主名称和别名；&lt;/li&gt;
&lt;li&gt;初投稿日期与年份；&lt;/li&gt;
&lt;li&gt;出道曲；&lt;/li&gt;
&lt;li&gt;五首代表曲 A—E；&lt;/li&gt;
&lt;li&gt;殿堂及以上、传说、神话曲数量；&lt;/li&gt;
&lt;li&gt;名 P 标记。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;生成脚本会在开发、测试和构建前读取 Excel，并输出独立的 &lt;code&gt;producers.generated.json&lt;/code&gt;。P 主数据不会混入歌曲 JSON，歌曲数据库与人物数据库只在界面层并列展示。&lt;/p&gt;&lt;p&gt;导入时还处理了实际表格中的数据问题，例如把泠鸢yousa的异常日期统一为 &lt;code&gt;2013-02-05&lt;/code&gt;，并将 Suya 重复出现的代表曲替换为另一首不同作品。名称唯一性、日期、非负数量、五首代表曲和名 P 标记都会在构建前校验。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;两种 P 主范围&lt;a href=&quot;#两种-p-主范围&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;玩家可以选择：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;名 P 模式：精选更具代表性、曲目更加出圈的 44 位 P 主，也是优先推荐的范围；&lt;/li&gt;
&lt;li&gt;全 P 主模式：使用全部 104 位 P 主。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;游戏支持名称、英文大小写、标点差异和括号内别名搜索。不存在或已经猜过的名字不会进入记录。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;P 主线索如何比较&lt;a href=&quot;#p-主线索如何比较&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;猜测表格包含 P 主、初投稿年份、出道曲、殿堂及以上、传说、神话和代表曲七列。&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;P 主名称只有猜中时标绿；&lt;/li&gt;
&lt;li&gt;初投稿年份相同标绿，相差不超过两年标黄，并用箭头表示答案更早或更晚；&lt;/li&gt;
&lt;li&gt;三类数量分别比较，相同标绿，接近时标黄；“接近”的阈值为 &lt;code&gt;max(2, ceil(答案数量 × 20%))&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;出道曲只有完全相同才标绿；&lt;/li&gt;
&lt;li&gt;五首代表曲拆成标签，与答案重合的具体曲目单独标绿。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;如果一次猜测与答案的初投稿年份完全相同，答案行会自动揭示初投稿年份和出道曲，但不会消耗提示次数。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;三次递进提示&lt;a href=&quot;#三次递进提示&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;目前提示顺序为：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;揭示初投稿年份、出道曲和代表曲 E；&lt;/li&gt;
&lt;li&gt;揭示殿堂及以上、传说、神话数量和代表曲 D；&lt;/li&gt;
&lt;li&gt;揭示代表曲 A、B、C，使五首代表曲全部可见。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;答案名称会一直保持隐藏，直到玩家猜中或投降。开发环境还保留了指定答案工具，便于对特定 P 主的线索和结算流程进行测试。&lt;/p&gt;&lt;p&gt;答对或投降后，结算弹窗会展示完整日期、出道曲、三类数量、五首代表曲、模式、猜测次数和提示次数。P 主统计表也被接入数据库模块，方便不游玩时直接检索资料。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;三个玩法背后的共通结构&lt;a href=&quot;#三个玩法背后的共通结构&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这三个模块看起来差异很大，但都依赖几项共同设计：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;题库先由构建脚本规范化，UI 不直接读取 Excel 或 Markdown；&lt;/li&gt;
&lt;li&gt;游戏规则放入独立服务层，React 页面负责展示状态和发送操作；&lt;/li&gt;
&lt;li&gt;随机选题可以注入固定随机源，便于编写可重复测试；&lt;/li&gt;
&lt;li&gt;结算结果保存完整历史，而不仅是一个总分；&lt;/li&gt;
&lt;li&gt;桌面拖拽始终提供移动端或键盘替代操作。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;当底层曲库持续增加时，这种分层能让同一份数据服务于完全不同的玩法，也让规则调整不必反复改动页面组件。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;“谁是老资历？”把发布时间变成连续的相对判断；“歌曲大排序”把相对判断扩展为完整时间线；“闪耀的 Producer”则把关注点从作品转向创作者。&lt;/p&gt;&lt;p&gt;它们没有要求玩家一次记住大量资料，而是在选择、反馈和复盘中逐渐建立认知。对一个以资料库为基础的小游戏合集来说，这也是数据真正转化为玩法的过程。&lt;/p&gt;&lt;p&gt;项目仓库：&lt;a href=&quot;https://github.com/LonakoBc/luo-yi-ba&quot; target=&quot;_blank&quot;&gt;LonakoBc/luo-yi-ba&lt;/a&gt;&lt;br /&gt;
在线试玩：&lt;a href=&quot;https://luo-yi-ba.pages.dev/&quot; target=&quot;_blank&quot;&gt;luo-yi-ba.pages.dev&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>从数据到游戏：曲目猜猜看与曲名填字的玩法实现</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-3-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-3-1/</guid><description>介绍“洛一把”如何把曲库字段转化为猜歌线索，并用歌曲标题生成可玩的填字棋盘。</description><pubDate>Mon, 03 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;第一个核心玩法：曲目猜猜看&lt;a href=&quot;#第一个核心玩法曲目猜猜看&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;当曲库终于能够稳定生成后，第一个要完成的玩法自然是项目最初设想的“猜歌版弗一把”。&lt;/p&gt;&lt;p&gt;每局游戏会从玩家选择的曲库中随机抽取一首歌曲作为答案。玩家在输入框中搜索并提交猜测，猜错的歌曲会加入记录表，与答案逐项比较；猜中则揭晓答案并结束本局。&lt;/p&gt;&lt;p&gt;为了让输入体验尽量顺畅，搜索支持：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;中文曲名；&lt;/li&gt;
&lt;li&gt;英文大小写差异；&lt;/li&gt;
&lt;li&gt;去除空格和标点后的匹配；&lt;/li&gt;
&lt;li&gt;曲名拼音文件名；&lt;/li&gt;
&lt;li&gt;联想列表点击提交；&lt;/li&gt;
&lt;li&gt;精确或唯一匹配时按回车提交。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;不存在的歌曲和已经猜过的歌曲不会加入表格，而是显示轻量提示。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;答案行为什么固定在最上方&lt;a href=&quot;#答案行为什么固定在最上方&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;游戏表格最上方始终显示答案行，但在答对之前，答案内容会被模糊隐藏。猜测记录位于其下，并按照“最新猜测在最前”的顺序排列。&lt;/p&gt;&lt;p&gt;这样设计有两个好处：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;玩家始终知道自己正在追踪哪些字段；&lt;/li&gt;
&lt;li&gt;提示系统可以直接揭开答案行中的某个单元格，不需要额外弹出说明。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;目前表格包含七类线索：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;曲名；&lt;/li&gt;
&lt;li&gt;STAFF；&lt;/li&gt;
&lt;li&gt;发布时间；&lt;/li&gt;
&lt;li&gt;演唱歌姬；&lt;/li&gt;
&lt;li&gt;使用声库；&lt;/li&gt;
&lt;li&gt;演唱会／生日会次数；&lt;/li&gt;
&lt;li&gt;特殊标注。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;曲名本身不提供高亮或字数方向，否则容易过快缩小范围。其余字段则根据类型使用不同判定方式。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;不同字段不能用同一套比较规则&lt;a href=&quot;#不同字段不能用同一套比较规则&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;STAFF、歌姬和声库&lt;a href=&quot;#staff歌姬和声库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;这些字段可能包含多个人员或多个成员，因此不能只比较整段字符串。&lt;/p&gt;&lt;p&gt;程序会先按分号拆分，再对 STAFF 去掉“UP主”“作曲”“作词”“编曲”等职责，只比较人员名称。猜测与答案重合的标签单独标绿，不重合部分保持普通样式。&lt;/p&gt;&lt;p&gt;例如：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;答案：UP主：ilem；作曲：ilem；作词：ilem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;猜测：UP主：另一位作者；作词：ilem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;虽然整段 STAFF 不同，但 &lt;code&gt;ilem&lt;/code&gt; 仍然是有效交集，应当高亮。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;发布时间&lt;a href=&quot;#发布时间&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;发布时间显示为 &lt;code&gt;YYYY-MM&lt;/code&gt;，年份和月份分别渲染：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;年份相同标绿；&lt;/li&gt;
&lt;li&gt;年份相差不超过两年标黄；&lt;/li&gt;
&lt;li&gt;月份相同标绿；&lt;/li&gt;
&lt;li&gt;完整年月不同时显示箭头，提示答案更早或更晚。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这种反馈既能提供方向，又不会因为月份不同而把整个日期判定为完全错误。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;演唱会／生日会次数&lt;a href=&quot;#演唱会生日会次数&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;次数相同标绿，差值不超过 2 标黄，并用箭头提示答案次数更多还是更少。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;特殊标注&lt;a href=&quot;#特殊标注&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;特殊标注只进行完全匹配，“单曲”同样是有效值。这里不提供部分匹配，避免把“生贺曲”和“系列／企划曲目”之间制造出并不存在的关联。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;提示、自动歌词与投降&lt;a href=&quot;#提示自动歌词与投降&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;曲目猜猜看提供三次提示，依次揭示：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;演唱歌姬与发布时间；&lt;/li&gt;
&lt;li&gt;完整 STAFF；&lt;/li&gt;
&lt;li&gt;歌词提示。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;另外还有一个自动歌词机制：如果某次猜测已经在 STAFF、歌姬、声库、发布时间年份和特殊标注上与答案完全一致，却仍然没有猜中，说明现有结构化线索已经很难继续区分，系统会自动显示歌词。&lt;/p&gt;&lt;p&gt;玩家也可以随时投降。答对或投降后都会出现结算卡片，展示答案、猜测次数，并提供 Bilibili 原视频和 VCPedia 页面入口。如果某首歌曲没有 Bilibili 地址，对应按钮会自动隐藏，不影响游戏。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;从“简单／困难”到完整曲库筛选&lt;a href=&quot;#从简单困难到完整曲库筛选&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;早期版本只有简单和困难模式。随着歌姬与歌曲数量不断增加，这种划分很快不够用了，于是模式页被改造成曲库范围选择器。&lt;/p&gt;&lt;p&gt;玩家可以筛选：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;主要曲库与演唱歌姬；&lt;/li&gt;
&lt;li&gt;使用声库；&lt;/li&gt;
&lt;li&gt;特殊标注；&lt;/li&gt;
&lt;li&gt;发布时间范围；&lt;/li&gt;
&lt;li&gt;是否只包含登上过演唱会／生日会的曲目。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;同时提供快速预设，如全曲库、洛天依入门曲库、洛天依经典曲目、乐正绫经典曲目、言和经典曲目、禾念系、五维介质系、忘川风华录和黄金时代。&lt;/p&gt;&lt;p&gt;预设本身使用 Markdown 维护，构建时会检查缺失曲名、重复曲名和空曲库。自定义筛选则写入 URL 查询参数，使刷新后仍能恢复相同范围。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第二个玩法：把歌名拼成一张棋盘&lt;a href=&quot;#第二个玩法把歌名拼成一张棋盘&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;曲目猜猜看之后，我想做一个不依赖 STAFF 或发布时间的玩法。最终形成的方案是“曲名填字”：随机选择六首歌，把相同汉字放到同一个格子，让歌曲横向和纵向交叉。&lt;/p&gt;&lt;p&gt;首版只允许完全由汉字组成的曲名参与。像《普通DISCO》《66CCFF》《滚！》这类包含英文、数字或标点的标题会整体排除，而不是清洗后强行加入。&lt;/p&gt;&lt;p&gt;原因很简单：棋盘中的每个格子必须对应原曲名中的一个真实字符，不能让显示规则和正确答案发生偏差。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;棋盘生成不是简单的字符串拼接&lt;a href=&quot;#棋盘生成不是简单的字符串拼接&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;程序首先建立曲名之间的连接关系：只要两首歌共享至少一个汉字，就可能在棋盘中交叉。&lt;/p&gt;&lt;p&gt;生成一局时，服务会随机选择候选，并通过回溯依次放置六首歌曲：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;第一首横向放置；&lt;/li&gt;
&lt;li&gt;后续歌曲必须通过相同汉字垂直交叉；&lt;/li&gt;
&lt;li&gt;所有歌曲必须属于同一个连通棋盘；&lt;/li&gt;
&lt;li&gt;交叉位置的文字必须一致；&lt;/li&gt;
&lt;li&gt;不允许字符冲突；&lt;/li&gt;
&lt;li&gt;不允许非预期的并排相贴或首尾相接；&lt;/li&gt;
&lt;li&gt;多个合法布局中优先选择面积更小、交叉更多的方案。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;如果多轮回溯仍然无法生成完整的六首棋盘，页面会明确提示本局生成失败，而不会把残缺布局交给玩家。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;“显示所有交叉字”带来的意外问题&lt;a href=&quot;#显示所有交叉字带来的意外问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最初规则是：所有交叉格在开局时直接显示，其余格挖空。但实际游玩后发现，一字歌或交叉较多的短歌可能在开局时已经全部填完整，失去了挑战意义。&lt;/p&gt;&lt;p&gt;后来规则改为：每盘只固定两个非交叉字符，分别来自两首不同歌曲的第一个字。交叉关系仍然决定棋盘结构，但交叉格不再全部免费显示。&lt;/p&gt;&lt;p&gt;这个调整说明，算法上“合法”的谜题不一定在体验上“好玩”。生成器除了保证无冲突，还需要控制开局信息量。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;填字交互的细节&lt;a href=&quot;#填字交互的细节&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;每个空格都是单汉字输入框：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;输入一个汉字后自动前进；&lt;/li&gt;
&lt;li&gt;退格可以删除并回到上一个可编辑格；&lt;/li&gt;
&lt;li&gt;粘贴一段纯汉字文本时会按当前歌曲顺序填入；&lt;/li&gt;
&lt;li&gt;点击格子会高亮它所属的整条歌曲；&lt;/li&gt;
&lt;li&gt;交叉格属于两首歌时，可以切换当前方向；&lt;/li&gt;
&lt;li&gt;已经答对的歌曲会锁定，避免误改。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;六首歌曲分别提交。未填完整不会计入提交次数；错误提交会保留内容并标红错误格；正确后整条标绿并显示完整曲名。&lt;/p&gt;&lt;p&gt;歌词提示可以无限展开和收起，不计入成绩。页面还提供“重置全部”和“投降”：投降后自动填入所有答案、停止计时并锁定操作，但不会伪装成正常通关。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;曲库范围与连通性&lt;a href=&quot;#曲库范围与连通性&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;填字页面提供精简的曲库选择：全曲库、禾念系和五维介质系。&lt;/p&gt;&lt;p&gt;但一个预设能否用于填字，不能只看它有多少首歌，还要看纯汉字曲名之间是否形成足够大的连通分量。如果某个范围虽然歌曲很多，却只能分成大量互不相连的小组，就难以稳定生成六首棋盘。&lt;/p&gt;&lt;p&gt;因此，填字服务会先筛选纯汉字曲名，再分析共享字符连接关系，只从可组成六首连通棋盘的候选中选题。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;响应式设计与可访问反馈&lt;a href=&quot;#响应式设计与可访问反馈&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;曲目猜猜看在桌面端使用横向表格，移动端转换为纵向数据卡片；曲名填字在桌面端使用“棋盘＋曲目列表”双栏，移动端则将棋盘放在上方、曲目列表放在下方。&lt;/p&gt;&lt;p&gt;正确、接近、错误、选中和固定格不仅依赖颜色，还同时使用边框、文字或图标表达。这既改善可访问性，也避免随机主题色或屏幕差异导致反馈不清楚。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;本阶段总结&lt;a href=&quot;#本阶段总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这两个玩法共用同一套曲库，却使用了完全不同的数据视角：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;曲目猜猜看把歌曲资料转化为逻辑推理线索；&lt;/li&gt;
&lt;li&gt;曲名填字只使用曲名字符与歌词，把数据库变成一张文字棋盘。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;它们让我开始确定“洛一把”后续扩展的方向：不是给同一个猜歌页面不断叠加功能，而是让同一批歌曲数据产生不同类型的小游戏。&lt;/p&gt;&lt;p&gt;下一篇将继续介绍两类扩展：围绕发布时间设计的“谁是老资历／小资历”和“歌曲大排序”，以及从歌曲转向创作者的“闪耀的 Producer”。&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;项目地址：&lt;a href=&quot;https://github.com/LonakoBc/luo-yi-ba&quot; target=&quot;_blank&quot;&gt;https://github.com/LonakoBc/luo-yi-ba&lt;/a&gt;&lt;br /&gt;
在线试玩：&lt;a href=&quot;https://luo-yi-ba.pages.dev/&quot; target=&quot;_blank&quot;&gt;https://luo-yi-ba.pages.dev/&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>从“弗一把”到“洛一把”：一个虚拟歌声小游戏的诞生与曲库重建</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-2-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-8-2-1/</guid><description>记录“洛一把”从一个猜歌点子起步，到重建多歌姬曲库与数据审核流程的过程。</description><pubDate>Sun, 02 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;一切从一个猜人小游戏开始&lt;a href=&quot;#一切从一个猜人小游戏开始&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;“洛一把”的最初灵感来自网络上流行的“弗一把”：系统随机选择一名《反恐精英》职业选手，玩家不断输入猜测，再根据国籍、年龄、战队等字段的匹配结果逐步缩小范围。&lt;/p&gt;&lt;p&gt;我很喜欢这种玩法。它没有复杂操作，核心乐趣来自“我知道这个对象，但能否根据有限线索把它找出来”。于是我开始思考：如果把猜测对象换成洛天依的歌曲，会不会也成立？&lt;/p&gt;&lt;p&gt;虚拟歌声作品天然具有适合比较的数据：曲名、STAFF、发布时间、演唱歌姬、使用声库、演唱会经历，以及歌曲所属的系列或企划。只要能建立一个结构稳定的曲库，就可以把这些字段转化为游戏线索。&lt;/p&gt;&lt;p&gt;项目最早因此被命名为“洛一把”。第一阶段的目标很简单：随机选择一首洛天依传说曲，玩家无限猜测，猜错后获得反馈，直到找到答案。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第一版数据范围：洛天依传说曲&lt;a href=&quot;#第一版数据范围洛天依传说曲&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;为了避免一开始就面对几千首作品，我把范围限定为“传说曲”，也就是播放量超过一百万的作品，同时纳入播放量超过一千万的神话曲。&lt;/p&gt;&lt;p&gt;最初设计的歌曲资料包括：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;曲名；&lt;/li&gt;
&lt;li&gt;作曲与作词；&lt;/li&gt;
&lt;li&gt;使用声库；&lt;/li&gt;
&lt;li&gt;原版发布时间；&lt;/li&gt;
&lt;li&gt;独唱或合唱；&lt;/li&gt;
&lt;li&gt;神话曲、生贺曲、演唱会曲目等特殊说明；&lt;/li&gt;
&lt;li&gt;歌词提示；&lt;/li&gt;
&lt;li&gt;Bilibili 原视频地址。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这些字段看起来清晰，但真正开始采集后，很快就暴露出问题：不同页面的资料结构并不统一。&lt;/p&gt;&lt;p&gt;有的页面把创作者写成“UP主”，有的分别列出作曲、作词和编曲；有的页面顶部是原版信息，页面下方还收录其他歌姬翻唱、重制或二次创作版本。若只依靠简单关键词匹配，很容易把二创 STAFF、翻唱歌姬甚至搬运视频混入原版资料。&lt;/p&gt;&lt;p&gt;因此，原来的“作曲／作词”被重构为更宽容的 STAFF 字段，并限定只保留：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;UP主：……；作曲：……；作词：……；编曲：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;UP 主优先展示，调校、混音、曲绘和 PV 等暂不作为猜歌字段。这个调整既提高了数据兼容性，也为后来按人员交集判断 STAFF 奠定了基础。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;从萌娘百科开始的第一轮爬取&lt;a href=&quot;#从萌娘百科开始的第一轮爬取&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;第一版爬虫以萌娘百科的洛天依模板为入口，按年度寻找“原创曲”栏目下的传说曲和神话曲，再访问歌曲详情页补齐字段。&lt;/p&gt;&lt;p&gt;为了减少对网站的压力，程序在连续请求之间主动等待，并为失败请求设置重试和缓存。爬取过程中坚持几个原则：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;只收录原创歌曲；&lt;/li&gt;
&lt;li&gt;排除翻唱、翻填、重填词、二创和后续改编；&lt;/li&gt;
&lt;li&gt;只使用页面明确记载的信息；&lt;/li&gt;
&lt;li&gt;无法可靠判断时写入“待核验”，不根据印象补全；&lt;/li&gt;
&lt;li&gt;生贺曲只认官方生贺，同人作者为生日创作的歌曲不自动标记。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;这套流程成功生成了第一批本地 Markdown 曲库，也让我第一次看到网站完整运行起来。但随着人工抽查深入，数据误差仍然不少。&lt;/p&gt;&lt;p&gt;典型问题包括：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;原版和翻唱版的 STAFF 混在同一个页面；&lt;/li&gt;
&lt;li&gt;“演唱者：洛天依、乐正绫”没有被正确识别为合唱；&lt;/li&gt;
&lt;li&gt;ACE、VOCALOID 等引擎信息明明出现在简介中，却没有被提取；&lt;/li&gt;
&lt;li&gt;歌词开头只有语气词，不能作为有效提示；&lt;/li&gt;
&lt;li&gt;演唱会记录、生日会记录和宣传翻唱难以依靠单一关键词区分。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这些问题让我意识到：爬虫不能只负责“找到文字”，还需要理解页面中哪一块属于原版、哪一块只是衍生信息。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;为什么后来迁移到 VCPedia&lt;a href=&quot;#为什么后来迁移到-vcpedia&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;之后我发现了 VCPedia。相比之前的数据源，它的年度模板和歌曲信息框更适合结构化采集。&lt;/p&gt;&lt;p&gt;以某位歌姬的年度模板为例，可以较清楚地定位：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;歌姬年度歌曲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└─ 原创曲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ 神话曲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ 传说曲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;候选曲目确定后，详情页中的 Songbox、视频模板和正文简介又可以提供原版 STAFF、发布时间、演唱者、声库、歌词及视频地址。相比“在整个页面中搜索关键词”，这种结构可靠得多。&lt;/p&gt;&lt;p&gt;新的采集流程采用年度模板定位候选，再批量读取详情页源码；请求缓存保存在本地，使爬虫可以中断后继续，而不必反复访问相同页面。对于跨年份重复出现的作品，以最早原版为准；对于同一作品的重制版，也不把后续歌姬、声库或 STAFF 合并到原版。&lt;/p&gt;&lt;p&gt;迁移后，数据口径进一步明确：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;STAFF 只保留 UP 主、作曲、作词、编曲；&lt;/li&gt;
&lt;li&gt;发布时间精确到 &lt;code&gt;YYYY-MM&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;演唱歌姬只记录歌声合成角色，不把真人合作歌手列入；&lt;/li&gt;
&lt;li&gt;声库限定为 VOCALOID、ACE Studio、X Studio、Synthesizer V；&lt;/li&gt;
&lt;li&gt;演唱会／生日会次数按明确记载的活动统计；&lt;/li&gt;
&lt;li&gt;特殊标注允许生贺曲、拜年／贺岁纪曲目、系列／企划曲目或单曲；&lt;/li&gt;
&lt;li&gt;Bilibili 地址缺失不会阻止歌曲入库。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;为什么没有让爬虫直接修改正式曲库&lt;a href=&quot;#为什么没有让爬虫直接修改正式曲库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;经历第一版数据错误后，我决定在自动采集和正式网站之间增加一层人工审核。&lt;/p&gt;&lt;p&gt;新的流程变成：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;年度模板与详情页&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;规范化采集结果&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Excel 审核表&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓ 人工校对&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;正式 JSON 数据&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Markdown 与前端题库&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Excel 审核表不仅保存歌曲资料，还增加“来源与核验”工作表，记录年度模板、歌曲页面、传说／神话等级、解析状态和待核验说明。自动程序擅长批量整理，人工则负责处理同名歌曲、页面歧义、演唱会统计和特殊标注。&lt;/p&gt;&lt;p&gt;正式发布后，每首歌曲同时存在三种形态：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;审核 Excel&lt;/strong&gt;：适合人工检查、排序和批量修改；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;歌姬 JSON&lt;/strong&gt;：作为数据库模块的正式数据源；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;十行 Markdown&lt;/strong&gt;：便于阅读、版本对比和生成猜歌题库。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;十行 Markdown 最终统一为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;曲名：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;staff：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;发布时间：YYYY-MM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;演唱歌姬：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;使用声库：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;演唱会\生日会次数：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;特殊标注：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;歌词：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;哔哩哔哩地址：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;歌曲页面URL：……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;多歌姬扩展与全局去重&lt;a href=&quot;#多歌姬扩展与全局去重&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;项目最初只有洛天依，后来陆续加入乐正绫、言和、乐正龙牙、徵羽摩柯、墨清弦、心华、星尘，以及五维介质系歌姬。&lt;/p&gt;&lt;p&gt;这时出现了一个必须解决的问题：合唱曲应该属于每一位参与演唱的歌姬，但在全曲库里又不能重复出现。&lt;/p&gt;&lt;p&gt;最终采用 VCPedia 页面 URL 作为作品的全局身份标识：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;在单个歌姬数据库中，合唱曲可以同时出现；&lt;/li&gt;
&lt;li&gt;在歌姬预设中，它对每位歌姬都算作一首；&lt;/li&gt;
&lt;li&gt;在全曲库、猜歌和填字题库中，多份归属会合并为同一个作品；&lt;/li&gt;
&lt;li&gt;共享歌曲数据不一致时，按照既定歌姬优先级统一事实字段。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这一规则让项目得以从一位歌姬扩展到十四位歌姬，同时保持全局题库稳定。目前合并去重后的曲库已经达到 496 首不同作品。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;数据构建也需要自动测试&lt;a href=&quot;#数据构建也需要自动测试&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;数据量增加后，人工浏览所有文件已经不现实，因此构建脚本会主动检查：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Markdown 是否严格十行；&lt;/li&gt;
&lt;li&gt;日期是否符合 &lt;code&gt;YYYY-MM&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;演唱会次数是否为非负整数；&lt;/li&gt;
&lt;li&gt;声库是否属于允许范围；&lt;/li&gt;
&lt;li&gt;Bilibili 和 VCPedia URL 是否合法；&lt;/li&gt;
&lt;li&gt;曲名、ID 和页面地址是否重复；&lt;/li&gt;
&lt;li&gt;每位歌姬的正式数量是否符合配置；&lt;/li&gt;
&lt;li&gt;全局去重数量是否发生异常变化。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;开发、测试和生产构建前都会重新生成题库。如果审核数据出现非法字段，构建会直接失败并指出具体记录，避免错误悄悄进入线上版本。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;本阶段总结&lt;a href=&quot;#本阶段总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;回头看，项目真正困难的部分并不是把表格显示在网页上，而是建立一套可以长期维护的数据流程。&lt;/p&gt;&lt;p&gt;从萌娘百科到 VCPedia，从直接生成 Markdown 到先输出 Excel 审核，再到 JSON、Markdown 和前端题库分层，数据结构经历了多次重写。但正是这些看似“游戏之外”的工作，让后续所有玩法可以共享同一套可信数据。&lt;/p&gt;&lt;p&gt;下一篇将进入真正的玩法实现：曲目猜猜看如何把数据库字段变成线索，以及曲名填字如何把六首歌名拼成一张可玩的交叉棋盘。&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;项目地址：&lt;a href=&quot;https://github.com/LonakoBc/luo-yi-ba&quot; target=&quot;_blank&quot;&gt;https://github.com/LonakoBc/luo-yi-ba&lt;/a&gt;&lt;br /&gt;
在线试玩：&lt;a href=&quot;https://luo-yi-ba.pages.dev/&quot; target=&quot;_blank&quot;&gt;https://luo-yi-ba.pages.dev/&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>结束一段实习，迎来下一个夏天</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-7-8-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-7-8-1/</guid><description>记录结束杭州十禾信息科技有限公司实习后的生日、休息，以及前往兰溪市灵洞乡人民政府开始下一段实习前的心情。</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今天是结束杭州十禾信息科技有限公司实习后的第一天，也正好是我的生日。&lt;/p&gt;
&lt;p&gt;这一天没有安排太多事情。我给自己留了一点时间休息，整理实习期间的经历，也想一想接下来要面对的生活。实习结束和生日碰在同一天附近，让这个普通的时间节点多了一点阶段转换的意味。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;十禾实习的结束&lt;a href=&quot;#十禾实习的结束&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在十禾实习期间，我主要参与内容运营相关工作，包括产品内容策划、短视频制作、数据复盘，以及使用 AI 工具辅助生成和整理视频素材。&lt;/p&gt;&lt;p&gt;这段经历让我第一次比较完整地接触到商业内容的工作流程。一个选题从讨论到制作，再到发布和数据反馈，中间会经历许多具体环节。内容表现也需要结合素材消耗、转化表现和过审率等指标进行观察，之后再决定下一轮测试和调整的方向。&lt;/p&gt;&lt;p&gt;虽然实习时间还不算很长，但它让我对内容运营岗位有了更实际的认识，也让我开始适应团队协作、工作节奏和按需求交付内容的方式。现在回头看，这段经历更像是一次对职场环境的初步了解，很多方面还需要在之后继续积累。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;生日与最后一个暑假&lt;a href=&quot;#生日与最后一个暑假&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;生日这天适合休息，也适合回头看看自己走过的路。&lt;/p&gt;&lt;p&gt;从小学到大学，我已经度过了大约十六年的学生生活。过去每年到了暑假，总觉得开学还很遥远，时间也有足够的余地可以安排。现在临近大四，毕业之后就要正式进入工作阶段，学生时代的暑假也许会在今年之后告一段落。&lt;/p&gt;&lt;p&gt;这可能是我十六年学生生涯里最后一个具有完整意义的暑假。以后或许仍然会有休假，也会有可以自由安排的时间，但它们和学生时期固定、漫长的暑假不会完全一样。&lt;/p&gt;&lt;p&gt;想到这里，心情有一点复杂。没有特别强烈的伤感，更多是意识到一个熟悉的阶段正在慢慢结束。能够在这个暑假里休息几天，准备下一段经历，也算是一件值得珍惜的事情。&lt;/p&gt;&lt;p&gt;今天也想对自己说一句，生日快乐。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;下一段实习&lt;a href=&quot;#下一段实习&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;接下来我准备回老家，前往兰溪市灵洞乡人民政府，在平安法治办担任实习生。&lt;/p&gt;&lt;p&gt;目前了解到的工作内容可能主要包括信访件的管理与答复，也可能参与公众号上的普法宣传。具体工作还要等正式开始后再进一步确认。和上一段内容运营实习相比，这会是完全不同的工作环境，也会接触到新的工作对象和事务类型。&lt;/p&gt;&lt;p&gt;我希望自己能够先把基础工作做好，熟悉文件、流程和沟通方式，遇到不清楚的内容及时请教。乡镇工作可能比想象中更具体，也更需要耐心。对于还没有接触过的部分，先保持认真和谨慎，慢慢建立自己的理解。&lt;/p&gt;&lt;p&gt;下一次更新时，希望可以记录一些真实的工作体验，也记录自己在毕业前最后一段学生时光里学到的东西。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>实习初期的内容运营工作记录</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-6-4-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-6-4-1/</guid><description>记录在杭州十禾信息科技有限公司担任内容运营实习生期间，参与内容策划、数据复盘和 AI 素材制作的阶段性工作。</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;六月开始，我在杭州十禾信息科技有限公司担任内容运营实习生。目前实习时间还不算长，主要参与自营业务的产品内容运营，工作内容集中在内容策划与制作、数据复盘，以及 AI 素材辅助三个方面。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;内容策划与制作&lt;a href=&quot;#内容策划与制作&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;日常工作会围绕产品种草、机制讲解和营销转化等方向参与选题。根据业务需求整理内容思路，配合完成短视频制作，并在后续投放和反馈中持续修改。&lt;/p&gt;&lt;p&gt;团队会定期进行策划讨论。会议中会结合产品卖点、用户反馈和投放表现，讨论哪些信息需要进一步解释，哪些表达方式更适合用户理解，以及后续可以测试哪些内容方向。对我来说，这个过程让我开始了解一条内容从选题到发布之间需要经过哪些环节，也让我看到产品信息和内容表达之间的联系。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;数据复盘与内容调整&lt;a href=&quot;#数据复盘与内容调整&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;团队会进行周度和月度数据复盘，常见的观察指标包括素材消耗、转化表现和过审率。复盘时需要对比表现较好和表现较弱的内容，分析它们在开头、信息安排、画面素材和表达方式上的差异。&lt;/p&gt;&lt;p&gt;这些分析会作为后续内容测试的参考。运营和内容成员会根据复盘结果共同确定下一阶段的素材方向，再通过新的内容验证判断。数据复盘的作用不只是记录结果，也帮助团队把下一轮制作的重点说得更具体。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;AI 素材在制作流程中的应用&lt;a href=&quot;#ai-素材在制作流程中的应用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;实习期间也参与使用即梦、即创等平台生成 AI 素材和数字人素材。具体使用时，需要根据脚本内容和营销需求补充合适的画面，再对生成结果进行筛选和二次编辑。&lt;/p&gt;&lt;p&gt;AI 工具可以帮助制作一些常规素材，减少部分前期准备时间，也方便快速尝试不同的画面方向。不过生成结果仍然需要结合脚本、产品信息和视频整体节奏进行调整。素材是否适用，最后还要回到内容目的和实际效果上判断。&lt;/p&gt;&lt;p&gt;目前接触到的工作主要集中在辅助素材生产、测试和迭代。通过这些实践，我对 AI 工具在商业内容流程中的使用方式有了更具体的认识，也开始关注工具本身之外的脚本、审核和后期处理环节。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;目前的阶段性认识&lt;a href=&quot;#目前的阶段性认识&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;实习初期接触到的内容运营工作，包含选题、制作、投放反馈和后续调整等连续环节。内容完成后，还需要通过数据和用户反馈了解实际表现，再决定下一步的测试方向。&lt;/p&gt;&lt;p&gt;我目前仍在熟悉产品、业务流程和团队协作方式，能够独立总结的经验还比较有限。现阶段更重要的是把每次参与过的工作记录下来，理解不同环节之间的关系，并逐渐提高自己在内容策划、素材制作和数据分析方面的判断能力。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>尝试生成生日贺卡。</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-5-19-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-5-19-1/</guid><description>为了给朋友庆生让ChatGpt生成一个电子贺卡网站。</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;闲来无事让ChatGpt生成了一个简单的生日贺卡。&lt;a href=&quot;#闲来无事让chatgpt生成了一个简单的生日贺卡&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;真的非常的简单的那种，纯静态，三页，只使用简单的html、css和JaveScript的那种。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;因为是第一次做项目，所以想特地记录一下&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;</content:encoded></item><item><title>第一篇文章。</title><link>https://lonako-blog.bocchi0708.workers.dev/posts/post2026-5-17-1/</link><guid isPermaLink="true">https://lonako-blog.bocchi0708.workers.dev/posts/post2026-5-17-1/</guid><description>搭建好个人博客后总要写点什么，对吧？</description><pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;花了一天来部署个人博客。&lt;a href=&quot;#花了一天来部署个人博客&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;为什么突然有这个想法呢？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个是因为正好刷到了相关的教程视频，然后也正好对Vibecoding很感兴趣，所以就感觉可以从搭建个人博客开始作为起点，练手也好，作为后续学习的记录网站也好，或者作为我嘉豪的资本也好，咳咳。。。。。。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;还有就是最近也要开始实习、投简历、准备秋招等等，所以可能有个人的博客也能方便我介绍自己，写到简历上，上传些作品集什么的呢？对吧？所以说呀————&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;“互联网漂流了这么久，也该有一个自己的小角落了。”&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;</content:encoded></item></channel></rss>