В процесс проверки знаний заложена простая идея, основанная на анализе исходных текстов программ для получения результатов. Здесь пользователь является своего рода интерпретатором, получая на вход, заранее заготовленный участок исходного текста программы, а на выходе предоставляя данные, которые анализируемая программа выдаст после выполнения. В зависимости от сложности и типа задания исходные данные могут быть как заранее заготовленными наборами тестов, так и полностью или параметрически сгенерированными. Таким образом, разработчику открывается широкий диапазон гибких возможностей для создания заданий различного уровня сложности и структуры, что позволяет с большей вероятностью устанавливать истинность знаний, при успешном прохождении заданий пользователем.
Отправной точкой в создании любого задания является разработка тестируемой программы. Пользователю предстоит задача интерпретации предоставленного ему участка кода или целой программы с целью понять и проэмулировать ход её выполнения.
В качестве среды для тестирования низкоуровневых программ, написанных на ассемблере, для различных платформ и процессоров был выбран WInter. Это программное средство позволяет с минимальными затратами времени и системных ресурсов позволяет проводить безопасное тестирование в режиме эмуляции различных процессоров и микросхем написанные для них программы. Теперь, разработчику открываются возможности по созданию заданий для проверки знаний не только для процессоров семейства Intel, но и для процессоров цифровой обработки сигналов и прочих устройств, в полном объеме обучение работе с которыми затруднительно организовать в обычных условиях и с имевшимися на то время средствами.
Начальным этапом в разработке данного типа задач является создание программы на языке ассемблера, для какого либо процессора в среде WInter.
Пример программы:
jmp begin
a dw 4
b dw 0fffeh
res dw 0
begin:
cmp ax, 0
jge plus
mov ax, a
idiv b
mov cx, ax
add cx, ax
mov res, cx
plus:
mov ax, a
imul b
mov bx, b
sub bx, ax
mov res, bx
halt:
jmp halt ;$e
Необходимым условием являются служебные метки begin и halt, использование их в своей программе не рекомендовано для корректного отображения задания пользователю. Убедившись в том, что программа работает корректно, необходимо её сохранить в файле с расширением соответствующего процессору, для которого разрабатывалась программа. Для процессора Intel 8086, например, оно будет .i86, для Intel 8051 – .i51, и т.д. в соответствии с WInter.
После успешного создания и внедрения подобной системы контроля и формирования знаний для низкоуровневых программ было решено двигаться дальше в этом направлении. Так появилось расширение данного продукта, которое обеспечивает схожую гибкость и функциональность для языков высокого уровня, которые могут быть откомпилированы в выполняемый консольный модуль.
В первую очередь необходимо написать некоторую программу на произвольном языке программирования, которые удовлетворяет вышеуказанным условиям.
Необходимо, чтобы все входные и выходные переменные вводились и выводились на консоль по одной в строке. Вместо имен выходных переменных можно использовать также и функции.
Допускается сокрытие реализации от пользователя путем использования псевдо-тегов |starts| и |ends|, которые помещаются в отдельной закомментированной строке. Все строки находящиеся между этими строками будут скрыты от пользователя при прохождении задания. Это позволяет значительно расширить возможности по представлению задания пользователю, позволяя скрывать от его глаз служебный код или участки, только косвенно относящиеся к заданию.
Пример программы для компилятора Borland Delphi:
{$APPTYPE CONSOLE}
Program Example;
Var
q, w, e :
Integer;
s : String;
begin
// |starts|
// Эта строка будут скрыта от пользователя
// |ends|
readln(w);
readln(e);
for q:=1 to w
do s:=s + chr(ord(‘0’) + e);
writeln(s);
end.
После создания исходного текста задания необходимо его откомпилировать в .exe модуль.
Следующим этапом в разработке задания является создание
файла описания настроек задания – task.var.
task.var – текстовый файл, которые хранит в себе все необходимые параметры и настройки задания в виде ключей. Ключ – строка файла вида:
имя_ключа=значение
Файл заканчивается пустой строкой. Порядок ключей значения не имеет, некоторые из них могут быть опущены или вообще не иметь собственного значения. Такая система хранения параметров позволяет поддерживать более старые задания при постоянном совершенствовании новых, то есть сохраняется совместимость на всем пути развития.
Таблица 1. Ключи описания.
Ключ |
Описание |
||||||||||||
type |
Тип задания. а – для заданий разработанных на
ассемблере в среде WInter. b – для заданий разработанных на языках разного уровня, исходный текст программы которых может быть скомпилирован в исполняемый консольный модуль. Для заданий данного типа значение этого параметра выставляется в а. |
||||||||||||
varin |
Количество входных переменных, для которых имеется описание в файле настроек. |
||||||||||||
varout |
Количество переменных, значения которых будут являться ответом пользователя на задание. На данном этапе разработке ограничено единицей. |
||||||||||||
tests |
Натуральное число, определяющее количество тестов, которые предстоит пройти пользователю. |
||||||||||||
in |
Входные переменные и их тип.
Переменные перечисляются через пробел, формат описания: var1 type1[lo,hi]
var2 type2[lo,hi] … varn[lo,hi] typen vark – имя переменной typek – тип переменной: одно из допустимых значений DB(1 байт), DW(2 байта), ST(строковая переменная) [lo,hi] – для числовых типов нижняя и верхняя граница диапазона генерирования случайных значений, данный параметр может быть опущен. [delimeter,lo,hi,symbols] – для строковых типов(описание обязательно): delimeter – символ окончания строки lo,hi – нижняя и верхняя граница диапазона генерирования длины строки symbols – непустое множество символов, определяющих диапазоны генерирования:
Также допускается
задание набора предустановленных значений для входных переменных, то есть
значения переменных генерироваться не будут, они будут случайным образом
выбираться из набора представленного разработчиком задания. Для этого вместо
описания диапазонов генерации переменных [...]
следует указать в круглых скобках через запятую желаемые значения, например: a db(0,2,4,6,8,10) -
генерируемые значения переменной a
будут выбраны из набора (0,2,4,6,8,10). Аналогично задаются предустановленные
наборы значений и для других типов переменных. |
||||||||||||
out |
Выходные переменные и их тип, описание аналогично входным переменным. |
Примечание: имена входных и выходных переменных совпадать не могут.
Пример файла настроек task.var:
type=a
varin=4
tests=4
varout=1
in=a db[1,10] str st[#,10,14,eRD] b dw(3414,6574,123,0,563,7543,345) u db
out=res db
Как видно из вышеописанного примера, здесь описано задание типа «а», с четырьмя тестами, одной выходной переменной размером в один байт, а также следующими выходными переменными:
· a - переменная размером в один байт, значение которой будет сгенерированно в диапазоне от 1 до 10.
· str - строка, длина которой варьируется от 10 до 14 символов, состоящая из латиницы нижнего регистра, кириллицы верхнего регистра и цифр. Символ завершения строки #.
· b - двух байтовая переменная, которая принимает значения из набора (3414,6574,123,0,563,7543,345).
· u - переменная размером в байт, значение которой после генерации будет находиться во всем допустимом для размера переменной диапазоне.
Весь путь установки задания подробно описан в документации системы Дистанционного Обучения DLDOC. Отметим лишь особенности, относящиеся непосредственно к данному типу задания.
Теперь, когда созданы исходный текст программы и файл настроек, необходимо дополнить архив задания некоторыми служебными файлами.
Файлы условий: taskrus.html и tasking.html. Для заданий такого типа они не имеют никакой прикладной ценности, их следует создать пустыми для совместимости с тестирующей системой дистанционного обучения.
Следующий файл task.xml – глобальное описание задания в системе, где:
· name – имя задания на русском языке
· ename – имя задания на английском языке
· author – автор задания
· cost – количество баллов за успешно пройденное задание
· type – идентификатор задания, который в данном случае равен 17
Пример файла task.xml:
<task
name="Наибольший Общий Делитель."
ename="Greatest
Common Divisor"
author="Медведский Виталий"
cost="10"
type="17"/>
Финальным этапом создания задания, пригодного для установки, является глобальное описание конфигураций задания в файле task.cfg. Особое внимание стоит отметить на следующих ключах конфигурации:
· CHECKER – путь к чекеру, осуществляющему тестирование результатов. На данном этапе разработке имеется уже 2 различных модуля для тестирования ответов для соответствующих типов заданий: wtcheck.cmd для заданий тестирумых в среде разработки WInter и wtcheck2.cmd для языков высокого уровня. Следовательно для заданий типа «а» эта строка будет выглядеть так:
CHECKER =
'D:\Delta\CHECKERS\WChecker\wtcheck.cmd task.i86 $MAXPOINT$ $SOLUTION$ >nul'
где task.i86 – имя файла с исходным текстом программы, а для заданий типа «b» иначе:
CHECKER =
'D:\Delta\CHECKERS\WChecker\wtcheck2.cmd test.dpr $MAXPOINT$ $SOLUTION$
>nul'
где test.dpr – соответственно имя аналогичного файла.
CHECKFILES
– маска файлов подлежащих для копирования в соответствующий каталог временного
тестирования. Поскольку все файлы понадобятся при тестировании, поэтому этот
параметр устанавливается в *.*.
Пример файла для заданий типа «b»:
TYPE =
USERS
CHECKER =
'D:\Delta\CHECKERS\WChecker\wtcheck2.cmd program.cpp $MAXPOINT$ $SOLUTION$
>nul'
CHECKFILES
= {*.*}
CHECKSUBJECT
= FILE
EXTTYPE = 'Пользовательская'