upgrade to python3;add some validator examples
This commit is contained in:
295
examples/README_自定义验证程序.md
Normal file
295
examples/README_自定义验证程序.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# BitOJ 自定义验证程序完整指南
|
||||
|
||||
## 概述
|
||||
|
||||
BitOJ系统支持4种判题方式:
|
||||
|
||||
1. **字符串精确比较** - 逐字符对比输出
|
||||
2. **字符串容错比较** - 允许格式错误(PE),主要比较数字内容
|
||||
3. **文件MD5校验** - 比较文件大小和MD5值
|
||||
4. **自定义验证程序** - 编写专门的验证逻辑
|
||||
|
||||
本文档重点介绍第4种方式:自定义验证程序的编写和使用。
|
||||
|
||||
## 自定义验证程序原理
|
||||
|
||||
### 工作流程
|
||||
1. 系统执行用户提交的代码
|
||||
2. 将输入、期望输出、实际输出传递给验证程序
|
||||
3. 验证程序分析这些文件并返回判题结果
|
||||
4. 系统根据返回结果确定最终评分
|
||||
|
||||
### 参数说明
|
||||
验证程序接收以下参数:
|
||||
- `argv[1]`: 标准输入文件路径
|
||||
- `argv[2]`: 期望输出文件路径
|
||||
- `argv[3]`: 用户程序输出文件路径
|
||||
|
||||
### 返回值
|
||||
验证程序必须输出以下结果之一:
|
||||
- `AC` - Accepted(答案正确)
|
||||
- `PE` - Presentation Error(格式错误)
|
||||
- `WA` - Wrong Answer(答案错误)
|
||||
|
||||
## 实际示例
|
||||
|
||||
### 1. 浮点数误差比较 (C++)
|
||||
|
||||
**适用场景**: 计算几何、数值计算等需要浮点数比较的题目
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cmath>
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1e-9;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
ifstream expected(argv[2]); // 标准答案
|
||||
ifstream result(argv[3]); // 用户输出
|
||||
|
||||
double exp_val, res_val;
|
||||
if (!(expected >> exp_val) || !(result >> res_val)) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fabs(exp_val - res_val) <= EPS) {
|
||||
cout << "AC" << endl;
|
||||
} else {
|
||||
cout << "WA" << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 多解答案验证 (Python)
|
||||
|
||||
**适用场景**: 排序题、图论路径题等有多种正确答案的题目
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
|
||||
def main():
|
||||
try:
|
||||
with open(sys.argv[2], 'r') as f:
|
||||
expected = f.read().strip()
|
||||
with open(sys.argv[3], 'r') as f:
|
||||
result = f.read().strip()
|
||||
|
||||
# 解析数字并排序比较
|
||||
expected_nums = sorted(map(int, expected.split()))
|
||||
result_nums = sorted(map(int, result.split()))
|
||||
|
||||
if expected_nums == result_nums:
|
||||
print("AC")
|
||||
else:
|
||||
print("WA")
|
||||
except:
|
||||
print("WA")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
### 3. 输出格式验证 (Java)
|
||||
|
||||
**适用场景**: 需要严格输出格式的竞赛题目
|
||||
|
||||
```java
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class StringFormatValidator {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
Scanner result = new Scanner(new File(args[2]));
|
||||
|
||||
int lineNum = 1;
|
||||
while (result.hasNextLine()) {
|
||||
String line = result.nextLine().trim();
|
||||
|
||||
// 检查格式:Case #X: Y
|
||||
Pattern pattern = Pattern.compile("^Case #" + lineNum + ": \\d+$");
|
||||
if (!pattern.matcher(line).matches()) {
|
||||
System.out.println("PE");
|
||||
return;
|
||||
}
|
||||
lineNum++;
|
||||
}
|
||||
|
||||
System.out.println("AC");
|
||||
} catch (Exception e) {
|
||||
System.out.println("WA");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 在BitOJ中配置自定义验证程序
|
||||
|
||||
### 代码配置示例
|
||||
|
||||
```python
|
||||
from judgescript import ExternalJudge
|
||||
|
||||
# 创建自定义验证程序
|
||||
validator = ExternalJudge(
|
||||
problemid='P1001', # 题目ID
|
||||
vlang='gcc-3.3', # 验证程序语言
|
||||
vcode=validator_code # 验证程序源代码
|
||||
)
|
||||
|
||||
# 在测试过程中调用
|
||||
result = validator.judge(
|
||||
sid='S123', # 提交ID
|
||||
tid='T001', # 测试用例ID
|
||||
tin='/path/to/input.txt', # 输入文件
|
||||
tout='/path/to/output.txt', # 期望输出
|
||||
result='/path/to/result.txt', # 实际输出
|
||||
errfile='/path/to/error.txt' # 错误输出
|
||||
)
|
||||
```
|
||||
|
||||
### 支持的编程语言
|
||||
|
||||
| 语言 | 标识符 | 说明 |
|
||||
|------|--------|------|
|
||||
| C | gcc-3.3 | 适合高性能验证 |
|
||||
| C++ | g++-3.3 | 支持STL容器 |
|
||||
| Java | java-1.6 | 强类型安全 |
|
||||
| Python | python-2.5 | 快速开发 |
|
||||
| Pascal | fpc-2.2 | 简单逻辑 |
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 错误处理
|
||||
```cpp
|
||||
// 文件读取失败处理
|
||||
if (!input_file.is_open()) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 数据解析失败处理
|
||||
try {
|
||||
value = stod(line);
|
||||
} catch (const exception& e) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 性能优化
|
||||
- 使用适当的数据结构
|
||||
- 避免不必要的字符串操作
|
||||
- 合理设置缓冲区大小
|
||||
- 注意内存使用限制
|
||||
|
||||
### 3. 安全考虑
|
||||
- 验证输入参数数量
|
||||
- 处理异常文件路径
|
||||
- 限制递归深度
|
||||
- 避免无限循环
|
||||
|
||||
### 4. 可维护性
|
||||
- 添加清晰的注释
|
||||
- 使用有意义的变量名
|
||||
- 模块化验证逻辑
|
||||
- 提供调试输出选项
|
||||
|
||||
## 常见应用场景
|
||||
|
||||
### 1. 数值计算题
|
||||
- 浮点数精度比较
|
||||
- 矩阵运算结果验证
|
||||
- 统计数值的近似比较
|
||||
|
||||
### 2. 图论算法题
|
||||
- 最短路径验证(允许多条等长路径)
|
||||
- 最小生成树验证(权重相等即可)
|
||||
- 图遍历结果验证
|
||||
|
||||
### 3. 字符串处理题
|
||||
- 忽略大小写比较
|
||||
- 忽略空白字符差异
|
||||
- 正则表达式匹配
|
||||
|
||||
### 4. 组合数学题
|
||||
- 排列组合结果验证
|
||||
- 集合操作结果比较
|
||||
- 数学证明步骤检查
|
||||
|
||||
### 5. 交互式题目
|
||||
- 模拟交互过程
|
||||
- 验证查询次数限制
|
||||
- 检查答案正确性
|
||||
|
||||
## 调试技巧
|
||||
|
||||
### 1. 本地测试
|
||||
```bash
|
||||
# 编译验证程序
|
||||
g++ -o validator validator.cpp
|
||||
|
||||
# 准备测试文件
|
||||
echo "3.14159" > expected.txt
|
||||
echo "3.14160" > result.txt
|
||||
|
||||
# 运行测试
|
||||
./validator input.txt expected.txt result.txt
|
||||
```
|
||||
|
||||
### 2. 日志输出
|
||||
```cpp
|
||||
#ifdef DEBUG
|
||||
cerr << "Expected: " << exp_val << ", Got: " << res_val << endl;
|
||||
cerr << "Difference: " << fabs(exp_val - res_val) << endl;
|
||||
#endif
|
||||
```
|
||||
|
||||
### 3. 边界情况测试
|
||||
- 空输出文件
|
||||
- 超大数值
|
||||
- 特殊字符
|
||||
- 格式错误的输入
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **编译失败**
|
||||
- 检查语言版本兼容性
|
||||
- 确认头文件引用正确
|
||||
- 验证语法错误
|
||||
|
||||
2. **运行超时**
|
||||
- 优化算法复杂度
|
||||
- 减少不必要的计算
|
||||
- 检查是否有死循环
|
||||
|
||||
3. **内存不足**
|
||||
- 减少内存分配
|
||||
- 及时释放资源
|
||||
- 使用流式处理
|
||||
|
||||
4. **判题结果异常**
|
||||
- 验证输出格式
|
||||
- 检查返回值规范
|
||||
- 确认异常处理逻辑
|
||||
|
||||
## 总结
|
||||
|
||||
自定义验证程序是BitOJ系统最灵活的判题方式,能够处理各种复杂的判题需求。通过合理设计验证逻辑,可以支持:
|
||||
|
||||
- 多解答案题目
|
||||
- 浮点数精度要求
|
||||
- 特殊输出格式
|
||||
- 交互式评测
|
||||
- 复杂的正确性检查
|
||||
|
||||
掌握自定义验证程序的编写技巧,能够大大扩展在线评测系统的应用范围,为各种类型的编程竞赛和教学需求提供支持。
|
||||
69
examples/StringFormatValidator.java
Normal file
69
examples/StringFormatValidator.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 字符串格式验证程序
|
||||
* 验证输出是否符合特定格式要求
|
||||
* 编译: javac StringFormatValidator.java
|
||||
* 使用: java StringFormatValidator input.txt expected.txt result.txt
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class StringFormatValidator {
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 3) {
|
||||
System.out.println("WA");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 读取期望的行数和格式信息
|
||||
Scanner expected = new Scanner(new File(args[1]));
|
||||
Scanner result = new Scanner(new File(args[2]));
|
||||
|
||||
// 假设期望文件第一行是期望的行数
|
||||
int expectedLines = expected.nextInt();
|
||||
expected.nextLine(); // 消费换行符
|
||||
|
||||
// 读取用户输出
|
||||
List<String> userLines = new ArrayList<>();
|
||||
while (result.hasNextLine()) {
|
||||
String line = result.nextLine().trim();
|
||||
if (!line.isEmpty()) {
|
||||
userLines.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查行数
|
||||
if (userLines.size() != expectedLines) {
|
||||
System.out.println("WA");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查每行格式
|
||||
boolean formatCorrect = true;
|
||||
for (int i = 0; i < userLines.size(); i++) {
|
||||
String line = userLines.get(i);
|
||||
|
||||
// 检查是否符合 "Case #X: Y" 格式
|
||||
Pattern pattern = Pattern.compile("^Case #" + (i + 1) + ": \\d+$");
|
||||
if (!pattern.matcher(line).matches()) {
|
||||
formatCorrect = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (formatCorrect) {
|
||||
System.out.println("AC");
|
||||
} else {
|
||||
System.out.println("PE"); // 格式错误
|
||||
}
|
||||
|
||||
expected.close();
|
||||
result.close();
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("WA");
|
||||
}
|
||||
}
|
||||
}
|
||||
328
examples/custom_validators.md
Normal file
328
examples/custom_validators.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# BitOJ 自定义验证程序示例
|
||||
|
||||
## 1. 基本验证程序结构
|
||||
|
||||
自定义验证程序接收以下参数:
|
||||
- 标准输入文件
|
||||
- 标准输出文件(期望答案)
|
||||
- 用户输出文件(用户程序的输出)
|
||||
|
||||
验证程序需要输出以下结果之一:
|
||||
- `AC` - 答案正确
|
||||
- `PE` - 格式错误(如果支持)
|
||||
- `WA` - 答案错误
|
||||
|
||||
## 2. 示例1:数值误差比较验证程序(C++)
|
||||
|
||||
适用于需要浮点数比较的题目:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1e-9;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// argv[1]: 标准输入文件
|
||||
// argv[2]: 标准输出文件
|
||||
// argv[3]: 用户输出文件
|
||||
|
||||
ifstream expected(argv[2]); // 标准答案
|
||||
ifstream result(argv[3]); // 用户输出
|
||||
|
||||
double exp_val, res_val;
|
||||
|
||||
// 读取期望的浮点数
|
||||
if (!(expected >> exp_val)) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 读取用户输出的浮点数
|
||||
if (!(result >> res_val)) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 比较数值,允许误差
|
||||
if (fabs(exp_val - res_val) <= EPS) {
|
||||
cout << "AC" << endl;
|
||||
} else {
|
||||
cout << "WA" << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 示例2:多解答案验证程序(Python)
|
||||
|
||||
适用于有多个正确答案的题目:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
|
||||
def main():
|
||||
# sys.argv[1]: 标准输入文件
|
||||
# sys.argv[2]: 标准输出文件
|
||||
# sys.argv[3]: 用户输出文件
|
||||
|
||||
try:
|
||||
with open(sys.argv[2], 'r') as f:
|
||||
expected = f.read().strip()
|
||||
|
||||
with open(sys.argv[3], 'r') as f:
|
||||
result = f.read().strip()
|
||||
|
||||
# 假设这是一个排序题目,任何正确的排序都是对的
|
||||
expected_nums = list(map(int, expected.split()))
|
||||
result_nums = list(map(int, result.split()))
|
||||
|
||||
# 检查数量是否相同
|
||||
if len(expected_nums) != len(result_nums):
|
||||
print("WA")
|
||||
return
|
||||
|
||||
# 检查排序后是否相同(原始数据相同)
|
||||
if sorted(expected_nums) != sorted(result_nums):
|
||||
print("WA")
|
||||
return
|
||||
|
||||
# 检查用户输出是否有序
|
||||
if result_nums == sorted(result_nums):
|
||||
print("AC")
|
||||
else:
|
||||
print("WA")
|
||||
|
||||
except Exception as e:
|
||||
print("WA")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
## 4. 示例3:图论路径验证程序(C++)
|
||||
|
||||
验证最短路径问题,检查路径是否合法且最优:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
using namespace std;
|
||||
|
||||
struct Edge {
|
||||
int to, weight;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
ifstream input(argv[1]); // 输入数据
|
||||
ifstream expected(argv[2]); // 期望答案
|
||||
ifstream result(argv[3]); // 用户答案
|
||||
|
||||
// 读取图的信息
|
||||
int n, m;
|
||||
input >> n >> m;
|
||||
|
||||
vector<vector<Edge>> graph(n + 1);
|
||||
for (int i = 0; i < m; i++) {
|
||||
int u, v, w;
|
||||
input >> u >> v >> w;
|
||||
graph[u].push_back({v, w});
|
||||
graph[v].push_back({u, w}); // 无向图
|
||||
}
|
||||
|
||||
int start, end;
|
||||
input >> start >> end;
|
||||
|
||||
// 读取期望的最短距离
|
||||
int expected_dist;
|
||||
expected >> expected_dist;
|
||||
|
||||
// 读取用户输出的路径
|
||||
string line;
|
||||
getline(result, line);
|
||||
istringstream iss(line);
|
||||
vector<int> path;
|
||||
int node;
|
||||
while (iss >> node) {
|
||||
path.push_back(node);
|
||||
}
|
||||
|
||||
// 验证路径
|
||||
if (path.empty() || path[0] != start || path.back() != end) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 计算路径长度
|
||||
int total_dist = 0;
|
||||
for (int i = 0; i < path.size() - 1; i++) {
|
||||
int u = path[i], v = path[i + 1];
|
||||
bool found = false;
|
||||
for (const Edge& e : graph[u]) {
|
||||
if (e.to == v) {
|
||||
total_dist += e.weight;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
cout << "WA" << endl; // 路径不存在
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否是最短路径
|
||||
if (total_dist == expected_dist) {
|
||||
cout << "AC" << endl;
|
||||
} else {
|
||||
cout << "WA" << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 示例4:字符串格式验证程序(Java)
|
||||
|
||||
验证输出格式是否正确:
|
||||
|
||||
```java
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws IOException {
|
||||
// args[0]: 标准输入文件
|
||||
// args[1]: 标准输出文件
|
||||
// args[2]: 用户输出文件
|
||||
|
||||
Scanner expected = new Scanner(new File(args[1]));
|
||||
Scanner result = new Scanner(new File(args[2]));
|
||||
|
||||
try {
|
||||
// 读取期望的行数
|
||||
int expectedLines = expected.nextInt();
|
||||
|
||||
// 检查用户输出的行数
|
||||
int actualLines = 0;
|
||||
List<String> userLines = new ArrayList<>();
|
||||
|
||||
while (result.hasNextLine()) {
|
||||
String line = result.nextLine().trim();
|
||||
if (!line.isEmpty()) {
|
||||
userLines.add(line);
|
||||
actualLines++;
|
||||
}
|
||||
}
|
||||
|
||||
if (actualLines != expectedLines) {
|
||||
System.out.println("WA");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查每行格式:必须是 "Case #X: Y" 的格式
|
||||
for (int i = 0; i < userLines.size(); i++) {
|
||||
String line = userLines.get(i);
|
||||
String pattern = "Case #" + (i + 1) + ": \\d+";
|
||||
if (!line.matches(pattern)) {
|
||||
System.out.println("PE"); // 格式错误
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("AC");
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("WA");
|
||||
} finally {
|
||||
expected.close();
|
||||
result.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 示例5:交互式题目验证程序(C++)
|
||||
|
||||
模拟交互过程:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
ifstream input(argv[1]); // 输入数据
|
||||
ifstream expected(argv[2]); // 期望的查询次数等
|
||||
ifstream result(argv[3]); // 用户的查询序列
|
||||
|
||||
// 读取隐藏的数字
|
||||
int secret;
|
||||
input >> secret;
|
||||
|
||||
// 读取最大查询次数
|
||||
int maxQueries;
|
||||
expected >> maxQueries;
|
||||
|
||||
// 模拟交互过程
|
||||
int queryCount = 0;
|
||||
int guess;
|
||||
bool found = false;
|
||||
|
||||
while (result >> guess && queryCount < maxQueries) {
|
||||
queryCount++;
|
||||
|
||||
if (guess == secret) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
// 在实际交互中,这里会给用户反馈 "too high" 或 "too low"
|
||||
}
|
||||
|
||||
// 检查结果
|
||||
if (found && queryCount <= maxQueries) {
|
||||
cout << "AC" << endl;
|
||||
} else {
|
||||
cout << "WA" << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. **编写验证程序**:选择合适的编程语言编写验证逻辑
|
||||
2. **设置题目**:在题目配置中指定使用自定义验证程序
|
||||
3. **上传验证代码**:将验证程序代码保存到系统中
|
||||
4. **测试验证**:确保验证程序能正确处理各种情况
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **异常处理**:验证程序必须处理所有可能的异常情况
|
||||
2. **性能考虑**:验证程序也有时间限制,需要高效实现
|
||||
3. **输出格式**:验证程序必须严格按照 AC/PE/WA 格式输出
|
||||
4. **安全性**:验证程序在沙箱环境中运行,有资源限制
|
||||
5. **可移植性**:确保验证程序在评测环境中能正常编译运行
|
||||
|
||||
## 配置示例
|
||||
|
||||
在 BitOJ 中配置自定义验证程序:
|
||||
|
||||
```python
|
||||
# 在题目配置中设置自定义验证程序
|
||||
problem.validator_language = 'gcc-3.3' # 验证程序语言
|
||||
problem.validator_code = '''
|
||||
// 这里放置验证程序的源代码
|
||||
'''
|
||||
```
|
||||
54
examples/float_validator.cpp
Normal file
54
examples/float_validator.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// 浮点数误差比较验证程序
|
||||
// 编译: g++ -o float_validator float_validator.cpp
|
||||
// 使用: ./float_validator input.txt expected.txt result.txt
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1e-9;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 4) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ifstream expected(argv[2]); // 标准答案
|
||||
ifstream result(argv[3]); // 用户输出
|
||||
|
||||
if (!expected.is_open() || !result.is_open()) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
double exp_val, res_val;
|
||||
|
||||
// 读取期望的浮点数
|
||||
if (!(expected >> exp_val)) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 读取用户输出的浮点数
|
||||
if (!(result >> res_val)) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 比较数值,允许相对误差和绝对误差
|
||||
double diff = fabs(exp_val - res_val);
|
||||
double rel_error = (fabs(exp_val) > EPS) ? diff / fabs(exp_val) : diff;
|
||||
|
||||
if (diff <= 1e-6 || rel_error <= 1e-6) { // 放宽误差范围
|
||||
cout << "AC" << endl;
|
||||
} else {
|
||||
cout << "WA" << endl;
|
||||
}
|
||||
|
||||
expected.close();
|
||||
result.close();
|
||||
return 0;
|
||||
}
|
||||
183
examples/integration_example.py
Normal file
183
examples/integration_example.py
Normal file
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
BitOJ 自定义验证程序集成示例
|
||||
演示如何在实际的BitOJ系统中使用自定义验证程序
|
||||
"""
|
||||
|
||||
import sys
|
||||
sys.path.append('../python')
|
||||
|
||||
try:
|
||||
from judgescript import InternalJudge
|
||||
except ImportError:
|
||||
# 如果无法导入,创建一个模拟的类
|
||||
class InternalJudge:
|
||||
def __init__(self, allowpe=False):
|
||||
self.allowpe = allowpe
|
||||
|
||||
def compare_string(self, output, result):
|
||||
if output == result:
|
||||
return 'AC'
|
||||
if self.allowpe:
|
||||
# 简化的PE检查
|
||||
if ''.join(output.split()) == ''.join(result.split()):
|
||||
return 'PE'
|
||||
return 'WA'
|
||||
|
||||
|
||||
def demo_internal_judge():
|
||||
"""演示内置判题器的使用"""
|
||||
print("=== 内置判题器演示 ===")
|
||||
|
||||
# 标准文本比较
|
||||
ij_strict = InternalJudge(allowpe=False)
|
||||
print("严格文本比较:")
|
||||
result = ij_strict.compare_string("hello world\n", "hello world\n")
|
||||
print(f" 'hello world\\n' vs 'hello world\\n' => {result}")
|
||||
|
||||
result = ij_strict.compare_string("hello world", "hello world\n")
|
||||
print(f" 'hello world' vs 'hello world\\n' => {result}")
|
||||
|
||||
# 允许格式错误的比较
|
||||
ij_pe = InternalJudge(allowpe=True)
|
||||
print("\n允许格式错误的比较:")
|
||||
result = ij_pe.compare_string("1 2 3 4 5\n", "1\n2\n3\n4\n5\n")
|
||||
print(f" '1 2 3 4 5\\n' vs '1\\n2\\n3\\n4\\n5\\n' => {result}")
|
||||
|
||||
result = ij_pe.compare_string("12345\n", "1 2 3 4 5\n")
|
||||
print(f" '12345\\n' vs '1 2 3 4 5\\n' => {result}")
|
||||
|
||||
|
||||
def demo_custom_judge_config():
|
||||
"""演示如何配置自定义验证程序"""
|
||||
print("\n=== 自定义验证程序配置示例 ===")
|
||||
|
||||
# 浮点数比较验证程序代码
|
||||
float_validator_code = """
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cmath>
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1e-9;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 4) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ifstream expected(argv[2]);
|
||||
ifstream result(argv[3]);
|
||||
|
||||
double exp_val, res_val;
|
||||
if (!(expected >> exp_val) || !(result >> res_val)) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fabs(exp_val - res_val) <= EPS) {
|
||||
cout << "AC" << endl;
|
||||
} else {
|
||||
cout << "WA" << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
|
||||
print("浮点数比较验证程序配置:")
|
||||
print(" 语言: gcc-3.3")
|
||||
print(" 代码长度:", len(float_validator_code), "字符")
|
||||
print(" 功能: 允许浮点数误差比较")
|
||||
|
||||
# 多解验证程序代码
|
||||
multi_solution_code = """
|
||||
import sys
|
||||
|
||||
def main():
|
||||
try:
|
||||
with open(sys.argv[2], 'r') as f:
|
||||
expected = f.read().strip()
|
||||
with open(sys.argv[3], 'r') as f:
|
||||
result = f.read().strip()
|
||||
|
||||
exp_nums = sorted(map(int, expected.split()))
|
||||
res_nums = sorted(map(int, result.split()))
|
||||
|
||||
if exp_nums == res_nums:
|
||||
print("AC")
|
||||
else:
|
||||
print("WA")
|
||||
except:
|
||||
print("WA")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"""
|
||||
|
||||
print("\n多解验证程序配置:")
|
||||
print(" 语言: python-2.5")
|
||||
print(" 代码长度:", len(multi_solution_code), "字符")
|
||||
print(" 功能: 允许多种正确答案")
|
||||
|
||||
|
||||
def demo_judge_usage_scenarios():
|
||||
"""演示不同判题方式的使用场景"""
|
||||
print("\n=== 判题方式使用场景 ===")
|
||||
|
||||
scenarios = [
|
||||
{
|
||||
"名称": "字符串精确比较",
|
||||
"适用": "输出结果固定的算法题",
|
||||
"示例": "计算1+1的结果",
|
||||
"验证方式": "InternalJudge(allowpe=False)"
|
||||
},
|
||||
{
|
||||
"名称": "字符串容错比较",
|
||||
"适用": "允许格式差异的题目",
|
||||
"示例": "输出多个数字,允许不同的分隔符",
|
||||
"验证方式": "InternalJudge(allowpe=True)"
|
||||
},
|
||||
{
|
||||
"名称": "文件大小+MD5校验",
|
||||
"适用": "二进制文件输出",
|
||||
"示例": "图像处理、音频处理等",
|
||||
"验证方式": "文件级别的校验"
|
||||
},
|
||||
{
|
||||
"名称": "自定义验证程序",
|
||||
"适用": "复杂的判题逻辑",
|
||||
"示例": "浮点数比较、多解题目、交互题等",
|
||||
"验证方式": "ExternalJudge"
|
||||
}
|
||||
]
|
||||
|
||||
for i, scenario in enumerate(scenarios, 1):
|
||||
print(f"{i}. {scenario['名称']}")
|
||||
print(f" 适用场景: {scenario['适用']}")
|
||||
print(f" 典型示例: {scenario['示例']}")
|
||||
print(f" 实现方式: {scenario['验证方式']}")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
"""主演示函数"""
|
||||
print("BitOJ 判题系统验证程序演示")
|
||||
print("=" * 50)
|
||||
|
||||
demo_internal_judge()
|
||||
demo_custom_judge_config()
|
||||
demo_judge_usage_scenarios()
|
||||
|
||||
print("演示完成!")
|
||||
print("\n更多示例文件:")
|
||||
print(" - float_validator.cpp: 浮点数比较验证程序")
|
||||
print(" - multi_solution_validator.py: 多解验证程序")
|
||||
print(" - StringFormatValidator.java: 格式验证程序")
|
||||
print(" - range_validator.cpp: 数值范围验证程序")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
58
examples/multi_solution_validator.py
Normal file
58
examples/multi_solution_validator.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
多解答案验证程序
|
||||
适用于有多个正确答案的题目,如排序、排列等
|
||||
使用: python multi_solution_validator.py input.txt expected.txt result.txt
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
print("WA")
|
||||
return
|
||||
|
||||
try:
|
||||
# 读取标准答案
|
||||
with open(sys.argv[2], 'r') as f:
|
||||
expected = f.read().strip()
|
||||
|
||||
# 读取用户输出
|
||||
with open(sys.argv[3], 'r') as f:
|
||||
result = f.read().strip()
|
||||
|
||||
# 去除多余的空白字符
|
||||
expected = re.sub(r'\s+', ' ', expected)
|
||||
result = re.sub(r'\s+', ' ', result)
|
||||
|
||||
# 解析数字
|
||||
try:
|
||||
expected_nums = list(map(int, expected.split()))
|
||||
result_nums = list(map(int, result.split()))
|
||||
except ValueError:
|
||||
print("WA")
|
||||
return
|
||||
|
||||
# 检查数量是否相同
|
||||
if len(expected_nums) != len(result_nums):
|
||||
print("WA")
|
||||
return
|
||||
|
||||
# 检查是否包含相同的元素(允许不同的排列)
|
||||
if sorted(expected_nums) != sorted(result_nums):
|
||||
print("WA")
|
||||
return
|
||||
|
||||
# 如果需要检查特定的排序顺序,可以添加额外的验证
|
||||
# 这里我们接受任何包含相同元素的排列
|
||||
print("AC")
|
||||
|
||||
except Exception:
|
||||
print("WA")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
68
examples/range_validator.cpp
Normal file
68
examples/range_validator.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// 数值范围验证程序
|
||||
// 验证输出的数值是否在合理范围内
|
||||
// 编译: g++ -o range_validator range_validator.cpp
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 4) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ifstream input(argv[1]); // 输入数据,包含范围信息
|
||||
ifstream expected(argv[2]); // 期望答案
|
||||
ifstream result(argv[3]); // 用户输出
|
||||
|
||||
if (!input.is_open() || !expected.is_open() || !result.is_open()) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 读取输入数据中的范围信息
|
||||
int n;
|
||||
input >> n;
|
||||
vector<int> arr(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
input >> arr[i];
|
||||
}
|
||||
|
||||
int min_val = *min_element(arr.begin(), arr.end());
|
||||
int max_val = *max_element(arr.begin(), arr.end());
|
||||
|
||||
// 读取期望答案
|
||||
int exp_result;
|
||||
if (!(expected >> exp_result)) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 读取用户答案
|
||||
int user_result;
|
||||
if (!(result >> user_result)) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 验证答案是否在合理范围内
|
||||
if (user_result < min_val || user_result > max_val) {
|
||||
cout << "WA" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 验证答案是否正确
|
||||
if (user_result == exp_result) {
|
||||
cout << "AC" << endl;
|
||||
} else {
|
||||
cout << "WA" << endl;
|
||||
}
|
||||
|
||||
input.close();
|
||||
expected.close();
|
||||
result.close();
|
||||
return 0;
|
||||
}
|
||||
103
examples/test_validator.py
Normal file
103
examples/test_validator.py
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
验证程序测试脚本
|
||||
用于测试自定义验证程序是否工作正常
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
def test_float_validator():
|
||||
"""测试浮点数验证程序"""
|
||||
print("测试浮点数验证程序...")
|
||||
|
||||
# 创建测试文件
|
||||
input_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
|
||||
input_file.write("1.0")
|
||||
input_file.close()
|
||||
|
||||
expected_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
|
||||
expected_file.write("3.14159")
|
||||
expected_file.close()
|
||||
|
||||
result_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
|
||||
result_file.write("3.14159001") # 轻微误差
|
||||
result_file.close()
|
||||
|
||||
try:
|
||||
# 编译验证程序
|
||||
cmd = ['g++', '-o', 'float_validator', 'float_validator.cpp']
|
||||
compile_result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if compile_result.returncode != 0:
|
||||
print("编译失败:", compile_result.stderr)
|
||||
return
|
||||
|
||||
# 运行验证程序
|
||||
cmd = ['./float_validator', input_file.name,
|
||||
expected_file.name, result_file.name]
|
||||
run_result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
print("验证结果:", run_result.stdout.strip())
|
||||
|
||||
# 清理
|
||||
if os.path.exists('float_validator'):
|
||||
os.remove('float_validator')
|
||||
|
||||
finally:
|
||||
# 清理临时文件
|
||||
for f in [input_file, expected_file, result_file]:
|
||||
if os.path.exists(f.name):
|
||||
os.unlink(f.name)
|
||||
|
||||
|
||||
def test_multi_solution_validator():
|
||||
"""测试多解验证程序"""
|
||||
print("测试多解验证程序...")
|
||||
|
||||
# 创建测试文件
|
||||
input_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
|
||||
input_file.write("")
|
||||
input_file.close()
|
||||
|
||||
expected_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
|
||||
expected_file.write("1 2 3 4 5")
|
||||
expected_file.close()
|
||||
|
||||
result_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
|
||||
result_file.write("5 4 3 2 1") # 相同元素,不同顺序
|
||||
result_file.close()
|
||||
|
||||
try:
|
||||
# 运行验证程序
|
||||
cmd = ['python3', 'multi_solution_validator.py',
|
||||
input_file.name, expected_file.name, result_file.name]
|
||||
run_result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
print("验证结果:", run_result.stdout.strip())
|
||||
|
||||
finally:
|
||||
# 清理临时文件
|
||||
for f in [input_file, expected_file, result_file]:
|
||||
if os.path.exists(f.name):
|
||||
os.unlink(f.name)
|
||||
|
||||
|
||||
def main():
|
||||
"""主测试函数"""
|
||||
print("=== 自定义验证程序测试 ===")
|
||||
|
||||
# 切换到示例目录
|
||||
os.chdir('/Users/liushuming/Desktop/bitoj/examples')
|
||||
|
||||
test_float_validator()
|
||||
print()
|
||||
test_multi_solution_validator()
|
||||
|
||||
print("\n测试完成!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user