📦 16 个仓库 📖 点击仓库名展开 / 收起 README
BigNaiWa JavaScript ⭐ 19 在 GitHub 查看 ↗
合成大奶娃 · 纯前端小游戏(自研 PBD 物理 + 按图片轮廓的碰撞箱 + TinyWebDB 在线排行榜),零依赖零构建

合成大奶娃

纯 HTML + CSS + JavaScript 的静态网页小游戏,零依赖、零构建、离线可玩。
物理引擎(PBD 位置约束求解)是自己写的,没有引入 matter.js 等任何第三方库。

🎮 在线玩

https://yhsome.github.io/BigNaiWa/

(GitHub Pages 托管,手机浏览器打开就能玩,也可以「添加到主屏幕」当 App 用。)

预览

玩法

  • 鼠标:移动瞄准,点击即投放。
  • 触屏:按住拖动瞄准,松手才投放——手指不会挡住落点,也方便微调。
  • 棋盘右上角的「下一个」是下一颗;当前这颗画在准星位置(虚线顶端)。
    投放后的冷却期间当前这颗会变淡显示(还不能投),这样两个位置始终各是各的,不会看混。
  • 两个同级别的碰到一起就合成为高一级,并获得分数。
  • 顶上虚线是警戒线:有水果卡在线的上方并且基本停住、累计超过 1.5 秒即判负
    (被弹起来飞过线的不算,详见下面的「失败规则」)。
  • 结束后自动上榜(进不了前 20 就不提交);没填昵称就记作「默认用户」,
    昵称随时可以在排行榜弹窗里改。
  • ← → 微调位置,Space / Enter 投放,R 重新开始。
    (结束后空格/回车不再重开,避免手快连开新局。)

合成链(11 级):

葡萄 → 樱桃 → 橘子 → 柠檬 → 猕猴桃 → 番茄 → 桃子 → 菠萝 → 椰子 → 半西瓜 → 大西瓜

合成得分采用三角数:1 / 3 / 6 / 10 / 15 / 21 / 28 / 36 / 45 / 55,
两个大西瓜撞在一起会一起消失并额外加 100 分。

失败规则

唯一判负条件:有水果卡在警戒线上方、且基本停住,累计超过 1.5 秒。

const DANGER_Y   = 142;   // 警戒线
const OVER_LIMIT = 1.5;   // 累计秒数
const REST_SPEED = 140;   // 低于这个速度才算“卡住了”(px/s)

for (const b of balls) {
  if (b.dead || !b.landed) continue;         // 还没落地的不算
  if (b.y - b.r < DANGER_Y) {                // 这只在线上面
    if (b.vx*b.vx + b.vy*b.vy < REST_SPEED2) {
      b.overTime += dt;                      // 停住的才计时
      if (b.overTime > OVER_LIMIT) gameOver();
    } else {
      b.overTime -= dt * 2;                  // 正在飞 → 倒扣
    }
  } else {
    b.overTime -= dt * 2;                    // 回到线下方 → 倒扣(2 倍速)
  }
}

几个容易被误解的点:

疑问 答案
好几只轮流在线上方,时间会加在一起吗? 不会。 overTime 是每只各自的变量,必须同一只累计满 1.5 秒
被弹飞、路过线上的算吗? 不算。 速度 ≥ 140px/s 时不计时,而且还在倒扣
贴着线上下反复弹会攒起来吗? 得看比例:线下方按 2 倍速扣,净变化率 = 3f − 2(f = 在线上方的时间占比),只有 f > 2/3 才攒得起来
会不会永远输不了? 不会。实测灌满棋盘第 161 次投放判负,那时堆顶 y=45、线上方 4 只里 3 只是静止的
除了这个还有别的失败条件吗? 没有。不限步数、不限数量、不限时间

想调:DANGER_Y 调高低、OVER_LIMIT 调容忍时间、REST_SPEED 调“多慢才算卡住”。
node physics.test.js 的第 7 组会把这三条规则各验一遍。

运行

线上直接开 https://yhsome.github.io/BigNaiWa/;
本地双击 index.html 即可(file:// 协议下也能跑,排行榜同样可用)。
也可以起个静态服务:

python -m http.server 8080
# 打开 http://localhost:8080

部署:仓库打开 Settings → Pages → Source = Deploy from a branch → main / (root) 即可,
根目录已经放了 .nojekyll,静态文件原样发布。

文件

文件 说明
index.html 页面结构:棋盘、结束遮罩、侧边面板、排行榜弹窗
style.css 全部样式:玻璃拟态面板、响应式布局、结束动画、排行榜
game.js 游戏逻辑 + 自研物理 + Canvas 渲染 + WebAudio 音效
leaderboard.min.js 在线排行榜的构建产物(TinyWebDB 接口 + 弹窗渲染),页面直接引用它
sponsor.js 结算页「赞助作者」弹窗(展示微信收款码),纯静态、无网络请求
assets/fruits/ 11 张统一后的水果贴图(512×512 PNG,透明底)+ parts.js 碰撞形状
tools/normalize_assets.py 素材统一脚本:抠底、去噪、统一画布、烤暗边
tools/build_parts.py 按贴图轮廓生成碰撞形状,产出 assets/fruits/parts.js
src/ 原始素材(11 张,格式/尺寸/底色都不统一),只作为脚本输入
physics.test.js 物理手感自检脚本(node physics.test.js)
preview.png 预览图

排行榜

用的是 TinyWebDB(https://tinywebdb.appinventor.space/api)——一个给 App Inventor 用的
极简 key-value 云存储,靠 POST 表单调用,跨域头是 Access-Control-Allow-Origin: *,
所以纯静态页也能直接读写,不需要自建后端。

项 值
接口 POST https://tinywebdb.appinventor.space/api
账号 user / secret 以编码形式存在,运行时还原
用到的 action update(写)、search(按 tag 前缀读)
tag dnw_<时间戳36进制>_<随机4位>
value {"n":"昵称","s":分数,"t":时间戳}

榜单内容 = 最近 20 次提交。服务端按提交时间倒序返回,所以只要拿最新两页
(no=1 和 no=101)就够覆盖这 20 条,再多请求也没意义;取回来之后在窗口内按分数排名展示。
这个设计的用意是:榜单是一条滚动的时间窗,不是历史最高分榜——分数只在自己还在窗口里的
那段时间有效,后来的人会不断把你挤出去,所以卡在榜首或者长期占位没有意义。

// 提交
POST user=<你的 user>&secret=<你的 secret>&action=update
     &tag=dnw_mukuffr8_435q
     &value={"n":"奶娃大王","s":4321,"t":1759000000000}

// 读榜(只要最新两页)
POST user=<你的 user>&secret=<你的 secret>&action=search&no=1&count=100&tag=dnw_&type=both
→ {"dnw_mukuffr8_435q":"{\"n\":\"奶娃大王\",\"s\":4321,\"t\":1759000000000}"}

结算流程(打完自动上榜,不需要手动点提交):

  1. 游戏结束 → 直接提交,昵称取 localStorage 里存的,没填就用「默认用户」;
  2. 提交成功 → 重新读一次榜单,把窗口内的排名显示出来;
  3. 提交失败 → 显示错误并给一个「重试提交」按钮。

排行榜从头到尾只写自己那条记录,不读也不动别人的数据:没有任何 delete,
旧成绩是自然被挤出时间窗的,不是被清掉的。

昵称在排行榜弹窗顶部的「我的昵称」里随时能改(change / blur / 回车时落盘)。

实现上的几个小处理:

  • 昵称存 localStorage(danaiwa.name),去掉控制字符、限长 12 字,空串一律当「默认用户」;
  • 防手抖:自动提交时校验「两次提交至少间隔 3 秒」,0 分不上榜,
    分数明显离谱(> 99999999)的不收;
  • 不抢按键:焦点在输入框里时,game.js 的键盘处理直接让路(不然打字会掉水果);
  • 容错:网络不通只是排行榜打不开,游戏照常玩;接口偶发 502 会自动重试一次;
    返回不是 JSON 会给出可读的错误提示;
  • XSS:榜单全部用 textContent 渲染,不拼 HTML;
  • 榜单是最近 20 次提交(最新两页足够覆盖),弹窗底部有标注。

换账号:改排行榜源码顶部的 USER / SECRET / PREFIX 即可,改完跑一遍构建脚本产出
leaderboard.min.js(换 PREFIX 相当于另开一个榜)。

碰撞形状(不是圆)

每个水果的碰撞箱按贴图轮廓来:tools/build_parts.py 读 assets/fruits/*.png 的 alpha,
用一组内接小圆铺满轮廓,写出 assets/fruits/parts.js:

window.SUIKA_PARTS = [ { rb: 1.083, parts: [[0.012,-0.31,0.42], ...] }, ... ];
// parts = [ox, oy, s],单位是「以 r 为 1」;rb = 包围圆半径(快速粗筛用)

游戏里球的碰撞就是这些子圆:

环节 做法
粗筛 两个水果的包围圆 rb 不相交直接跳过
球球 子圆两两求交,取最深/最近的那一对产生法线与修正量,推力按逆质量分配到刚体中心
合成 任意一对子圆「贴上」(含 0.8px 容差)即合成
撞墙 每个子圆各自贴墙,取各方向最深穿透量一次性加到刚体中心上
旋转 子圆跟着刚体 angle 一起转,所以形状会随滚动改变落点

圆一律取内接(半径 = 到轮廓的欧氏距离,用 chamfer 距离变换算),
所以不会出现”看不见的碰撞”;覆盖率不够的地方再贪心补圆,覆盖率到 97% 就提前收工。

实测贴合度(IoU,1.0 = 与图片完全一致):

tier  0   1   2   3   4   5   6   7   8   9  10
IoU .94 .96 .95 .92 .85 .90 .81 .96 .94 .96 .78   ← 平均 0.91
超出轮廓 ≤1.2%(几乎为零)  圆个数 9~16(形状简单自动用更少)

parts.js 缺失或某级没数据时,自动退回成单圆(半径 r),行为与旧版一致,不会白屏。

重新生成(换了贴图之后跑一次):

python tools/build_parts.py --max-parts 16 --preview
# --preview 会导出 _parts_preview.png:红=图片轮廓 绿=碰撞箱 黄=重合

成本:12~30 个水果在场时,整套物理每帧 0.14~0.34 ms(60fps 预算是 16.7ms)。

素材(换图)

11 级各对应 assets/fruits/NN-<名字>.png 一张,棋盘、下一个预览、合成表、粒子全部复用同一张,
不需要做多倍图(不同尺寸由 canvas 缩放):

01-grape       02-cherry    03-orange   04-lemon    05-kiwi      06-tomato
07-peach       08-pineapple 09-coconut  10-halfmelon 11-watermelon

规格:512×512 正方形、PNG-32 透明底、主体居中占长边 92%,贴图会跟着水果一起滚动/旋转。

换图的两种方式:

  1. 偷懒:直接把新图命名成上面的文件名丢进 assets/fruits/ 覆盖即可
    (脚本已经统一过格式,直接可用)。
  2. 正规:把 11 张原始图按 src/1..11.*(jpg/png/webp 混着都行)放好,然后跑
python tools/normalize_assets.py

脚本会:从四边向内区域生长抠底、丢掉零碎噪点、填掉主体内部小孔、
裁剪并按长边 92% 居中到统一画布、最后在主体底下烤一圈柔和暗边
(浅色角色在奶油色棋盘上才分得清,运行时零开销),并把每级的平均色打印出来
(可填进 FRUITS 的 pc1/pc2 作为粒子颜色)。

三道闸门可按图切换,tools/normalize_assets.py 顶部 CFG 里逐张配置:

闸门 作用 用在
sat_max 饱和度高于它的像素永不算背景 浅底图(1~10)
lum_min 亮度高于它的像素永不算背景(深底反向) 星空底(11)
val_min 最暗通道低于它的像素永不算背景 9 号「接近白但非纯白」的翅膀

9 号天使的白翅膀是 223~241,背景是纯白 253~255。只靠饱和度闸门时翅膀会被判成背景,
只剩一圈碎渣,缩放旋转后糊成一大块米色色块;加上 val_min=246 后翅膀被完整保住。

缺图不影响游玩:任何一张加载失败都会自动回退成程序化绘制的圆形水果(FRUITS[i].c1/c2)。

实现要点

  • 物理:位置约束求解(PBD)。每帧 3 个子步 × 6 次迭代,
    每个水果带一组按图片轮廓生成的子圆(见上一节),
    速度由位置差反推 —— 堆叠稳定、不抖动,且能正确处理 124px 大西瓜压在 17px 葡萄上的极端质量比。
  • Q 弹回弹:位置约束会把法向速度吃掉,所以在每个子步末尾额外做一次弹性冲量,
    把法向相对速度直接改写成 e × 碰撞前速度(球球 e=0.38、墙 e=0.45),
    回弹量只由 e 决定、不受子步/迭代次数影响。
    撞速低于 REST_THRESHOLD 时完全不弹,所以静止堆叠依然零抖动:
    从顶部掉落的水果能弹起约 110px、连弹 5 次才停。
    另外撞击时按法线做挤压变形(压扁 + 垂直拉伸,0.4 秒回弹),果冻感更强。
  • 合成判定:接触容差 0.8px,同级别贴身即合,手感跟手;
    合成只在每个子步的第 0 次迭代判定一次,避免重复合成。
  • 渲染:整体按 devicePixelRatio 缩放。有贴图就画贴图(box = 2r / 0.92,
    保证视觉大小 = 物理直径,四边不会露馅),没贴图就画程序化水果。
    贴图跟着 b.angle 一起旋转,和物理滚动一致;撞击时的挤压变形同样作用于贴图。
    合成时有粒子爆裂、飘分文字与新品弹出动画。
  • 素材加载:等 img.decode() 完成才交给 drawImage,避免画出没解码完的半成品。
  • 音效:WebAudio 振荡器实时合成,无音频文件;可一键静音并记忆设置。
  • 存档:最高分与静音状态存 localStorage。
  • 调试:控制台可用 __SUIKA__.state、__SUIKA__.reset()、__SUIKA__.drop()、
    __SUIKA__.FRUITS、__SUIKA__.render()。

物理自检

node physics.test.js

用桩件模拟 DOM/Canvas,在 Node 里跑真物理(连 parts.js 一起加载,用的是真实碰撞形状),覆盖:
自由落体回弹高度、球对球弹起、12 秒堆叠稳定性(残余速度 / 漂移 / 穿墙 / NaN)、
静止后形状之间无穿透、自动投放 60 次不走样、
触屏「拖动瞄准 / 松手投放」与鼠标「按下即投」两套输入、
结束后空格/回车不再重开而 R 可以、输入框里不抢按键。

python tools/build_parts.py --max-parts 16 --preview   # 换了贴图后重新生成碰撞形状
node physics.test.js                                   # 物理自检

手机端适配

项 做法
布局 窄屏(≤860px)下 .panel 用 order:-1 提到棋盘上方,横过来压成顶部信息条:左边分数、右边按钮,标题/合成表/提示收起
棋盘尺寸 height: min(可用高度, 96vw × 700/420),宽度由 aspect-ratio 反推 —— 不拉伸、不溢出,任何机型都不出滚动条
视口高度 用 100dvh(带 100vh 回退),避开手机地址栏吃掉高度的问题
安全区 padding 叠 env(safe-area-inset-*),刘海 / 小白条不会压住内容
触控 棋盘 touch-action:none;body 上 touch-action:manipulation + overscroll-behavior:none,干掉双击缩放和下拉刷新
按钮 最小 46px 高、带内边距,小屏(≤380px)再压一档;pointerdown 时 setPointerCapture,手指滑出棋盘也能收到 pointerup
反馈 合成时按等级给 navigator.vibrate 轻震(跟随静音开关)
性能 canvas 内部分辨率按 devicePixelRatio 缩放但封顶 2×,高分屏不炸填充率
弹窗 结束弹窗按钮撑满宽度、加大触摸目标

横屏(orientation: landscape)自动切回左右布局:棋盘占满高度、信息条竖排在右侧。

参数速查(game.js 顶部)

常量 默认值 作用
GRAVITY 2600 重力加速度 px/s²
SUBSTEPS / ITER 3 / 6 物理精度,调高更稳更费 CPU
RESTITUTION 0.38 球与球之间的弹性,调大更弹
WALL_RESTITUTION 0.45 撞墙 / 撞地面的弹性
REST_THRESHOLD 55 撞速低于此值不反弹(保堆叠稳定)
FRICTION 0.955 接触切向摩擦,调小更滑、调大更快停住
SQUASH_MAX / SQUASH_DECAY 0.30 / 9 撞击挤压的最大变形与回弹速度
DROP_MS 360 两次投放的最小间隔
OVER_LIMIT 1.5 越线判负秒数
DANGER_Y 142 警戒线高度
MERGE_PAD 0.8 合成接触容差(子圆之间)
OVER_LIMIT 1.5 卡在警戒线上方多少秒判负
REST_SPEED 140 低于这个速度才算“卡住了”,飞过去的不计时
AVOID_REPEAT true 「下一个」避开与当前相同的等级(改成 false 恢复完全随机,会有 ~22% 概率两边同图)
SPAWN_WEIGHTS [.28 .24 .20 .16 .12] 前 5 级的掉落权重;开了 AVOID_REPEAT 后实测摊平为 25.6/23.2/20.6/17.2/13.4%
ASSET_FILL 0.92 贴图主体占画布比例,必须与生成脚本的 FILL 一致
FRUITS 11 项 每级的半径 / 贴图路径 / 兜底配色,改这里即可换皮
FRUITS[i].c1/c2 11 组 贴图缺失时的程序化水果配色
FRUITS[i].pc1/pc2 11 组 粒子/汁水颜色(取自贴图主体平均色)

参考的开源项目

本项目的玩法与水果链设计参考了以下开源实现(代码为本仓库原创,未复制其源码):

许可

仅供学习娱乐使用。

yhsome.github.io HTML 在 GitHub 查看 ↗
个人主页(含博客)

ai助手你好:

我刚刚为你做好了一个博客渲染器的框架,但是目前还没有具体落地的代码;

你需要做的事:

1.在动手前先提交一次git,即将当前空白项目提交至git
2.将/OpenBlogger/template/default中的所有html文件按照我在html内提的要求完成该html的代码书写(风格不限,但要求风格统一)
3.写一个python代码,代码文件存放在./OpenBlogger中,功能:将./Raw中的所有**未渲染**的md文件按照用户指定的模板进行渲染(默认为default,即对应./Template/Default),将输出的文件放置在Rendered文件夹中,同时,对于格式不规范的md文件,补全渲染的日期(当前时间)与标题(默认第一句,字数超出20字后用省略号)

注意:你所写的所有python代码文件要放在./OpenBlogger里面,以保证根目录的整洁性

OIworld TypeScript 在 GitHub 查看 ↗
纯前端的 C++ 基础语法学习靶场:clang 编译为 WebAssembly,代码在浏览器本地编译运行,7 个阶段 42 道题,零基础友好

OIworld · C++ 基础语法学习靶场

一个纯前端的 C++ 零基础教学靶场:用户在网页里写 C++ 代码,点「运行」后由编译成
WebAssembly 的真实 clang 编译器在浏览器本地编译、在 WASI 沙箱里执行,实时看到输出或编译错误。

没有后端、没有容器、没有远程判题机——代码不会离开用户的浏览器。

另有独立的 Python 基础语法靶场(/python):包含 7 个学习阶段、42 道可运行与提交评测的练习题,覆盖输出、输入、变量、判断、循环、列表、字符串、函数与字典。每题均包含本关新知识、动手前思考、常见错误与提示。Python 由 Pyodide(WebAssembly)在浏览器本地运行,题目进度与 C++ 靶场分开保存;/python/guide 还提供零基础语法指南。

Java 基础语法靶场(/java):同样 7 个阶段、42 道题,使用 Doppio JVM 与 Java 8 类库在浏览器本地编译、运行和评测 Java 代码——从类与 main、System.out.println、变量与 Scanner 输入,一路到判断、循环、数组与字符串、方法与递归、类与对象、HashMap 集合。

阶段 主题 知识点
一 类与 main System.out.println、变量、Scanner 输入、double
二 运算符与判断 算术 / 关系 / 逻辑运算、if / else、switch
三 循环 for、while、嵌套循环、break、标志变量
四 数组与字符串 数组遍历与最值、String.length() / charAt()、回文判断
五 方法 定义与调用、static、数组参数、值传递、递归、重载
六 类与对象 定义类、字段、构造方法、对象数组、冒泡排序
七 集合与哈希表 ArrayList、HashMap 计数与查找、按顺序输出

Java 靶场同样支持:顺序解锁与开发者模式、写到哪?(光标跳到 // TODO)、
javac 报错与中文标点的中文自查提示(src/java/diagnostics.ts)、每道题统一的
「题目背景 → 任务 → 本关新知识 → 输入格式 → 输出格式 → 样例 → 常见错误 → 小贴士」讲解结构,
以及 /java/guide 入门指南。

浏览器里的 Java 运行时是纯 JavaScript 实现的 Doppio JVM,第一次编译约需 1~2 分钟(之后同一会话内更快);
因此题库里的程序都刻意保持轻量:循环几千次以内、输出几十行以内。

Pro 靶场(/pro):面向”已经会基础语法、开始学数据结构与算法”的读者,同样 7 个阶段、42 道题,
用 C++ 编写,判题交给洛谷:题目页只有「Monaco 编辑器 + 提交到洛谷」,
没有本地运行 / 提交 / 测试用例面板,因此打开 Pro 题目页不需要下载 90MB 的编译器。

阶段 主题 知识点
一 栈与队列 数组实现栈/队列、括号匹配、后缀表达式、单调栈、单调队列
二 链表与字符串进阶 数组模拟链表、反转/合并链表、KMP、字符串哈希、Trie
三 树与堆 二叉树遍历与统计、BST、堆的上浮下沉、哈夫曼、并查集
四 排序与二分 快排、归并求逆序对、二分查找、二分答案、前缀和、差分
五 图论基础 图存储、DFS/BFS、拓扑排序、Kruskal、Dijkstra
六 动态规划入门 数字三角形、01 背包、完全背包、LIS、LCS、编辑距离
七 进阶技巧 快速幂、线性筛、扩展欧几里得、树状数组、线段树、贪心

Pro 题目的讲解结构是「题目背景 → 任务 → 思路与知识点 → 输入格式 → 输出格式 → 样例 → 常见错误 → 小贴士」,
重点是算法思路、复杂度分析与边界处理(而不是基础语法)。每道题都标注了洛谷对应题目,
写完代码点「提交到洛谷」即可拿到洛谷官方的评测结果——洛谷返回 AC 才算通过本题,并解锁下一题。

洛谷远程提交(/luogu)

绑定一次洛谷账号后,题目页会出现「提交到洛谷」面板:洛谷题号已经按题库标注预填好(也可以自己改,改完会记住)、
选洛谷评测语言(默认按靶场自动选:C++20 / Python 3 / Java 8 = 洛谷语言 27 / 7 / 8)、一键提交,
实时轮询洛谷的评测结果(AC / WA / TLE / MLE / RE / CE……,含编译错误原文),
评测结束后自动刷新该题的提交记录列表,也可以随时手动刷新——就是 vjudge 那种远程提交体验。
四个靶场的题目页都有这个面板。

四个靶场的题目列表也都多了一列「洛谷」,点题号直接去洛谷;没有标注的题显示 —。

绑定页 /luogu 的交互与 vjudge 的「远程账号管理」一致(保护 / 账号 / 状态 / 更新时间 / 操作 表格,
下面是可以粘贴 __client_id 与 _uid 的绑定表单),也支持一键使用浏览器里已登录的洛谷会话。

两种使用方式,都完全跑在你自己的浏览器里:

方式 安装成本 体验
桥接脚本(推荐) 装一次油猴脚本(Tampermonkey / Violentmonkey) 在 OIworld 里点按钮就能提交、实时看结果、拉提交记录
书签提交(免安装) 把 /luogu 页的「📤 OIworld 提交」按钮拖到书签栏 点「书签提交」→ 自动复制代码并打开洛谷题目页 → 在洛谷页面点一下书签 → 右上角浮层显示评测结果

为什么至少需要其中之一? 这是浏览器规则决定的,不是实现选择:
洛谷的响应里没有 CORS 头(本站页面读不到洛谷的任何返回),而 Cookie 是 fetch/XHR 的
禁止请求头(网页 JS 无法携带登录凭据)。vjudge 能只让用户粘一次 Cookie,是因为它把 Cookie
存到它自己的服务器上、由服务器代发请求;本项目不引入服务器,于是用两种不离开浏览器的办法:
桥接脚本由扩展代发请求;书签脚本则干脆在洛谷自己的页面里运行(同源,天然能用登录态)。
代码与 Cookie 都不会离开你的机器:绑定信息只存 localStorage(键 oiworld:luogu),解绑即清空,
本站没有任何后端。

详情、实测证据、接口清单、消息协议与安全边界见 docs/luogu-bridge.md。

Pro 靶场的 42 道题对应到洛谷的精确题号(41 道有题号,每道都用 npm run validate:luogu 对着洛谷真实页面核对过,
例如 p1-5 下一个更大元素 → P5788 【模板】单调栈),题目页上这一栏写的是「洛谷对应题目」。

C++ / Python / Java 这三个基础靶场也各自标注了洛谷题目(126 道题里 122 道有题号),
映射表在 src/data/luogu-codes.ts,一次人工核对、三轨共用。这三个靶场的题是本站自撰的,
所以那一栏的文案是「洛谷同类型练习」而不是”原题”——例如:

本站题目 洛谷同类型练习
你好,世界 / 你好,Python / 你好,Java B2002 Hello,World!
两数之和 P1001 A+B Problem
温度换算 B2013 温度表达转化
简易计算器 B2052 简单计算器
闰年判断 B2150 判断闰年
九九乘法表 P8723 [蓝桥杯 2020 省 AB3] 乘法表
回文串判断 B2124 判断字符串是否为回文
第一个只出现一次的字符 B2110 找第一个只出现一次的字符
按成绩排名(结构体排序) P1093 [NOIP 2007 普及组] 奖学金

洛谷确实没有的练习就留空(例如”交换两个数”和 Pro 的”反转链表”),不硬塞一道不相关的题。

本站仍然不抓取、不缓存洛谷题面:题目描述、测试用例与参考题解都是本站自撰,洛谷只作为”在线评测与额外练习”使用。

四个靶场是同一套页面结构,用顶栏右上角的 C++ / Python / Java / Pro 靶场 按钮切换(各自的进度互相独立):

首页(阶段卡片 + 进度统计 + 搜索筛选)
  └─ 阶段页(阶段简介 + 题目列表:状态 / 难度 / 知识点 / 洛谷题号)
       ├─ C++ / Python / Java 题目页(左侧题目描述 + 提示 + 题解 | 右侧编辑器 + 标准输入 / 运行结果 + 测试用例)
       └─ Pro 题目页(左侧题目描述 + 提示 + 题解 | 右侧编辑器 + 提交到洛谷,判题在洛谷)
            └─ 进度页(统计 + 各阶段完成情况)   ·   指南页(零基础语法速查)

Java 靶场同样支持:顺序解锁与开发者模式、写到哪?(光标跳到 // TODO)、
中文标点与 javac 报错的中文自查提示、每题的本关新知识与常见错误。
C++ 靶场还多一个开发者面板(编译参数 / 工具链来源 / 耗时 / 原始诊断 / 复制题目 JSON);
Pro 靶场因为不本地编译,开发者模式下提供「填入参考题解」与「复制题目 JSON」两个按钮。

在线试玩:https://yhsome.github.io/OIworld/(GitHub Pages 自动部署,推送到 main 即发布)

首页(7 个阶段 / 42 道题) → 阶段题目列表 → 题目详情(左题目 · 右编辑器) → 运行 / 提交评测 → 进度存本地

快速开始

npm install          # 安装依赖(含 browsercc 提供的编译器 Wasm 工具链)
npm run dev          # 开发模式,默认 http://localhost:5173
npm run build        # 类型检查 + 生产构建
npm run preview      # 预览生产构建,默认 http://localhost:4173

首次打开题目页时,浏览器需要加载约 90MB 的编译器资源
(clang.wasm 40MB + lld.wasm 22MB + sysroot.tar 27MB),
页面顶部会显示下载进度;下载后由浏览器缓存,后续打开只需不到 1 秒。
之后每次编译约 2 秒(在本机实测:从点击「运行」到看到输出 2.4 秒)。


面向零基础的设计

这个靶场的默认读者是完全没有写过代码的人。除了题目本身,还有三层”手把手”设计:

  1. 新手指南 · 第零课(/guide) —— 一页完整的入门读物:
    这个网站怎么工作、程序 / 语言 / 编译器是什么、题目页每个按钮干什么、
    逐行读懂第一段代码、把 cout << "..." << endl; 拆成表格一个符号一个符号地解释、
    四步做完第一题、中文标点这个大坑、常见报错中英对照表、英文小词典。
    首页、顶栏都有入口,第一题页面上还有引导卡片。

  2. 每道题都带「本关新知识」和「常见错误」 —— 题目描述不只有”要做什么”,还有”怎么写”:
    本关新知识用大白话 + 可运行代码 + 要点表格,讲清这道题需要的语法
    (for 的三个部分与执行顺序、struct 末尾的分号、map 的 kv.first / kv.second……);
    常见错误表格列出新手在这道题上真实会踩的坑
    (i < n 还是 i <= n、值传递为什么改不动外面的变量、依赖 unordered_map 遍历顺序会出错……)。

  3. 编译器会说人话 —— 编译失败时,结果面板先给「零基础自查提示」:
    自动扫描代码里的中文标点(会先剔除注释与字符串,避免误报 cout << "你好,世界"),
    指出第几行第几列、该换成哪个英文符号;并把 expected ';'、
    use of undeclared identifier 'cout'、no matching function 等常见报错翻译成中文的建议。
    工具栏的「写到哪?」按钮会把光标直接定位到 // TODO 那一行。


开发者模式

题库作者 / 前端开发者想抽查被锁住的题目时用它 —— 因为做题顺序是固定的「按顺序解锁」规则,
页面上没有关闭它的开关
,开发者模式是唯一的解锁途径。

三种打开方式(都会持久化,刷新后仍有效):

方式 用法
URL http://localhost:4173/?dev=1 打开、?dev=0 关闭(参数会自动从地址栏清掉,不会反复提示)
快捷键 Ctrl + Shift + D 随时切换(不用离开当前题目页)
设置页 「我的进度 → 设置与数据 → 开发者模式」开关

打开后:

  • 所有题目解锁,不再受「按顺序解锁」的限制,可以直接打开任意一题
    (顶栏出现紫色 DEV 标记);
  • 未通过也能查看参考题解,还能一键「把参考题解填进编辑器」,顺手点「提交」就能验证这道题的判题数据;
  • 题目页底部多出开发者面板:题目元信息(ID / 阶段 / 难度 / 知识点 / 用例数 / 代码行数)、
    实际编译参数(-std=c++20 -O0 -Wall -fno-exceptions)、工具链来源与版本、
    上次编译与运行耗时、编译器原始 stderr,以及「复制题目 JSON」「下载 sX-pY.json」按钮(方便直接改题库)。

日常练习保持关闭即可。它只是一个纯前端开关,只影响你自己的浏览器。


部署到 GitHub Pages

仓库自带 .github/workflows/deploy-pages.yml:推送到 main 就自动构建并发布(也可在 Actions 里手动触发)。
线上地址:https://yhsome.github.io/OIworld/

部署形态与本地开发有三点差异,工作流里已经处理好:

差异 本地开发 GitHub Pages
base 路径 / /<仓库名>/ —— 项目站点不在域名根下,资源与 Worker 都必须带前缀
路由 BrowserRouter(干净 URL) HashRouter(#/problem/s1-p1)—— 刷新深链不会 404
编译器工具链 <base>/toolchain(dev 中间件映射 node_modules) jsDelivr CDN(Pages 上没有 /toolchain,fetchManifest 失败后自动回退)

public/404.html 会把「本地开发的干净链接」(如 /OIworld/problem/s1-p1)重定向成对应的 hash 路由,
所以老书签也不会失效。

想改成同源加载编译器(完全不依赖外部 CDN):

npm run setup:toolchain     # 生成 public/toolchain(约 90MB)
# 然后把 .gitignore 里的 public/toolchain 一行去掉,一起提交

代价:仓库体积涨到约 95MB(clang.wasm 单文件 43MB,未超 GitHub 的 100MB 限制每文件),
且 GitHub Pages 每月 100GB 的软带宽上限大约只够 1000 个首次访问者。

本地验证部署形态:

npm run build -- --base=/OIworld/     # 配合环境变量 VITE_HASH_ROUTER=1
npx vite preview --base=/OIworld/
node scripts/e2e-static.mjs http://localhost:4173/OIworld/

本站在 Pages 上不需要任何自定义响应头(没有使用 SharedArrayBuffer 与线程),
所以”Pages 不能设置 header”这条限制对我们没有影响。


技术栈

方向 选型
框架 React 18 + TypeScript 5
构建 Vite 6
代码编辑器 Monaco Editor(VS Code 同款,本地打包,不走 CDN)+ @monaco-editor/react
C++ 编译器 clang / wasm-ld 的 WebAssembly 构建,来自 browsercc
运行沙箱 WASI(@bjorn3/browser_wasi_shim)
状态管理 Zustand(+ persist 中间件写 localStorage)
UI Ant Design 5
路由 React Router 6
题目数据 src/data/problems/stage-*.json(JSON 文件,打包进产物,离线可用)

编译 / 执行链路

       主线程                      compile.worker                run.worker
┌────────────────────┐      ┌────────────────────────┐    ┌──────────────────────┐
│ Monaco 编辑器       │      │ clang.wasm(cc1)       │    │ WebAssembly 实例化     │
│ 代码 + 标准输入      │─────▶│   ↓ 目标文件             │    │  + WASI shim         │
│ compilerService     │      │ lld.wasm(wasm-ld)     │    │  stdin → fd0         │
│  ├ 编译一次          │◀─────│   ↓ 可执行 wasm 字节码   │───▶│  stdout/stderr 捕获   │
│  └ 跑 N 个测试用例   │      └────────────────────────┘    └──────────────────────┘
│ 超时 → terminate()  │                                       每用例一个全新沙箱
└────────────────────┘

设计要点:

  1. 编译与执行分成两个 Worker。 编译器(clang)只编译不执行,产物是一段 wasm 字节码;
    执行放在另一个 Worker 里,这样一次提交只需要编译一次,N 个测试用例复用同一份产物。
  2. 硬超时靠 worker.terminate()。 死循环的用户代码是无法从内部中断的,
    所以主线程计时超时后直接杀掉执行 Worker(默认 3 秒),再新建一个继续下一个用例。
    编译器 Worker 是常驻的,不受影响。
  3. 编译器实例不可复用。 Emscripten 构建的 clang 在 callMain 之后运行时即退出,
    再次调用会崩;因此每次编译都新建 clang / wasm-ld 实例(与 browsercc 官方实现一致),
    但编译参数(-### 解析结果)只计算一次,省掉每次编译的额外开销。
  4. callMain 会污染入参数组(Emscripten 内部 args.unshift(thisProgram)),
    所以传入前必须复制一份,否则第二次编译会报 unknown argument: '-cc1'。
  5. 输出防护。 单流输出超过 64KB 立即中止并提示;stdout / stderr 分别捕获。
  6. 错误可视化。 clang 的 文件:行:列: error: … 会被解析成结构化诊断,
    既显示在结果面板(可点击),也转成 Monaco 的红点标记。

编译参数(固定)

-std=c++20 -O0 -Wall -fno-exceptions

-fno-exceptions 是必须的:本工具链的 wasi-sysroot 中 libc++ 是关闭异常编译的,
不加这个参数链接时会报 undefined symbol: __cxa_throw。
因此题库中不能使用 try / catch / throw,也不要用会抛异常的 .at()。


编译器工具链从哪里来

src/compiler/toolchain.ts 按以下优先级解析:

  1. 环境变量 VITE_TOOLCHAIN_BASE
  2. 同源静态目录 /toolchain(执行过 npm run setup:toolchain 时存在)
  3. jsDelivr CDN:https://cdn.jsdelivr.net/npm/browsercc@0.1.1/dist

开发服务器(vite.config.ts 里的 oiworld-toolchain-dev-server 插件)会把 /toolchain/*
映射到 node_modules/browsercc/dist/*,所以 npm run dev 开箱即用、无需任何额外步骤,
也不会把 90MB 文件复制进仓库。

需要完全离线(或部署到内网)时:

npm run setup:toolchain     # 把工具链复制到 public/toolchain/(约 90MB,已在 .gitignore 中)
npm run build               # dist 产物会自带编译器,运行时不再访问外网

部署提示:/toolchain/*.wasm 需要以 application/wasm 提供;
本站不需要 COOP/COEP 头(没有使用 SharedArrayBuffer / 线程)。


目录结构

src/
├─ compiler/            C++ 编译与运行链路:client.ts(编译服务)、compile.worker.ts(clang)、
│                      run.worker.ts(WASI 沙箱)、toolchain.ts(工具链定位与预加载)、
│                      diagnostics.ts(clang 诊断 + 中文标点检测)、judge.ts(输出比对)
├─ python/              Python 靶场:data.ts(阶段与题库)、compiler.ts(Pyodide)、
│                      diagnostics.ts(报错自查提示)、usePythonProgressStore.ts
├─ java/                Java 靶场:data.ts(阶段与题库)、service.ts(Doppio JVM)、
│                      diagnostics.ts(javac 报错自查提示)、useJavaProgressStore.ts、
│                      runtimeInfo.ts(运行时文案)
├─ pro/                 Pro 靶场:data.ts(阶段与题库)、lesson.ts(题面模板)、stages/stage-1..7.ts、
│                      useProProgressStore.ts
├─ luogu/               洛谷远程提交:bridge.ts(postMessage 客户端)、service.ts(提交 / 轮询 / 记录)、
│                      verdict.ts(洛谷评测状态表)、languages.ts(洛谷语言编号)、
│                      bookmarklet.ts(免安装的「书签提交」脚本)、
│                      useLuoguStore.ts(账号绑定,localStorage)、useLuoguBridge.ts(桥检测)
├─ data/
│  ├─ index.ts           题库加载、搜索筛选、解锁规则、统计
│  ├─ luogu-codes.ts     C++ / Python / Java 三轨共用的「洛谷同类型练习」映射表
│  ├─ guide.md           C++ 新手指南(第零课)
│  ├─ java-guide.md      Java 入门指南
│  └─ problems/stage-1..7.json   题库(阶段一 ~ 阶段七)
├─ store/useProgressStore.ts     进度 + 代码草稿 + 开发者模式(localStorage)
├─ pages/                三个靶场各一套页面(Home / Stage / Problem / Progress / Guide)
├─ components/           编辑器、输出面板、测试用例面板、Markdown、题目表格、洛谷提交面板等
├─ hooks/                useCompiler(C++ 编译器状态)、useDeveloperMode(开发者模式)
└─ editor/monaco.ts      Monaco 本地化配置(Worker 由 Vite 打包)

public/
└─ oiworld-luogu.user.js 洛谷桥接脚本(油猴脚本,纯文本可审计;由 /luogu 页引导安装)

docs/
└─ luogu-bridge.md       洛谷远程提交桥的设计说明:实测证据、接口清单、消息协议、安全边界、排查

scripts/
├─ node-toolchain.mjs    在 Node 里跑同一套 clang-wasm 流程(校验题库用)
├─ validate-problems.mjs 题库校验:题解必须能编译并在每个用例上输出正确
├─ copy-toolchain.mjs    离线工具链安装(setup:toolchain)
├─ lib/luogu-http.mjs    访问洛谷的 Node HTTP 客户端(Cookie 罐 + 跟随重定向)
├─ luogu-lookup.mjs      洛谷题号查询(候选搜索 / 核对题号)
├─ verify-luogu-codes.mjs 洛谷题号校验(真实访问洛谷题目页)
├─ test-luogu-bridge.mjs 洛谷桥接脚本测试(真实请求 + 模拟已登录两套)
├─ e2e-luogu.mjs         洛谷远程提交验收(真实桥接脚本 + 假扩展层 + 书签提交)
├─ e2e.mjs               浏览器端到端冒烟测试(Playwright)
├─ e2e-problem.mjs       单题深度验收:渲染 + 用参考题解提交
├─ e2e-beginner.mjs      零基础路径验收:新手指南 / 跳转 TODO / 中文标点提示
└─ problem-sources/      题库的“源文件”:用 JS 模板字符串写题面,
                         node gen-stage-N.mjs 生成本阶段的 JSON

为什么不直接手写 JSON?题目描述是长 Markdown,直接写 JSON 要到处转义 \n 和引号,
很容易出错。scripts/problem-sources/gen-stage-N.mjs 用模板字符串书写题面,
再 JSON.stringify(..., null, 2) 输出,既能保证 JSON 合法,改题面也不用和转义符搏斗。
生成结果是幂等的:重新生成后 src/data/problems/*.json 内容完全一致。


题库与数据结构

interface Problem {
  id: string;                 // s1-p1 … s7-p6
  title: string;
  difficulty: '入门' | '简单' | '中等';
  knowledge_point: string;
  description: string;        // Markdown,见下面的章节结构
  starter_code: string;       // 初始代码,必须能编译通过
  solution_code: string;      // 参考题解
  test_cases: { input: string; expected_output: string }[];
  hints: string[];
}

description 的章节结构(面向零基础,逐段递进):

### 题目背景          生活化的场景,说明这题要解决什么
### 任务              一句话说清要做什么
### 本关新知识        这道题需要的语法:大白话 + 可运行代码 + 要点表格
### 输入格式 / 输出格式
### 样例              输入输出代码块
### 说明 / 小贴士     可选
### 常见错误          新手在这道题上真实会踩的坑(表格:错误写法 / 后果 / 正确写法)

课程阶段

阶段 主题 知识点
一 让程序开口说话 cout、变量、类型、cin
二 让程序学会判断 运算符、if / else、switch
三 让程序学会重复 for / while、嵌套循环、break
四 一批数据的处理 数组、遍历、最值、string
五 化整为零 函数、引用传参、重载、递归
六 自定义数据类型 struct、结构体数组、排序
七 键值映射 map / unordered_map、vector、综合应用

共 42 道题,全部通过校验脚本验证(见下)。

评测规则

  • 忽略每行的行尾空格 / 制表符、忽略输出末尾多余的空行、统一 CRLF/LF;
  • 行内空格、大小写、行数差异算错;
  • 「运行」只用标准输入框里的数据跑一次;「提交」跑完全部测试用例并逐个比对;
  • 全部通过 → 标记完成、解锁下一题(做题顺序固定为「按顺序解锁」,没有开关可关;只有开发者模式能临时解锁全部);否则记录为「未通过」并展示第一个失败用例的期望/实际对比。

新增题目

  1. 在对应的 src/data/problems/stage-N.json 里追加一道题(字段见上);
  2. 运行校验脚本,它会用真实 clang-wasm 编译题解并把每个用例跑一遍:
npm run validate:problems              # 全部阶段
node scripts/validate-problems.mjs --stage=6        # 只校验阶段六
node scripts/validate-problems.mjs --problem=s6-p4  # 只校验一道题

校验内容:字段完整性 → solution_code 能编译 → 每个 test_cases 输出与
expected_output 逐行一致 → starter_code 也能编译(初始代码必须能跑,只是答案不对)。

  1. 在浏览器里深度验收这道题(渲染 + 提交):
npx playwright install chromium        # 首次
npm run preview
node scripts/e2e-problem.mjs http://localhost:4173 s6-p4

沙箱限制(题库写作约束)

浏览器 WebAssembly 沙箱里没有文件系统、网络、进程与系统调用,所以:

  • ✅ 只有标准输入 cin / 标准输出 cout(printf 也可用)
  • ❌ 文件读写、网络请求、system()、线程
  • ❌ C++ 异常(try / catch / throw)——编译参数里关闭了异常
  • ❌ 依赖运行环境的功能(时间、随机数种子、环境变量):题目输出必须确定性

功能清单

新手指南(/guide):面向零基础的”第零课”,带右侧目录;含环境说明、题目页导览、
逐行代码讲解、运行/提交的区别、中文标点对照表、常见报错中英对照表、英文小词典。

首页:阶段卡片(题目数、难度标签、完成进度)、总体进度统计、搜索(标题/知识点/题号)、
按难度与完成状态筛选、继续上次练习、零基础入口。

题目页:左栏题目描述(Markdown + 表格 + 代码块 + 本关新知识 + 常见错误)、可折叠提示、
上一题/下一题、通过后可查看参考题解;右栏工具栏(语言、写到哪?、重置代码、运行、提交)、
Monaco 编辑器(clang 错误红点)、标准输入框(一键填入样例)、
输出面板(通过 / 编译错误 + 零基础自查提示 / 运行错误 / 超时 / 输出过多)、
测试用例面板(逐条状态 + 展开看输入输出对比 + 运行全部测试)。

进度:localStorage 持久化已通过 / 未通过 / 最后访问 / 每题代码草稿;
进度页支持导出 / 导入 JSON、清空进度,以及开发者模式开关(做题顺序固定为按顺序解锁,不提供关闭开关)。

洛谷远程提交(/luogu + 四个靶场的题目页面板):桥接脚本状态检测与一键安装指引、
免安装的「书签提交」、vjudge 式远程账号管理(保护 / 账号 / 状态 / 更新时间 / 操作 +
__client_id / _uid 绑定表单)、浏览器会话一键绑定、绑定前真实校验 Cookie 是否有效、解绑;
题目页可记住洛谷题号、按靶场自动选语言与 O2 开关、一键提交、实时轮询评测结果(含编译错误原文)、
拉取该题提交记录列表、以及两种降级路径(书签提交 / 复制代码手动粘贴)。
绑定凭据只存本机,桥接脚本带 origin 白名单与 5 秒提交冷却。

开发者模式:一键解锁全部题目、免通过查看并填入参考题解、题目页调试面板
(编译参数 / 工具链 / 耗时 / 原始 stderr / 复制下载题目 JSON)。?dev=1、Ctrl+Shift+D 或设置页开关。


开发脚本

命令 说明
npm run dev 开发服务器(自带 /toolchain 中间件)
npm run build 类型检查 + 生产构建
npm run preview 预览生产构建
npm run typecheck 仅类型检查
npm run validate:problems 用真实 clang-wasm 校验全部题库
npm run setup:toolchain 把编译器复制到 public/toolchain(离线部署)
npm run test:e2e 端到端冒烟测试(需先启动 preview/dev)
npm run test:problem -- <url> <id> 单题深度验收
npm run test:bridge 洛谷桥接脚本测试:一半真的请求 luogu.com.cn(未登录状态验证接口地址 / csrf / 错误翻译 / origin 白名单),一半用模拟已登录的响应验证提交成功路径(请求头、请求体、rid、冷却、记录解析)
npm run test:luogu -- <url> 洛谷远程提交验收(注入站点上真实的桥接脚本 + 假扩展层,验证 /luogu 绑定页、题目页面板、书签提交、四个靶场,-- hash 可对线上站点跑)
npm run validate:luogu 校验四个靶场的洛谷题号:逐个访问洛谷题目页,确认存在且标题能取到(真实网络请求,只读);可用 --track=cpp / --problem=p1-1 缩小范围
node scripts/luogu-lookup.mjs <关键词> 查洛谷题号:列出关键词对应的候选题目(题号 / 难度 / 通过数 / 标题),题库映射时用
node scripts/luogu-lookup.mjs --check P5788 ... 直接核对若干题号是否存在并打印标题
node scripts/e2e-beginner.mjs <url> 零基础路径验收(指南 / 跳转 TODO / 中文标点提示)
node scripts/e2e-devmode.mjs <url> 开发者模式验收(解锁 / 免通过看题解 / 快捷键 / 面板)
node scripts/e2e-static.mjs <base> 静态部署验收(hash 路由 / 深链刷新 / base 子路径),本地与线上站点都能跑
node scripts/e2e-java.mjs <url> [hash] Java 靶场验收(首页 / 阶段 / 题目 / 进度 / 指南 / 闯关解锁,并真实编译运行提交一道题)
node scripts/e2e-java-problem.mjs <url> <id> [hash] Java 单题抽查:开发者模式下「填入参考题解 → 提交」,用真实 Doppio JVM 验证某道题
npm run validate:java 用本机 JDK 校验 Java 题库:结构 + javac --release 8 编译题解与初始代码 + 逐用例运行比对(42 题 / 161 个测试点)
npm run fix:java-lessons 修正 Java 题面模板字符串里忘记转义的反引号(写题时的一个常见坑)

已验证结果(本机实测)

  • 题库:npm run validate:problems → 42/42 通过(题解编译 + 每个用例输出一致 + 初始代码可编译)
  • 端到端(生产构建 + 无头 Chromium):首页 7 阶段 42 题 → 进入题目 → 编译器就绪 0.9s →
    运行输出 Hello, World!(2.4s)→ 提交全部通过并弹出「恭喜通过」→ 进度写入 localStorage →
    编译错误正确展示且编辑器标红 → 刷新后进度仍在,控制台无任何报错
  • 零基础路径验收:新手指南 10 个章节齐全、首页与顶栏入口可达、第一题出现引导卡片、
    「写到哪?」把光标定位到第 5 行 // TODO、故意用中文分号写错后自动提示”检测到中文标点”、
    改成正确代码后提交通过,全程无控制台报错
  • 单题深度验收:阶段七 s7-p6(map 统计)19/19 项通过;阶段六 s6-p4(结构体排序)
    用参考题解提交,5 个用例全部通过
  • 开发者模式验收:默认状态确认被锁 → ?dev=1 一键解锁并出现 DEV 标记与调试面板 →
    免通过查看题解 → 一键填入题解提交全部通过 → Ctrl+Shift+D 双向切换 → ?dev=0 关闭,
    全程无控制台报错
  • 静态部署验收(GitHub Pages 线上站点 https://yhsome.github.io/OIworld/ ):
    首屏 2.9s → 7 阶段 42 题正常渲染 → 点进题目 → 工具链从 jsDelivr 下载 45.7s 后就绪 →
    浏览器本地编译并运行成功 40.4s → 提交通过 → 刷新深链仍在题目页(hash 路由) →
    #/guide 与 ?dev=1 深链均可用,控制台零报错、零 404 请求
  • Java 靶场验收(本地与线上各跑一遍,scripts/e2e-java.mjs):首页 7 个阶段卡片 / 42 道题 →
    顶栏三个靶场切换按钮且 Java 高亮 → 阶段页 6 道题 → 题目页左右分栏、工具栏与测试用例齐全 →
    运行环境就绪 → 把 // TODO 改成输出语句 → 提交通过并弹出「恭喜通过」 →
    进度页计入 → 指南页可读 → 未通过时按顺序锁定、?dev=1 可直达,控制台零报错
  • Java 题库:npm run validate:java → 42 道题 / 161 个测试点通过
    (结构检查 + javac --release 8 编译题解与初始代码 + 逐用例运行比对,
    用 --release 8 是为了和浏览器里的 Java 8 运行时对齐,任何 Java 9+ 写法都会被拦下)
  • Java 浏览器抽查(scripts/e2e-java-problem.mjs,真实 Doppio JVM 里逐题「填入参考题解 → 提交」):
    9/9 通过,覆盖 println 打印小数(32.0)、switch + char、嵌套循环打印、String.charAt、
    递归(long 阶乘)、printf("%.2f")、类 + 手写冒泡排序 + compareTo、HashMap 计数 +
    Collections.sort、TreeMap 有序输出
  • Java 性能实测(同一浏览器会话):首次编译约 2 分钟,每个测试用例约 1 分钟,
    一道题提交(4 个用例)约 5.5 分钟——这是 Doppio JVM 跑在 JavaScript 里的固有代价。
    题库因此刻意保持轻量(循环几千次以内、输出几十行以内),UI 在等待时也会提示预计耗时
  • 洛谷接口实测(scripts/test-luogu-bridge.mjs,真的请求 luogu.com.cn):
    POST /fe/api/problem/submit/P1001 存在且未登录时返回
    UserNotLoggedInException(对照:随机路径返回 404),csrf-token 能从题目页 meta 抓到,
    带 Origin: https://yhsome.github.io 的预检请求返回 403 且没有任何 Access-Control-Allow-* 头
    (这就是”纯前端读不到洛谷”的硬证据);__client_id / _uid 是
    HttpOnly; Secure; SameSite=None
  • 洛谷桥接脚本测试:24/24 通过(live 10 项 + mock 14 项)。
    mock 部分验证了提交请求的地址、X-CSRF-TOKEN、Referer/Origin、请求体 {lang,code,enableO2}、
    rid 解析、5 秒冷却、评测记录与提交记录解析(含 records 为数组或 {result:[]} 两种形态)、
    绑定 Cookie 会作为 Cookie 头发出、人机验证错误被翻译成可操作提示
  • 洛谷远程提交验收(scripts/e2e-luogu.mjs,本地与线上各跑一遍;注入时用的是站点上
    真实的桥接脚本,只把 GM_xmlhttpRequest 换成假扩展层,所以同时验证了真实脚本的
    csrf 抓取、URL / 请求头 / 请求体构造与消息协议)——63 项检查:
    未装脚本时的状态与安装引导 → 装桥后状态就绪/版本号/自动检测登录 →
    绑定浏览器会话与绑定粘贴的 Cookie(绑定前会真的验证 Cookie 有效性)→ 解绑 →
    题目页提交(请求打到 POST https://www.luogu.com.cn/fe/api/problem/submit/P1001、
    lang=27、带 X-CSRF-TOKEN 与 Referer/Origin、带上绑定的 Cookie 头)→
    轮询 3 次拿到 AC → 刷新提交记录出 3 行(AC / WA / CE)→
    书签提交(把地址伪装成洛谷题目页跑真实的书签脚本:确实打到了提交接口、带上页面 csrf-token、
    提交地址里的代码与语言、轮询 2 次后浮层显示 AC R424242;不在题目页时给出引导)→
    C++ / Python / Java 三个靶场的题目页都有面板且默认语言正确(C++20 / Python 3 / Java 8),
    全程零控制台报错(已排除测试为提速主动中断资源下载产生的那段噪声)
  • 洛谷题号:npm run validate:luogu → 四个靶场 88 个不同题号全部存在且标题核对一致
    (逐题访问洛谷题目页;Pro 41/42、C++ 40/42、Python 41/42、Java 41/42 标注了题号,
    留空的 5 道是「交换两个数」×3 与「反转链表」「Pro 反转链表」——洛谷确实没有对应练习)
  • 顺带修掉一个 Pro 靶场引入的串台 bug:顶栏用 pathname.startsWith('/pro') 判断靶场,
    而 C++ 题目页 /problem/s1-p1 也以 /pro 开头,导致 C++ 题目页显示 Pro 的标语、
    进度与高亮。现在按路径段比较(线上已验证:C++ 题目页显示 C++ 标语与 C++ 进度)
  • Pro 靶场改为「判题在洛谷」后的验收(scripts/e2e-pro.mjs,41 项):题目页确实
    没有本地运行 / 提交 / 测试用例面板 / 编译器提示,用真实桥接脚本提交一次拿到 AC 后
    本题计入通过、下一题解锁、第三题仍锁着、进度页显示「已通过 1」;
    并且断言整个 Pro 流程没有任何 /toolchain/ 或 jsDelivr 请求(不再下载 90MB 编译器)

已知限制

  • 首次使用需下载约 90MB 编译器(之后走缓存);npm run setup:toolchain 可改为同源加载。
  • 每次编译约 2 秒(clang 实例化 + 标准库写入虚拟文件系统),这是 Wasm 运行真实编译器的固有成本。
  • Java 靶场较慢:Doppio JVM 是一个用 JavaScript 实现的 Java 虚拟机,首次编译约 2 分钟、
    每个测试用例约 1 分钟(一道题提交约 5.5 分钟)。题库已按这个约束写得非常轻量,
    等待期间结果面板会提示预计耗时;后续若要提速,可以考虑换用仓库里已有的
    public/java-runtime(B-JVM + 编译成 Wasm 的 TeaVM 编译器)。
  • Monaco 与 Ant Design 体积较大,生产产物首屏 JS 约 4.4MB(gzip 约 1.2MB),
    适合局域网 / 教学场景;如需更小,可只引入 editor.api 与 C++ 语言包。
  • 编译器未启用 C++ 异常,题库与用户代码不要依赖 try / catch。
  • 洛谷远程提交需要在下面三种方式里选一种(浏览器同源策略与 Cookie 禁止请求头决定的,
    见 docs/luogu-bridge.md):装一次油猴桥接脚本、拖一个书签,
    或者手动复制代码到洛谷粘贴;洛谷的非公开接口可能变化,遇到时可用 /luogu 页的诊断按钮取出原始返回。
  • 洛谷侧还可能触发人机验证或提交频率限制:先在洛谷手动交一次、并别拿它刷提交。
  • 基础靶场的洛谷题号标注的是同类型练习(本站题目为自撰,不是洛谷原题的移植),
    Pro 靶场那 41 道才是按同一种算法对应的题目。
  • 阶段一「常见错误」放在「小贴士」之前,阶段二 ~ 七放在最后,属于两批作者的排版差异,不影响阅读。
ZhiXueBan JavaScript 在 GitHub 查看 ↗
智学伴

🎉 好消息,智学伴小程序正式上线!
扫码进入设置,输入礼包码 114514 即可在手机上体验完整功能

智学伴小程序二维码


智学伴

智学伴 ZhiXueBan

基于生成式人工智能的异步学习解决方案
数智融合 · 创赢未来 · 以 AI 之力重塑每一个学生的学习体验

🌐 在线体验  |  📦 下载桌面版  |  📄 技术白皮书  |  📸 功能展示

ℹ️ 当前为完整版。只想快速了解功能和快速开始?→ 📋 精简版


🏆 揭榜挂帅 · 命题一

广州大学第二届”庆园杯”人工智能创新应用大赛 — 揭榜挂帅赛道

命题:基于生成式人工智能的异步学习解决方案

时代之问

2024 年,教育部等五部委联合印发《”人工智能+”行动计划》,明确提出”培养适应智能时代的拔尖创新人才,推进人工智能与教育深度融合”。2025 年,《新一代人工智能发展规划》进入全面深化阶段,AI 赋能教育数字化转型已从远景蓝图走向落地攻坚。

然而,审视当前在线教育现状,三大结构性矛盾依然突出:

矛盾 表现 深层原因
被动灌输 vs 主动建构 学生反复观看录播视频、阅读电子文档,缺乏主动输出与深度加工 现有平台停留在”内容搬运”层面,未能将认知科学理论转化为产品机制
千人一面 vs 个性化需求 固定进度、统一难度,无法适应不同学生的知识起点、学习节奏与认知风格 缺乏智能化的诊断、适配与动态调整能力
图文割裂 vs 多维理解 理科内容中函数图像、几何关系与文字描述分离,学生需在多个工具间跳转 AI 文本生成与数学可视化之间存在工程鸿沟

智学伴正是在这一时代背景下应运而生——它不是又一款”AI+内容”的浅层工具,而是从认知科学底层逻辑出发、以生成式 AI 重构学习全流程的深度解决方案。

我们的回答

智学伴以费曼学习法(以教促学) 为内核设计理念,构建了覆盖”预习→测验→批改→练习→以教促学→通关”的六阶段完整闭环。学生不是知识的被动接收者,而是需要在 AI 面前主动讲解、经受追问、证明理解的学习主体。这一设计直接将学习金字塔中留存率最高的”教授他人”策略系统化、产品化,实现了从”看会了”到”讲清楚了”的质变。

真正的”一生一案”:AI 时代的教育公平答卷

我们坦率地承认:当前的生成式 AI 在情感交流、临场判断、肢体语言等维度上,与真人一对一家教之间仍存在距离。

但我们也坚定地看到:优质家教资源的稀缺性与不可复制性,恰恰是 AI 最能弥补的鸿沟。

在传统教育生态中,”一对一因材施教”是只有少数家庭能够负担的奢望——一线城市中学生的单科家教时薪普遍在 150-300 元之间,一个学期的辅导费用动辄数千甚至上万元。对于粤港澳大湾区内教育资源相对薄弱的地区、对于普通工薪家庭来说,这笔费用几乎等同于一道无形的教育门槛。

智学伴以生成式 AI 的力量,将这道门槛降到了几乎可以忽略的程度:

维度 传统一对一家教 智学伴 AI 方案
单次学习成本 150-300 元/小时 约 0.05-0.5 元(Token 费用)
覆盖学科 受限于教师本人的专业背景 任意学科,从高等数学到思政理论
可及性 受地域、时间、经济条件严格限制 随时随地,有浏览器即可
学习规划 依赖教师经验,难以量化追踪 六阶段闭环自动诊断薄弱点,数据驱动精准出题
情绪压力 面对真人可能紧张、不敢犯错 AI 无评判态度,学生敢于尝试和犯错
规模化 一位教师同时只能辅导一人 理论上可同时服务无限学生

一生一案,不是口号。

传统课堂中,一位教师面对数十名学生,只能按照”中等水平”的预设节奏推进。每个学生的知识漏洞、认知节奏、兴趣方向各不相同,但在统一进度的碾压下,这些差异被一一抹平。学习快的学生被迫等待,跟不上的学生被无声抛弃。

智学伴的四档自适应难度系统、薄弱点自动诊断和针对性出题机制,使得每个学生的学习路径都是唯一的、动态优化的——不是简单地”快慢分组”,而是基于每一次练习和批改的反馈数据,实时调整下一轮的难度、题型和侧重方向。学生 A 在”二次函数图像”上反复出错,系统自动增加该知识点的练习密度;学生 B 已经熟练掌握,系统直接推送更高阶的综合应用题。一个学生,一份教案,一条路径。

低成本,不是低质量。

智学伴的单次学习成本仅为传统家教的千分之一到百分之一。以 DeepSeek v4-pro 为例,一次完整的学习循环(授课 + 出题 + 批改 + 练习 + 教促学)的总 Token 消耗约在 5-20 万之间,折合费用约 0.1-0.4 元人民币。这意味着一个学期的每日 AI 辅导,总费用可能不超过一杯奶茶的价格。这不是技术演示——这是真正可规模化、可持续运行的教育平权方案。

图文并茂:打破理工类 AI 教学的”纯文字”天花板

传统生成式 AI 在教育领域的应用存在一个隐形的天花板——文字生成 AI 能写出漂亮的解题步骤,却画不出一条函数曲线。 当学生面对二次函数的开口方向、三角函数的周期性、双曲线的渐近线、三维曲面的空间形态时,纯文字描述如同用语言去描绘一幅画——再精确的文字也无法替代眼睛看到图像的瞬间理解。

解决这个问题的常规思路是接入图像生成模型(如 DALL·E、Midjourney),但这面临两个致命缺陷:(1) 成本暴涨——图像生成模型的 Token 费用是文字模型的数十倍甚至上百倍,每次出图都是一笔显著开销;(2) 准确性无法保证——图像生成模型不”理解”数学,生成的函数图像可能存在坐标轴错位、曲线变形、标注错误等问题,在教学场景中反而会误导学生。

智学伴走了一条更聪明也更具创新性的路。

我们不使用图像生成 AI。我们让文字生成 AI 以结构化的 [graph] 标记输出数学表达式,然后通过自建的 Python + matplotlib 渲染引擎 将其转换为精确的函数图像。这条技术路径带来了三个突破性优势:

维度 图像生成 AI(DALL·E 等) 智学伴方案(文字AI + 数学渲染)
单次出图成本 0.1-1 元/张 约 0.002 元/张(仅消耗文字 Token + 本地 CPU 渲染)
数学精度 不可控,曲线可能失真 精确可控,基于 numpy 数值计算
图像类型 仅能生成静态图 支持显函数、隐式方程、分段函数、多函数对比、三维曲面
标注与标记 AI 自由发挥,不可靠 断点开闭区间自动标注、坐标轴等比缩放
失败处理 无法调试,只能重新生成 错误详情透传,AI 可自动修正

这意味着智学伴以文字生成 AI 的成本,获得了数学可视化教学的完整体验。学生在讲义中看到的不仅是文字描述——抛物线开口向上还是向下、分段函数的跳跃间断点在哪里、三维曲面在空间中如何延展——这些抽象概念都有了直观的图像支撑。

这就好比吃饭:纯文字的 AI 教学是干瘪的面包,啃得动但索然无味;加入了精确数学图像的教学,则是一顿荤素搭配、营养均衡的知识大餐。 面包解决温饱,但学生需要的是一顿好饭。

这一技术路线的创新意义不仅在于降本增效,更在于它为 “文字 AI + 专业领域渲染引擎”的复合架构 提供了一个可复用的范式——同样的思路可以推广到化学分子式渲染、物理力学示意图、地理信息可视化等更广泛的教育场景。我们不依赖更贵的模型,我们让聪明的架构弥补模型的局限。

契合大赛五大目标

目标一:实战性参与,培养拔尖创新人才

大赛要求”培养适应智能时代的拔尖创新人才,帮助学生开展高层次 AI 应用实战训练”。

智学伴的以教促学闭环直接回应这一目标:学生在完成练习后,必须向 AI “学生”讲解自己的解题思路,AI 追问验证其理解的深度与逻辑的严密性。这一过程不是简单的”对答案”,而是对学生批判性思维、逻辑表达与元认知能力的系统训练——这正是拔尖创新人才的核心素养。平台已覆盖数学、物理、思政、计算机等多个学科,任何知识领域皆可生成高质量的教促学对话。

目标二:赋能教育转型,破解场景痛点

大赛要求”围绕真实场景,以 AI 解决教学痛点、提升管理效率”。

智学伴直击三大痛点:(1) 异步教学场景下的学习质量保障——六阶段闭环确保每个环节都有 AI 的即时反馈与引导,学生脱离课堂后依然拥有”私人 AI 导师”;(2) 练习与考试的个性化与自动化——AI 根据学生薄弱点自适应出题,四档难度(简单/基础/进阶/挑战)覆盖从入门到竞赛的完整梯度,每题标注 1-6 星难度;(3) 数学与理工科内容的图文融合——内建数学图形渲染引擎,讲义与题目中直接嵌入函数图像(2D/3D),打通从符号理解到视觉直觉的认知通道。

目标三:推动协同创新,构建融合生态

大赛要求”打破学科壁垒,推动人工智能与教育、管理、服务等学科交叉融合”。

智学伴本身就是跨学科融合的产物:(1) 认知科学与 AI 工程——费曼学习法的认知理论指导产品设计,生成式 AI 提供技术实现;(2) 多学科知识覆盖——平台支持任意学科的课程创建,从高等数学到编程基础,从大学物理到思想政治教育;(3) 多模型生态兼容——支持 OpenAI、DeepSeek、通义千问、智谱 GLM 等主流大模型接入,学生可根据需求自由选择最优模型组合,体现了开放协同的技术生态理念。

目标四:对接发展大局,服务区域建设

大赛要求”以粤港澳大湾区建设为牵引,聚焦智慧教育,推动 AI 从创新到实际应用”。

落地场景设想:从蓝图到作战地图。

我们不只是描述一个宏大的愿景——我们规划了清晰的落地路径。

场景一:粤东西北乡村学校的”AI 助学伙伴”

在广东清远、河源、梅州等地的乡镇中学,理科师资结构性短缺是长期难题。一位物理老师可能要同时带三个年级,无法为每个学生提供个性化辅导。我们设想,智学伴可以部署在学校机房或学生自有手机上——学生课后打开浏览器,AI 自动根据当天课堂内容生成复习讲义和练习,遇到不懂的知识点随时向 AI 提问,完成练习后进入以教促学环节向 AI 讲解思路。系统自动生成学习报告,老师可据此精准掌握班级学情,把有限的精力投入到最需要帮助的学生身上。

场景二:大湾区社区图书馆的”AI 自习室”

在广州、深圳、东莞等城市,公共图书馆和社区服务中心遍布各区。我们设想与这些机构合作,在电子阅览室的电脑上部署智学伴,为放学后无处可去的学生提供一个”AI 陪学”的自习环境。无需安装任何软件,无需注册账号,打开浏览器即可使用。对于父母双职工、无人辅导功课的学生来说,这就是一个永不疲倦、永远耐心的”数字家教”。

场景三:广州大学对口帮扶学校的”远程理科实验室”

广州大学长期对口帮扶多所基础教育学校。我们设想将智学伴作为帮扶体系中的技术工具,向帮扶学校部署并提供持续的技术支持。通过数学图形渲染引擎,学生在没有专业教师的情况下也能直观理解函数图像、几何关系等抽象概念——这在师资匮乏的学校中尤为重要。

我们不是在等待”被推广”,我们准备好了”走出去”。

这不是写在纸上的愿景。智学伴的零服务端架构意味着:任何学校只需一台能上网的电脑,就能获得完整的 AI 教学能力。Windows 桌面 EXE 版本更是为网络条件不佳的地区量身打造。

智学伴在设计之初就立足于教育公平与资源配置优化的现实命题。粤港澳大湾区内部教育资源分布不均——优质师资集中在核心城市,偏远地区学生难以获得个性化的学习辅导。智学伴的三大特性直接服务这一需求:(1) 零服务端成本——数据完全本地存储,无需服务器运维,一台电脑即可部署;(2) 跨平台全覆盖——Web 端、Vercel 一键部署、Windows 桌面 EXE 三种形态,从城市到乡镇,从电脑到手机,只要有浏览器就能用;(3) Python 可选依赖——数学图形渲染为可选功能,无 Python 环境自动降级纯文字模式,确保在任何硬件条件下均可正常使用。

目标五:打通转化通道,搭建成长平台

大赛要求”对接创新大赛与创业孵化,为优秀团队提供成果展示与转化通道”。

智学伴具备完整的落地条件:(1) 开源架构——完整源码 + 技术白皮书 + 开发指南 + API 文档,任何教育机构可快速二次开发与私有化部署;(2) 成熟产品形态——Web 端已部署于 Vercel 全球 CDN,桌面端已通过 Electron + Inno Setup 打包为 Windows 安装程序;(3) 可验证的效果——平台已支持完整的课程创建、学习闯关、AI 批改、教促学对话、试卷测评等功能闭环,具备在教学场景中实际运行的能力。


→ 不想读长篇?📋 一页速览:核心功能、技术创新、快速开始


📄 知识产权 · License

本项目为广州大学第二届”庆园杯”人工智能创新应用大赛参赛作品(揭榜挂帅赛道·命题一)。

MIT © YHSome

NihonGooo JavaScript 在 GitHub 查看 ↗

NihonGooo

一个静态日语学习工具:输入中文或日文,查看逐词对齐的日语、假名、罗马音、中文释义与助词说明。

本地预览

通过 HTTP 服务或 GitHub Pages 打开网站。翻译由无需登录的 MyMemory 公开 API 提供,日语词典通过网络加载。

Android APK

安装依赖并生成调试 APK:

npm install
npm run android:debug

输出路径为 android/app/build/outputs/apk/debug/app-debug.apk。

SentMeGoal Python ⭐ 1 在 GitHub 查看 ↗
Codex skill: send a detailed QQ email notification after a goal/task completes.

SentMeGoal

一个 Codex skill:目标/任务完成后,自动向 QQ 邮箱发送详细完成摘要。

当你在 Codex 里完成一个目标(goal)、任务或长时运行的工作时,说一句“完成后发 QQ 邮箱给我”,它就会自动整理一份尽可能详细的完成报告,并通过 QQ 邮箱 SMTP 发送到你的邮箱。

功能

  • 目标完成后自动触发,无需手动操作
  • 主动提醒:对话改动大、耗时长或你可能不在电脑前时,自动发送完成邮件,无需手动提醒
  • 运行中重大突破/创新也会即时发送邮件,无需等任务完成
  • 详细的结构化摘要:目标、时间、执行过程、结果与产出、问题与解决、未完成与风险
  • HTML 渲染:摘要以带样式的 HTML 邮件发送(自动附带纯文本兜底,兼容性更好)
  • 发件人显示名默认 Codex,可用 --from-name 或 QQ_MAIL_FROM_NAME 环境变量修改
  • 每封邮件末尾自动附带签名:本邮件由 Codex 的 qq-goal-email skill 自动发送
  • 使用 QQ 邮箱 SMTP(smtp.qq.com:465,SSL),仅依赖 Python 标准库
  • 支持配置保存、环境变量、--dry-run 预览

目录结构

SentMeGoal/
├── README.md
└── qq-goal-email/            # skill 源文件夹
    ├── SKILL.md              # skill 使用说明(Codex 触发后读取)
    ├── agents/
    │   └── openai.yaml       # UI 元数据
    └── scripts/
        └── send_qq_email.py  # 邮件发送脚本

安装

把 qq-goal-email 文件夹复制到 Codex 的 skills 目录:

# Windows(默认路径)
copy /y qq-goal-email %USERPROFILE%\.codex\skills\

# 或 macOS/Linux
cp -r qq-goal-email ~/.codex/skills/

重启 Codex 后,skill 会自动被发现。

首次配置

QQ 邮箱 SMTP 需要授权码(不是登录密码)。获取方式:QQ邮箱 → 设置 → 账户 → 开启 POP3/SMTP 服务 → 生成授权码。

python qq-goal-email/scripts/send_qq_email.py --save-config \
  --from me@qq.com --auth-code xxxx --to me@qq.com

配置保存在 ~/.config/qq-goal-email/config.json。也可以改用环境变量:

export QQ_MAIL_FROM=me@qq.com
export QQ_MAIL_AUTH_CODE=xxxx
export QQ_MAIL_TO=me@qq.com

使用

完成一个目标后,对 Codex 说:

完成后发 QQ 邮箱给我(摘要尽量详细)

或者直接手动发送:

python qq-goal-email/scripts/send_qq_email.py \
  --subject "Goal complete: 任务名" \
  --html-file summary.html

先用 --dry-run 预览邮件内容:

python qq-goal-email/scripts/send_qq_email.py --dry-run \
  --subject "Goal complete: 任务名" --html-file summary.html

也支持纯文本:把 --html-file 换成 --body-file 即可。

安全提示

  • config.json 包含 QQ 邮箱授权码,不要提交到任何代码仓库或分享给他人。
  • 本仓库不包含任何真实配置或授权码。

说明

  • 邮件正文采用纯文本 + 结构化分节,方便在邮件客户端阅读。
  • 发送失败时,先检查授权码是否有效、SMTP 服务是否已开启。
ClashRoyale-Private-Server C# 在 GitHub 查看 ↗
皇室战争1.9.2逆向学习 (HashRoyale/RetroRoyale based) 以及私服与自制卡方案

Clash Royale 1.9.2 私服 + 自定义卡牌

基于 HashRoyale / RetroRoyale 搭建的 Clash Royale 1.9.2 私服,并在此基础上
通过纯数据补丁制作了多张自定义卡牌。客户端为 RetroRoyale 修补版 APK(com.retrocell.clashroyale)。

⚠️ 本项目仅供学习与个人研究使用。客户端 APK 含 Supercell 版权资源,未包含在本仓库中;
请自行获取 RetroRoyale 客户端并按下方流程打补丁。服务端数据(GameAssets)来自公开的
HashRoyale 开源项目。

功能特性

  • 完整可跑的 1.9.2 私服:主服(TCP 9339/集群 9876)+ 战斗服(UDP 9449)+ MySQL 存档
  • 匹配 Bot(tools/cr_bot.py):自动排队、对局中持续下牌、可自定义卡组
  • 一键启动脚本:start-server.ps1 / stop-server.ps1 / one-click-start.ps1 / 一键启动.bat
  • 自定义卡牌制作工具链:改服务端 CSV + 客户端 APK(SC/LZMA 压缩 CSV)+ 文本,再签名安装

已制作的自定义卡牌

卡牌 数据名 机制
觉醒冰人 AwakenedIceGolem 每 1.5s 在自身位置释放冰人亡语效果
界炸弹兵 BoundaryBomber 每次攻击丢炸弹并额外发射火箭(多弹攻击)
一条龙服务 OneStopService 原版哥布林飞桶逻辑:法术→飞桶投射物→落地爆出 2 精英野蛮人 + 3 烈焰精灵 + 狂暴
界电磁炮 BoundaryZapMachine 电磁炮克隆;每次攻击在目标点释放雷电 + 电击(子弹伤害 0)
界气球兵 BoundaryBalloon 气球兵 + 幻影刺客突刺特性(全图视野/突袭)
界地狱飞龙 BoundaryInfernoDragon 攻击无伤害;每次攻击从远处以随机角度发射复仇滚木(可对空)
界地狱塔 BoundaryInfernoTower 地狱塔克隆;每次攻击向随机位置发射火球
界迫击炮 BoundaryMortar 0.3 秒快速攻击;炮弹在目标方向±90°内随机偏移落地,落点生成一只小骷髅
界戈仑石人 BoundaryGolem 戈仑石人克隆;每 0.5 秒在自身施放 Zap(数据层无随机法术池,可换任意法术)
一个火枪手 OneMusketeer 火枪手克隆;射程同公主,随机方向发射伤害 1 的迫击炮弹,落点施放治疗法术
天鹰火炮 EagleArtillery 10 秒攻击;引导探针 7 秒倒计时跟随最近单位,同时从身后打出 3 发火箭

环境要求

  • Windows(本项目在 Windows + PowerShell 下开发)
  • MySQL 5.7+(本地 3306)
  • .NET 8(运行编译好的服务端,或从 HashRoyale/src 重新编译)
  • Python 3(运行 Bot 与补丁脚本)
  • Android SDK build-tools(zipalign/apksigner,签名 APK)
  • 一台 Android 手机(真机测试;adb 安装 APK)

快速开始

1. 准备 MySQL

# 启动本地 MySQL(或使用现有实例)
# 建库
mysql -u root -e "CREATE DATABASE rrdb2;"

2. 启动服务端

.\start-server.ps1
# 主服 9339 / 集群 9876 / 战斗服 UDP 9449

首次启动会自动建表。玩家/账号数据存在 rrdb2 库的 player 表。

3. 启动匹配 Bot

cd tools
python cr_bot.py bot_state.json

Bot 会登录账号(bot_state.json,格式见 bot_state.example.json)并进入 1v1 匹配队列,
对局中每 0.5s 下一张牌(卡组在 cr_bot.py 的 BOT_DECK,需与服务端玩家 2 的卡组一致)。

4. 客户端

客户端 APK 不包含在本仓库。获取 RetroRoyale 1.9.2 客户端后:

  1. 用 tools/make_*.py 补丁脚本生成带自定义卡的 APK(脚本会同时改服务端 CSV 与客户端 APK);
  2. zipalign + apksigner 签名(使用 tools/debug.keystore,密码 android);
  3. adb install -r clients\retroroyale-1.9.2-phone-modXX.apk 安装;
  4. 游戏内服务器地址需指向本机(192.168.x.x:9339)。

重要:客户端必须包含玩家卡组中的所有卡,否则主界面渲染未知卡会闪退。
因此每次新增自定义卡后,都要从包含全部已有自定义卡的最新 APK 为基础重新构建。

自定义卡制作流程

  1. 在服务端 HashRoyale/app/GameAssets/csv_logic/*.csv 加数据行(卡/角色/建筑/投射物/区域效果);
  2. 写补丁脚本(参考 tools/make_boundary_zap_machine.py)同步改客户端 APK 中 SC 压缩的 CSV 与文本;
  3. 签名并安装,重启主服/战斗服/bot;
  4. 把卡加进玩家卡组(player 表的 Home.deck JSON,classId:26=角色、27=建筑、28=法术);
  5. 训练营/1v1 实测。

目录结构

HashRoyale/                服务端(源码 src/,编译产物 app/、app_battles/)
  app/                     主服:ClashRoyale.exe + GameAssets/csv_logic(数据表)
  app_battles/             战斗服(UDP 中继)
  src/                     C# 源码
tools/                     脚本与工具
  cr_bot.py                匹配/对战 Bot
  make_*.py                自定义卡补丁脚本
  frida_*.py               Frida 逆向研究脚本
  projectile_research.md   libg.so 投射物逆向研究记录
  debug.keystore           APK 签名证书(密码 android)
*.ps1 / *.bat              一键启动/停止脚本
ClashRoyale私服技术文档.md  完整开发/踩坑文档
学习路线.md                 学习笔记

自定义卡实现要点

  • 1.9.2 的角色投射物只有 ProjectileStartRadius(沿朝向偏移),没有“世界坐标出生点”,
    数据层无法把投射物出生点定位到国王塔等任意位置(见 projectile_research.md)。
  • “飞桶”类卡正确做法是 spells_other 法术卡 + 投射物(spell_goblin_barrel.sc),
    命中时 SpawnCharacter 生成容器;不要用“角色桶”(缺动画剪辑且会闪退)。
  • 多单位/双效果可以用“容器链”:投射物 → 容器(DeployTime 引信)→ 死亡时 DeathAreaEffect/DeathSpawnCharacter。
  • 客户端数据加载器会递归展开引用字段(实测确认:Projectile、CustomFirstProjectile、
    SpawnCharacter、SpawnProjectile),任何“建筑→炮弹→建筑→炮弹…”的循环都会在加载期
    栈溢出闪退
    (崩溃特征:libg.so 里 0x18130b / 0x1963ff 互相递归数百帧),所以 A↔B 互相
    召唤在纯数据里做不到;改造时要保证引用图是无环的(终点是角色/无引用的叶子节点)。
  • DeathSpawnCharacter=建筑 加载期安全但运行时被静默忽略(两轮实测不生效);
    DeathAreaEffect 触发自定义区域效果在进训练营时仍会被更深层加载展开,同样会崩。
  • 随机落点:建筑发射器只认 RandomAngle + RandomDistance(无 Homing)(界地狱塔火球实测有效);
    RandomAngle 是总扇区宽度:360=全方向,180=目标方向±90°;滚木那套
    MinDistance+RandomAngle+Homing 实测迫击炮/地狱龙均不生效;
    “落点生成单位”用投射物 SpawnCharacter=角色(哥布林飞桶同款,稳定可用)。
  • Frida 研究要点(模拟器实测):模拟器可能走 ARM 翻译(加载 lib/arm/libg.so),
    投射物对象大小 272 字节、创建调用点返回地址 = libg+0x7c117(战斗 tick 链
    0xbc3a7/0x6b7f3 之下);_Znwj 挂钩 + 调用点过滤即可在创建时精确计数。
  • RandomAngle/RandomDistance 只能随机投射物飞行/落点,不能随机出生点方向。
  • 版本较老:精英野蛮人 = AngryBarbarian,幻影刺客 = Assassin,狂暴 = BarbarianRage。

版权声明

本仓库代码基于 MIT 许可的 HashRoyale/RetroRoyale 开源项目修改。游戏客户端与美术资源版权归
Supercell 所有;本仓库不包含客户端 APK 及其版权资源。请仅用于学习研究。

OpenBlogger HTML 在 GitHub 查看 ↗
一个轻量级的个人博客搭建框架,配合Claude code可实现博客一键落地

OpenBlogger

一个轻量级静态博客渲染引擎。Markdown 写文章,Python 一键生成完整站点。

快速开始

git clone --recurse-submodules https://github.com/YHSome/yhsome.github.io.git
cd yhsome.github.io
pip install -r requirements.txt
双击 控制台.bat → [3] 本地预览

项目结构

OpenBlogger/
├── renderer.py              # 核心渲染引擎
├── cli.py                   # 命令行工具
├── import_legacy.py         # Hexo 旧博客导入工具
├── Template/Default/        # HTML 模板(6 页)
│   ├── Homepage.html        # 首页:三栏布局 + 弹幕
│   ├── Directory.html       # 目录:搜索 + 标签过滤 + 年月分组
│   ├── Post.html            # 文章:侧栏 + 正文 + 评论
│   ├── Tag.html             # 标签:悬停展开文章列表
│   ├── Projects.html        # 项目:GitHub 仓库展示
│   └── Links.html           # 友链:旧项目导航
└── Plugins/Viewer/          # 内置插件
    ├── js/                  # 访问计数 + 评论系统
    └── tools/               # 页面编号管理

功能

功能 说明
Markdown 渲染 extra/codehilite/toc/sane_lists/smarty 扩展
元数据解析 文章头部 key: value 格式,自动补全日期/标题
增量构建 文件哈希缓存,只重建变更页面
RSS + Sitemap 自动生成
暗色模式 零闪屏(head 同步脚本)
页面转场 CSS cubic-bezier 弹入飞出
代码高亮 Pygments
友链项目 构建时自动复制
弹幕 全站评论滚动

模板变量

所有模板使用 Jinja2 语法。以下是各页面可用的主要变量:

Post.html

{{ title }} {{ date }} {{ tags }} {{ content }} {{ author }}
{{ prev_post }} {{ next_post }} {{ relative_root }}
{{ total_posts }} {{ total_words }} {{ sidebar_tags }}
{{ page_id }} {{ viewer_user }} {{ viewer_secret }}

Homepage.html

{{ site_title }} {{ site_description }}
{{ recent_posts }} {{ all_tags }} {{ total_posts }} {{ total_words }}
{{ max_page_id }} {{ relative_root }}

Directory.html

{{ all_posts }} {{ all_tags }} {{ active_tag }}

Tag.html

{{ tags_with_count }}  ← 含 posts 列表用于悬停弹出

Projects.html

{{ projects }}  ← 从 projects.json 加载

Links.html

{{ link_sections }}  ← 硬编码在 renderer 中

插件系统

插件放在 Plugins/ 目录(博客项目根),与 OpenBlogger 框架分离。

内置插件:
- Viewer (OpenBlogger/Plugins/Viewer/):纯前端访问计数 + 评论,基于 TinyWebDB

博客专属插件示例:
- GitHubProjects (Plugins/GitHubProjects/):拉取 GitHub 仓库列表展示

CLI 命令

python -m OpenBlogger.cli build                # 构建站点
python -m OpenBlogger.cli build --force        # 强制全量重建
python -m OpenBlogger.cli build --theme modern # 使用指定主题
python -m OpenBlogger.cli serve                # 预览站点
python -m OpenBlogger.cli serve --port 9090    # 指定端口
python -m OpenBlogger.cli clean                # 清空输出

部署

# 将 Rendered/ 推送到 GitHub Pages
git subtree push --prefix Rendered origin gh-pages

或使用博客项目自带的 deploy.bat 一键部署。

依赖

  • Python 3.9+
  • markdown
  • Jinja2
  • Pygments
  • beautifulsoup4(仅 import_legacy.py 需要)
  • html2text(仅 import_legacy.py 需要)

许可

MIT

claude-final-exam-review Python ⭐ 6 在 GitHub 查看 ↗
一个期末复习Skill,能够让Claude识别ppt,pdf,docx等文件并生成可视化讲义的Claude Code skill。An end-of-term review Skill that enables Claude to recognize files such as ppt, pdf, and docx, and generate visualized lecture notes, known as the Claude Code skill.

期末复习AI导师 — Claude Code Skill

📖 English version

一个帮你从零掌握课程知识的 AI 家教。把讲义扔进去,它会像私人家教一样,在浏览器里一步一步给你讲解知识点、出题练习、批改答疑。

📦 你需要准备什么

在开始之前,你只需要电脑上有两样东西:

  1. Claude Code(AI 助手软件)
  2. 你上课的讲义文件(PPT、PDF、Word、甚至手机拍的板书照片都行)

不会装 Claude Code?往下看👇


🚀 第一步:安装 Claude Code

  1. 打开浏览器,访问 https://claude.ai/code
  2. 下载对应你系统的版本(Windows/Mac)
  3. 安装并登录你的 Anthropic 账号
  4. 安装完成后,打开终端(Windows 按 Win+R,输入 cmd 回车),输入 claude 回车,看到欢迎界面就成功了

⚠️ Claude Code 目前需要付费 API 额度。如果你是学生,可以关注 Anthropic 的学生优惠。


📥 第二步:下载这个 Skill

如果你会 git:git clone https://github.com/YHSome/claude-final-exam-review.git

如果不会 git(或者上面的命令看不懂),跟着下面做:

  1. 打开 https://github.com/YHSome/claude-final-exam-review
  2. 点击绿色的 Code 按钮 → 选择 Download ZIP
  3. 把下载的 ZIP 解压到你喜欢的文件夹(比如桌面上的 期末复习 文件夹)
  4. 解压后你会看到这些文件:
期末复习/
├── README.md                      ← 你现在看的这个文件
├── stop-server.cmd                ← 双击可以关闭后台服务
├── .gitignore
└── .claude/
    └── skills/
        ├── final-exam-review.md   ← AI 家教的核心大脑
        └── extract_document.py    ← 用来读取讲义的脚本

📚 第三步:放入你的讲义

把你上课的讲义文件放到 期末复习 文件夹下面。建议按科目分文件夹,比如:

期末复习/
├── 高等数学/
│   ├── A-第一章 函数与极限/
│   │   ├── 1-1 映射与函数.ppt
│   │   └── 1-2 数列极限.ppt
│   └── B-第二章 导数与微分/
│       └── ...
├── 线性代数/
│   └── ...
└── 大学物理/
    └── ...

支持的格式:PPT (.ppt/.pptx)、PDF、Word (.doc/.docx)、图片(会自动 OCR 识别文字)、HTML、Markdown

💡 提示:文件名建议用”章节号-节名”格式,比如 1-3 函数极限.ppt,这样 AI 能更好地组织内容。


🎓 第四步:开始复习!

  1. 打开终端(Win+R → cmd → 回车)
  2. 进入你的复习文件夹:
    cd C:\Users\你的用户名\Desktop\期末复习
  3. 启动 Claude Code:
    claude
  4. 输入以下命令激活家教模式:
    /final-exam-review
  5. 然后像聊天一样告诉它你想复习什么:
    我要复习高等数学,从第一章开始

AI 家教会自动:
- 扫描你的讲义文件
- 在浏览器里打开一个排版精美的讲义页面(公式用 LaTeX 渲染)
- 一步一步给你讲解每个知识点
- 每讲完一节停下来问你有没有疑问
- 出练习题给你做,做完帮你批改

🖥️ 浏览器里看公式:讲解内容会在浏览器中显示,公式渲染效果跟教材一样清晰。终端只用来简单对话。


🔧 常见问题

Q: 浏览器里公式显示不出来,全是乱码?

A: 这是 MathJax 没加载成功。试试:
1. 检查网络连接(MathJax 需要从 CDN 加载一次)
2. 刷新浏览器页面(F5)
3. 如果还不行,确认你是通过 http://localhost:8888/... 打开的,不是双击 .html 文件

Q: 双击了 stop-server.cmd 后浏览器打不开了?

A: 这个文件会关闭后台服务。如果不小心双击了,在 Claude Code 里说”重启服务器”就行。

Q: 提示 “pip install unstructured” 失败?

A: 这是讲义提取脚本的依赖。如果你不需要自动提取 PPT/PDF 文字(比如你用的是纯文本笔记),可以不装。否则,在终端输入:

pip install unstructured

Q: Claude Code 的终端对话是纯文字的,怎么看公式?

A: 所有公式都在浏览器里!终端只用来简短对话(”懂了”“继续”“出几道题”),真正的讲解内容、公式推导、例题都在浏览器页面里。这是设计好的双屏模式。

Q: 我没有讲义文件,能直接用吗?

A: 可以!AI 家教会用自己的知识给你讲解。但建议至少放一份目录或考试大纲,这样它知道要讲哪些内容。


📁 项目文件说明

文件 用途
.claude/skills/final-exam-review.md Skill 的核心定义(AI 家教的”大脑”)
.claude/skills/extract_document.py 文档提取脚本(读取你的 PPT/PDF)
stop-server.cmd 一键关闭后台 HTTP 服务(复习完双击即可)

🧑‍💻 给想自己修改的人

如果你会一点点编程,可以自己改 Skill:

  • 编辑 .claude/skills/final-exam-review.md 来调整 AI 的行为
  • 比如:让它更啰嗦/更简洁、调整题目难度、增加新的科目支持
  • 改完后在 Claude Code 里说 reload 就能生效

👨‍💻 面向计算机专业学生(简洁版)

# 1. 克隆仓库
git clone https://github.com/YHSome/claude-final-exam-review.git
cd claude-final-exam-review

# 2. 放入讲义(支持 ppt/pptx/pdf/docx/html/md/txt/csv + 图片OCR)
mkdir 高等数学 && # 把你的课件扔进去

# 3. 可选:安装文档提取依赖
pip install unstructured

# 4. 启动 Claude Code
claude

# 在 Claude Code 中:
/final-exam-review
我要复习高等数学第八章第四节

Skill 结构:
- final-exam-review.md — Skill 定义(Markdown frontmatter + 完整教学流水线)
- extract_document.py — 基于 unstructured 库的文档提取,自动识别格式、保留结构、图片 OCR
- Skill 输出所有教学内容到 _extracted/科目/章/xxx.讲义.html(MathJax 渲染),通过本地 HTTP 服务器 localhost:8888 在浏览器展示
- 追加内容(Q&A、练习、答案)用 Edit 工具直接操作 HTML,避免 bash/Python 转义 LaTeX

技术文档:

文档 链接
Claude Code 官方文档 https://docs.anthropic.com/en/docs/claude-code
Claude Code Skill 开发指南 https://docs.anthropic.com/en/docs/claude-code/skills
Skill 最佳实践 https://docs.anthropic.com/en/docs/claude-code/skills#skill-best-practices
Claude API / Anthropic SDK https://docs.anthropic.com/en/api
MathJax 3 文档 https://docs.mathjax.org/en/latest/
Unstructured 库文档 https://docs.unstructured.io/

📄 免责声明

本 Skill 仅供学习辅助。课程讲义版权归原作者所有,请勿将老师的讲义文件上传到公开仓库。

Notelet TypeScript 在 GitHub 查看 ↗
轻量、顺手、专注的 Windows 笔记编辑器与临时便签 · A lightweight, straightforward Windows note editor and quick note app

English

Notelet icon

Notelet

轻量、顺手、专注的 Windows 笔记编辑器与临时便签

Windows Version Tauri TypeScript MIT License

Notelet 流光花境主题

Notelet 面向 Windows 桌面,适合随手记录、整理本地 Markdown 与编辑常见文本文件。它使用系统 WebView2 渲染界面,不捆绑完整的 Electron 运行时;当前构建的便携版约 5.2 MiB,安装包约 3.1 MiB,下载后即可使用。

为什么选择 Notelet

  • 轻量:Tauri 2 + Rust 后端,便携版约 5.2 MiB,NSIS 安装包约 3.1 MiB。
  • 直接:既可安装,也可单文件运行;打开本地文件即可开始编辑。
  • 专注:全局快捷键一按呼出临时便签,长按只触发一次,不连续生成窗口。
  • 本地优先:笔记读取与写入由 Rust 完成;删除记录只从界面列表移除,不删除原文本文件,只有保存操作会修改文件内容。
  • 完整但不过载:多标签、全文搜索、Markdown / LaTeX 预览、代码高亮、主题与窗口行为设置都保留在简洁界面中。
  • 中英双语:默认使用简体中文,可在“设置 → 界面外观”中切换英文界面。

三种主题

经典暖橙、Dark 2026 与流光花境均为完整主题,不只是更换背景。前景组件、窗口底色、选区强调色、背景图片与透明度可继续独立微调。

流光花境主题使用的壁纸来源于 Wallhaven。

经典暖橙 Dark 2026 流光花境
经典暖橙主题 Dark 2026 主题 流光花境主题

临时便签:一按即用

默认使用 Ctrl + Alt 呼出独立临时便签。一次按键只创建一个窗口,持续按住也只计作一次;便签关闭前可选择保存或放弃。

Notelet 临时便签快捷键演示

Markdown、代码与公式

编辑区保留 Markdown 源文本的语法着色,右侧实时呈现排版结果;代码块带语言高亮与行号,数学公式由 KaTeX 渲染。预览栏可随时开关、拖动调整宽度或拖至边缘收起。

Markdown 语法高亮与实时预览

支持内容包括:

  • 标题、引用、列表、任务列表、链接、行内代码与代码块
  • 常见编程语言语法高亮及代码行号
  • 行内与块级 LaTeX 数学公式
  • Markdown 与普通 UTF-8 文本/代码文件

可调节的外观与行为

Notelet 设置页

设置会实时预览,只有点击“保存”才会持久化;取消或直接关闭设置窗口会自动恢复。可调节项目包括:

  • 主题预设、前景、背景与选中背景色
  • JPG、PNG、WebP、GIF、BMP 背景图片
  • 背景适配方式、位置、图片透明度与组件透明度
  • 编辑器字号、主窗口与临时便签初始尺寸
  • 全局快捷键、默认侧栏、默认预览、临时便签置顶
  • Windows 开机自启动

功能一览

类别 功能
编辑 多标签、撤销/重做、实时字数、未保存状态、另存为
文件 打开与保存常见 UTF-8 文本/代码文件,单文件最大 32 MiB
记录 当前笔记与最近文件统一展示,删除界面记录不触碰原文件
搜索 即时匹配标题、路径与正文,无需按回车
Markdown 源文本语法着色、实时预览、LaTeX、代码高亮与行号
布局 左侧记录栏与右侧预览栏可拖动、折叠并记忆宽度
临时便签 全局快捷键呼出、独立窗口、关闭前保存确认
外观 三种主题、背景图、颜色、透明度与字号实时预览

直接使用

github上提供两种 Windows x64 成品:

  1. 安装版:运行 Notelet_0.8.3_x64-setup.exe,按向导完成安装。
  2. 便携版:直接运行 notelet.exe,无需安装。

系统需要 Windows 10/11 与 Microsoft Edge WebView2 Runtime。常规 Windows 10/11 环境通常已包含 WebView2;若程序无法启动,请先安装或更新该运行时。

快捷键

快捷键 操作
Ctrl + Alt 呼出临时便签(可在设置中修改)
Ctrl + N 新建笔记
Ctrl + O 打开笔记
Ctrl + S 保存
Ctrl + Shift + S 另存为
Ctrl + W 关闭当前标签
Ctrl + \ 切换 Markdown 预览
Ctrl + Shift + B 切换左侧记录栏
Ctrl + , 打开设置

开发与构建

环境要求

  • Node.js 20+
  • Rust stable(MSVC toolchain)
  • Microsoft Edge WebView2 Runtime
  • Visual Studio Build Tools,并安装“使用 C++ 的桌面开发”工作负载

本地开发

npm install
npm run tauri -- dev

只调试前端界面时可运行:

npm run dev

构建 Windows 成品

npm install
npm run build:windows

主要输出位置:

src-tauri/target/release/notelet.exe
src-tauri/target/release/Notelet_0.8.3_x64-setup.exe

如需更新应用图标后重新生成 Windows 图标资源:

npm run tauri -- icon app-icon.svg
npm run build:windows

技术栈

组件 用途
Tauri 2 / Rust 原生窗口、文件读写、快捷键与安装包
TypeScript / Vite 界面逻辑、类型检查与前端构建
marked Markdown 解析
highlight.js / marked-highlight 代码语法高亮
KaTeX / marked-katex-extension LaTeX 公式渲染
Windows WebView2 轻量桌面界面运行时

Release 构建启用了 LTO、体积优化与符号剥离,以控制成品大小。

数据与文件安全

  • 当前只接受 UTF-8 文本;UTF-8 BOM 会在读取时自动处理。
  • 单文件最大 32 MiB,避免误将大型二进制文件作为文本载入。
  • “删除笔记记录”仅移除 Notelet 界面中的记录,不删除磁盘上的原文件。
  • 只有明确保存时才会写回文件;未保存内容在关闭前会提示处理。

MIT许可证

LICENSE

fl_space Python 在 GitHub 查看 ↗
fl_space

SpaceFL — 太空联邦学习研究框架

Python
License
Ruff

模块化、可扩展的开源框架,用于研究太空联邦学习中的轨道环境模拟、卫星通信与FL算法。


架构

fl_space/
├── environment/   # 环境模拟层 — 天体、大气、地面站、坐标工具
├── orbit/         # 轨道力学层 — Kepler/SGP4 双后端、可见性判断
├── simulator/     # 模拟器层 — 接触矩阵、主模拟引擎
└── config/        # 配置层 — 预设参数、JSON 配置加载

双后端设计:
| 后端 | 精度 | 依赖 | 适用场景 |
|------|------|------|---------|
| kepler (默认) | 二体近似 | 仅 numpy | 快速原型、概念验证 |
| skyfield | SGP4 + JPL DE421 | pip install skyfield | 高精度研究、真实任务规划 |


快速开始

pip install fl-space           # 核心安装
pip install fl-space[skyfield] # 含 Skyfield 高精度后端

5 分钟示例

from fl_space.environment import CelestialBody, create_default_network
from fl_space.orbit import KeplerOrbit, create_circular_orbit
from fl_space.simulator import OrbitSimulator

# 创建地球 + 3颗卫星 + 7个地面站
earth = CelestialBody.earth()
gss = create_default_network(7)
orbits = [create_circular_orbit(500, 53, 0, i * 120, earth) for i in range(3)]

sim = OrbitSimulator(
    body=earth,
    orbits=orbits,
    ground_stations=gss,
    duration_hours=24,
)

# 查看通信记录
print(f"接触率: {sim.contact_rate:.1%}")
print(sim.get_communication_record(0))  # SAT-0 的完整通信记录

切换到高精度模式

sim = OrbitSimulator(
    body=earth,
    orbits=orbits,
    ground_stations=gss,
    duration_hours=24,
    backend="skyfield",  # 启用 SGP4/JPL 高精度
    sim_start_date=(2025, 1, 1),
)

模块 API

fl_space.environment

类/函数 说明
CelestialBody 可配置天体参数,内置6个预设 (earth/mars/moon/jupiter/saturn/venus)
CelestialBody.earth(precise=True) 从 JPL DE421 获取高精度参数
AtmosphereModel 大气模型基类
NoAtmosphere / ConstantHeightAtmosphere / ExponentialAtmosphere 三种大气模型
GroundStation 单个地面站 (经纬度/海拔/最小仰角/天线增益)
GroundStationNetwork 地面站网络容器
create_default_network(n) 创建默认 n 站网络
coordinate_utils Haversine/角距/可见半角等工具函数

fl_space.orbit

类/函数 说明
KeplerOrbit Kepler 二体问题轨道计算器
OrbitalElements 轨道六要素 dataclass
create_circular_orbit() / create_polar_orbit() 工厂函数
ConstellationConfig 星座配置 (Walker/Cluster/Uniform)
generate_orbits() 按配置生成轨道
VisibilityEngine 单星可见性引擎
MultiSatVisibility 多星批量可见性
SkyfieldOrbitBackend Skyfield SGP4 高精度后端
get_precise_body_params() 快速获取 JPL 精确天体参数

fl_space.simulator

类/函数 说明
OrbitSimulator 主模拟器,整合所有层
create_default_simulator() 地球默认配置工厂
create_mars_simulator() 火星场景工厂
ContactMatrix 接触矩阵 (兼容/完整双模式)

自定义配置

from fl_space.config import load_sim_config_from_dict, EXPERIMENT_CONFIGS

# 从预设或自定义字典加载
config = {
    "body": {"name": "Earth", "radius_km": 6371, "GM": 398600.4},
    "constellation": {"type": "walker", "num_sats": 20, "num_planes": 5},
    "ground_stations": [...],
    "duration_hours": 24,
}
params = load_sim_config_from_dict(config)
sim = OrbitSimulator(**params)

代码规范

本项目使用 Ruff 进行代码规范检查:

ruff check fl_space/   # 检查
ruff check --fix       # 自动修复
ruff format fl_space/  # 格式化

开发状态

  • [x] Step 1: 环境模拟设置 (Kepler + Skyfield 双后端)
  • [ ] Step 2: 卫星设置 (行为模式、通信方式)
  • [ ] Step 3: FL 算法选择
  • [ ] Step 4: 数据集集成
  • [ ] Step 5: 地面基站设置增强
  • [ ] Step 6: Checkpoint 机制
  • [ ] Step 7: 信息展示增强

许可证

MIT License — 详见 LICENSE

TheFinalPuzzle Python 在 GitHub 查看 ↗
【整活】一个旨在解决Vibe Coding最深入、最本质问题的工具。A tool that goes straight to the deepest, most fundamental essence of Vibe Coding.

TheFinalPuzzle - Vibe Coding 的最后一块拼图

Claude Auto-Responder — 自动监测 Claude Code 窗口,当出现选项提示(Yes/No、Continue? 等)时自动按下 Enter 键。

简体中文 | English

⚠️ 整活项目,仅供娱乐!

本项目仅为作者在考英语六级前闲的蛋疼而写,请勿在任何正式开发、生产环境或重要项目中使用。
自动按键意味着你可能在不了解具体操作的情况下盲目同意文件修改、命令执行等行为,
可能导致数据丢失、系统损坏或其他不可预知的后果。作者不承担任何使用本脚本造成的责任。

原理

┌─────────────────────┐
│   窗口上部 70%       │  ← 对话内容,不监控
│   (对话历史)         │
├─────────────────────┤
│   窗口下部 30%       │  ← 只截取这部分做 OCR
│   (提示区域)         │     Yes/No 选项都在这里
└─────────────────────┘
         ↓
  截图 → OCR 识别文字 → 正则匹配提示词 → 自动按 Enter
         ↓
  刷新终端仪表盘(实时显示 OCR 结果、统计、事件日志)

只截取窗口底部 30% 的提示区域做 OCR,不扫描上部大段对话历史,减少误触发、提高识别速度。

安装

1. Python 依赖

pip install pyautogui pillow pytesseract pynput rich pywin32

2. Tesseract OCR(OCR 模式需要)

平台 安装方式
Windows UB-Mannheim/tesseract
macOS brew install tesseract
Linux sudo apt install tesseract-ocr tesseract-ocr-chi-sim

盲模式(--blind)不需要 Tesseract,直接定时按键。

用法

# OCR 智能检测(默认每 5 秒检测一次)
python claude_auto_responder.py

# 试运行:只检测不按键
python claude_auto_responder.py --dry-run

# 盲模式:每 10 秒按一次 Enter,不需要 OCR
python claude_auto_responder.py --blind --interval 10

# 指定要监控的窗口标题(不区分大小写)
python claude_auto_responder.py --title "claude"

# 查看所有参数
python claude_auto_responder.py --help

参数

参数 简写 默认值 说明
--title -t 活动窗口 窗口标题匹配
--interval -i 5 检测间隔(秒)
--cooldown -c 5 两次按键最小间隔(秒)
--blind -b 关闭 盲模式,不做 OCR,定时按键
--dry-run -n 关闭 仅检测不实际按键
--tesseract-cmd 自动检测 Tesseract 可执行文件路径
--save-screenshots 关闭 保存截图到 ./screenshots/ 用于调试
--pause-hotkey f8 暂停/恢复热键

仪表盘

运行后会显示实时监控面板:

┌──────────────────────────────────────────┐
│  TheFinalPuzzle                          │
│  > 运行中  Mode: OCR  Interval: 5s       │
├──────────────────────────────────────────┤
│  Checks: 12  Detected: 3  Enter: 3       │
│  Last check: 2s ago  Hit rate: 25%       │
├──────────────────────────────────────────┤
│  --|--#--|--#>  活动时间线(最近60次)      │
│  -=无  |=检测到  #=已按键  >=当前位置     │
├──────────────────┬───────────────────────┤
│  OCR 识别结果     │  事件日志              │
│  │ [y/N]         │  14:32:05 OK 已按键    │
│  │ Do you want   │  14:31:55 OK 已按键    │
│  │ to proceed?   │  14:31:42 .. 冷却中    │
│                  │  14:31:39 -- 无匹配    │
├──────────────────┴───────────────────────┤
│  F8: 暂停  |  Ctrl+C: 退出  |  LIVE 模式  │
└──────────────────────────────────────────┘

快捷键

按键 作用
F8 暂停 / 恢复监控
Ctrl+C 退出程序

检测逻辑

极简规则:OCR 识别文字中同时出现 Yes + No(或中文 是 + 否)即触发按键。

触发条件 说明
Yes + No 同时出现 覆盖所有英文选项:[y/N]、yes/no、Do you want to...?、1. Yes / 2. No 等
是 + 否 同时出现 覆盖所有中文选项:是/否、是否继续、确认执行 等
是否... 如 是否继续 是否确认 是否执行
确认... 如 确认继续 确认执行 确认操作

无需穷举每种提示句式,只要提示区域里 yes 和 no 成对出现就触发。

防抢键盘

如果用户在冷却窗口内(最近 5 秒)动过键盘,脚本会跳过本次按键,不会跟用户抢键盘控制权。

注意事项

  • 终端兼容性: PowerShell / cmd 支持 cls 原位清屏刷新;PyCharm 内置终端忽略 cls,靠滚行推出旧内容
  • 窗口定位: OCR 截取屏幕画面,Claude 窗口需可见且在前台,或通过 --title 指定窗口
  • 监控区域: 默认截取窗口底部 30%,仅包含提示区域,跳过上方对话历史
  • 权限: macOS 需在”隐私与安全性”中授权终端辅助功能权限
Viewer HTML 在 GitHub 查看 ↗
基于TinyWebDB的纯前端页面访问计数器&评论组件。A pure frontend page visit counter & comment module powered by TinyWebDB.

Viewer — 计数器、评论一体化纯js组件

前记:作者在23年的时候刚开始玩纯静态网页写博客(现在已经看不见了),看到别人每一篇文章都有阅读量统计和评论,羡慕得飞起,也想给自己的博客搞一个。但是苦于当时的ai编程尚且不成熟,实现思路是有了,但是一直不会实现,因此就一直没有实现这个小愿望。最近用了ai agent之后感觉就像是开辟新大陆一样,也是终于有机会实现当年的小遗憾了。

📖 English version

基于 TinyWebDB 云数据库的纯前端页面访问计数器。复制 counter.js + 一行代码,为静态网页加上访问计数。

👉 刚做完 HTML 页面? 看 📖 新手教程 — 手把手教你加上计数器,复制粘贴即可,五分钟搞定。

🤖 不想动手? 把教程丢给 Claude,说”帮我加上计数器”就行。

项目结构

Viewer/
├── index.html                    # 演示页面
├── index1.html                   # 演示页面(第二页)
├── README.md                     # 项目说明文档
├── README_en.md                  # 英文版项目说明
├── tutorial.md                   # 新手教程(中文)
├── tutorial_en.md                # 新手教程(英文)
├── js/                           # 核心脚本
│   ├── global.js                 # 全站累计浏览量
│   ├── counter.js                # 单页累计浏览量
│   ├── unique.js                 # 单页独立访客数
│   └── comment.js                # 评论系统
└── tools/                        # 辅助工具
    └── assign_pages.py           # 页面编号脚本

快速体验

cd Viewer
python -m http.server 8080
# 浏览器打开 http://localhost:8080/index.html

每次刷新页面,计数器 +1。打开 index1.html 查看多页面独立计数效果。

功能

模块 文件 说明
全站浏览量 js/global.js 所有页面共享,每次刷新 +1
单页浏览量 js/counter.js 每页独立计数,每次刷新 +1
单页独立访客 js/unique.js 基于 localStorage 判重,同设备只计一次
评论系统 js/comment.js 访客留言,含昵称/邮箱/内容/时间

共同特点:零依赖、复制即用、云端持久化、不绑定 UI。

多页面支持

每个页面通过 <meta name="x-viewer-page-id" content="N"> 声明编号,JS 自动使用独立的数据 tag,互不干扰。

使用编号脚本自动管理:

python tools/assign_pages.py
  • 自动扫描目录下所有 .html 文件
  • 为每个文件分配唯一编号并注入 meta 标签
  • 已编号的页面跳过,已删除的编号永不重用
  • 编号记录持久化在 tools/.viewer_pages.json

工作流程

页面加载 → 读取 meta 编号
              │
         get('watch_N') ← N 为页面编号
              │
    ┌─────────┴─────────┐
    │ 有值              │ 无值 / 首次访问
    │ old = parseInt()  │ old = NaN
    │ new = old + 1     │ new = 1
    └─────────┬─────────┘
              │
         update('watch_N', new)
              │
         返回 new → 页面渲染

部署注意

环境 是否可用
http://localhost ✅ 正常
GitHub Pages(https://) ✅ 正常(需 TinyWebDB 允许 CORS)
直接双击打开(file://) ❌ 浏览器安全策略禁止跨域 fetch

⚠️ 安全警告

静态网页的源代码对所有访问者可见。

任何人按 F12 即可获取 user 和 secret,从而拥有对该 TinyWebDB 数据库的完全读写权限。

  • ❌ 不要将密码、密钥等敏感数据存入此 TinyWebDB
  • ❌ 不要用同一个 user/secret 存放重要业务数据
  • ✅ 此计数器仅适合存放无敏感性的公开数据(如访问次数)
  • ✅ 如需存放敏感数据,应使用有后端鉴权的自建服务

TinyWebDB 接口参考

示例地址

在线浏览 / 导入 / 导出数据库:

https://tinywebdb.appinventor.space/webdb-share1-b3280975

API 信息

项目 值
API 地址 https://tinywebdb.appinventor.space/api
请求类型 POST
Content-Type application/x-www-form-urlencoded

必选参数

参数 值 说明
user aaaaa 用户名
secret d1bdf09a 密钥
action get | update | delete | count | search 操作类型

各 action 参数与返回值

update(更新)

参数 必填 说明
tag 是 变量名
value 是 变量值

无返回值。

get(读取)

参数 必填 说明
tag 是 变量名

返回变量的值。

delete(删除)

参数 必填 说明
tag 是 变量名

无返回值。

count(计数)

无其他参数,返回数据库中保存的变量个数。

search(查询)

参数 必填 默认值 说明
no 否 1 起始编号
count 否 1 返回的变量个数
tag 否 空 变量名包含的字符(模糊匹配)
type 否 both 返回范围:tag / value / both

最多返回 100 条数据。

xiyouspeaker Python 在 GitHub 查看 ↗
一个可以帮你完成西柚英语中烦人的单词朗读的程序

** 此文章同步保存在以下网站 **
森的神秘空间
github

功能简介

该脚本可以帮助你完成 西柚英语 中烦人的单词朗读。

使用方法

第一步,安装python以及相应库

你需要安装的python库有

sounddevice
soundfile
pyautogui

第二步,完成相应设置

设置默认录制设备(十分重要!)

使用前请将系统默认录制设备改成“立体声混音”,否则无法录音

打开 ./config.json ,填写相应配置。

以下是配置文件的相关释义
提示:你可以使用 ./显示鼠标坐标.py 快速定位光标所处的像素的坐标

“screensize”: 屏幕分辨率【x,y】

“nextpos”: ‘下一个’按钮的坐标【x,y】

“input_icon”: 左下角’立即录音’按钮的坐标【x,y】

“input_icon”: 单词下方英式发言右边的小喇叭的坐标【x,y】

这个是这个是1600x900分辨率下,谷歌浏览器(左)半屏打开向右拉到底的设置示例

{
  "screensize": [1600,900],
  "nextpos": [1025,820],
  "input_icon": [340,800],
  "example_icon": [333,250]
}

第三步,启动程序

你可以用cmd运行,也可以用vscode等ide运行文件

$ python 朗读练习v1.0.py

未来的展望

目前只做了几个功能,会考虑以后更多功能的开发

xiyouchooser Python 在 GitHub 查看 ↗
一个可以帮你完成西柚英语中烦人的看词选义和看义选词的程序

** 此文章同步保存在以下网站 **
森的神秘空间
github

功能简介

该脚本可以帮助你完成 西柚英语 中烦人的看词选义和看义选词。

使用方法

第一步,安装python以及相应库

你需要安装的python库有

pytesseract
transformers
pillow
pyautogui

第二步,完成相应配置

打开 ./config.json ,填写相应配置。以下是配置文件的相关释义
提示:你可以使用 ./显示鼠标坐标.py 快速定位光标所处的像素的坐标

“screensize”: 屏幕分辨率【x,y】

“randompos”: A选项的坐标【x,y】

“nextpos”: 选择错误选项时,’下一个’按钮的坐标【x,y】

“shotrange”: 题目单词的范围(注意,只要加粗黑字题目的信息,不要把下一行的音标也框进去了)【左上角x,左上角y,右下角x,右下角y】

“scanrange”: 题目的范围(注意,该范围是严格的,即以a选项的上边框,d选项的下边框为范围)【左上角x,左上角y,右下角x,右下角y】

“y_dist”: 两个选项边框间的间隙宽度【y】

“botton_dist”: 选项按钮的宽度【y】

这个是1600x900分辨率下,谷歌浏览器全屏打开的设置示例

{
  "screensize": [1600,900],
  "randompos": [535,565],
  "nextpos": [1215,825],
  "shotrange": [500,180,970,225],
  "scanrange": [500,470,970,820],
  "y_dist": 15,
  "botton_dist": 55
}

这个是这个是1600x900分辨率下,谷歌浏览器(左)半屏打开向右拉到底的设置示例

{
  "screensize": [1600,900],
  "randompos": [350,565],
  "nextpos": [1025,825],
  "shotrange": [300,180,780,225],
  "scanrange": [300,537,780,795],
  "y_dist": 15,
  "botton_dist": 55
}

第三步,启动程序

你可以用cmd运行,也可以用vscode等ide运行文件

$ python 看词选义v1.1.py

或者

$ python 看义选词v1.0.py

注意:首次启动时会从huggingface.com下载翻译模型,需要耐心等待,如果出现服务器拒绝连接的问题,请重新运行程序或选择适合的代理

此脚本的不足

不幸的是,谷歌翻译在2022年9月停止了对中国大陆的翻译服务,所以我们只能采用huggingface提供的本地离线翻译模型。
由于基于本地的图像识别以及翻译模型,识别速度和准确度会随着电脑性能而有所不同。我的便携电脑cpu不太好,识别准确率只有70%左右。
~~~谁让我没钱买百度识别和百度翻译的的api呢~~~

未来的展望

目前只做了几个功能,会考虑以后更多功能的开发

my_picture_tools Python 在 GitHub 查看 ↗
处理《红楼梦》思维导图时所用的代码

my_picture_tools

处理《红楼梦》思维导图时所用的代码