feat: print includes answer only after unlocking solutions
- If solutions already unlocked (full mode), print button shows '打印题目+答案' - If not unlocked, print button shows '打印题目' (problem only) - No longer forces unlock when printing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
这个提交包含在:
164
SeedCppBasic_20_items.array.cc
普通文件
164
SeedCppBasic_20_items.array.cc
普通文件
@@ -0,0 +1,164 @@
|
||||
const CourseItem items[] = {
|
||||
{
|
||||
"cpp-basic-01-hello",
|
||||
"C++基础01:环境配置与Hello World(VSCode)",
|
||||
1,
|
||||
"course:cpp-basic:01",
|
||||
"# C++基础01:环境配置与Hello World(VSCode)\n\n## 学习目标\n- 安装并打开 VSCode,创建并运行第一个 C++14 程序\n- 学会新建文件、保存、运行、查看输出\n- 了解 `main()`、`#include <iostream>`、`cout`\n\n## 推荐视频(观看后写笔记)\n- 保姆级:VSCode + mingw64 配置 C/C++(BV1tg411N7Fq)\n - https://www.bilibili.com/video/BV1tg411N7Fq/\n- 每天五分钟学C++:01 开发工具(BV1dK4y137bk,系列入口)\n - https://www.bilibili.com/video/BV1dK4y137bk/\n\n## 参考图文\n- LoongBa 极简配置:GCC/VSCode/HelloWorld\n - https://github.com/LoongBa/Cpp_Beginner_Guide\n\n## 练习(完成至少 2 题)\n- B2002 Hello, World! https://www.luogu.com.cn/problem/B2002\n- P1000 超级玛丽游戏 https://www.luogu.com.cn/problem/P1000\n\n## 提交要求\n- 在本题页面下方“学习笔记”区域写下:\n 1) 你安装了什么、遇到什么坑、怎么解决\n 2) 你的 HelloWorld 代码\n 3) 你学到的 3 个关键词\n",
|
||||
{"cpp-basic","vscode","io","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-02-io",
|
||||
"C++基础02:输入输出与变量",
|
||||
1,
|
||||
"course:cpp-basic:02",
|
||||
"# C++基础02:输入输出与变量\n\n## 学习目标\n- 会用 `cin` 读入、`cout` 输出\n- 理解变量:`int / long long / double / char / string`\n\n## 推荐视频\n- 每天五分钟学C++:02 输出、03 变量(系列入口见上)\n - https://www.bilibili.com/video/BV1dK4y137bk/\n\n## 练习\n- P1001 A+B Problem https://www.luogu.com.cn/problem/P1001\n- B2008 计算 (a+b)×c 的值 https://www.luogu.com.cn/problem/B2008\n- P5704 字母转换 https://www.luogu.com.cn/problem/P5704\n\n## 提交要求\n- 上传/填写学习笔记:写出 `cin/cout` 模板、常见错误(空格/换行)\n",
|
||||
{"cpp-basic","io","types","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-03-branch",
|
||||
"C++基础03:分支结构(if / switch)",
|
||||
2,
|
||||
"course:cpp-basic:03",
|
||||
"# C++基础03:分支结构(if / switch)\n\n## 学习目标\n- 会写 `if / else if / else` 与基本逻辑运算\n- 能处理边界与分类讨论\n\n## 练习\n- B2035 判断数正负 https://www.luogu.com.cn/problem/B2035\n- P5711 闰年判断 https://www.luogu.com.cn/problem/P5711\n- P1909 买铅笔 https://www.luogu.com.cn/problem/P1909\n\n## 提交要求\n- 笔记里写清:你如何找“边界”(例如 0、最小/最大、等于条件)\n",
|
||||
{"cpp-basic","branch","logic","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-04-loop",
|
||||
"C++基础04:循环结构(for / while)",
|
||||
2,
|
||||
"course:cpp-basic:04",
|
||||
"# C++基础04:循环结构(for / while)\n\n## 学习目标\n- 会用循环做:计数、累加、打印图形\n\n## 练习\n- B2083 画矩形 https://www.luogu.com.cn/problem/B2083\n- P1421 小玉买文具 https://www.luogu.com.cn/problem/P1421\n\n## 提交要求\n- 笔记里写:循环三要素(初始化/条件/更新)+ 你调试的方法\n",
|
||||
{"cpp-basic","loop","debug","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-05-array",
|
||||
"C++基础05:数组入门(一维)",
|
||||
3,
|
||||
"course:cpp-basic:05",
|
||||
"# C++基础05:数组入门(一维)\n\n## 学习目标\n- 会定义数组、遍历、统计\n\n## 练习\n- P1427 小鱼的数字游戏 https://www.luogu.com.cn/problem/P1427\n- P1428 小鱼比可爱 https://www.luogu.com.cn/problem/P1428\n\n## 提交要求\n- 笔记里写:数组下标从 0/1 的选择;如何避免越界\n",
|
||||
{"cpp-basic","array","", "", "", ""}
|
||||
},
|
||||
|
||||
// 06~20:新增课程
|
||||
{
|
||||
"cpp-basic-06-char-string",
|
||||
"C++基础06:字符与字符串(char / string)",
|
||||
3,
|
||||
"course:cpp-basic:06",
|
||||
"# C++基础06:字符与字符串(char / string)\n\n## 学习目标\n- 区分 `char`(一个字符)与 `string`(一串字符)\n- 会做:大小写转换、统计字母、简单加密\n\n## 重点\n- `char c='A'`,`string s=\"abc\"`\n- `s.size()` / `s[i]` / 遍历字符串\n\n## 练习(完成至少 2 题)\n- P5704 字母转换 https://www.luogu.com.cn/problem/P5704\n- P5733 自动修正 https://www.luogu.com.cn/problem/P5733\n- P1914 小书童——凯撒密码 https://www.luogu.com.cn/problem/P1914\n\n## 提交要求\n- 笔记写:`string` 下标访问与越界风险(最后一个下标是 size()-1)\n",
|
||||
{"cpp-basic","string","char","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-07-operator-cast",
|
||||
"C++基础07:运算、取整与类型转换(/ %)",
|
||||
3,
|
||||
"course:cpp-basic:07",
|
||||
"# C++基础07:运算、取整与类型转换(/ %)\n\n## 学习目标\n- 写对 `+ - * / %`\n- 理解:整数除法会向下取整;`%` 只能用于整数\n- 学会用 `long long` 防止溢出\n\n## 练习\n- B2008 计算 (a+b)×c 的值 https://www.luogu.com.cn/problem/B2008\n- P1421 小玉买文具 https://www.luogu.com.cn/problem/P1421\n- P5708 三角形面积 https://www.luogu.com.cn/problem/P5708\n\n## 提交要求\n- 笔记写:什么时候要用 `long long`(例如钱、人数、乘法很大)\n",
|
||||
{"cpp-basic","math","cast","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-08-logic",
|
||||
"C++基础08:逻辑运算与边界(&& || !)",
|
||||
3,
|
||||
"course:cpp-basic:08",
|
||||
"# C++基础08:逻辑运算与边界(&& || !)\n\n## 学习目标\n- 能把题目里的“并且/或者/不是”写成条件\n- 学会找边界:等于、最小/最大、0\n\n## 练习\n- P5710 数的性质 https://www.luogu.com.cn/problem/P5710\n- P5711 闰年判断 https://www.luogu.com.cn/problem/P5711\n- P1909 买铅笔 https://www.luogu.com.cn/problem/P1909\n\n## 提交要求\n- 笔记写:你列出的边界测试用例(至少 3 个)\n",
|
||||
{"cpp-basic","logic","boundary","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-09-switch",
|
||||
"C++基础09:switch 与枚举列情况",
|
||||
3,
|
||||
"course:cpp-basic:09",
|
||||
"# C++基础09:switch 与枚举列情况\n\n## 学习目标\n- 会写 `switch/case/break`\n- 面对“情况很少”的题,会列举所有情况\n\n## 练习\n- P2433 小学数学N合一 https://www.luogu.com.cn/problem/P2433\n\n## 提交要求\n- 笔记写:为什么每个 case 后要 `break`(避免贯穿执行)\n",
|
||||
{"cpp-basic","switch","enum","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-10-for",
|
||||
"C++基础10:for循环(计数与累加)",
|
||||
3,
|
||||
"course:cpp-basic:10",
|
||||
"# C++基础10:for循环(计数与累加)\n\n## 学习目标\n- 熟练掌握 `for(初始化; 条件; 更新)`\n- 会写:求和、求最大最小、统计个数\n\n## 练习\n- P1425 小鱼的游泳时间 https://www.luogu.com.cn/problem/P1425\n- P5718 找最小值 https://www.luogu.com.cn/problem/P5718\n\n## 提交要求\n- 笔记写:循环“三要素”分别是什么\n",
|
||||
{"cpp-basic","loop","for","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-11-while",
|
||||
"C++基础11:while循环(未知次数)",
|
||||
3,
|
||||
"course:cpp-basic:11",
|
||||
"# C++基础11:while循环(未知次数)\n\n## 学习目标\n- 会用 `while` 处理:读到结束、读到 0 停止\n- 避免死循环:每次循环必须改变状态\n\n## 练习\n- P1427 小鱼的数字游戏 https://www.luogu.com.cn/problem/P1427\n- P1047 校门外的树(选做)https://www.luogu.com.cn/problem/P1047\n\n## 提交要求\n- 笔记写:你是如何避免死循环的(更新变量写在哪里)\n",
|
||||
{"cpp-basic","loop","while","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-12-nested",
|
||||
"C++基础12:嵌套循环与打印图形",
|
||||
3,
|
||||
"course:cpp-basic:12",
|
||||
"# C++基础12:嵌套循环与打印图形\n\n## 学习目标\n- 外层循环=行,内层循环=列\n- 能打印:矩形、三角形、乘法表\n\n## 练习\n- B2083 画矩形 https://www.luogu.com.cn/problem/B2083\n- P5717 三角形分类(练分类讨论 + 输出格式)https://www.luogu.com.cn/problem/P5717\n\n## 提交要求\n- 笔记写:你画图时是怎么想“行/列”的\n",
|
||||
{"cpp-basic","nested-loop","print","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-13-array-ops",
|
||||
"C++基础13:数组进阶(统计/查找/反转)",
|
||||
4,
|
||||
"course:cpp-basic:13",
|
||||
"# C++基础13:数组进阶(统计/查找/反转)\n\n## 学习目标\n- 用数组做:统计、查找位置、反转、去重(了解)\n\n## 练习\n- P1428 小鱼比可爱 https://www.luogu.com.cn/problem/P1428\n- P5727 冰雹猜想 https://www.luogu.com.cn/problem/P5727\n\n## 提交要求\n- 笔记写:数组越界的 2 个典型原因 + 你的检查方法\n",
|
||||
{"cpp-basic","array","practice","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-14-function",
|
||||
"C++基础14:函数入门(把重复代码变成工具)",
|
||||
4,
|
||||
"course:cpp-basic:14",
|
||||
"# C++基础14:函数入门(参数/返回值/作用域)\n\n## 学习目标\n- 会定义并调用函数\n- 知道局部变量作用域({} 内有效)\n\n## 练习\n- P5735 距离函数 https://www.luogu.com.cn/problem/P5735\n- P5739 计算阶乘(也可用递归)https://www.luogu.com.cn/problem/P5739\n\n## 提交要求\n- 笔记写:你把哪段重复代码抽成了函数?为什么这样更清晰?\n",
|
||||
{"cpp-basic","function","scope","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-15-recursion",
|
||||
"C++基础15:递归入门(选学)",
|
||||
4,
|
||||
"course:cpp-basic:15",
|
||||
"# C++基础15:递归入门(选学)\n\n## 学习目标\n- 理解递归:函数调用自己\n- 必须有终止条件(否则会无限调用)\n\n## 练习\n- P5739 计算阶乘 https://www.luogu.com.cn/problem/P5739\n- P1028 数的计算(选做)https://www.luogu.com.cn/problem/P1028\n\n## 提交要求\n- 笔记写:你的递归“终止条件”是什么?\n",
|
||||
{"cpp-basic","recursion","", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-16-struct",
|
||||
"C++基础16:结构体 struct(选学)",
|
||||
4,
|
||||
"course:cpp-basic:16",
|
||||
"# C++基础16:结构体 struct(选学)\n\n## 学习目标\n- 用 `struct` 把多个字段打包成一个“人物卡/记录”\n- 学会 `struct` 数组\n\n## 练习\n- P5740 最厉害的学生 https://www.luogu.com.cn/problem/P5740\n- P5744 培训 https://www.luogu.com.cn/problem/P5744\n\n## 提交要求\n- 笔记写:struct 适合用在什么场景(多个属性属于同一个对象)\n",
|
||||
{"cpp-basic","struct","", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-17-sort",
|
||||
"C++基础17:排序初步(sort)",
|
||||
4,
|
||||
"course:cpp-basic:17",
|
||||
"# C++基础17:排序初步(sort)\n\n## 学习目标\n- 会用 `#include <algorithm>` 和 `sort`\n- 排序后做统计/查找会更简单\n\n## 练习\n- P1059 明明的随机数 https://www.luogu.com.cn/problem/P1059\n- P1093 奖学金(选做)https://www.luogu.com.cn/problem/P1093\n\n## 提交要求\n- 笔记写:sort 的区间是 `[begin, end)`(end 不包含)\n",
|
||||
{"cpp-basic","sort","algorithm","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-18-binary-search",
|
||||
"C++基础18:二分查找初步(lower_bound)",
|
||||
4,
|
||||
"course:cpp-basic:18",
|
||||
"# C++基础18:二分查找初步(lower_bound)\n\n## 学习目标\n- 理解“在有序数组里快速找答案”\n- 会用 `lower_bound` 找第一个 >= x 的位置\n\n## 练习\n- P2249 查找 https://www.luogu.com.cn/problem/P2249\n\n## 提交要求\n- 笔记写:二分查找的前提:数组必须有序\n",
|
||||
{"cpp-basic","binary-search","", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-19-prefix-sum",
|
||||
"C++基础19:前缀和(区间求和神器)",
|
||||
4,
|
||||
"course:cpp-basic:19",
|
||||
"# C++基础19:前缀和(区间求和神器)\n\n## 学习目标\n- 会构造前缀和 `s[i]=s[i-1]+a[i]`\n- 会算区间和 `sum(l,r)=s[r]-s[l-1]`\n\n## 练习\n- P8218 求区间和(前缀和基础)https://www.luogu.com.cn/problem/P8218\n\n## 提交要求\n- 笔记写:下标偏移是怎么处理的(从 1 开始更方便)\n",
|
||||
{"cpp-basic","prefix-sum","", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-20-simulation",
|
||||
"C++基础20:模拟题与综合复盘",
|
||||
4,
|
||||
"course:cpp-basic:20",
|
||||
"# C++基础20:模拟题与综合复盘\n\n## 学习目标\n- 学会按题意一步步做(模拟)\n- 复盘:总结常见错误 + 常用模板\n\n## 练习(从这里选 3~5 题做小测)\n- P1957 口算练习题 https://www.luogu.com.cn/problem/P1957\n- P1055 ISBN号码 https://www.luogu.com.cn/problem/P1055\n- P2433 小学数学N合一(回顾)https://www.luogu.com.cn/problem/P2433\n\n## 提交要求\n- 笔记写:\n 1) 你最常犯的 3 类错误\n 2) 你最常用的 5 行代码模板\n 3) 下周你准备怎么练\n",
|
||||
{"cpp-basic","simulation","review","", "", ""}
|
||||
},
|
||||
};
|
||||
二进制
_csp.db.tmp
普通文件
二进制
_csp.db.tmp
普通文件
二进制文件未显示。
@@ -759,120 +759,165 @@ void SeedDemoData(SqliteDb& db) {
|
||||
"C++基础01:环境配置与Hello World(VSCode)",
|
||||
1,
|
||||
"course:cpp-basic:01",
|
||||
R"MD(# C++基础01:环境配置与Hello World(VSCode)
|
||||
|
||||
## 学习目标
|
||||
- 安装并打开 VSCode,创建并运行第一个 C++14 程序
|
||||
- 学会新建文件、保存、运行、查看输出
|
||||
- 了解 `main()`、`#include <iostream>`、`cout`
|
||||
|
||||
## 推荐视频(观看后写笔记)
|
||||
- 保姆级:VSCode + mingw64 配置 C/C++(BV1tg411N7Fq)
|
||||
- https://www.bilibili.com/video/BV1tg411N7Fq/
|
||||
- 每天五分钟学C++:01 开发工具(BV1dK4y137bk,系列入口)
|
||||
- https://www.bilibili.com/video/BV1dK4y137bk/
|
||||
|
||||
## 参考图文
|
||||
- LoongBa 极简配置:GCC/VSCode/HelloWorld
|
||||
- https://github.com/LoongBa/Cpp_Beginner_Guide
|
||||
|
||||
## 练习(完成至少 2 题)
|
||||
- B2002 Hello, World! https://www.luogu.com.cn/problem/B2002
|
||||
- P1000 超级玛丽游戏 https://www.luogu.com.cn/problem/P1000
|
||||
|
||||
## 提交要求
|
||||
- 在本题页面下方“学习笔记”区域写下:
|
||||
1) 你安装了什么、遇到什么坑、怎么解决
|
||||
2) 你的 HelloWorld 代码
|
||||
3) 你学到的 3 个关键词
|
||||
)MD",
|
||||
{"cpp-basic", "vscode", "io", "", "", ""}
|
||||
"# C++基础01:环境配置与Hello World(VSCode)\n\n## 学习目标\n- 安装并打开 VSCode,创建并运行第一个 C++14 程序\n- 学会新建文件、保存、运行、查看输出\n- 了解 `main()`、`#include <iostream>`、`cout`\n\n## 推荐视频(观看后写笔记)\n- 保姆级:VSCode + mingw64 配置 C/C++(BV1tg411N7Fq)\n - https://www.bilibili.com/video/BV1tg411N7Fq/\n- 每天五分钟学C++:01 开发工具(BV1dK4y137bk,系列入口)\n - https://www.bilibili.com/video/BV1dK4y137bk/\n\n## 参考图文\n- LoongBa 极简配置:GCC/VSCode/HelloWorld\n - https://github.com/LoongBa/Cpp_Beginner_Guide\n\n## 练习(完成至少 2 题)\n- B2002 Hello, World! https://www.luogu.com.cn/problem/B2002\n- P1000 超级玛丽游戏 https://www.luogu.com.cn/problem/P1000\n\n## 提交要求\n- 在本题页面下方“学习笔记”区域写下:\n 1) 你安装了什么、遇到什么坑、怎么解决\n 2) 你的 HelloWorld 代码\n 3) 你学到的 3 个关键词\n",
|
||||
{"cpp-basic","vscode","io","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-02-io",
|
||||
"C++基础02:输入输出与变量",
|
||||
1,
|
||||
"course:cpp-basic:02",
|
||||
R"MD(# C++基础02:输入输出与变量
|
||||
|
||||
## 学习目标
|
||||
- 会用 `cin` 读入、`cout` 输出
|
||||
- 理解变量:`int / long long / double / char / string`
|
||||
|
||||
## 推荐视频
|
||||
- 每天五分钟学C++:02 输出、03 变量(系列入口见上)
|
||||
- https://www.bilibili.com/video/BV1dK4y137bk/
|
||||
|
||||
## 练习
|
||||
- P1001 A+B Problem https://www.luogu.com.cn/problem/P1001
|
||||
- B2008 计算 (a+b)×c 的值 https://www.luogu.com.cn/problem/B2008
|
||||
- P5704 字母转换 https://www.luogu.com.cn/problem/P5704
|
||||
|
||||
## 提交要求
|
||||
- 上传/填写学习笔记:写出 `cin/cout` 模板、常见错误(空格/换行)
|
||||
)MD",
|
||||
{"cpp-basic", "io", "types", "", "", ""}
|
||||
"# C++基础02:输入输出与变量\n\n## 学习目标\n- 会用 `cin` 读入、`cout` 输出\n- 理解变量:`int / long long / double / char / string`\n\n## 推荐视频\n- 每天五分钟学C++:02 输出、03 变量(系列入口见上)\n - https://www.bilibili.com/video/BV1dK4y137bk/\n\n## 练习\n- P1001 A+B Problem https://www.luogu.com.cn/problem/P1001\n- B2008 计算 (a+b)×c 的值 https://www.luogu.com.cn/problem/B2008\n- P5704 字母转换 https://www.luogu.com.cn/problem/P5704\n\n## 提交要求\n- 上传/填写学习笔记:写出 `cin/cout` 模板、常见错误(空格/换行)\n",
|
||||
{"cpp-basic","io","types","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-03-branch",
|
||||
"C++基础03:分支结构(if / switch)",
|
||||
2,
|
||||
"course:cpp-basic:03",
|
||||
R"MD(# C++基础03:分支结构(if / switch)
|
||||
|
||||
## 学习目标
|
||||
- 会写 `if / else if / else` 与基本逻辑运算
|
||||
- 能处理边界与分类讨论
|
||||
|
||||
## 练习
|
||||
- B2035 判断数正负 https://www.luogu.com.cn/problem/B2035
|
||||
- P5711 闰年判断 https://www.luogu.com.cn/problem/P5711
|
||||
- P1909 买铅笔 https://www.luogu.com.cn/problem/P1909
|
||||
|
||||
## 提交要求
|
||||
- 笔记里写清:你如何找“边界”(例如 0、最小/最大、等于条件)
|
||||
)MD",
|
||||
{"cpp-basic", "branch", "logic", "", "", ""}
|
||||
"# C++基础03:分支结构(if / switch)\n\n## 学习目标\n- 会写 `if / else if / else` 与基本逻辑运算\n- 能处理边界与分类讨论\n\n## 练习\n- B2035 判断数正负 https://www.luogu.com.cn/problem/B2035\n- P5711 闰年判断 https://www.luogu.com.cn/problem/P5711\n- P1909 买铅笔 https://www.luogu.com.cn/problem/P1909\n\n## 提交要求\n- 笔记里写清:你如何找“边界”(例如 0、最小/最大、等于条件)\n",
|
||||
{"cpp-basic","branch","logic","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-04-loop",
|
||||
"C++基础04:循环结构(for / while)",
|
||||
2,
|
||||
"course:cpp-basic:04",
|
||||
R"MD(# C++基础04:循环结构(for / while)
|
||||
|
||||
## 学习目标
|
||||
- 会用循环做:计数、累加、打印图形
|
||||
|
||||
## 练习
|
||||
- B2083 画矩形 https://www.luogu.com.cn/problem/B2083
|
||||
- P1421 小玉买文具 https://www.luogu.com.cn/problem/P1421
|
||||
|
||||
## 提交要求
|
||||
- 笔记里写:循环三要素(初始化/条件/更新)+ 你调试的方法
|
||||
)MD",
|
||||
{"cpp-basic", "loop", "debug", "", "", ""}
|
||||
"# C++基础04:循环结构(for / while)\n\n## 学习目标\n- 会用循环做:计数、累加、打印图形\n\n## 练习\n- B2083 画矩形 https://www.luogu.com.cn/problem/B2083\n- P1421 小玉买文具 https://www.luogu.com.cn/problem/P1421\n\n## 提交要求\n- 笔记里写:循环三要素(初始化/条件/更新)+ 你调试的方法\n",
|
||||
{"cpp-basic","loop","debug","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-05-array",
|
||||
"C++基础05:数组入门(一维)",
|
||||
3,
|
||||
"course:cpp-basic:05",
|
||||
R"MD(# C++基础05:数组入门(一维)
|
||||
"# C++基础05:数组入门(一维)\n\n## 学习目标\n- 会定义数组、遍历、统计\n\n## 练习\n- P1427 小鱼的数字游戏 https://www.luogu.com.cn/problem/P1427\n- P1428 小鱼比可爱 https://www.luogu.com.cn/problem/P1428\n\n## 提交要求\n- 笔记里写:数组下标从 0/1 的选择;如何避免越界\n",
|
||||
{"cpp-basic","array","", "", "", ""}
|
||||
},
|
||||
|
||||
## 学习目标
|
||||
- 会定义数组、遍历、统计
|
||||
// 06~20:新增课程
|
||||
{
|
||||
"cpp-basic-06-char-string",
|
||||
"C++基础06:字符与字符串(char / string)",
|
||||
3,
|
||||
"course:cpp-basic:06",
|
||||
"# C++基础06:字符与字符串(char / string)\n\n## 学习目标\n- 区分 `char`(一个字符)与 `string`(一串字符)\n- 会做:大小写转换、统计字母、简单加密\n\n## 重点\n- `char c='A'`,`string s=\"abc\"`\n- `s.size()` / `s[i]` / 遍历字符串\n\n## 练习(完成至少 2 题)\n- P5704 字母转换 https://www.luogu.com.cn/problem/P5704\n- P5733 自动修正 https://www.luogu.com.cn/problem/P5733\n- P1914 小书童——凯撒密码 https://www.luogu.com.cn/problem/P1914\n\n## 提交要求\n- 笔记写:`string` 下标访问与越界风险(最后一个下标是 size()-1)\n",
|
||||
{"cpp-basic","string","char","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-07-operator-cast",
|
||||
"C++基础07:运算、取整与类型转换(/ %)",
|
||||
3,
|
||||
"course:cpp-basic:07",
|
||||
"# C++基础07:运算、取整与类型转换(/ %)\n\n## 学习目标\n- 写对 `+ - * / %`\n- 理解:整数除法会向下取整;`%` 只能用于整数\n- 学会用 `long long` 防止溢出\n\n## 练习\n- B2008 计算 (a+b)×c 的值 https://www.luogu.com.cn/problem/B2008\n- P1421 小玉买文具 https://www.luogu.com.cn/problem/P1421\n- P5708 三角形面积 https://www.luogu.com.cn/problem/P5708\n\n## 提交要求\n- 笔记写:什么时候要用 `long long`(例如钱、人数、乘法很大)\n",
|
||||
{"cpp-basic","math","cast","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-08-logic",
|
||||
"C++基础08:逻辑运算与边界(&& || !)",
|
||||
3,
|
||||
"course:cpp-basic:08",
|
||||
"# C++基础08:逻辑运算与边界(&& || !)\n\n## 学习目标\n- 能把题目里的“并且/或者/不是”写成条件\n- 学会找边界:等于、最小/最大、0\n\n## 练习\n- P5710 数的性质 https://www.luogu.com.cn/problem/P5710\n- P5711 闰年判断 https://www.luogu.com.cn/problem/P5711\n- P1909 买铅笔 https://www.luogu.com.cn/problem/P1909\n\n## 提交要求\n- 笔记写:你列出的边界测试用例(至少 3 个)\n",
|
||||
{"cpp-basic","logic","boundary","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-09-switch",
|
||||
"C++基础09:switch 与枚举列情况",
|
||||
3,
|
||||
"course:cpp-basic:09",
|
||||
"# C++基础09:switch 与枚举列情况\n\n## 学习目标\n- 会写 `switch/case/break`\n- 面对“情况很少”的题,会列举所有情况\n\n## 练习\n- P2433 小学数学N合一 https://www.luogu.com.cn/problem/P2433\n\n## 提交要求\n- 笔记写:为什么每个 case 后要 `break`(避免贯穿执行)\n",
|
||||
{"cpp-basic","switch","enum","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-10-for",
|
||||
"C++基础10:for循环(计数与累加)",
|
||||
3,
|
||||
"course:cpp-basic:10",
|
||||
"# C++基础10:for循环(计数与累加)\n\n## 学习目标\n- 熟练掌握 `for(初始化; 条件; 更新)`\n- 会写:求和、求最大最小、统计个数\n\n## 练习\n- P1425 小鱼的游泳时间 https://www.luogu.com.cn/problem/P1425\n- P5718 找最小值 https://www.luogu.com.cn/problem/P5718\n\n## 提交要求\n- 笔记写:循环“三要素”分别是什么\n",
|
||||
{"cpp-basic","loop","for","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-11-while",
|
||||
"C++基础11:while循环(未知次数)",
|
||||
3,
|
||||
"course:cpp-basic:11",
|
||||
"# C++基础11:while循环(未知次数)\n\n## 学习目标\n- 会用 `while` 处理:读到结束、读到 0 停止\n- 避免死循环:每次循环必须改变状态\n\n## 练习\n- P1427 小鱼的数字游戏 https://www.luogu.com.cn/problem/P1427\n- P1047 校门外的树(选做)https://www.luogu.com.cn/problem/P1047\n\n## 提交要求\n- 笔记写:你是如何避免死循环的(更新变量写在哪里)\n",
|
||||
{"cpp-basic","loop","while","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-12-nested",
|
||||
"C++基础12:嵌套循环与打印图形",
|
||||
3,
|
||||
"course:cpp-basic:12",
|
||||
"# C++基础12:嵌套循环与打印图形\n\n## 学习目标\n- 外层循环=行,内层循环=列\n- 能打印:矩形、三角形、乘法表\n\n## 练习\n- B2083 画矩形 https://www.luogu.com.cn/problem/B2083\n- P5717 三角形分类(练分类讨论 + 输出格式)https://www.luogu.com.cn/problem/P5717\n\n## 提交要求\n- 笔记写:你画图时是怎么想“行/列”的\n",
|
||||
{"cpp-basic","nested-loop","print","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-13-array-ops",
|
||||
"C++基础13:数组进阶(统计/查找/反转)",
|
||||
4,
|
||||
"course:cpp-basic:13",
|
||||
"# C++基础13:数组进阶(统计/查找/反转)\n\n## 学习目标\n- 用数组做:统计、查找位置、反转、去重(了解)\n\n## 练习\n- P1428 小鱼比可爱 https://www.luogu.com.cn/problem/P1428\n- P5727 冰雹猜想 https://www.luogu.com.cn/problem/P5727\n\n## 提交要求\n- 笔记写:数组越界的 2 个典型原因 + 你的检查方法\n",
|
||||
{"cpp-basic","array","practice","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-14-function",
|
||||
"C++基础14:函数入门(把重复代码变成工具)",
|
||||
4,
|
||||
"course:cpp-basic:14",
|
||||
"# C++基础14:函数入门(参数/返回值/作用域)\n\n## 学习目标\n- 会定义并调用函数\n- 知道局部变量作用域({} 内有效)\n\n## 练习\n- P5735 距离函数 https://www.luogu.com.cn/problem/P5735\n- P5739 计算阶乘(也可用递归)https://www.luogu.com.cn/problem/P5739\n\n## 提交要求\n- 笔记写:你把哪段重复代码抽成了函数?为什么这样更清晰?\n",
|
||||
{"cpp-basic","function","scope","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-15-recursion",
|
||||
"C++基础15:递归入门(选学)",
|
||||
4,
|
||||
"course:cpp-basic:15",
|
||||
"# C++基础15:递归入门(选学)\n\n## 学习目标\n- 理解递归:函数调用自己\n- 必须有终止条件(否则会无限调用)\n\n## 练习\n- P5739 计算阶乘 https://www.luogu.com.cn/problem/P5739\n- P1028 数的计算(选做)https://www.luogu.com.cn/problem/P1028\n\n## 提交要求\n- 笔记写:你的递归“终止条件”是什么?\n",
|
||||
{"cpp-basic","recursion","", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-16-struct",
|
||||
"C++基础16:结构体 struct(选学)",
|
||||
4,
|
||||
"course:cpp-basic:16",
|
||||
"# C++基础16:结构体 struct(选学)\n\n## 学习目标\n- 用 `struct` 把多个字段打包成一个“人物卡/记录”\n- 学会 `struct` 数组\n\n## 练习\n- P5740 最厉害的学生 https://www.luogu.com.cn/problem/P5740\n- P5744 培训 https://www.luogu.com.cn/problem/P5744\n\n## 提交要求\n- 笔记写:struct 适合用在什么场景(多个属性属于同一个对象)\n",
|
||||
{"cpp-basic","struct","", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-17-sort",
|
||||
"C++基础17:排序初步(sort)",
|
||||
4,
|
||||
"course:cpp-basic:17",
|
||||
"# C++基础17:排序初步(sort)\n\n## 学习目标\n- 会用 `#include <algorithm>` 和 `sort`\n- 排序后做统计/查找会更简单\n\n## 练习\n- P1059 明明的随机数 https://www.luogu.com.cn/problem/P1059\n- P1093 奖学金(选做)https://www.luogu.com.cn/problem/P1093\n\n## 提交要求\n- 笔记写:sort 的区间是 `[begin, end)`(end 不包含)\n",
|
||||
{"cpp-basic","sort","algorithm","", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-18-binary-search",
|
||||
"C++基础18:二分查找初步(lower_bound)",
|
||||
4,
|
||||
"course:cpp-basic:18",
|
||||
"# C++基础18:二分查找初步(lower_bound)\n\n## 学习目标\n- 理解“在有序数组里快速找答案”\n- 会用 `lower_bound` 找第一个 >= x 的位置\n\n## 练习\n- P2249 查找 https://www.luogu.com.cn/problem/P2249\n\n## 提交要求\n- 笔记写:二分查找的前提:数组必须有序\n",
|
||||
{"cpp-basic","binary-search","", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-19-prefix-sum",
|
||||
"C++基础19:前缀和(区间求和神器)",
|
||||
4,
|
||||
"course:cpp-basic:19",
|
||||
"# C++基础19:前缀和(区间求和神器)\n\n## 学习目标\n- 会构造前缀和 `s[i]=s[i-1]+a[i]`\n- 会算区间和 `sum(l,r)=s[r]-s[l-1]`\n\n## 练习\n- P8218 求区间和(前缀和基础)https://www.luogu.com.cn/problem/P8218\n\n## 提交要求\n- 笔记写:下标偏移是怎么处理的(从 1 开始更方便)\n",
|
||||
{"cpp-basic","prefix-sum","", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-20-simulation",
|
||||
"C++基础20:模拟题与综合复盘",
|
||||
4,
|
||||
"course:cpp-basic:20",
|
||||
"# C++基础20:模拟题与综合复盘\n\n## 学习目标\n- 学会按题意一步步做(模拟)\n- 复盘:总结常见错误 + 常用模板\n\n## 练习(从这里选 3~5 题做小测)\n- P1957 口算练习题 https://www.luogu.com.cn/problem/P1957\n- P1055 ISBN号码 https://www.luogu.com.cn/problem/P1055\n- P2433 小学数学N合一(回顾)https://www.luogu.com.cn/problem/P2433\n\n## 提交要求\n- 笔记写:\n 1) 你最常犯的 3 类错误\n 2) 你最常用的 5 行代码模板\n 3) 下周你准备怎么练\n",
|
||||
{"cpp-basic","simulation","review","", "", ""}
|
||||
},
|
||||
};
|
||||
|
||||
## 练习
|
||||
- P1427 小鱼的数字游戏 https://www.luogu.com.cn/problem/P1427
|
||||
- P1428 小鱼比可爱 https://www.luogu.com.cn/problem/P1428
|
||||
|
||||
## 提交要求
|
||||
- 笔记里写:数组下标从 0/1 的选择;如何避免越界
|
||||
)MD",
|
||||
{"cpp-basic", "array", "", "", "", ""}
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& it : items) {
|
||||
InsertProblem(raw, it.slug, it.title, it.md, it.diff, it.source, "", "", created);
|
||||
|
||||
@@ -0,0 +1,892 @@
|
||||
#include "csp/db/sqlite_db.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace csp::db {
|
||||
|
||||
namespace {
|
||||
|
||||
void ThrowSqlite(int rc, sqlite3* db, const char* what) {
|
||||
if (rc == SQLITE_OK || rc == SQLITE_DONE || rc == SQLITE_ROW) return;
|
||||
const char* msg = db ? sqlite3_errmsg(db) : "";
|
||||
throw std::runtime_error(std::string(what) + ": " + msg);
|
||||
}
|
||||
|
||||
int64_t NowSec() {
|
||||
using namespace std::chrono;
|
||||
return duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
bool ColumnExists(sqlite3* db, const char* table, const char* col) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const std::string sql = std::string("PRAGMA table_info(") + table + ")";
|
||||
const int rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);
|
||||
if (rc != SQLITE_OK) {
|
||||
if (stmt) sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
const unsigned char* name = sqlite3_column_text(stmt, 1);
|
||||
if (name && std::string(reinterpret_cast<const char*>(name)) == col) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return found;
|
||||
}
|
||||
|
||||
void EnsureColumn(SqliteDb& db,
|
||||
const char* table,
|
||||
const char* col_name,
|
||||
const char* col_def) {
|
||||
if (ColumnExists(db.raw(), table, col_name)) return;
|
||||
db.Exec(std::string("ALTER TABLE ") + table + " ADD COLUMN " + col_def + ";");
|
||||
}
|
||||
|
||||
int CountRows(sqlite3* db, const char* table) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const std::string sql = std::string("SELECT COUNT(1) FROM ") + table;
|
||||
int rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);
|
||||
if (rc != SQLITE_OK) {
|
||||
if (stmt) sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
int count = 0;
|
||||
if (rc == SQLITE_ROW) count = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
return count;
|
||||
}
|
||||
|
||||
std::optional<int64_t> QueryOneId(sqlite3* db, const std::string& sql) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const int rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);
|
||||
if (rc != SQLITE_OK) {
|
||||
if (stmt) sqlite3_finalize(stmt);
|
||||
return std::nullopt;
|
||||
}
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW) {
|
||||
sqlite3_finalize(stmt);
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto id = sqlite3_column_int64(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
return id;
|
||||
}
|
||||
|
||||
void InsertProblem(sqlite3* db,
|
||||
const std::string& slug,
|
||||
const std::string& title,
|
||||
const std::string& statement,
|
||||
int difficulty,
|
||||
const std::string& source,
|
||||
const std::string& sample_in,
|
||||
const std::string& sample_out,
|
||||
int64_t created_at) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const char* sql =
|
||||
"INSERT INTO problems(slug,title,statement_md,difficulty,source,sample_input,sample_output,created_at) "
|
||||
"VALUES(?,?,?,?,?,?,?,?)";
|
||||
ThrowSqlite(sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr), db,
|
||||
"prepare insert problem");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 1, slug.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind problem.slug");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 2, title.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind problem.title");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 3, statement.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind problem.statement");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 4, difficulty), db,
|
||||
"bind problem.difficulty");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 5, source.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind problem.source");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 6, sample_in.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind problem.sample_input");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 7, sample_out.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind problem.sample_output");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 8, created_at), db,
|
||||
"bind problem.created_at");
|
||||
ThrowSqlite(sqlite3_step(stmt), db, "insert problem");
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
void InsertProblemTag(sqlite3* db, int64_t problem_id, const std::string& tag) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const char* sql =
|
||||
"INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES(?,?)";
|
||||
ThrowSqlite(sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr), db,
|
||||
"prepare insert problem_tag");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 1, problem_id), db,
|
||||
"bind problem_tag.problem_id");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 2, tag.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind problem_tag.tag");
|
||||
ThrowSqlite(sqlite3_step(stmt), db, "insert problem_tag");
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
void InsertKbArticle(sqlite3* db,
|
||||
const std::string& slug,
|
||||
const std::string& title,
|
||||
const std::string& content_md,
|
||||
int64_t created_at) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const char* sql =
|
||||
"INSERT INTO kb_articles(slug,title,content_md,created_at) VALUES(?,?,?,?)";
|
||||
ThrowSqlite(sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr), db,
|
||||
"prepare insert kb_article");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 1, slug.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind kb_article.slug");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 2, title.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind kb_article.title");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 3, content_md.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind kb_article.content");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 4, created_at), db,
|
||||
"bind kb_article.created_at");
|
||||
ThrowSqlite(sqlite3_step(stmt), db, "insert kb_article");
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
void InsertKbLink(sqlite3* db, int64_t article_id, int64_t problem_id) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const char* sql =
|
||||
"INSERT OR IGNORE INTO kb_article_links(article_id,problem_id) VALUES(?,?)";
|
||||
ThrowSqlite(sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr), db,
|
||||
"prepare insert kb_article_link");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 1, article_id), db,
|
||||
"bind kb_article_link.article_id");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 2, problem_id), db,
|
||||
"bind kb_article_link.problem_id");
|
||||
ThrowSqlite(sqlite3_step(stmt), db, "insert kb_article_link");
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
void InsertContest(sqlite3* db,
|
||||
const std::string& title,
|
||||
int64_t starts_at,
|
||||
int64_t ends_at,
|
||||
const std::string& rule_json) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const char* sql =
|
||||
"INSERT INTO contests(title,starts_at,ends_at,rule_json) VALUES(?,?,?,?)";
|
||||
ThrowSqlite(sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr), db,
|
||||
"prepare insert contest");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 1, title.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind contest.title");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 2, starts_at), db,
|
||||
"bind contest.starts_at");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 3, ends_at), db,
|
||||
"bind contest.ends_at");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 4, rule_json.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind contest.rule_json");
|
||||
ThrowSqlite(sqlite3_step(stmt), db, "insert contest");
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
void InsertContestProblem(sqlite3* db,
|
||||
int64_t contest_id,
|
||||
int64_t problem_id,
|
||||
int idx) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const char* sql =
|
||||
"INSERT OR IGNORE INTO contest_problems(contest_id,problem_id,idx) VALUES(?,?,?)";
|
||||
ThrowSqlite(sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr), db,
|
||||
"prepare insert contest_problem");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 1, contest_id), db,
|
||||
"bind contest_problem.contest_id");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 2, problem_id), db,
|
||||
"bind contest_problem.problem_id");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 3, idx), db,
|
||||
"bind contest_problem.idx");
|
||||
ThrowSqlite(sqlite3_step(stmt), db, "insert contest_problem");
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
void InsertRedeemItem(sqlite3* db,
|
||||
const std::string& name,
|
||||
const std::string& description,
|
||||
const std::string& unit_label,
|
||||
int holiday_cost,
|
||||
int studyday_cost,
|
||||
int is_active,
|
||||
int is_global,
|
||||
int64_t created_by,
|
||||
int64_t created_at) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const char* sql =
|
||||
"INSERT INTO redeem_items("
|
||||
"name,description,unit_label,holiday_cost,studyday_cost,is_active,is_global,created_by,created_at,updated_at"
|
||||
") VALUES(?,?,?,?,?,?,?,?,?,?)";
|
||||
ThrowSqlite(sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr), db,
|
||||
"prepare insert redeem_item");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind redeem_item.name");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 2, description.c_str(), -1, SQLITE_TRANSIENT),
|
||||
db, "bind redeem_item.description");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 3, unit_label.c_str(), -1, SQLITE_TRANSIENT),
|
||||
db, "bind redeem_item.unit_label");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 4, holiday_cost), db,
|
||||
"bind redeem_item.holiday_cost");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 5, studyday_cost), db,
|
||||
"bind redeem_item.studyday_cost");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 6, is_active), db,
|
||||
"bind redeem_item.is_active");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 7, is_global), db,
|
||||
"bind redeem_item.is_global");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 8, created_by), db,
|
||||
"bind redeem_item.created_by");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 9, created_at), db,
|
||||
"bind redeem_item.created_at");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 10, created_at), db,
|
||||
"bind redeem_item.updated_at");
|
||||
ThrowSqlite(sqlite3_step(stmt), db, "insert redeem_item");
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SqliteDb SqliteDb::OpenFile(const std::string& path) {
|
||||
sqlite3* db = nullptr;
|
||||
const int rc = sqlite3_open(path.c_str(), &db);
|
||||
if (rc != SQLITE_OK) {
|
||||
const char* msg = db ? sqlite3_errmsg(db) : "";
|
||||
if (db) sqlite3_close(db);
|
||||
throw std::runtime_error(std::string("sqlite3_open failed: ") + msg);
|
||||
}
|
||||
return SqliteDb(db);
|
||||
}
|
||||
|
||||
SqliteDb SqliteDb::OpenMemory() {
|
||||
sqlite3* db = nullptr;
|
||||
const int rc = sqlite3_open(":memory:", &db);
|
||||
ThrowSqlite(rc, db, "sqlite3_open(:memory:) failed");
|
||||
return SqliteDb(db);
|
||||
}
|
||||
|
||||
SqliteDb::~SqliteDb() {
|
||||
if (db_) sqlite3_close(db_);
|
||||
}
|
||||
|
||||
SqliteDb::SqliteDb(SqliteDb&& other) noexcept : db_(other.db_) {
|
||||
other.db_ = nullptr;
|
||||
}
|
||||
|
||||
SqliteDb& SqliteDb::operator=(SqliteDb&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
if (db_) sqlite3_close(db_);
|
||||
db_ = other.db_;
|
||||
other.db_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void SqliteDb::Exec(const std::string& sql) {
|
||||
char* err = nullptr;
|
||||
const int rc = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &err);
|
||||
if (rc != SQLITE_OK) {
|
||||
std::string msg = err ? err : "";
|
||||
sqlite3_free(err);
|
||||
ThrowSqlite(rc, db_, msg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyMigrations(SqliteDb& db) {
|
||||
// Keep it simple for MVP: create missing tables, then patch missing columns.
|
||||
db.Exec("PRAGMA foreign_keys = ON;");
|
||||
|
||||
db.Exec(R"SQL(
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password_salt TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
rating INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
token TEXT PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
expires_at INTEGER NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS problems (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
statement_md TEXT NOT NULL,
|
||||
difficulty INTEGER NOT NULL DEFAULT 1,
|
||||
source TEXT NOT NULL DEFAULT "",
|
||||
statement_url TEXT NOT NULL DEFAULT "",
|
||||
llm_profile_json TEXT NOT NULL DEFAULT "{}",
|
||||
sample_input TEXT NOT NULL DEFAULT "",
|
||||
sample_output TEXT NOT NULL DEFAULT "",
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS problem_tags (
|
||||
problem_id INTEGER NOT NULL,
|
||||
tag TEXT NOT NULL,
|
||||
PRIMARY KEY(problem_id, tag),
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS submissions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
problem_id INTEGER NOT NULL,
|
||||
contest_id INTEGER,
|
||||
language TEXT NOT NULL,
|
||||
code TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
score INTEGER NOT NULL DEFAULT 0,
|
||||
time_ms INTEGER NOT NULL DEFAULT 0,
|
||||
memory_kb INTEGER NOT NULL DEFAULT 0,
|
||||
compile_log TEXT NOT NULL DEFAULT "",
|
||||
runtime_log TEXT NOT NULL DEFAULT "",
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(contest_id) REFERENCES contests(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wrong_book (
|
||||
user_id INTEGER NOT NULL,
|
||||
problem_id INTEGER NOT NULL,
|
||||
last_submission_id INTEGER,
|
||||
note TEXT NOT NULL DEFAULT "",
|
||||
note_score INTEGER NOT NULL DEFAULT 0,
|
||||
note_rating INTEGER NOT NULL DEFAULT 0,
|
||||
note_feedback_md TEXT NOT NULL DEFAULT "",
|
||||
note_scored_at INTEGER NOT NULL DEFAULT 0,
|
||||
updated_at INTEGER NOT NULL,
|
||||
PRIMARY KEY(user_id, problem_id),
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(last_submission_id) REFERENCES submissions(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS contests (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
starts_at INTEGER NOT NULL,
|
||||
ends_at INTEGER NOT NULL,
|
||||
rule_json TEXT NOT NULL DEFAULT "{}"
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS contest_problems (
|
||||
contest_id INTEGER NOT NULL,
|
||||
problem_id INTEGER NOT NULL,
|
||||
idx INTEGER NOT NULL,
|
||||
PRIMARY KEY(contest_id, problem_id),
|
||||
FOREIGN KEY(contest_id) REFERENCES contests(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS contest_registrations (
|
||||
contest_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
registered_at INTEGER NOT NULL,
|
||||
PRIMARY KEY(contest_id, user_id),
|
||||
FOREIGN KEY(contest_id) REFERENCES contests(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS kb_articles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
content_md TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS kb_article_links (
|
||||
article_id INTEGER NOT NULL,
|
||||
problem_id INTEGER NOT NULL,
|
||||
PRIMARY KEY(article_id, problem_id),
|
||||
FOREIGN KEY(article_id) REFERENCES kb_articles(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS import_jobs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
status TEXT NOT NULL,
|
||||
trigger TEXT NOT NULL DEFAULT "manual",
|
||||
total_count INTEGER NOT NULL DEFAULT 0,
|
||||
processed_count INTEGER NOT NULL DEFAULT 0,
|
||||
success_count INTEGER NOT NULL DEFAULT 0,
|
||||
failed_count INTEGER NOT NULL DEFAULT 0,
|
||||
options_json TEXT NOT NULL DEFAULT "{}",
|
||||
last_error TEXT NOT NULL DEFAULT "",
|
||||
started_at INTEGER NOT NULL,
|
||||
finished_at INTEGER,
|
||||
updated_at INTEGER NOT NULL,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS import_job_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
job_id INTEGER NOT NULL,
|
||||
source_path TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT "queued",
|
||||
title TEXT NOT NULL DEFAULT "",
|
||||
difficulty INTEGER NOT NULL DEFAULT 0,
|
||||
problem_id INTEGER,
|
||||
error_text TEXT NOT NULL DEFAULT "",
|
||||
started_at INTEGER,
|
||||
finished_at INTEGER,
|
||||
updated_at INTEGER NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(job_id) REFERENCES import_jobs(id) ON DELETE CASCADE,
|
||||
UNIQUE(job_id, source_path)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS problem_drafts (
|
||||
user_id INTEGER NOT NULL,
|
||||
problem_id INTEGER NOT NULL,
|
||||
language TEXT NOT NULL DEFAULT "cpp",
|
||||
code TEXT NOT NULL DEFAULT "",
|
||||
stdin TEXT NOT NULL DEFAULT "",
|
||||
updated_at INTEGER NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
PRIMARY KEY(user_id, problem_id),
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS problem_solution_jobs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
problem_id INTEGER NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT "queued",
|
||||
progress INTEGER NOT NULL DEFAULT 0,
|
||||
message TEXT NOT NULL DEFAULT "",
|
||||
created_by INTEGER NOT NULL DEFAULT 0,
|
||||
max_solutions INTEGER NOT NULL DEFAULT 3,
|
||||
created_at INTEGER NOT NULL,
|
||||
started_at INTEGER,
|
||||
finished_at INTEGER,
|
||||
updated_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS problem_solutions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
problem_id INTEGER NOT NULL,
|
||||
variant INTEGER NOT NULL DEFAULT 1,
|
||||
title TEXT NOT NULL DEFAULT "",
|
||||
idea_md TEXT NOT NULL DEFAULT "",
|
||||
explanation_md TEXT NOT NULL DEFAULT "",
|
||||
code_cpp TEXT NOT NULL DEFAULT "",
|
||||
complexity TEXT NOT NULL DEFAULT "",
|
||||
tags_json TEXT NOT NULL DEFAULT "[]",
|
||||
source TEXT NOT NULL DEFAULT "llm",
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS problem_solution_view_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
problem_id INTEGER NOT NULL,
|
||||
day_key TEXT NOT NULL,
|
||||
viewed_at INTEGER NOT NULL,
|
||||
charged INTEGER NOT NULL DEFAULT 0,
|
||||
cost INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS submission_feedback (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
submission_id INTEGER NOT NULL UNIQUE,
|
||||
feedback_md TEXT NOT NULL DEFAULT "",
|
||||
links_json TEXT NOT NULL DEFAULT "[]",
|
||||
model_name TEXT NOT NULL DEFAULT "",
|
||||
status TEXT NOT NULL DEFAULT "ready",
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(submission_id) REFERENCES submissions(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS redeem_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT "",
|
||||
unit_label TEXT NOT NULL DEFAULT "小时",
|
||||
holiday_cost INTEGER NOT NULL DEFAULT 5,
|
||||
studyday_cost INTEGER NOT NULL DEFAULT 25,
|
||||
duration_minutes INTEGER NOT NULL DEFAULT 0,
|
||||
is_active INTEGER NOT NULL DEFAULT 1,
|
||||
is_global INTEGER NOT NULL DEFAULT 1,
|
||||
created_by INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS redeem_records (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
item_id INTEGER NOT NULL,
|
||||
item_name TEXT NOT NULL,
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
day_type TEXT NOT NULL DEFAULT "studyday",
|
||||
unit_cost INTEGER NOT NULL DEFAULT 0,
|
||||
total_cost INTEGER NOT NULL DEFAULT 0,
|
||||
note TEXT NOT NULL DEFAULT "",
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(item_id) REFERENCES redeem_items(id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS daily_task_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
task_code TEXT NOT NULL,
|
||||
day_key TEXT NOT NULL,
|
||||
reward INTEGER NOT NULL DEFAULT 1,
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE(user_id, task_code, day_key)
|
||||
);
|
||||
)SQL");
|
||||
|
||||
// Backward-compatible schema upgrades for existing deployments.
|
||||
EnsureColumn(db, "problems", "sample_input",
|
||||
"sample_input TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "problems", "sample_output",
|
||||
"sample_output TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "problems", "statement_url",
|
||||
"statement_url TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "problems", "llm_profile_json",
|
||||
"llm_profile_json TEXT NOT NULL DEFAULT '{}'");
|
||||
EnsureColumn(db, "import_jobs", "trigger",
|
||||
"trigger TEXT NOT NULL DEFAULT 'manual'");
|
||||
EnsureColumn(db, "import_jobs", "options_json",
|
||||
"options_json TEXT NOT NULL DEFAULT '{}'");
|
||||
EnsureColumn(db, "import_jobs", "last_error",
|
||||
"last_error TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "submissions", "contest_id", "contest_id INTEGER");
|
||||
EnsureColumn(db, "submissions", "compile_log",
|
||||
"compile_log TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "submissions", "runtime_log",
|
||||
"runtime_log TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "problem_drafts", "stdin", "stdin TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "problem_solution_jobs", "max_solutions",
|
||||
"max_solutions INTEGER NOT NULL DEFAULT 3");
|
||||
EnsureColumn(db, "wrong_book", "note_score", "note_score INTEGER NOT NULL DEFAULT 0");
|
||||
EnsureColumn(db, "wrong_book", "note_rating", "note_rating INTEGER NOT NULL DEFAULT 0");
|
||||
EnsureColumn(db, "wrong_book", "note_feedback_md", "note_feedback_md TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "wrong_book", "note_scored_at", "note_scored_at INTEGER NOT NULL DEFAULT 0");
|
||||
EnsureColumn(db, "wrong_book", "note_images_json", "note_images_json TEXT NOT NULL DEFAULT '[]'");
|
||||
EnsureColumn(db, "problem_solutions", "variant", "variant INTEGER NOT NULL DEFAULT 1");
|
||||
EnsureColumn(db, "problem_solutions", "idea_md", "idea_md TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "problem_solutions", "explanation_md",
|
||||
"explanation_md TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "problem_solutions", "code_cpp", "code_cpp TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "problem_solutions", "complexity",
|
||||
"complexity TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(db, "problem_solutions", "tags_json", "tags_json TEXT NOT NULL DEFAULT '[]'");
|
||||
EnsureColumn(db, "redeem_items", "duration_minutes",
|
||||
"duration_minutes INTEGER NOT NULL DEFAULT 0");
|
||||
|
||||
// Build indexes after compatibility ALTERs so old schemas won't fail on
|
||||
// missing columns (e.g. legacy submissions table without contest_id).
|
||||
db.Exec(R"SQL(
|
||||
CREATE INDEX IF NOT EXISTS idx_submissions_user_created_at ON submissions(user_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_submissions_problem_created_at ON submissions(problem_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_submissions_contest_user_created_at ON submissions(contest_id, user_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_problem_tags_tag ON problem_tags(tag);
|
||||
CREATE INDEX IF NOT EXISTS idx_kb_article_links_problem_id ON kb_article_links(problem_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_import_jobs_created_at ON import_jobs(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_import_jobs_status ON import_jobs(status, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_import_job_items_job_status ON import_job_items(job_id, status, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_problem_drafts_updated ON problem_drafts(updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_problem_solution_jobs_problem ON problem_solution_jobs(problem_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_problem_solutions_problem ON problem_solutions(problem_id, variant, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_solution_view_logs_user_problem ON problem_solution_view_logs(user_id, problem_id, viewed_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_solution_view_logs_user_day ON problem_solution_view_logs(user_id, day_key, viewed_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_redeem_items_active ON redeem_items(is_active, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_redeem_records_user_created ON redeem_records(user_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_daily_task_logs_user_day ON daily_task_logs(user_id, day_key, created_at DESC);
|
||||
)SQL");
|
||||
}
|
||||
|
||||
void SeedDemoData(SqliteDb& db) {
|
||||
sqlite3* raw = db.raw();
|
||||
const int64_t now = NowSec();
|
||||
|
||||
if (CountRows(raw, "problems") == 0) {
|
||||
InsertProblem(
|
||||
raw,
|
||||
"a-plus-b",
|
||||
"A + B",
|
||||
"给定两个整数 A 与 B,输出它们的和。",
|
||||
1,
|
||||
"CSP-入门",
|
||||
"1 2\n",
|
||||
"3\n",
|
||||
now);
|
||||
|
||||
InsertProblem(
|
||||
raw,
|
||||
"fibonacci-n",
|
||||
"Fibonacci 第 n 项",
|
||||
"输入 n (0<=n<=40),输出第 n 项 Fibonacci 数。",
|
||||
2,
|
||||
"CSP-基础",
|
||||
"10\n",
|
||||
"55\n",
|
||||
now);
|
||||
|
||||
InsertProblem(
|
||||
raw,
|
||||
"sort-numbers",
|
||||
"整数排序",
|
||||
"输入 n 和 n 个整数,按升序输出。",
|
||||
2,
|
||||
"CSP-基础",
|
||||
"5\n5 1 4 2 3\n",
|
||||
"1 2 3 4 5\n",
|
||||
now);
|
||||
}
|
||||
|
||||
if (CountRows(raw, "problem_tags") == 0) {
|
||||
const auto p1 =
|
||||
QueryOneId(raw, "SELECT id FROM problems WHERE slug='a-plus-b'");
|
||||
const auto p2 =
|
||||
QueryOneId(raw, "SELECT id FROM problems WHERE slug='fibonacci-n'");
|
||||
const auto p3 =
|
||||
QueryOneId(raw, "SELECT id FROM problems WHERE slug='sort-numbers'");
|
||||
if (p1) {
|
||||
InsertProblemTag(raw, *p1, "math");
|
||||
InsertProblemTag(raw, *p1, "implementation");
|
||||
}
|
||||
if (p2) {
|
||||
InsertProblemTag(raw, *p2, "dp");
|
||||
InsertProblemTag(raw, *p2, "recursion");
|
||||
}
|
||||
if (p3) {
|
||||
InsertProblemTag(raw, *p3, "sort");
|
||||
InsertProblemTag(raw, *p3, "array");
|
||||
}
|
||||
}
|
||||
|
||||
if (CountRows(raw, "kb_articles") == 0) {
|
||||
InsertKbArticle(
|
||||
raw,
|
||||
"cpp-fast-io",
|
||||
"C++ 快速输入输出",
|
||||
"# C++ 快速输入输出\n\n在 OI/CSP 中,建议关闭同步并解绑 cin/cout:\n\n```cpp\nstd::ios::sync_with_stdio(false);\nstd::cin.tie(nullptr);\n```\n",
|
||||
now);
|
||||
|
||||
InsertKbArticle(
|
||||
raw,
|
||||
"intro-dp",
|
||||
"动态规划入门",
|
||||
"# 动态规划入门\n\n动态规划的核心是:**状态定义**、**状态转移**、**边界条件**。\n",
|
||||
now);
|
||||
}
|
||||
|
||||
if (CountRows(raw, "kb_article_links") == 0) {
|
||||
const auto p1 =
|
||||
QueryOneId(raw, "SELECT id FROM problems WHERE slug='a-plus-b'");
|
||||
const auto p2 =
|
||||
QueryOneId(raw, "SELECT id FROM problems WHERE slug='fibonacci-n'");
|
||||
const auto a1 =
|
||||
QueryOneId(raw, "SELECT id FROM kb_articles WHERE slug='cpp-fast-io'");
|
||||
const auto a2 =
|
||||
QueryOneId(raw, "SELECT id FROM kb_articles WHERE slug='intro-dp'");
|
||||
if (a1 && p1) InsertKbLink(raw, *a1, *p1);
|
||||
if (a2 && p2) InsertKbLink(raw, *a2, *p2);
|
||||
}
|
||||
|
||||
if (CountRows(raw, "contests") == 0) {
|
||||
InsertContest(
|
||||
raw,
|
||||
"CSP 模拟赛(示例)",
|
||||
now - 3600,
|
||||
now + 7 * 24 * 3600,
|
||||
R"({"type":"acm","desc":"按通过题数与罚时排名"})");
|
||||
}
|
||||
|
||||
if (CountRows(raw, "contest_problems") == 0) {
|
||||
const auto contest_id = QueryOneId(raw, "SELECT id FROM contests ORDER BY id LIMIT 1");
|
||||
const auto p1 = QueryOneId(raw, "SELECT id FROM problems ORDER BY id LIMIT 1");
|
||||
const auto p2 = QueryOneId(raw, "SELECT id FROM problems ORDER BY id LIMIT 1 OFFSET 1");
|
||||
if (contest_id && p1) InsertContestProblem(raw, *contest_id, *p1, 1);
|
||||
if (contest_id && p2) InsertContestProblem(raw, *contest_id, *p2, 2);
|
||||
}
|
||||
|
||||
if (CountRows(raw, "redeem_items") == 0) {
|
||||
InsertRedeemItem(
|
||||
raw,
|
||||
"私人玩游戏时间",
|
||||
"全局用户可兑换:假期 1 小时 5 Rating;学习日/非节假日 1 小时 25 Rating。",
|
||||
"小时",
|
||||
5,
|
||||
25,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
now);
|
||||
}
|
||||
|
||||
// Always seed C++基础课程任务(幂等:按 slug 检测存在)。
|
||||
{
|
||||
const auto existing = QueryOneId(raw, "SELECT id FROM problems WHERE slug='cpp-basic-01-hello' LIMIT 1");
|
||||
if (!existing.has_value()) {
|
||||
const int64_t created = now;
|
||||
struct CourseItem {
|
||||
const char* slug;
|
||||
const char* title;
|
||||
int diff;
|
||||
const char* source;
|
||||
const char* md;
|
||||
const char* tags[6];
|
||||
};
|
||||
const CourseItem items[] = {
|
||||
{
|
||||
"cpp-basic-01-hello",
|
||||
"C++基础01:环境配置与Hello World(VSCode)",
|
||||
1,
|
||||
"course:cpp-basic:01",
|
||||
R"MD(# C++基础01:环境配置与Hello World(VSCode)
|
||||
|
||||
## 学习目标
|
||||
- 安装并打开 VSCode,创建并运行第一个 C++14 程序
|
||||
- 学会新建文件、保存、运行、查看输出
|
||||
- 了解 `main()`、`#include <iostream>`、`cout`
|
||||
|
||||
## 推荐视频(观看后写笔记)
|
||||
- 保姆级:VSCode + mingw64 配置 C/C++(BV1tg411N7Fq)
|
||||
- https://www.bilibili.com/video/BV1tg411N7Fq/
|
||||
- 每天五分钟学C++:01 开发工具(BV1dK4y137bk,系列入口)
|
||||
- https://www.bilibili.com/video/BV1dK4y137bk/
|
||||
|
||||
## 参考图文
|
||||
- LoongBa 极简配置:GCC/VSCode/HelloWorld
|
||||
- https://github.com/LoongBa/Cpp_Beginner_Guide
|
||||
|
||||
## 练习(完成至少 2 题)
|
||||
- B2002 Hello, World! https://www.luogu.com.cn/problem/B2002
|
||||
- P1000 超级玛丽游戏 https://www.luogu.com.cn/problem/P1000
|
||||
|
||||
## 提交要求
|
||||
- 在本题页面下方“学习笔记”区域写下:
|
||||
1) 你安装了什么、遇到什么坑、怎么解决
|
||||
2) 你的 HelloWorld 代码
|
||||
3) 你学到的 3 个关键词
|
||||
)MD",
|
||||
{"cpp-basic", "vscode", "io", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-02-io",
|
||||
"C++基础02:输入输出与变量",
|
||||
1,
|
||||
"course:cpp-basic:02",
|
||||
R"MD(# C++基础02:输入输出与变量
|
||||
|
||||
## 学习目标
|
||||
- 会用 `cin` 读入、`cout` 输出
|
||||
- 理解变量:`int / long long / double / char / string`
|
||||
|
||||
## 推荐视频
|
||||
- 每天五分钟学C++:02 输出、03 变量(系列入口见上)
|
||||
- https://www.bilibili.com/video/BV1dK4y137bk/
|
||||
|
||||
## 练习
|
||||
- P1001 A+B Problem https://www.luogu.com.cn/problem/P1001
|
||||
- B2008 计算 (a+b)×c 的值 https://www.luogu.com.cn/problem/B2008
|
||||
- P5704 字母转换 https://www.luogu.com.cn/problem/P5704
|
||||
|
||||
## 提交要求
|
||||
- 上传/填写学习笔记:写出 `cin/cout` 模板、常见错误(空格/换行)
|
||||
)MD",
|
||||
{"cpp-basic", "io", "types", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-03-branch",
|
||||
"C++基础03:分支结构(if / switch)",
|
||||
2,
|
||||
"course:cpp-basic:03",
|
||||
R"MD(# C++基础03:分支结构(if / switch)
|
||||
|
||||
## 学习目标
|
||||
- 会写 `if / else if / else` 与基本逻辑运算
|
||||
- 能处理边界与分类讨论
|
||||
|
||||
## 练习
|
||||
- B2035 判断数正负 https://www.luogu.com.cn/problem/B2035
|
||||
- P5711 闰年判断 https://www.luogu.com.cn/problem/P5711
|
||||
- P1909 买铅笔 https://www.luogu.com.cn/problem/P1909
|
||||
|
||||
## 提交要求
|
||||
- 笔记里写清:你如何找“边界”(例如 0、最小/最大、等于条件)
|
||||
)MD",
|
||||
{"cpp-basic", "branch", "logic", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-04-loop",
|
||||
"C++基础04:循环结构(for / while)",
|
||||
2,
|
||||
"course:cpp-basic:04",
|
||||
R"MD(# C++基础04:循环结构(for / while)
|
||||
|
||||
## 学习目标
|
||||
- 会用循环做:计数、累加、打印图形
|
||||
|
||||
## 练习
|
||||
- B2083 画矩形 https://www.luogu.com.cn/problem/B2083
|
||||
- P1421 小玉买文具 https://www.luogu.com.cn/problem/P1421
|
||||
|
||||
## 提交要求
|
||||
- 笔记里写:循环三要素(初始化/条件/更新)+ 你调试的方法
|
||||
)MD",
|
||||
{"cpp-basic", "loop", "debug", "", "", ""}
|
||||
},
|
||||
{
|
||||
"cpp-basic-05-array",
|
||||
"C++基础05:数组入门(一维)",
|
||||
3,
|
||||
"course:cpp-basic:05",
|
||||
R"MD(# C++基础05:数组入门(一维)
|
||||
|
||||
## 学习目标
|
||||
- 会定义数组、遍历、统计
|
||||
|
||||
## 练习
|
||||
- P1427 小鱼的数字游戏 https://www.luogu.com.cn/problem/P1427
|
||||
- P1428 小鱼比可爱 https://www.luogu.com.cn/problem/P1428
|
||||
|
||||
## 提交要求
|
||||
- 笔记里写:数组下标从 0/1 的选择;如何避免越界
|
||||
)MD",
|
||||
{"cpp-basic", "array", "", "", "", ""}
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& it : items) {
|
||||
InsertProblem(raw, it.slug, it.title, it.md, it.diff, it.source, "", "", created);
|
||||
const auto pid = QueryOneId(raw, std::string("SELECT id FROM problems WHERE slug='") + it.slug + "' LIMIT 1");
|
||||
if (pid.has_value()) {
|
||||
for (const char* tag : it.tags) {
|
||||
if (!tag || !*tag) continue;
|
||||
InsertProblemTag(raw, *pid, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace csp::db
|
||||
363
cpp_basic_20_upsert.sql
普通文件
363
cpp_basic_20_upsert.sql
普通文件
@@ -0,0 +1,363 @@
|
||||
BEGIN;
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-01-hello','C++基础01:环境配置与Hello World(VSCode)','# C++基础01:环境配置与Hello World(VSCode)
|
||||
|
||||
## 学习目标
|
||||
- 安装并打开 VSCode,创建并运行第一个 C++14 程序
|
||||
- 学会新建文件、保存、运行、查看输出
|
||||
- 了解 `main()`、`#include <iostream>`、`cout`
|
||||
|
||||
## 推荐视频(观看后写笔记)
|
||||
- 保姆级:VSCode + mingw64 配置 C/C++(BV1tg411N7Fq)
|
||||
- https://www.bilibili.com/video/BV1tg411N7Fq/
|
||||
- 每天五分钟学C++:01 开发工具(BV1dK4y137bk,系列入口)
|
||||
- https://www.bilibili.com/video/BV1dK4y137bk/
|
||||
|
||||
## 参考图文
|
||||
- LoongBa 极简配置:GCC/VSCode/HelloWorld
|
||||
- https://github.com/LoongBa/Cpp_Beginner_Guide
|
||||
|
||||
## 练习(完成至少 2 题)
|
||||
- B2002 Hello, World! https://www.luogu.com.cn/problem/B2002
|
||||
- P1000 超级玛丽游戏 https://www.luogu.com.cn/problem/P1000
|
||||
|
||||
## 提交要求
|
||||
- 在本题页面下方“学习笔记”区域写下:
|
||||
1) 你安装了什么、遇到什么坑、怎么解决
|
||||
2) 你的 HelloWorld 代码
|
||||
3) 你学到的 3 个关键词
|
||||
',1,'course:cpp-basic:01',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-01-hello');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-01-hello'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-01-hello'),'vscode');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-01-hello'),'io');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-02-io','C++基础02:输入输出与变量','# C++基础02:输入输出与变量
|
||||
|
||||
## 学习目标
|
||||
- 会用 `cin` 读入、`cout` 输出
|
||||
- 理解变量:`int / long long / double / char / string`
|
||||
|
||||
## 推荐视频
|
||||
- 每天五分钟学C++:02 输出、03 变量(系列入口见上)
|
||||
- https://www.bilibili.com/video/BV1dK4y137bk/
|
||||
|
||||
## 练习
|
||||
- P1001 A+B Problem https://www.luogu.com.cn/problem/P1001
|
||||
- B2008 计算 (a+b)×c 的值 https://www.luogu.com.cn/problem/B2008
|
||||
- P5704 字母转换 https://www.luogu.com.cn/problem/P5704
|
||||
|
||||
## 提交要求
|
||||
- 上传/填写学习笔记:写出 `cin/cout` 模板、常见错误(空格/换行)
|
||||
',1,'course:cpp-basic:02',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-02-io');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-02-io'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-02-io'),'io');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-02-io'),'types');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-03-branch','C++基础03:分支结构(if / switch)','# C++基础03:分支结构(if / switch)
|
||||
|
||||
## 学习目标
|
||||
- 会写 `if / else if / else` 与基本逻辑运算
|
||||
- 能处理边界与分类讨论
|
||||
|
||||
## 练习
|
||||
- B2035 判断数正负 https://www.luogu.com.cn/problem/B2035
|
||||
- P5711 闰年判断 https://www.luogu.com.cn/problem/P5711
|
||||
- P1909 买铅笔 https://www.luogu.com.cn/problem/P1909
|
||||
|
||||
## 提交要求
|
||||
- 笔记里写清:你如何找“边界”(例如 0、最小/最大、等于条件)
|
||||
',2,'course:cpp-basic:03',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-03-branch');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-03-branch'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-03-branch'),'branch');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-03-branch'),'logic');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-04-loop','C++基础04:循环结构(for / while)','# C++基础04:循环结构(for / while)
|
||||
|
||||
## 学习目标
|
||||
- 会用循环做:计数、累加、打印图形
|
||||
|
||||
## 练习
|
||||
- B2083 画矩形 https://www.luogu.com.cn/problem/B2083
|
||||
- P1421 小玉买文具 https://www.luogu.com.cn/problem/P1421
|
||||
|
||||
## 提交要求
|
||||
- 笔记里写:循环三要素(初始化/条件/更新)+ 你调试的方法
|
||||
',2,'course:cpp-basic:04',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-04-loop');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-04-loop'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-04-loop'),'loop');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-04-loop'),'debug');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-05-array','C++基础05:数组入门(一维)','# C++基础05:数组入门(一维)
|
||||
|
||||
## 学习目标
|
||||
- 会定义数组、遍历、统计
|
||||
|
||||
## 练习
|
||||
- P1427 小鱼的数字游戏 https://www.luogu.com.cn/problem/P1427
|
||||
- P1428 小鱼比可爱 https://www.luogu.com.cn/problem/P1428
|
||||
|
||||
## 提交要求
|
||||
- 笔记里写:数组下标从 0/1 的选择;如何避免越界
|
||||
',3,'course:cpp-basic:05',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-05-array');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-05-array'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-05-array'),'array');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-06-char-string','C++基础06:字符与字符串(char / string)','# C++基础06:字符与字符串(char / string)
|
||||
|
||||
## 学习目标
|
||||
- 区分 `char`(一个字符)与 `string`(一串字符)
|
||||
- 会做:大小写转换、统计字母、简单加密
|
||||
|
||||
## 重点
|
||||
- `char c=''A''`,`string s="abc"`
|
||||
- `s.size()` / `s[i]` / 遍历字符串
|
||||
|
||||
## 练习(完成至少 2 题)
|
||||
- P5704 字母转换 https://www.luogu.com.cn/problem/P5704
|
||||
- P5733 自动修正 https://www.luogu.com.cn/problem/P5733
|
||||
- P1914 小书童——凯撒密码 https://www.luogu.com.cn/problem/P1914
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:`string` 下标访问与越界风险(最后一个下标是 size()-1)
|
||||
',3,'course:cpp-basic:06',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-06-char-string');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-06-char-string'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-06-char-string'),'string');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-06-char-string'),'char');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-07-operator-cast','C++基础07:运算、取整与类型转换(/ %)','# C++基础07:运算、取整与类型转换(/ %)
|
||||
|
||||
## 学习目标
|
||||
- 写对 `+ - * / %`
|
||||
- 理解:整数除法会向下取整;`%` 只能用于整数
|
||||
- 学会用 `long long` 防止溢出
|
||||
|
||||
## 练习
|
||||
- B2008 计算 (a+b)×c 的值 https://www.luogu.com.cn/problem/B2008
|
||||
- P1421 小玉买文具 https://www.luogu.com.cn/problem/P1421
|
||||
- P5708 三角形面积 https://www.luogu.com.cn/problem/P5708
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:什么时候要用 `long long`(例如钱、人数、乘法很大)
|
||||
',3,'course:cpp-basic:07',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-07-operator-cast');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-07-operator-cast'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-07-operator-cast'),'math');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-07-operator-cast'),'cast');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-08-logic','C++基础08:逻辑运算与边界(&& || !)','# C++基础08:逻辑运算与边界(&& || !)
|
||||
|
||||
## 学习目标
|
||||
- 能把题目里的“并且/或者/不是”写成条件
|
||||
- 学会找边界:等于、最小/最大、0
|
||||
|
||||
## 练习
|
||||
- P5710 数的性质 https://www.luogu.com.cn/problem/P5710
|
||||
- P5711 闰年判断 https://www.luogu.com.cn/problem/P5711
|
||||
- P1909 买铅笔 https://www.luogu.com.cn/problem/P1909
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:你列出的边界测试用例(至少 3 个)
|
||||
',3,'course:cpp-basic:08',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-08-logic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-08-logic'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-08-logic'),'logic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-08-logic'),'boundary');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-09-switch','C++基础09:switch 与枚举列情况','# C++基础09:switch 与枚举列情况
|
||||
|
||||
## 学习目标
|
||||
- 会写 `switch/case/break`
|
||||
- 面对“情况很少”的题,会列举所有情况
|
||||
|
||||
## 练习
|
||||
- P2433 小学数学N合一 https://www.luogu.com.cn/problem/P2433
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:为什么每个 case 后要 `break`(避免贯穿执行)
|
||||
',3,'course:cpp-basic:09',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-09-switch');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-09-switch'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-09-switch'),'switch');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-09-switch'),'enum');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-10-for','C++基础10:for循环(计数与累加)','# C++基础10:for循环(计数与累加)
|
||||
|
||||
## 学习目标
|
||||
- 熟练掌握 `for(初始化; 条件; 更新)`
|
||||
- 会写:求和、求最大最小、统计个数
|
||||
|
||||
## 练习
|
||||
- P1425 小鱼的游泳时间 https://www.luogu.com.cn/problem/P1425
|
||||
- P5718 找最小值 https://www.luogu.com.cn/problem/P5718
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:循环“三要素”分别是什么
|
||||
',3,'course:cpp-basic:10',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-10-for');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-10-for'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-10-for'),'loop');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-10-for'),'for');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-11-while','C++基础11:while循环(未知次数)','# C++基础11:while循环(未知次数)
|
||||
|
||||
## 学习目标
|
||||
- 会用 `while` 处理:读到结束、读到 0 停止
|
||||
- 避免死循环:每次循环必须改变状态
|
||||
|
||||
## 练习
|
||||
- P1427 小鱼的数字游戏 https://www.luogu.com.cn/problem/P1427
|
||||
- P1047 校门外的树(选做)https://www.luogu.com.cn/problem/P1047
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:你是如何避免死循环的(更新变量写在哪里)
|
||||
',3,'course:cpp-basic:11',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-11-while');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-11-while'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-11-while'),'loop');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-11-while'),'while');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-12-nested','C++基础12:嵌套循环与打印图形','# C++基础12:嵌套循环与打印图形
|
||||
|
||||
## 学习目标
|
||||
- 外层循环=行,内层循环=列
|
||||
- 能打印:矩形、三角形、乘法表
|
||||
|
||||
## 练习
|
||||
- B2083 画矩形 https://www.luogu.com.cn/problem/B2083
|
||||
- P5717 三角形分类(练分类讨论 + 输出格式)https://www.luogu.com.cn/problem/P5717
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:你画图时是怎么想“行/列”的
|
||||
',3,'course:cpp-basic:12',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-12-nested');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-12-nested'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-12-nested'),'nested-loop');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-12-nested'),'print');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-13-array-ops','C++基础13:数组进阶(统计/查找/反转)','# C++基础13:数组进阶(统计/查找/反转)
|
||||
|
||||
## 学习目标
|
||||
- 用数组做:统计、查找位置、反转、去重(了解)
|
||||
|
||||
## 练习
|
||||
- P1428 小鱼比可爱 https://www.luogu.com.cn/problem/P1428
|
||||
- P5727 冰雹猜想 https://www.luogu.com.cn/problem/P5727
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:数组越界的 2 个典型原因 + 你的检查方法
|
||||
',4,'course:cpp-basic:13',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-13-array-ops');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-13-array-ops'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-13-array-ops'),'array');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-13-array-ops'),'practice');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-14-function','C++基础14:函数入门(把重复代码变成工具)','# C++基础14:函数入门(参数/返回值/作用域)
|
||||
|
||||
## 学习目标
|
||||
- 会定义并调用函数
|
||||
- 知道局部变量作用域({} 内有效)
|
||||
|
||||
## 练习
|
||||
- P5735 距离函数 https://www.luogu.com.cn/problem/P5735
|
||||
- P5739 计算阶乘(也可用递归)https://www.luogu.com.cn/problem/P5739
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:你把哪段重复代码抽成了函数?为什么这样更清晰?
|
||||
',4,'course:cpp-basic:14',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-14-function');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-14-function'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-14-function'),'function');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-14-function'),'scope');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-15-recursion','C++基础15:递归入门(选学)','# C++基础15:递归入门(选学)
|
||||
|
||||
## 学习目标
|
||||
- 理解递归:函数调用自己
|
||||
- 必须有终止条件(否则会无限调用)
|
||||
|
||||
## 练习
|
||||
- P5739 计算阶乘 https://www.luogu.com.cn/problem/P5739
|
||||
- P1028 数的计算(选做)https://www.luogu.com.cn/problem/P1028
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:你的递归“终止条件”是什么?
|
||||
',4,'course:cpp-basic:15',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-15-recursion');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-15-recursion'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-15-recursion'),'recursion');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-16-struct','C++基础16:结构体 struct(选学)','# C++基础16:结构体 struct(选学)
|
||||
|
||||
## 学习目标
|
||||
- 用 `struct` 把多个字段打包成一个“人物卡/记录”
|
||||
- 学会 `struct` 数组
|
||||
|
||||
## 练习
|
||||
- P5740 最厉害的学生 https://www.luogu.com.cn/problem/P5740
|
||||
- P5744 培训 https://www.luogu.com.cn/problem/P5744
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:struct 适合用在什么场景(多个属性属于同一个对象)
|
||||
',4,'course:cpp-basic:16',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-16-struct');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-16-struct'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-16-struct'),'struct');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-17-sort','C++基础17:排序初步(sort)','# C++基础17:排序初步(sort)
|
||||
|
||||
## 学习目标
|
||||
- 会用 `#include <algorithm>` 和 `sort`
|
||||
- 排序后做统计/查找会更简单
|
||||
|
||||
## 练习
|
||||
- P1059 明明的随机数 https://www.luogu.com.cn/problem/P1059
|
||||
- P1093 奖学金(选做)https://www.luogu.com.cn/problem/P1093
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:sort 的区间是 `[begin, end)`(end 不包含)
|
||||
',4,'course:cpp-basic:17',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-17-sort');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-17-sort'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-17-sort'),'sort');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-17-sort'),'algorithm');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-18-binary-search','C++基础18:二分查找初步(lower_bound)','# C++基础18:二分查找初步(lower_bound)
|
||||
|
||||
## 学习目标
|
||||
- 理解“在有序数组里快速找答案”
|
||||
- 会用 `lower_bound` 找第一个 >= x 的位置
|
||||
|
||||
## 练习
|
||||
- P2249 查找 https://www.luogu.com.cn/problem/P2249
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:二分查找的前提:数组必须有序
|
||||
',4,'course:cpp-basic:18',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-18-binary-search');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-18-binary-search'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-18-binary-search'),'binary-search');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-19-prefix-sum','C++基础19:前缀和(区间求和神器)','# C++基础19:前缀和(区间求和神器)
|
||||
|
||||
## 学习目标
|
||||
- 会构造前缀和 `s[i]=s[i-1]+a[i]`
|
||||
- 会算区间和 `sum(l,r)=s[r]-s[l-1]`
|
||||
|
||||
## 练习
|
||||
- P8218 求区间和(前缀和基础)https://www.luogu.com.cn/problem/P8218
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:下标偏移是怎么处理的(从 1 开始更方便)
|
||||
',4,'course:cpp-basic:19',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-19-prefix-sum');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-19-prefix-sum'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-19-prefix-sum'),'prefix-sum');
|
||||
INSERT INTO problems(slug,title,statement_md,difficulty,source,created_at) VALUES('cpp-basic-20-simulation','C++基础20:模拟题与综合复盘','# C++基础20:模拟题与综合复盘
|
||||
|
||||
## 学习目标
|
||||
- 学会按题意一步步做(模拟)
|
||||
- 复盘:总结常见错误 + 常用模板
|
||||
|
||||
## 练习(从这里选 3~5 题做小测)
|
||||
- P1957 口算练习题 https://www.luogu.com.cn/problem/P1957
|
||||
- P1055 ISBN号码 https://www.luogu.com.cn/problem/P1055
|
||||
- P2433 小学数学N合一(回顾)https://www.luogu.com.cn/problem/P2433
|
||||
|
||||
## 提交要求
|
||||
- 笔记写:
|
||||
1) 你最常犯的 3 类错误
|
||||
2) 你最常用的 5 行代码模板
|
||||
3) 下周你准备怎么练
|
||||
',4,'course:cpp-basic:20',1771241843) ON CONFLICT(slug) DO UPDATE SET title=excluded.title, statement_md=excluded.statement_md, difficulty=excluded.difficulty, source=excluded.source;
|
||||
DELETE FROM problem_tags WHERE problem_id=(SELECT id FROM problems WHERE slug='cpp-basic-20-simulation');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-20-simulation'),'cpp-basic');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-20-simulation'),'simulation');
|
||||
INSERT OR IGNORE INTO problem_tags(problem_id,tag) VALUES((SELECT id FROM problems WHERE slug='cpp-basic-20-simulation'),'review');
|
||||
COMMIT;
|
||||
@@ -950,14 +950,18 @@ export default function ProblemDetailPage() {
|
||||
|
||||
const printProblemWithAnswer = async () => {
|
||||
let resolved = solutionData;
|
||||
if (resolved?.access?.mode !== "full") {
|
||||
const fetched = await loadSolutions("full");
|
||||
if (fetched) resolved = fetched;
|
||||
} else if ((resolved?.items.length ?? 0) === 0) {
|
||||
// If user has already unlocked solutions (full mode), use them directly
|
||||
if (resolved?.access?.mode === "full" && (resolved?.items.length ?? 0) > 0) {
|
||||
// Already have full solutions loaded — use as-is
|
||||
} else if (resolved?.access?.mode === "full") {
|
||||
// Full access but items not loaded yet — fetch
|
||||
const fetched = await loadSolutions("full");
|
||||
if (fetched) resolved = fetched;
|
||||
}
|
||||
setPrintAnswerMarkdown(buildPrintableAnswerMarkdown(resolved, tx));
|
||||
// If not in full mode, just print problem without answer
|
||||
setPrintAnswerMarkdown(
|
||||
resolved?.access?.mode === "full" ? buildPrintableAnswerMarkdown(resolved, tx) : ""
|
||||
);
|
||||
await new Promise<void>((resolve) => {
|
||||
window.requestAnimationFrame(() => {
|
||||
window.requestAnimationFrame(() => resolve());
|
||||
@@ -997,7 +1001,11 @@ export default function ProblemDetailPage() {
|
||||
onClick={() => void printProblemWithAnswer()}
|
||||
disabled={solutionLoading}
|
||||
>
|
||||
{solutionLoading ? tx("卷轴准备中...", "Scribing...") : tx("打印卷轴", "Scribe Scroll")}
|
||||
{solutionLoading
|
||||
? tx("卷轴准备中...", "Scribing...")
|
||||
: solutionData?.access?.mode === "full"
|
||||
? tx("🖨️ 打印题目+答案", "🖨️ Print with Answer")
|
||||
: tx("🖨️ 打印题目", "🖨️ Print Problem")}
|
||||
</button>
|
||||
<button
|
||||
className="mc-btn text-sm py-1"
|
||||
|
||||
43
remote_patch_cpp_basic_20.py
普通文件
43
remote_patch_cpp_basic_20.py
普通文件
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('--file', required=True)
|
||||
ap.add_argument('--items', required=True)
|
||||
args = ap.parse_args()
|
||||
|
||||
p = Path(args.file)
|
||||
items = Path(args.items).read_text(encoding='utf-8')
|
||||
s = p.read_text(encoding='utf-8')
|
||||
|
||||
marker = 'const CourseItem items[] = {'
|
||||
i = s.find(marker)
|
||||
if i < 0:
|
||||
raise SystemExit(f'cannot find marker: {marker}')
|
||||
|
||||
j = s.find('};', i)
|
||||
if j < 0:
|
||||
raise SystemExit('cannot find end marker "};" after items start')
|
||||
|
||||
before = s[:i]
|
||||
after = s[j+2:]
|
||||
|
||||
out = before + items + after
|
||||
if out == s:
|
||||
print('no change')
|
||||
return
|
||||
|
||||
backup = p.with_suffix(p.suffix + '.bak_cppbasic20')
|
||||
backup.write_text(s, encoding='utf-8')
|
||||
p.write_text(out, encoding='utf-8')
|
||||
print('patched', str(p))
|
||||
print('backup', str(backup))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
在新工单中引用
屏蔽一个用户