从 GitHub 抓取的各开源仓库说明文档
纯 HTML + CSS + JavaScript 的静态网页小游戏,零依赖、零构建、离线可玩。
物理引擎(PBD 位置约束求解)是自己写的,没有引入 matter.js 等任何第三方库。
https://yhsome.github.io/BigNaiWa/
(GitHub Pages 托管,手机浏览器打开就能玩,也可以「添加到主屏幕」当 App 用。)

← → 微调位置,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}"}
结算流程(打完自动上榜,不需要手动点提交):
localStorage 里存的,没填就用「默认用户」;排行榜从头到尾只写自己那条记录,不读也不动别人的数据:没有任何 delete,
旧成绩是自然被挤出时间窗的,不是被清掉的。
昵称在排行榜弹窗顶部的「我的昵称」里随时能改(change / blur / 回车时落盘)。
实现上的几个小处理:
localStorage(danaiwa.name),去掉控制字符、限长 12 字,空串一律当「默认用户」;game.js 的键盘处理直接让路(不然打字会掉水果);textContent 渲染,不拼 HTML;换账号:改排行榜源码顶部的 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%,贴图会跟着水果一起滚动/旋转。
换图的两种方式:
assets/fruits/ 覆盖即可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)。
e × 碰撞前速度(球球 e=0.38、墙 e=0.45),e 决定、不受子步/迭代次数影响。REST_THRESHOLD 时完全不弹,所以静止堆叠依然零抖动:box = 2r / 0.92,b.angle 一起旋转,和物理滚动一致;撞击时的挤压变形同样作用于贴图。img.decode() 完成才交给 drawImage,避免画出没解码完的半成品。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 组 | 粒子/汁水颜色(取自贴图主体平均色) |
本项目的玩法与水果链设计参考了以下开源实现(代码为本仓库原创,未复制其源码):
仅供学习娱乐使用。
ai助手你好:
我刚刚为你做好了一个博客渲染器的框架,但是目前还没有具体落地的代码;
你需要做的事:
1.在动手前先提交一次git,即将当前空白项目提交至git
2.将/OpenBlogger/template/default中的所有html文件按照我在html内提的要求完成该html的代码书写(风格不限,但要求风格统一)
3.写一个python代码,代码文件存放在./OpenBlogger中,功能:将./Raw中的所有**未渲染**的md文件按照用户指定的模板进行渲染(默认为default,即对应./Template/Default),将输出的文件放置在Rendered文件夹中,同时,对于格式不规范的md文件,补全渲染的日期(当前时间)与标题(默认第一句,字数超出20字后用省略号)
注意:你所写的所有python代码文件要放在./OpenBlogger里面,以保证根目录的整洁性
一个纯前端的 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 秒)。
这个靶场的默认读者是完全没有写过代码的人。除了题目本身,还有三层”手把手”设计:
新手指南 · 第零课(/guide) —— 一页完整的入门读物:
这个网站怎么工作、程序 / 语言 / 编译器是什么、题目页每个按钮干什么、
逐行读懂第一段代码、把 cout << "..." << endl; 拆成表格一个符号一个符号地解释、
四步做完第一题、中文标点这个大坑、常见报错中英对照表、英文小词典。
首页、顶栏都有入口,第一题页面上还有引导卡片。
每道题都带「本关新知识」和「常见错误」 —— 题目描述不只有”要做什么”,还有”怎么写”:
本关新知识用大白话 + 可运行代码 + 要点表格,讲清这道题需要的语法
(for 的三个部分与执行顺序、struct 末尾的分号、map 的 kv.first / kv.second……);
常见错误表格列出新手在这道题上真实会踩的坑
(i < n 还是 i <= n、值传递为什么改不动外面的变量、依赖 unordered_map 遍历顺序会出错……)。
编译器会说人话 —— 编译失败时,结果面板先给「零基础自查提示」:
自动扫描代码里的中文标点(会先剔除注释与字符串,避免误报 cout << "你好,世界"),
指出第几行第几列、该换成哪个英文符号;并把 expected ';'、
use of undeclared identifier 'cout'、no matching function 等常见报错翻译成中文的建议。
工具栏的「写到哪?」按钮会把光标直接定位到 // TODO 那一行。
题库作者 / 前端开发者想抽查被锁住的题目时用它 —— 因为做题顺序是固定的「按顺序解锁」规则,
页面上没有关闭它的开关,开发者模式是唯一的解锁途径。
三种打开方式(都会持久化,刷新后仍有效):
| 方式 | 用法 |
|---|---|
| URL | http://localhost:4173/?dev=1 打开、?dev=0 关闭(参数会自动从地址栏清掉,不会反复提示) |
| 快捷键 | Ctrl + Shift + D 随时切换(不用离开当前题目页) |
| 设置页 | 「我的进度 → 设置与数据 → 开发者模式」开关 |
打开后:
DEV 标记);-std=c++20 -O0 -Wall -fno-exceptions)、工具链来源与版本、sX-pY.json」按钮(方便直接改题库)。日常练习保持关闭即可。它只是一个纯前端开关,只影响你自己的浏览器。
仓库自带 .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() │ 每用例一个全新沙箱
└────────────────────┘
设计要点:
clang)只编译不执行,产物是一段 wasm 字节码;worker.terminate()。 死循环的用户代码是无法从内部中断的,callMain 之后运行时即退出,-### 解析结果)只计算一次,省掉每次编译的额外开销。callMain 会污染入参数组(Emscripten 内部 args.unshift(thisProgram)),unknown argument: '-cc1'。文件:行:列: error: … 会被解析成结构化诊断,-std=c++20 -O0 -Wall -fno-exceptions
-fno-exceptions 是必须的:本工具链的 wasi-sysroot 中 libc++ 是关闭异常编译的,
不加这个参数链接时会报 undefined symbol: __cxa_throw。
因此题库中不能使用 try / catch / throw,也不要用会抛异常的 .at()。
src/compiler/toolchain.ts 按以下优先级解析:
VITE_TOOLCHAIN_BASE/toolchain(执行过 npm run setup:toolchain 时存在)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 道题,全部通过校验脚本验证(见下)。
src/data/problems/stage-N.json 里追加一道题(字段见上);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 也能编译(初始代码必须能跑,只是答案不对)。
npx playwright install chromium # 首次
npm run preview
node scripts/e2e-problem.mjs http://localhost:4173 s6-p4
浏览器 WebAssembly 沙箱里没有文件系统、网络、进程与系统调用,所以:
cin / 标准输出 cout(printf 也可用)system()、线程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 通过(题解编译 + 每个用例输出一致 + 初始代码可编译)Hello, World!(2.4s)→ 提交全部通过并弹出「恭喜通过」→ 进度写入 localStorage →// TODO、故意用中文分号写错后自动提示”检测到中文标点”、s7-p6(map 统计)19/19 项通过;阶段六 s6-p4(结构体排序)?dev=1 一键解锁并出现 DEV 标记与调试面板 →Ctrl+Shift+D 双向切换 → ?dev=0 关闭,#/guide 与 ?dev=1 深链均可用,控制台零报错、零 404 请求scripts/e2e-java.mjs):首页 7 个阶段卡片 / 42 道题 →// TODO 改成输出语句 → 提交通过并弹出「恭喜通过」 →?dev=1 可直达,控制台零报错npm run validate:java → 42 道题 / 161 个测试点通过javac --release 8 编译题解与初始代码 + 逐用例运行比对,--release 8 是为了和浏览器里的 Java 8 运行时对齐,任何 Java 9+ 写法都会被拦下)scripts/e2e-java-problem.mjs,真实 Doppio JVM 里逐题「填入参考题解 → 提交」):println 打印小数(32.0)、switch + char、嵌套循环打印、String.charAt、long 阶乘)、printf("%.2f")、类 + 手写冒泡排序 + compareTo、HashMap 计数 +Collections.sort、TreeMap 有序输出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=NoneX-CSRF-TOKEN、Referer/Origin、请求体 {lang,code,enableO2}、rid 解析、5 秒冷却、评测记录与提交记录解析(含 records 为数组或 {result:[]} 两种形态)、Cookie 头发出、人机验证错误被翻译成可操作提示scripts/e2e-luogu.mjs,本地与线上各跑一遍;注入时用的是站点上GM_xmlhttpRequest 换成假扩展层,所以同时验证了真实脚本的POST https://www.luogu.com.cn/fe/api/problem/submit/P1001、lang=27、带 X-CSRF-TOKEN 与 Referer/Origin、带上绑定的 Cookie 头)→AC R424242;不在题目页时给出引导)→npm run validate:luogu → 四个靶场 88 个不同题号全部存在且标题核对一致pathname.startsWith('/pro') 判断靶场,/problem/s1-p1 也以 /pro 开头,导致 C++ 题目页显示 Pro 的标语、scripts/e2e-pro.mjs,41 项):题目页确实/toolchain/ 或 jsDelivr 请求(不再下载 90MB 编译器)npm run setup:toolchain 可改为同源加载。public/java-runtime(B-JVM + 编译成 Wasm 的 TeaVM 编译器)。editor.api 与 C++ 语言包。try / catch。Cookie 禁止请求头决定的,/luogu 页的诊断按钮取出原始返回。
🎉 好消息,智学伴小程序正式上线!
扫码进入设置,输入礼包码 114514 即可在手机上体验完整功能
基于生成式人工智能的异步学习解决方案
数智融合 · 创赢未来 · 以 AI 之力重塑每一个学生的学习体验
🌐 在线体验 | 📦 下载桌面版 | 📄 技术白皮书 | 📸 功能展示
ℹ️ 当前为完整版。只想快速了解功能和快速开始?→ 📋 精简版
广州大学第二届”庆园杯”人工智能创新应用大赛 — 揭榜挂帅赛道
命题:基于生成式人工智能的异步学习解决方案
2024 年,教育部等五部委联合印发《”人工智能+”行动计划》,明确提出”培养适应智能时代的拔尖创新人才,推进人工智能与教育深度融合”。2025 年,《新一代人工智能发展规划》进入全面深化阶段,AI 赋能教育数字化转型已从远景蓝图走向落地攻坚。
然而,审视当前在线教育现状,三大结构性矛盾依然突出:
| 矛盾 | 表现 | 深层原因 |
|---|---|---|
| 被动灌输 vs 主动建构 | 学生反复观看录播视频、阅读电子文档,缺乏主动输出与深度加工 | 现有平台停留在”内容搬运”层面,未能将认知科学理论转化为产品机制 |
| 千人一面 vs 个性化需求 | 固定进度、统一难度,无法适应不同学生的知识起点、学习节奏与认知风格 | 缺乏智能化的诊断、适配与动态调整能力 |
| 图文割裂 vs 多维理解 | 理科内容中函数图像、几何关系与文字描述分离,学生需在多个工具间跳转 | 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 能写出漂亮的解题步骤,却画不出一条函数曲线。 当学生面对二次函数的开口方向、三角函数的周期性、双曲线的渐近线、三维曲面的空间形态时,纯文字描述如同用语言去描绘一幅画——再精确的文字也无法替代眼睛看到图像的瞬间理解。
解决这个问题的常规思路是接入图像生成模型(如 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 批改、教促学对话、试卷测评等功能闭环,具备在教学场景中实际运行的能力。
→ 不想读长篇?📋 一页速览:核心功能、技术创新、快速开始
本项目为广州大学第二届”庆园杯”人工智能创新应用大赛参赛作品(揭榜挂帅赛道·命题一)。
MIT © YHSome
一个 Codex skill:目标/任务完成后,自动向 QQ 邮箱发送详细完成摘要。
当你在 Codex 里完成一个目标(goal)、任务或长时运行的工作时,说一句“完成后发 QQ 邮箱给我”,它就会自动整理一份尽可能详细的完成报告,并通过 QQ 邮箱 SMTP 发送到你的邮箱。
Codex,可用 --from-name 或 QQ_MAIL_FROM_NAME 环境变量修改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 邮箱授权码,不要提交到任何代码仓库或分享给他人。基于 HashRoyale / RetroRoyale 搭建的 Clash Royale 1.9.2 私服,并在此基础上
通过纯数据补丁制作了多张自定义卡牌。客户端为 RetroRoyale 修补版 APK(com.retrocell.clashroyale)。
⚠️ 本项目仅供学习与个人研究使用。客户端 APK 含 Supercell 版权资源,未包含在本仓库中;
请自行获取 RetroRoyale 客户端并按下方流程打补丁。服务端数据(GameAssets)来自公开的
HashRoyale 开源项目。
tools/cr_bot.py):自动排队、对局中持续下牌、可自定义卡组start-server.ps1 / stop-server.ps1 / one-click-start.ps1 / 一键启动.bat| 卡牌 | 数据名 | 机制 |
|---|---|---|
| 觉醒冰人 | 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 发火箭 |
HashRoyale/src 重新编译)zipalign/apksigner,签名 APK)adb 安装 APK)# 启动本地 MySQL(或使用现有实例)
# 建库
mysql -u root -e "CREATE DATABASE rrdb2;"
.\start-server.ps1
# 主服 9339 / 集群 9876 / 战斗服 UDP 9449
首次启动会自动建表。玩家/账号数据存在 rrdb2 库的 player 表。
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 的卡组一致)。
客户端 APK 不包含在本仓库。获取 RetroRoyale 1.9.2 客户端后:
tools/make_*.py 补丁脚本生成带自定义卡的 APK(脚本会同时改服务端 CSV 与客户端 APK);zipalign + apksigner 签名(使用 tools/debug.keystore,密码 android);adb install -r clients\retroroyale-1.9.2-phone-modXX.apk 安装;重要:客户端必须包含玩家卡组中的所有卡,否则主界面渲染未知卡会闪退。
因此每次新增自定义卡后,都要从包含全部已有自定义卡的最新 APK 为基础重新构建。
HashRoyale/app/GameAssets/csv_logic/*.csv 加数据行(卡/角色/建筑/投射物/区域效果);tools/make_boundary_zap_machine.py)同步改客户端 APK 中 SC 压缩的 CSV 与文本;player 表的 Home.deck JSON,classId:26=角色、27=建筑、28=法术);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 学习笔记
ProjectileStartRadius(沿朝向偏移),没有“世界坐标出生点”,projectile_research.md)。spells_other 法术卡 + 投射物(spell_goblin_barrel.sc),SpawnCharacter 生成容器;不要用“角色桶”(缺动画剪辑且会闪退)。DeathAreaEffect/DeathSpawnCharacter。Projectile、CustomFirstProjectile、SpawnCharacter、SpawnProjectile),任何“建筑→炮弹→建筑→炮弹…”的循环都会在加载期DeathSpawnCharacter=建筑 加载期安全但运行时被静默忽略(两轮实测不生效);DeathAreaEffect 触发自定义区域效果在进训练营时仍会被更深层加载展开,同样会崩。RandomAngle + RandomDistance(无 Homing)(界地狱塔火球实测有效);RandomAngle 是总扇区宽度:360=全方向,180=目标方向±90°;滚木那套MinDistance+RandomAngle+Homing 实测迫击炮/地狱龙均不生效;SpawnCharacter=角色(哥布林飞桶同款,稳定可用)。_Znwj 挂钩 + 调用点过滤即可在创建时精确计数。RandomAngle/RandomDistance 只能随机投射物飞行/落点,不能随机出生点方向。AngryBarbarian,幻影刺客 = Assassin,狂暴 = BarbarianRage。本仓库代码基于 MIT 许可的 HashRoyale/RetroRoyale 开源项目修改。游戏客户端与美术资源版权归
Supercell 所有;本仓库不包含客户端 APK 及其版权资源。请仅用于学习研究。
一个轻量级静态博客渲染引擎。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 仓库列表展示
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 一键部署。
MIT
一个帮你从零掌握课程知识的 AI 家教。把讲义扔进去,它会像私人家教一样,在浏览器里一步一步给你讲解知识点、出题练习、批改答疑。
在开始之前,你只需要电脑上有两样东西:
不会装 Claude Code?往下看👇
Win+R,输入 cmd 回车),输入 claude 回车,看到欢迎界面就成功了⚠️ Claude Code 目前需要付费 API 额度。如果你是学生,可以关注 Anthropic 的学生优惠。
如果你会 git:
git clone https://github.com/YHSome/claude-final-exam-review.git
如果不会 git(或者上面的命令看不懂),跟着下面做:
期末复习 文件夹)期末复习/
├── 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 能更好地组织内容。
Win+R → cmd → 回车)cd C:\Users\你的用户名\Desktop\期末复习claude/final-exam-review我要复习高等数学,从第一章开始AI 家教会自动:
- 扫描你的讲义文件
- 在浏览器里打开一个排版精美的讲义页面(公式用 LaTeX 渲染)
- 一步一步给你讲解每个知识点
- 每讲完一节停下来问你有没有疑问
- 出练习题给你做,做完帮你批改
🖥️ 浏览器里看公式:讲解内容会在浏览器中显示,公式渲染效果跟教材一样清晰。终端只用来简单对话。
A: 这是 MathJax 没加载成功。试试:
1. 检查网络连接(MathJax 需要从 CDN 加载一次)
2. 刷新浏览器页面(F5)
3. 如果还不行,确认你是通过 http://localhost:8888/... 打开的,不是双击 .html 文件
stop-server.cmd 后浏览器打不开了? ¶A: 这个文件会关闭后台服务。如果不小心双击了,在 Claude Code 里说”重启服务器”就行。
A: 这是讲义提取脚本的依赖。如果你不需要自动提取 PPT/PDF 文字(比如你用的是纯文本笔记),可以不装。否则,在终端输入:
pip install unstructured
A: 所有公式都在浏览器里!终端只用来简短对话(”懂了”“继续”“出几道题”),真正的讲解内容、公式推导、例题都在浏览器页面里。这是设计好的双屏模式。
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 的行为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 面向 Windows 桌面,适合随手记录、整理本地 Markdown 与编辑常见文本文件。它使用系统 WebView2 渲染界面,不捆绑完整的 Electron 运行时;当前构建的便携版约 5.2 MiB,安装包约 3.1 MiB,下载后即可使用。
经典暖橙、Dark 2026 与流光花境均为完整主题,不只是更换背景。前景组件、窗口底色、选区强调色、背景图片与透明度可继续独立微调。
流光花境主题使用的壁纸来源于 Wallhaven。
| 经典暖橙 | Dark 2026 | 流光花境 |
|---|---|---|
![]() |
![]() |
![]() |
默认使用 Ctrl + Alt 呼出独立临时便签。一次按键只创建一个窗口,持续按住也只计作一次;便签关闭前可选择保存或放弃。
编辑区保留 Markdown 源文本的语法着色,右侧实时呈现排版结果;代码块带语言高亮与行号,数学公式由 KaTeX 渲染。预览栏可随时开关、拖动调整宽度或拖至边缘收起。

支持内容包括:

设置会实时预览,只有点击“保存”才会持久化;取消或直接关闭设置窗口会自动恢复。可调节项目包括:
| 类别 | 功能 |
|---|---|
| 编辑 | 多标签、撤销/重做、实时字数、未保存状态、另存为 |
| 文件 | 打开与保存常见 UTF-8 文本/代码文件,单文件最大 32 MiB |
| 记录 | 当前笔记与最近文件统一展示,删除界面记录不触碰原文件 |
| 搜索 | 即时匹配标题、路径与正文,无需按回车 |
| Markdown | 源文本语法着色、实时预览、LaTeX、代码高亮与行号 |
| 布局 | 左侧记录栏与右侧预览栏可拖动、折叠并记忆宽度 |
| 临时便签 | 全局快捷键呼出、独立窗口、关闭前保存确认 |
| 外观 | 三种主题、背景图、颜色、透明度与字号实时预览 |
github上提供两种 Windows x64 成品:
Notelet_0.8.3_x64-setup.exe,按向导完成安装。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 + , |
打开设置 |
npm install
npm run tauri -- dev
只调试前端界面时可运行:
npm run dev
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、体积优化与符号剥离,以控制成品大小。
模块化、可扩展的开源框架,用于研究太空联邦学习中的轨道环境模拟、卫星通信与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 高精度后端
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),
)
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/ # 格式化
MIT License — 详见 LICENSE
Claude Auto-Responder — 自动监测 Claude Code 窗口,当出现选项提示(Yes/No、Continue? 等)时自动按下 Enter 键。
简体中文 | English
⚠️ 整活项目,仅供娱乐!
本项目仅为作者在考英语六级前闲的蛋疼而写,请勿在任何正式开发、生产环境或重要项目中使用。
自动按键意味着你可能在不了解具体操作的情况下盲目同意文件修改、命令执行等行为,
可能导致数据丢失、系统损坏或其他不可预知的后果。作者不承担任何使用本脚本造成的责任。
┌─────────────────────┐
│ 窗口上部 70% │ ← 对话内容,不监控
│ (对话历史) │
├─────────────────────┤
│ 窗口下部 30% │ ← 只截取这部分做 OCR
│ (提示区域) │ Yes/No 选项都在这里
└─────────────────────┘
↓
截图 → OCR 识别文字 → 正则匹配提示词 → 自动按 Enter
↓
刷新终端仪表盘(实时显示 OCR 结果、统计、事件日志)
只截取窗口底部 30% 的提示区域做 OCR,不扫描上部大段对话历史,减少误触发、提高识别速度。
pip install pyautogui pillow pytesseract pynput rich pywin32
| 平台 | 安装方式 |
|---|---|
| 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 秒)动过键盘,脚本会跳过本次按键,不会跟用户抢键盘控制权。
cls 原位清屏刷新;PyCharm 内置终端忽略 cls,靠滚行推出旧内容--title 指定窗口前记:作者在23年的时候刚开始玩纯静态网页写博客(现在已经看不见了),看到别人每一篇文章都有阅读量统计和评论,羡慕得飞起,也想给自己的博客搞一个。但是苦于当时的ai编程尚且不成熟,实现思路是有了,但是一直不会实现,因此就一直没有实现这个小愿望。最近用了ai agent之后感觉就像是开辟新大陆一样,也是终于有机会实现当年的小遗憾了。
基于 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 文件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存放重要业务数据- ✅ 此计数器仅适合存放无敏感性的公开数据(如访问次数)
- ✅ 如需存放敏感数据,应使用有后端鉴权的自建服务
在线浏览 / 导入 / 导出数据库:
https://tinywebdb.appinventor.space/webdb-share1-b3280975
| 项目 | 值 |
|---|---|
| API 地址 | https://tinywebdb.appinventor.space/api |
| 请求类型 | POST |
| Content-Type | application/x-www-form-urlencoded |
| 参数 | 值 | 说明 |
|---|---|---|
user |
aaaaa |
用户名 |
secret |
d1bdf09a |
密钥 |
action |
get | update | delete | count | search |
操作类型 |
update(更新)
| 参数 | 必填 | 说明 |
|---|---|---|
tag |
是 | 变量名 |
value |
是 | 变量值 |
无返回值。
get(读取)
| 参数 | 必填 | 说明 |
|---|---|---|
tag |
是 | 变量名 |
返回变量的值。
delete(删除)
| 参数 | 必填 | 说明 |
|---|---|---|
tag |
是 | 变量名 |
无返回值。
count(计数)
无其他参数,返回数据库中保存的变量个数。
search(查询)
| 参数 | 必填 | 默认值 | 说明 |
|---|---|---|---|
no |
否 | 1 |
起始编号 |
count |
否 | 1 |
返回的变量个数 |
tag |
否 | 空 | 变量名包含的字符(模糊匹配) |
type |
否 | both |
返回范围:tag / value / both |
最多返回 100 条数据。
** 此文章同步保存在以下网站 **
森的神秘空间
github
该脚本可以帮助你完成 西柚英语 中烦人的单词朗读。
你需要安装的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
目前只做了几个功能,会考虑以后更多功能的开发
** 此文章同步保存在以下网站 **
森的神秘空间
github
该脚本可以帮助你完成 西柚英语 中烦人的看词选义和看义选词。
你需要安装的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呢~~~
目前只做了几个功能,会考虑以后更多功能的开发
处理《红楼梦》思维导图时所用的代码