ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NOI Linux 2.0 下用 Wine 部署 Arbiter 评测环境

NOI Linux 2.0 下用 Wine 部署 Arbiter 评测环境 1. 先把两个主角的角色分清楚1.1 NOI Linux 2.0 这套环境究竟是什么很多刚接触竞赛的同学会把 NOI Linux 2.0 当成一个软件其实它是一整套打包好的操作系统镜像。底层选的是 Ubuntu 20.04 LTS内核版本常年停在 5.4 系列好处是足够稳坏处是很多新硬件驱动偏老笔记本装实体机偶尔会卡在网卡或无线驱动上。它预装的工具链以竞赛需求为准g 9.3.0、gcc、gdb、Free Pascal 3.0.4、Python 3.8编辑器给了 vim、emacs、gedit、Code::Blocks、Geany 这几样评测时用的时间限制、内存限制也都是按这套环境标定的。默认账户名一般为noilinux登录后sudo是免密的桌面上还有个终端快捷方式日常操作基本都在终端里完成。这套环境的核心价值不在于好用而在于一致。你在自己机器上跑出来的程序和考场上跑出来的程序编译参数、库版本、时间测量方式都一样成绩才具备可比性。所以哪怕是平时练题只要想认真模拟一次比赛就应该在这套环境里跑而不是在 Windows 上用 Dev-C 随便点一下运行。1.2 Arbiter 在评测链路里站的位置Arbiter 是 Windows 平台上的一个图形化评测程序用 .NET 写的界面是典型的 WinForms 风格。它干的事情说白了就四件管理选手名单、管理试题数据、批量编译并运行选手源码、按规则比对输出并生成成绩单。和它同类的还有 Lemon、Cena功能上大同小异区别主要在比较器的可扩展性和界面习惯上。需要提前说清楚的一点是Arbiter 本身是 Windows 程序它并不属于NOI Linux。我们这篇要做的是在 NOI Linux 2.0 里把 Arbiter 跑起来也就是用 Wine 当成 Windows 运行层再给 Arbiter 配一套能用的 Windows 版编译器。这条路能走通但中间有几个必须提前知道的坑后面会逐个讲透。提示如果你只是想在 NOI Linux 里验证自己写的程序对不对其实不需要 Arbiter直接g -O2 -o a a.cpp再手动对拍就够了速度还更快。Arbiter 真正的价值在于批量比如一个班几十个选手、四五道题、几百个测试点手工跑会崩溃这时候它才值得折腾。1.3 什么场景下适合走这套组合我给三类人推荐这套方案。第一类是学校的信息学教练或者集训队组织者需要收上来一批选手的源码然后一次性跑完出成绩单这种情况 Arbiter 的选手管理功能能省下大量时间。第二类是想完整模拟一场比赛流程的自学者包括赛前怎么整理数据、赛中怎么收源码、赛后怎么出成绩这套流程走一遍会理解很多平时注意不到的细节。第三类是赛事运维相关的从业者需要在 Linux 服务器上做评测环境的一致性验证。反过来如果只是刷一两道题或者只是想知道我这题能过几个点那完全不必要。搞清楚适用边界比盲目上手更重要这也是我在多年使用过程中最想对新手说的一句话。2. 环境搭建把 Arbiter 在 NOI Linux 2.0 里跑起来2.1 先把 NOI Linux 2.0 装好并做基础自检安装方式有两种实体机和虚拟机我都试过作为日常练题我推荐虚拟机因为折腾坏了直接回滚快照不用重装系统。虚拟机里内存建议给到 4GB 以上CPU 至少两核硬盘 30GB 起。安装过程本身很标准一路下一步即可唯一要注意的是语言和键盘布局选中文环境能省掉后面打字的麻烦。装完之后别急着装 Arbiter先花三分钟自检确认基础工具链是正常的g --version # 确认 g 9.3.0 存在 free -h # 确认可用内存 ulimit -a # 看栈空间、文件句柄限制 df -h # 确认根分区剩余空间这四条命令看起来简单却能提前暴露 80% 的跑评测跑不起来的问题。比如ulimit -s如果是 8192就意味着选手程序在深度递归时更容易爆栈和考场的标准环境不一致再比如根分区只剩 2GB评测跑一半写日志写满磁盘成绩直接作废。这些都不是危言耸听我本人就踩过一次磁盘写满导致结果文件残缺的坑排查了整整一个晚上。自检完之后建议先把系统更新一遍sudo apt update sudo apt upgrade -y。不要担心版本升级破坏环境一致性NOI Linux 2.0 的源里 g 就是 9.3.0不会跳到 10 或者 11这一点是安全的。2.2 用 Wine 把 Windows 运行层架起来Wine 是 Linux 上运行 Windows 程序的一种兼容层它把 Windows 的系统调用翻译成 Linux 的所以不需要装完整的 Windows 系统。Ubuntu 20.04 的官方源里就有 wine版本是 5.0 系列够用了。安装步骤sudo dpkg --add-architecture i386 sudo apt update sudo apt install -y wine64 wine32 winetricks第一行是开启 32 位架构支持有些老版本的 .NET 程序是 32 位的不装这个会直接报不是有效的 Win32 程序。装完之后用wine --version确认一下能看到版本号就说明通了。接下来这一步很多人会忽略但非常关键中文字体。Wine 默认环境下中文会显示成一堆方块或者乱码Arbiter 界面上选手名字全是问号根本没法看。解决办法有两个一个是sudo apt install fonts-wine另一个是把系统里的中文字体拷进 Wine 的字体目录cp /usr/share/fonts/truetype/*/NotoSansCJK*.ttc ~/.wine/drive_c/windows/Fonts/如果系统里没有 Noto 系列中文字体先sudo apt install fonts-noto-cjk装上再拷。拷完之后最好重启一次 Wine 前缀wineserver -k让字体缓存生效。注意不要在这个环节尝试联网下载各种增强包很多第三方脚本会改动系统级的库把你原本干净的 NOI Linux 环境搞乱后面再想复现考场环境就难了。能用系统源解决的就不要走别的路。2.3 给 Wine 配一套 Windows 版编译器这是整条路线里最容易翻车的地方必须讲透原理。Arbiter 在评测时会去调用编译器默认配置一般是g。在 Windows 上它会去 PATH 里找一个叫g.exe的东西。但在 Linux 里我们的g是 ELF 格式的可执行文件Wine 是没法直接运行 ELF 的它只会去找 PE 格式的.exe。结果就是点下评测按钮界面转两圈然后一片编译错误或者干脆报找不到文件。解法只有一个给 Wine 的 C 盘里放一套 Windows 版的 MinGW-w64 编译器。具体做法是在一台有网络的环境里提前准备好 mingw-w64 的压缩包选i686或x86_64都行解压到 Wine 的 C 盘目录下mkdir -p ~/.wine/drive_c/mingw64 # 假设已经拿到 mingw64 压缩包 tar -xf mingw-w64.tar.xz -C ~/.wine/drive_c/mingw64 --strip-components1然后验证一下 Wine 能不能找到它WINEPREFIX~/.wine wine C:\\mingw64\\bin\\g.exe --version能打出g (MinGW-W64 ...) 8.x.x之类的版本号就说明编译器通了。这一步过了后面的路才走得下去。这里有个绕不开的取舍需要说明用 MinGW 编译出来的是 Windows PE 程序跑在 Wine 里时间测量会比原生 Linux 慢一些判 TLE 的边界会漂。所以这套方案适合流程演练和结果正确性验证如果要做正式成绩认定还是建议在 Windows 环境里跑 Arbiter或者用 NOI Linux 原生的评测脚本重新标定时间限制。2.4 部署 Arbiter 并完成首次启动编译器通了之后把 Arbiter 的程序目录整个拷到 Wine 的 C 盘里比如~/.wine/drive_c/Arbiter/。不要放在 Linux 路径下直接双击运行那样路径里带Z:\前缀程序读取相对路径的配置文件时容易出问题。启动命令cd ~/.wine/drive_c/Arbiter WINEPREFIX~/.wine wine Arbiter.exe第一次启动可能会弹一个 .NET 运行时的提示。Arbiter 依赖 .NET FrameworkWine 里自带的 mono 有时能顶住有时不行。如果弹窗报缺少组件用winetricks dotnet48补一下这个过程比较慢中途可能弹好几个安装窗口按提示点就行别中途关掉。装完之后wineserver -k重启一次再启动 Arbiter。启动成功后主界面一般会分成几个区域上方是菜单和工具栏左侧或者上方有选手列表和试题列表右边是操作区。不同版本的 Arbiter 界面布局略有差别但核心操作就那几个后面按流程走。实操心得给 Arbiter 建一个专用的 Wine 前缀比如WINEPREFIX~/.arbiter-wine不要和系统默认前缀混用。这样万一某个前缀被折腾坏了直接删目录重建不影响其他 Wine 程序反之亦然。这个习惯能帮你省下大量重复折腾的时间。3. 目录结构与数据规范评测能不能跑八成就看这一步3.1 整场比赛的落地目录怎么摆Arbiter 对目录结构是有约定的摆错了它认不出来。我习惯用下面这种布局经过多次实践是最稳的~/contest/ ├── players.txt # 选手名单一行一个名字 ├── data/ # 所有试题的数据 │ ├── perm/ # 题目一 │ │ ├── 1.in │ │ ├── 1.out │ │ ├── 2.in │ │ └── 2.out │ └── game/ # 题目二 │ ├── 1.in │ └── 1.out ├── source/ # 选手源码一人一个文件夹 │ ├── zhangsan/ │ │ ├── perm.cpp │ │ └── game.cpp │ └── lisi/ │ ├── perm.cpp │ └── game.cpp └── result/ # 评测结果输出目录三块内容各司其职data放数据和标准答案source放选手提交result留给 Arbiter 写结果文件。注意source下面每个选手一个文件夹文件夹名必须和players.txt里的名字完全一致一个字符都不能差。中文名和英文名都能用但强烈建议全用英文或者全用中文不要混着来之前遇到过一个选手叫张三名单里写成zhangsan结果提交被判成未提交白忙活一场。另外要提醒的是Linux 的文件名区分大小写Perm.cpp和perm.cpp是两个文件。Windows 上不区分所以从 Windows 拷过来的文件在这里可能出问题。这一点在后面讲源码命名时还会再强调一次。3.2 单道题的数据文件怎么命名与准备数据文件的命名Arbiter 默认按编号配对的模式也就是1.in配1.out2.in配2.out依此类推。编号必须从 1 开始连续中间不能跳号跳了会导致后面的数据全部读不到。需要特别留意的是扩展名。有的评测系统习惯用.ans作为答案文件Arbiter 默认用的是.out。如果你的题目数据是从别处拿来的、用的是.ans要么在题目设置里手动改配对规则要么批量重命名cd data/perm for f in *.ans; do mv $f ${f%.ans}.out; done数据内容本身也有讲究。输入文件末尾不要留多余的空行虽然多数比较器会忽略行尾空白但有些严格模式会判错。输出文件的最后一个换行可以有这是标准做法。另外文件编码统一用无 BOM 的 UTF-8带 BOM 的文件在读取时会在开头多出三个不可见字节程序读进来的第一个数字就变成乱码了这是相当隐蔽的坑。数据本身的规模也要检查。有个简单的体检方法wc -l data/perm/*.in | tail -1 # 看总行数 du -sh data/perm/ # 看数据总大小如果单道题的数据超过 200MB评测过程会明显变慢写结果也会占空间可能要考虑精简。如果1.in是空的0 字节那基本可以确定数据整理出了问题赶紧回头检查。注意准备数据的时候最好随手拿一个明显正确的参考程序跑一遍确认它能拿到满分。这是最省事的自检方式比对着数据一个个看快得多。我见过太多次数据本身出错、最后判了一整场错案的情况。3.3 选手名单与源码文件的命名规则players.txt里的名字决定了源文件夹的查找路径。格式很简单一行一个不要有多余空格不要有空行编码同样是无 BOM 的 UTF-8。在 Linux 下可以用cat -A players.txt检查如果每行末尾显示的是$就对了如果显示^M$说明是从 Windows 拷过来的 CRLF 换行需要转换sed -i s/\r$// players.txt源码文件的命名规则是选手名文件夹 题目名文件。比如张三提交的perm题路径应该是source/zhangsan/perm.cpp。这里有两层匹配文件夹名要匹配名单文件名要匹配题目名两个都不对上Arbiter 就会认为该选手这题没交。关于扩展名.cpp是通用的。有些选手会写成.cc、.cxx虽然理论上 g 都认但 Arbiter 的文件扫描是按扩展名过滤的可能扫不到。最稳的办法是统一要求所有人用.cpp并在赛前说明里写清楚。还有一个隐蔽的坑文件名的大小写。如果题目在 Arbiter 里登记的名字是perm选手提交的文件叫Perm.cpp在 Linux 上就是找不到。这个错误在 Windows 上完全不会出现所以从 Windows 迁移过来的选手特别容易中招。解决办法是评测前写一个小脚本统一检查for d in source/*/; do for f in $d*.cpp; do base$(basename $f .cpp) name$(basename $d) echo $name - $base done done把输出和题目名单对一遍几分钟就能把这类问题清干净比起跑完整场再发现要划算太多。4. 界面实操从新建比赛到拿到成绩单4.1 新建比赛与批量导入选手启动 Arbiter 之后第一件事是新建一个比赛。菜单里找文件或者左上角的新建按钮选择刚才规划的~/contest作为工作目录。程序会在里面生成自己的配置文件和结果目录原来的data和source一般不会被破坏但保险起见操作前先备份一份。接下来是导入选手。Arbiter 支持两种方式一种是在选手列表里右键逐个添加另一种是从文件导入。选手多的时候当然用后者把players.txt路径填进去确认编码选 UTF-8点导入。导入完成后一定要扫一眼列表看看人名有没有变成问号或者乱码。如果出现了说明编码没对上回退重来不要硬着头皮往下走否则后面编译时找不到文件夹报的错会让你一脸懵。导入之后还有一个细节确认每个选手的源码目录被正确关联。有的版本会默认把source目录作为源码根有的版本需要你手动指定。这个设置一般藏在选手列表的属性里翻一下就有了。实操心得选手名单导入之后建议先拿一两个选手做试跑确认路径关联没问题再批量放开。整场跑一次可能要十几分钟如果路径错了这十几分钟就是白等。小步验证是评测运维里最值钱的习惯。4.2 添加试题并做一次数据体检试题的添加类似在试题列表里选择添加试题输入题目名称必须和源文件里用的名字完全一致然后指定数据目录。指定完之后Arbiter 会扫描目录里的.in和.out配对界面上一般会显示找到 N 个测试点。这里有两个地方要盯紧。第一是测试点数量对不对说好 10 个点结果只识别出 8 个说明命名或者编号有问题。第二是看一眼每个测试点的输入输出是不是都非空有些版本会在列表里显示文件大小为空的一眼就能看出来。题目属性里还有几个必填项时间限制、内存限制、比较方式、源代码文件名规则。时间限制填毫秒数比如 1000 表示 1 秒。内存限制填 MB。比较方式默认是忽略行尾空白的文本比较如果题目有特殊要求比如浮点数允许误差、或者多解需要换成自定义比较器这部分留到第 5 节讲。源代码文件名规则这一项容易被忽略。有的版本默认只找和题目同名的.cpp有的版本允许配置。如果你的题目叫perm但选手提交的是permutation.cpp那就要么改规则要么让选手改名。统一要求文件名等于题目名是最省事的做法。全部配置好之后界面上应该能看到一个矩阵行是选手列是题目格子里显示待评测或者类似状态。到这一步环境、数据、名单、源码四样东西就都挂上了。4.3 跑评测与读懂结果点击开始评测之前最后确认三件事编译器的路径配置对不对、结果输出目录有没有写权限、磁盘空间够不够。这三条任何一条出问题都会让评测跑到一半崩掉。评测过程中界面会实时刷新每个测试点的状态。常见状态有这么几种正确通常显示绿色或者AC、答案错误WA、超出时间TLE、超出内存MLE、运行时错误RE包括段错误、除零、异常退出、编译错误CE。评测结束后Arbiter 会在结果目录里生成成绩文件常见格式是.csv或者.txt内容是按选手和题目展开的分数表有的还会附上每个测试点的耗时和内存占用。拿到成绩后第一件事不是发出去而是抽查几个结果从对应的源文件里翻出代码人工看一眼逻辑确认分数对得上。这个动作叫复查是评测流程里不能省的一步。结果的解读也有讲究。如果一个选手所有测试点都是RE大概率是读文件路径写错了比如用了绝对路径如果所有点都是TLE可能是死循环也可能是文件读入方式太慢用了cin没关同步如果前几个点对、后几个点错那通常是算法问题小数据能过大数据挂掉这种情况不用怀疑评测系统直接看算法。提示评测跑完之后别急着删中间产物。编译日志、运行日志这些东西是排查争议的唯一依据。有选手对成绩有疑问时能拿出编译日志和运行日志沟通成本会低很多。建议把这些文件按日期归档保留至少一个赛季。5. 评测规则进阶比较器与特殊题型5.1 默认比较器到底在做什么Arbiter 的默认比较逻辑说起来就三条规则逐行比较、忽略行尾空白包括行尾空格、制表符和\r、忽略文件末尾多余的空行。符合这三条的差异不算错超出这三条的差异一律判WA。这三条规则看起来宽松实际上能挡掉很多问题。比如选手用 Windows 写的输出带了\r\nLinux 判题程序按\n分割如果不忽略\r每一行都会多一个字符全盘WA。再比如有些程序的输出末尾多打一个空行严格比较也会挂。所以这个默认规则是保护选手的。但反过来如果题目要求精确输出比如输出一个字符串要求逐字符一致那默认比较器的宽松规则可能就不合适了。这时候需要换比较方式。5.2 特殊题型怎么接评测里最容易出问题的是三类题浮点数输出、多解题、交互题。浮点数题标准答案是3.1415926535选手输出3.1415926536默认比较器会判错。这时候需要换成带误差的浮点比较通常做法是用自定义比较器按相对误差或绝对误差判断。误差值怎么定要看题目要求常见的是1e-6或者1e-9。多解题是指答案不唯一只要满足题目条件都算对。比如图论里要求输出任意一条合法路径。这类题默认比较器无能为力必须写自定义校验程序。思路是读入测试点输入、选手输出、标准输出然后自己写逻辑验证选手输出的合法性返回正确或者错误。交互题的复杂度更高选手程序需要在运行过程中和评测程序通信。Arbiter 支持这类题但配置起来相对繁琐需要指定交互程序还要处理管道通信。这类题在平时练习中很少用到如果遇到建议先把通信机制想清楚再动手配。需要说明的是Arbiter 的自定义比较器用的是 .NET 脚本语法接近 C#对于只会 C 的同学来说有额外的学习成本。如果题目本身不复杂一个更轻量的替代思路是先用 Arbiter 跑一遍默认比较把明显错的筛掉剩下的模糊情况用自己写的 Python 脚本手动复查。这个两步走的策略在数据量不大的时候非常好用。6. 踩坑实录与常见问题速查6.1 编码、换行、权限这三大经典坑编码问题排第一。从 Windows 拷过来的文本文件默认可能是 GBK 编码或者带 BOM 的 UTF-8这两种在 Linux 下都会出问题。检查方法file -i players.txt data/perm/1.in输出里如果带charsetiso-8859-1或者charsetunknown-8bit基本就是 GBK需要转成 UTF-8iconv -f GBK -t UTF-8 players.txt -o players_utf8.txt带 BOM 的可以这样去掉sed -i 1s/^\xEF\xBB\xBF// players.txt换行符问题排第二前面提过CRLF 转 LF 用sed -i s/\r$//就行批量处理可以加find。权限问题排第三。从 U 盘或者共享文件夹拷进来的文件权限位可能全乱导致 Arbiter 读不到或者运行不了。目录需要可读可执行文件需要可读。一条命令统一修chmod -R urwX,gorX ~/contestX大写是有讲究的它表示目录一定加执行位普通文件只有在本来就是可执行的时候才加不会误伤数据文件。6.2 评测结果异常时的排查思路结果异常分几种情况处理方式不一样。全盘未提交先查选手名单和源文件名的匹配关系多半是名字对不上。全盘编译错误先看编译器路径配置再手动拿一份源码跑一次g确认编译器本身没问题。全盘超时先确认时间限制单位填的是毫秒还是秒这是最常见的低级错误。部分测试点运行时错误看是不是内存越界或者数据规模问题随机数据的题目尤其容易在前面几个点正常、后面崩。还有一类异常是分数看着不对但没明显报错。这种情况优先怀疑数据本身拿一份公认正确的参考程序跑一遍看它能不能满分。如果参考程序都拿不到满分那就是数据或者评测配置的问题跟选手无关。排查的时候有个技巧永远从最小的可复现单元开始。不要一上来就跑整场先跑单个选手单道题缩小范围很快就能定位。整场跑一遍十几分钟单题跑一下几秒钟差距是几十倍。6.3 常见问题速查表现象可能原因处理办法界面中文显示为方块Wine 未配置中文字体拷字体到 Wine 的 Fonts 目录并重启前缀点评测后立即全盘编译错误Wine 里找不到 Windows 版 g配置 MinGW-w64 并在 Arbiter 中指定路径全部选手都显示未提交名单与源文件夹名不匹配逐字比对注意大小写和空格只识别出部分测试点编号不连续或扩展名不一致重新编号统一改为.in/.out每行输出都被判错换行符是 CRLF批量转换换行符输入数据开头异常字符文件带 BOM去掉 BOM结果目录写不进去权限不足用chmod修目录权限评测速度明显偏慢Wine 时间测量开销适当放宽时间限制仅用于流程演练实操心得把上面这张表打印出来贴在显示器边上能省下不少查资料的时间。我带了几年集训队发现新手出问题基本都逃不出这几条重复讲不如让他们自己对着表排查动手一次比听十遍记得牢。7. 一点个人体会和后续可以延伸的方向整套流程我自己反复走过很多次从最早的实体机装 NOI Linux到后来虚拟机快照一把梭再到用 Wine 把 Arbiter 拉起来跑每一次踩的坑都不太一样但规律是相通的问题几乎从不出在核心逻辑上而是出在环境、编码、路径这些看起来最不重要的地方。所以我现在组织任何一次评测都会先花十分钟做那四项基础自检再花五分钟拿一个选手试跑宁可在开头慢一点也不要在结尾返工。还有个经验想分享Wine 下跑 Arbiter 的时间测量确实不如原生准确所以对于时间限制卡得很紧的题目我一般会按 1.5 倍左右放宽限制来做流程验证等确认逻辑都对了再到标准环境里重新标定一次。这个做法不一定是最严谨的但确实是省事又不容易出错的折中方案。如果你已经把这套流程跑顺了下一步可以往两个方向延伸。一是把选手源码的收集过程自动化写个小脚本从共享目录里批量归档、重命名、建目录省掉手工整理的一两个小时。二是把评测结果接进表格软件自动算排名、生成奖状模板一次组织几十人的比赛就不用手忙脚乱了。这两个方向都不复杂值得一试。
返回列表