OJ 每日知识点讲义(wechat-md)
边界
| 技能 | 分工 |
|---|---|
| 本 skill | 班级当日知识点 / CSP 模拟卷理论题 / Python 启蒙课讲义 → md/tex/pdf |
gesp-text |
GESP 真题讲义 tex(不管每日稿);C++ 码风以 gesp-text §1 为准 |
pdf-2-md |
权威源 md → tk/OJ |
文风见 writing-style.md。
> 每天课程结束,根据班里学生当天在 OJ 的做题情况,自动生成一份纯知识点整理讲义。
>
> ⚠️ 交付铁律:每次报告交付 3 个文件 = md + tex + pdf。流程必须分两步:第 1 步只给用户看 md,等用户确认「可以/没问题」;第 2 步才用 md2tex.py 生成 tex、再用 xelatex 编译出 pdf。md 未确认前绝不生成 tex/pdf。
> ⚠️ 改之前先读最新版:用户会手动编辑交付目录里的 md,重新生成前必须先 diff 用户版,用户删过的内容绝不弄回来。
> ⚠️ 试编译出的 pdf 默认保留(用户明确说删再删);说「先只要 tex / 先不生成 pdf」时只出 .tex。
开工必问(闸门 · 最高优先级)
任何拉 OJ、写 md、生成 tex/pdf 之前,若用户本轮未明确给出下列三项,必须先提问并等答复;禁止默认替用户选定(哪怕「上次做过」「看起来像 Python 班」)。
| # | 必问 | 问什么 | 备注 |
|---|---|---|---|
| 1 | 哪位老师 | 例题代码对应的 OJ 用户名(如「七七」) | 用于 fetch_teacher_multi.py --teacher;页眉作者可用显示名(如「琦琦老师」)——显示名也可一并问,未问则默认「琦琦老师」 |
| 2 | 哪个班级 | OJ 班级名 或 gid,以及 哪一天(默认今天) | 可用 get-group-list 搜关键字列选项给用户点选;模拟卷/纯理论题若无 OJ 班级,问清「班次称呼」即可(用于页眉) |
| 3 | 存到哪个路径 | 最终 md/tex/pdf 目录 + 文件名前缀(如 26-SAT0900)+ 页眉文案 |
把下方「常见备选」当选项列出,让用户选或改;禁止未确认就写进某个目录 |
提问模板(可原样用):
开工前确认三件事:
1. 哪位老师?(OJ 用户名,例题代码用谁的 AC)
2. 哪个班级?(班级名或 gid;哪一天)
3. 东西存到哪?(目录 + 文件名前缀如 26-SAT0900;页眉要写什么)
用户已在本轮消息里写清的项不必再问;缺哪项问哪项。三项齐了再进入工作流。
会话中途若切换班级/老师/路径,重新确认后再动文件。
交付目录与文件名
常见备选(仅作提问选项,不是默认)
| 班次备选 | 目录备选 | 文件名备选 | 页眉备选 |
|---|---|---|---|
| C++ · 周六 09:00 / CSP-J | …/typora/2026-2/C++ |
26-SAT0900-MMDD.md |
CSP-J · 周六09:00 |
Python · 周六 13:00 / SAT-1300-PY |
…/typora/2026-2/Python |
26-SAT1300-MMDD.md |
Python · 周六13:00 |
完整根路径示例(OneDrive):/Users/sonic/Library/CloudStorage/OneDrive-Personal/typora/2026-2/
确认后的落盘规则
| 项 | 规则 |
|---|---|
后缀 MMDD |
最近一次更新日期(不是开课日) |
| 新内容怎么加 | 追加到该文件最上面(旧内容整体下沉);同时把文件名日期后缀改成今天 |
| tex/pdf | 与 md 同目录同名;写 tex 时先写 /tmp/...tex 再 cp 进交付目录(避免 OneDrive 同步吞写) |
页眉 --header / --author |
用用户确认的页眉与老师显示名 |
操作注意:
- 开工写 md 前,先
ls用户确认的目录,找到已有同前缀*-MMDD.md(取最新一份)。 - 有旧文件:读入 → 新讲义块写在文件最顶部 →
---分隔 → 保留旧内容 →mv改名为今天日期。 - 没有旧文件:按确认的前缀新建
前缀-MMDD.md。 - 禁止把最终交付写到 Desktop /
/tmp/wechat-md;/tmp只可作中间数据。 - 模拟卷/理论题同样遵守「先问路径」;用户未指定时列出常见备选请选,不要擅自写入。
When to use
- 用户说「今天 XX 班的做题情况」「出个知识点讲义/知识点整理」「今日小结」。
- 用户给出班级名(如
CSP-J/S暑期集训2608、SAT-1300-PY)或 gid,要求整理当天练的知识点。 - 用户手上没有现成数据,需要先从 OJ 拉取。
- 模拟卷 / 选择题理论讲义(如 CSP-J 模拟卷 10 的 1–15):按题号写讲义,同样走本 skill 的目录、码风、
md2tex。
模拟卷 / 理论选择题讲义
适用于权威源客观题(如 reputable-sources/.../CSPJ2077-M10.md)整理成课后讲义。
比赛卷/模拟赛:加 ## 比赛结果分析,放在三题讲评之前(总览之后);只写共性问题,不写学生姓名、个人分数、人数、平均分、人次、满分人数。
结构(按题号,不要按知识点大类拆):
# YYMMDD更新——CSP-J模拟卷X·选择题1–15
## 第1题 · 题眼标题
**题意**:…
#### 方法一:…
#### 方法二:…
- ==易错点==:…
**答案:B(…)**
---
## 第2题 · …
内容约定:
- 重点题加细(用户点名的必展开:推导 / 小例子 / 代码 / 排除法)。
- 不要「答案速查」总表(每题已有答案框)。
- ==易错点== 在 md 里保留 typora 高亮写法;进 PDF 渲染为赭石加粗「易错」(\pitlabel),禁止白字蓝底徽章(像 App 标签,违和)。
- 其他 ==关键词== → 淡黄底深字(\markhl,对齐 typora 荧光笔)。
- TikZ / 复杂示意图不要写进 md(typora 不方便);在 md2tex.py 的 _AUTO_FIGS 里按小标题自动插入(例:先二维→单位正方形,再三维立方体(本题)→单位立方体)。md 只写文字要点。
- 代码强制 gesp-text §1:Allman、4 空格、'\n' 禁 endl、and/or、单语句省 {}。
- 页眉:用开工确认的 --header(常见备选 CSP-J · 周六09:00);文字与蓝线间距极近(自绘 vbox)。
权威源路径备忘: CSP-J 通关之路模拟卷 → ~/Documents/reputable-sources/10_卷子/CSP-J/专题/CSPJ2077-M#.md。
GESP 比赛复盘
- 用
/api/get-contest-list?keyword=<比赛名>实时查比赛,响应的id才是 cid;不要把某次比赛编号长期当默认值。 - 拉比赛题单;接口不可用时先汇报,再以用户确认的题号范围和权威题面为准,禁止按零散自由练习猜题单。
fetch_codes.py必须同时读取自由练习和比赛提交:
bash
python3 skills/wechat-md/scripts/fetch_codes.py --gid <GID> --date <YYYY-MM-DD> --cid <CID> --out /tmp/wechat-md/codes.json
- 每题先写评价,再写真实 AC 对应的方法。固定比赛、gid、题号范围只可作为当次任务记录,不写入长期技能。
编程派 Python 项目讲义
适用于 SUN1330 等“编程派做一个项目”的课,不是 OJ 做题。
拉项目
BC_USER=<手机号> BC_PASS=<密码> python3 skills/wechat-md/scripts/fetch_biancheng.py \
--project <projectId> --out-dir /tmp/bc<projectId>
- 登录
POST https://api.biancheng.love/login,cookie 会话。 - 项目信息:
GET /project/info/{id};代码:GET https://asset.biancheng.love/sands/{id}/project.json。 - 课程文档:
GET /python/paimon/{id};data.paimon是 JSON 字符串,空对象表示还没写文档。 - 班级课程:
/classroom/{classId}/courses/complete;classId 从班级中心或 URL 确认。 - 找最近项目可看 Edge 历史里的
sands/editor?id=.../project/show?id=...。
编程派文档(paimon)格式
{
"version": 1,
"baseCode": {"<代码文件key>": "<起始代码>"},
"stages": [{"stage": 1, "title": "第一阶段:…", "goal": "完成后运行:…"}],
"stepJson": [{
"stage": 1,
"script": "Main",
"title": "这一步做什么",
"explanation": "为什么这样做",
"changes": {"<代码文件key>": "<这一步完成后的完整代码>"},
"images": ["<截图URL,可空>"]
}],
"questions": [{
"stepIndex": 0,
"type": "choice|qa|judge",
"question": "…",
"options": ["…"],
"answer": "A|错误|…",
"analysis": "…"
}],
"keyPoints": ["…"],
"intro": "项目一句话介绍"
}
changes存的是这一步完成后的完整代码,不是 diff。stepIndex对应stepJson的下标。- 题目类型:
choice选项 + 答案字母;judge正确/错误;qa只给参考答案。 - 先有
stages,再把每阶段拆成 3~6 个stepJson,最后按步骤出题。
讲义结构
- 项目目标 / 最终效果 / 素材(
tiles里的wall、dot、box、ball)。 - 按阶段:搭场景 → 核心交互 → 计分/胜负 → 可扩展。
- 每个阶段给完整代码 + 新函数解释 + 易错点。
- 只写项目里真实存在的函数和逻辑;项目名里有 Idle/升级,不代表代码已经实现。
- 首页独立:第一页只放项目目标、函数速查、项目信息;阶段一前写
<!-- pagebreak -->,保证「阶段一」从第二页开始。首页不要把阶段标题或代码带出来。 - 首页尽量占满一页;不够就加项目信息/素材/函数表,不要把后面的阶段内容提前塞进首页。
Python 启蒙课讲义
适用于用户指定的 Python 启蒙班当日 OJ 练习整理;gid 每次从班级列表确认,不长期写死。
- 一题多解全列:写 md 前逐条看
raw_submissions的代码,按写法归类。循环、切片、内置函数、if 打表/暴力都算方法;真实提交里出现的写法都要并列,不能只写自己想的循环。 - 禁止自己编方法:每一种方法都必须能指回老师/学员的真实提交;只能改缩进、引号、空行、变量名这类风格,不能改逻辑,也不能写一个没人交过的写法。没有真实写法就不写。
- 打表就是真的把答案列出来:不能用切片循环生成表冒充打表;学生没交过
chr循环,就不要自己补一个。 - 方法标题不写作者:只写「方法一:if 打表」「方法二:切片」,不写「老师写法」「学生写法」。
- 例:
T06-006至少有 if 打表、切片s[:n]、循环chr三种;T06-007至少有切片s[n-1:m]和循环两种。 - 切片、
in、sum、max、chr/ord都是正式解法,不是偷懒;不要只写 for。
结构要对齐 C++ 讲义手感(用户明确要求):
- 一题一节:每题独立 ## \题号` 题名,**禁止**「知识点大章##+ 多题塞进###」的目录式写法。
- **题目名逐字用 OJtitle**:不改写、不缩写、不加副标题;## `题号` 题名里的题名必须和 OJ 一致。
- 节奏:题意 → 为什么 / 怎么想 →#### 方法→ 代码 →==易错点==。
- 讲解密度要够:嵌套子弹讲「为什么」;不要干巴巴的题单摘要。
- **不要合并题目**(用户明确:竖式等每题各自一节,不要「同类题打包」)。
- 按**教学顺序**排题(如:纯 print 图形 → 交换 → 格式拼接 →int运算 → 字符/比较),不是 OJ 提交时间序。
- 页眉:用开工确认的--header(常见备选Python · 周六13:00`)。
- 零元信息 / 读者测试:禁止写数据来源、AC 人数、老师提交情况、班级内部代号等;删掉页眉后正文仍应像给学生家长看的讲义。
数据源(铁律)
| 数据 | 接口/方式 | 认证 |
|---|---|---|
| 班级列表/搜索 gid | GET /api/get-group-list?keyword= |
匿名 |
| 班级成员 | GET /api/group/get-member-list?gid=<gid> |
需登录(Authorization 头) |
| 当天编程题提交 | GET /api/get-submission-list?username=<名> |
匿名 |
| 提交源码 | GET /api/get-submission-detail?submitId=<id> |
需登录 |
| 题目标签(知识点) | GET /api/get-problem-detail?problemId=WLX<pid> |
需登录 |
| 当天客观题记录 | SSH + MySQL:question_record join question |
SSH 免密 |
- 登录:
POST /api/login,凭证见skills/wechat-md/env.example(OJ_LOGIN_USER/OJ_LOGIN_PASSWORD,不写进 Git)。token 取响应头Oj-Token。 - 192 是自签名/IP 证书:所有请求等价
curl -k,脚本已内置忽略 SSL。 - 提交记录时间是 UTC(+0000):脚本按本地日期(CST +8)过滤当天,勿手算偏移。
- 客观题无批量查询接口:只能 SSH 到服务器
docker exec qiqioj-mysql mysql查question_record(按班级成员 uid + 当天create_time过滤),脚本已封装。密码只在服务器读密钥文件。 - 知识点归一(琦琦 OJ 短名体系,内置脚本):路径计数Ⅰ~Ⅳ→
DP(路径计数)、简单的路径计数VI→卡特兰数(不越对角线路径=卡特兰数)、快速幂→快速幂、逆元→数论(逆元)、魔术扑克牌排列→卡特兰数、栈→卡特兰数(出栈序列=卡特兰数,不是"栈队列"!)、各位数字都不同→数位、最多的花生/0与1/堆石子→模拟、求三个数的大小顺序→分支。客观题:栈队列→栈队列、图论→图论、数制转换→数制转换。伪知识点不写:真题套卷/计算机基础/未分类(客观题标题抠出的分类)。本表是 OJ 编程题提交侧的短名归一;客观题/tk 标签长名词表见pdf-2-md「标签」,两套不要混用。
工作流
0. 先过「开工必问」闸门
三项(老师 / 班级·日期 / 路径·前缀·页眉)未齐 → 只提问,不拉数据、不写文件。齐了再往下。
1. 拉全量提交 + 回执
- 每个班写 md 前,拉 老师 + 全班 当天 全部提交(AC/WA/TLE/CE,练习+比赛),
raw_submissions不截断、不按 AC 过滤、不去重成 candidates。 - 只按
submitTime取本节课时段;老师代码单独并进来。考试/比赛题可能不在自由练习里,不能拿 0 条推断没考。 - 每次交付必须回执:班级学员名字(不含老师)、老师+学员总提交数、老师提交数。少一项就是没拉全,重拉。
- 示例:
SAT1600:高泊然、刘可欣、…(9 人);总提交 306,老师 5,学员 301。
# 成员 / 客观题
python3 skills/wechat-md/scripts/fetch_oj_data.py --gid <GID> --date <YYYY-MM-DD> --out /tmp/wechat-md/data.json
# 自由练习 + 比赛编程提交
python3 skills/wechat-md/scripts/fetch_codes.py --gid <GID> --date <YYYY-MM-DD> [--cid <CID>] --out /tmp/wechat-md/codes.json
# 老师本人
python3 skills/wechat-md/scripts/fetch_teacher_multi.py --teacher <老师OJ用户名> --out /tmp/wechat-md/teacher_codes.json
- 没 gid 用
--group-name;fetch_codes.py有--cid时同时读比赛提交。 - 输出
raw_submissions;problems[].solutions只是候选,不能代替全量。 - 老师 OJ 用户名必须显式传入,禁止写死「七七」。
2. 筛例题代码
- 先爬代码再写 md:老师+学生当天涉及题目的全部提交代码 → 按思路去重(同思路只留 1 个代表,不同思路都留)。
- 老师有 AC:用老师真实源码,只允许去掉运行标记注释、把
endl改\n;不改算法、控制流、个人写法。 - 老师无 AC:才可选班级成员的真实 AC,并按老师既有写法整理;老师和学生都无 AC 时跳过该题,不编标准程序。
- 多方法必须来自真实的不同思路 AC;同思路只保留一份。
- 例外(Python 启蒙基础题):老师当日无 Python AC、但题目是固定样例/极简 input,可用老师历史 Python AC;仍禁止编复杂算法题解。
3. 生成知识点讲义 md(先给用户看,等确认)
⚠️ 铁律:改之前先读最新版! 用户可能手动编辑过交付目录里的 md(删句子/改措辞/删知识点)。任何重新生成/覆盖前,必须:
1. 先读用户确认目录下、确认前缀的最新一份 前缀-*.md;
2. 先 diff 我上次生成的版本 vs 用户版,找出用户删改的内容;
3. 把用户删除的内容从 gen_report.py 里同步删掉(用户删过的,即使觉得合理也绝不弄回来;用户删的知识点加入 _SKIP_KP 过滤);
4. 新内容 prepend 到文件最上面,旧内容保留在下方;写完后把文件名日期后缀改成今天;
5. 禁止盲目覆盖用户编辑过的文件。
python3 skills/wechat-md/scripts/gen_report.py /tmp/wechat-md/data.json /tmp/wechat-md/codes.json /tmp/wechat-md/teacher_multi_codes.json --out-dir /tmp/wechat-md
中间产物可写 /tmp/wechat-md;最终 md 必须落到用户确认的目录 + 前缀(见「开工必问」与「交付目录」):
- 前缀-MMDD.md — 知识点整理讲义(主交付物,新内容在最上面)
- 同目录稍后确认后的 .tex / .pdf
- 可选:微信总结 txt 可同目录或仅聊天里给用户
讲义结构(C++ OJ 日报 · 按知识点,琦琦风格):
# 260811更新——dp、卡特兰数、快速幂、数论、图论 ← 6位日期 + 更新—— + 知识点短名列表(自动按实际输出,无班级名无导语)
## 卡特兰数 ← 知识点
- 要点(公式用 LaTeX \frac、代码符号用行内代码 `b & 1`)
### `TS77020` 简单的路径计数VI ← 例题名(题号+题名)当标题;题名逐字用 OJ title,禁止改写
#### 方法一:表格 DP + 高精度加法(纯加法) ← 方法一/二/三
- 讲解(含「为什么…」推导)
- 子要点(多层级:4 空格缩进嵌套列表)
```cpp
// 老师真实提交代码(规范化后)
- ==易错点==
**多日累积时文件内部形态:**
260912更新——…… ← 今天新写的整块(在最上面)
……
260905更新——…… ← 更早的内容下沉保留
……
文件名只保留**最近更新日期**:`26-SAT0900-0912.md` / `26-SAT1300-0912.md`。
**内容铁律:**
- **纯知识点整理**:不写学生情况/表扬榜/提交明细/AC 人数/班级名内部代号;结尾无客套话。
- **零元信息**:禁止夹带内部规范提示(如「数据来自 OJ」「用七七历史 AC」「课堂 AC 规范化」——讲义是给学生/家长看的)。
- **读者测试**:删掉页眉班级内部名后,正文仍应像「琦琦写给孩子家长的讲义」,不能像工作备忘录。
- **每题不只写公式,必须讲「为什么」**:为什么这样递推/为什么是组合数/为什么是卡特兰数/为什么 b^(p-2)——讲解第二层展开子要点(多层级列表,对齐 typora 风格 `- 要点` 下缩进 `- 子点`)。
- **公式一律 LaTeX \frac**:$A_n^m=\frac{n!}{(n-m)!}$、$\frac{a}{b} \bmod p$、$\frac{1}{3}$;代码符号用行内代码(`` `b & 1` ``、`` `res` ``、`` `dfs(pos, mask, used)` ``)。
- **类比必须贴切**:不贴切的类比宁可不写(用户删过 DP 的「类比"倒水"」)。
- **非主线知识点过滤**(`_SKIP_KP`,如某天数位/数制转换非主线):标题自动只列实际输出的知识点。
- 客观题只讲真知识点(图论/栈队列/数制转换),伪知识点(真题套卷/计算机基础/未分类)不写。
- 图论多总结公式(握手定理、完全图、树、欧拉公式);队列重点写循环队列。
**代码规范化:** 无老师 AC、需要整理学生代码时遵循 `gesp-text` §1;老师真实源码以上述保真规则优先。不要为了统一模板改写老师代码。
- **Allman**:左大括号独占一行;单语句 for/if/while 省 `{}`,多语句块保留大括号
- 缩进 **4 空格**;禁 `#define int long long` / `signed main` → `#define ll long long` + `int main`
- 输出换行用 `'\n'`,**禁止 `endl`**(含 `#define endl`)
- `and` / `or`(禁 `&&` / `||`);bool 用 `1` / `0`
- `#include <bits/stdc++.h>`(`<` 后有空格);二元 `<<`/`>>` 两侧加空格
- 模数科学计数法:`1000000007` → `1e9 + 7`
- 普通数组优先:DP 等定长数组用全局裸数组,不用 vector(图论邻接表除外)
- 几乎不写注释;关键一步可点一下;空行极简
**然后在回复中提供可点击的 md 文件链接,等用户说“可以/没问题”。** 用户要求修改时先读最新版并保留其手工改动;当前工具没有专门展示命令时,不得引用不存在的工具名。
### 4. 生成 tex + pdf(**md 确认后才做**)
**用户确认 md 后**(说「可以/没问题」),生成 tex 和 pdf:
```bash
# 路径 / 前缀 / 页眉 / 作者 均来自开工必问确认结果
python3 skills/wechat-md/scripts/md2tex.py \
"<确认目录>/<前缀>-MMDD.md" \
/tmp/<前缀>-MMDD.tex \
--header "<确认页眉>" \
--author "<老师显示名>"
cp /tmp/<前缀>-MMDD.tex "<确认目录>/<前缀>-MMDD.tex"
cd "<确认目录>"
xelatex -interaction=nonstopmode <前缀>-MMDD.tex
xelatex -interaction=nonstopmode <前缀>-MMDD.tex
md2tex.py能力与版式约定:- 标题:
#section /##subsection /####→\methodhead(独立成行+留白,禁 run-in\paragraph) $...$/$$...$$、表格、多级/有序列表、代码块(listings,自动识别 python/cpp)、---、答案绿框==易错点==→\pitlabel(赭石「易错」);其它==…==→\markhl(淡黄底)_AUTO_FIGS:按小标题自动插 TikZ(图不进 md)- 页眉
--header:文字紧贴蓝线;页脚页码 - 写文件:先
/tmp再cp到 OneDrive 目录,避免同步覆盖 - 三件套交付:同目录
26-SAT…-MMDD.md+.tex+.pdf。 - 铁律:md 未过用户审核前,不生成 tex/pdf。 若用户只改了 md 措辞,重新生成时先 diff 用户版,改完再走 md→tex→pdf。用户说「先只要 tex / 先不生成 pdf」时,只出
.tex;试编译 pdf 默认保留。 - 编译两次
xelatex;工作目录就在 typora 该文件夹内。
常见坑
- 成员接口 500:token 没带对(必须
Authorization头),先重新 login。 - 客观题 0 条:① 该班当天确实没做客观题;② SSH 不通;③ SQL 引号被 shell 吃掉(脚本已处理,勿手动改)。
- 班级名空:
--gid模式会自动补班级名;旧 JSON 没有 name 时重跑一次。 - 凭证:见
env.example→ 本地.env.local(不进 Git;发布 skillhub 时也绝不能打进 zip)。 - 未问就开干:缺老师/班级/路径却直接拉数据或写文件 = 违规;先问再做。
- 卡特兰数:栈题/魔术扑克牌/路径计数VI 都归"卡特兰数",不归"栈队列"/"组合"/"DP"。
- 改 md 前先 diff 用户版:用户删过的(哪怕觉得合理)绝不弄回来。
- 文件名里有
/会变成目录:文件名一律去掉/(用CSP-JS)。 - OneDrive 写 tex:先写
/tmp再cp,避免同步把内容吞回旧版。 - skillhub 发布:从不含
.env.local的干净目录打包;本仓skills/wechat-md与.agents/skills/wechat-md为同一 inode。