Примеры задания ограничений
Задание ограничений — уникальная возможность TRIK Studio, предназначенная для автоматизированной проверки заданий. Этот инструмент имеет множество применен ий:
- самопроверка домашнего задания учениками,
- ускорение процесса проверки заданий учителем,
- проведение онлайн-соревнований с автоматической проверкой решений участников.
Язык ограничений в TRIK Studio — очень мощный инструмент, который позволяет проверить практически все аспекты выполнения программы и поведения робота, и может показаться сложным на первый взгляд. Однако не стоит расстраиваться — в данной статье мы разберем процесс создания задач с ограничениями, начиная от простых ограничений, которые покрывают большинство задач, и заканчивая сложными ограничениями, что может быть полезно для энтузиастов.
Для тех, кто только начинает знакомиться с языком создания ограничений в TRIK Studio, мы предлагаем начать с простых ограничений на время исполнения программы, зону «Старт» и «Финиш». Для большинства робототехнических задач этого более чем достаточно.
1. Определите для задачи и модели мира следующие условия и параметры.
- Оптимальное время выполнения программы. Если программа будет выполняться больше этого времени, проверяющая система выдаст ошибку «Программа работала слишком долго».
- Зона «Старт». Зона, из которой робот должен начать выполнение программы, иначе проверяющая система выдаст ошибку.
- Зона «Финиш». Зона, в которую должен приехать робот по окончании выполнения программы, иначе проверяющая система выдаст ошибку.
2. Отредактируйте в xml-файле мира значение тега
<regions>
, добавив туда зоны «Старт» и «Финиш». Меняя значения x
, y
, width
и height
, вы можете редактировать местоположение и размер зон. Чтобы проверить положение зоны, загрузите файл в виртуальную модель мира и проверьте расположение зон. После этого, при необходимости, вы можете сделать зоны «Старт» и «Финиш» невидимыми, выставив атрибут visible="false"
. <regions>
<region type="rectangle" visible="true" color="blue" text="Старт" x="-450" y="-400" width="150" height="150" id="start_zone"/>
<region type="rectangle" visible="true" color="green" text="Финиш" x="450" y="-400" width="150" height="150" id="finish_zone"/>
</regions>
3. Скопируйте в xml-файл мира, для которого необходимы ограничения, шаблон блока
<constraints>
, приведенный ниже.<constraints>
<!-- Ограничение на время выполнения программы -->
<timelimit value="300000"/>
<!-- Ограничение, которое проверится один раз перед началом программы -->
<constraint checkOnce="true" failMessage="Робот должен находиться в зоне старта перед запуском!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
<!-- Событие, которое проверяет, что робот находится в зоне финиша по окончании выполнения программы -->
<event id="finish checker" settedUpInitially="true">
<condition>
<inside objectId="robot1" regionId="finish_zone"/>
</condition>
<trigger>
<success/>
</trigger>
</event>
</constraints>
4. Заполните тег
<timelimit value="300000">
. Укажите оптимальное, на ваш взгляд, время выполнения программы. Обратите внимание, что время указывается в миллисекундах, т.е. 300000 мс = 5 минут, 120000 мс = 2 минуты и т.п.5. Задание с ограничениями готово! Теперь вы умеете проверять стартовое и конечное положение робота, время исполнения программы.
Теперь давайте разберем применение данной техники для различных задач и рассмотрим варианты более детальных проверок.
Задача прохождения лабиринта. Стартовав в синем квадрате, необходимо проехать до зоны финиша. Для того чтобы проверять, что пользователь действительно п роходит лабиринт, а не объезжает его, задано пространственное ограничение.

Синий квадрат - зона старта, красный квадрат - зона финиша, черный прямоугольник - поле, которое нельзя покидать роботу
Ниже приведен полный код для проверки этой задачи.
<?xml version='1.0' encoding='utf-8'?>
<root>
<!-- Описание мира -->
<world>
<trace/>
<walls>
<wall end="450:-400" id="" begin="-300:-400"/>
<wall end="-300:200" id="" begin="-300:-250"/>
<wall end="450:200" id="" begin="-300:200"/>
<wall end="450:200" id="" begin="450:-250"/>
<wall end="150:-100" id="" begin="150:-400"/>
<wall end="300:50" id="" begin="300:-250"/>
<wall end="450:-250" id="" begin="300:-250"/>
<wall end="-150:-250" id="" begin="-300:-250"/>
<wall end="0:-250" id="" begin="-150:-250"/>
<wall end="-150:-100" id="" begin="150:-100"/>
<wall end="150:50" id="" begin="-150:50"/>
<wall end="150:200" id="" begin="150:50"/>
</walls>
<colorFields/>
<!-- Задание регионов (зон) на карте мира -->
<regions>
<region type="rectangle" visible="true" color="blue" text="Старт" x="-450" y="-400" width="150" height="150" id="start_zone"/>
<region type="rectangle" visible="true" color="green" text="Финиш" x="450" y="-400" width="150" height="150" id="finish_zone"/>
<region id="warzone" type="rectangle" filled="false" color="black" visible="true" x="-450" y="-400" width="1050" height="600"/>
</regions>
</world>
<robots>
<robot id="trikKitRobot" direction="0" position="-401:-351">
<sensors>
<sensor type="kitBase::robotModel::robotParts::Motor" direction="0" port="M4###output###JM4$$$D$$$4###" position="75:25"/>
<sensor type="kitBase::robotModel::robotParts::Motor" direction="0" port="M3###output###JM3$$$C$$$3###" position="75:25"/>
<sensor type="kitBase::robotModel::robotParts::Motor" direction="0" port="M2###output###JM2$$$B$$$2###" position="75:25"/>
<sensor type="kitBase::robotModel::robotParts::Motor" direction="0" port="M1###output###JM1$$$A$$$1###" position="75:25"/>
</sensors>
<startPosition direction="0" x="-376" y="-326"/>
</robot>
</robots>
<!-- Описание ограничений -->
<constraints>
<!-- Ограничение на время 5 минут -->
<timelimit value="300000"/>
<constraint checkOnce="true" failMessage="Робот должен находиться в зоне старта перед запуском!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
<!-- Ограничение, проверяющее, что робот находится в допустимой зоне -->
<constraint failMessage="Робот попытался объехать лабиринт!">
<inside objectId="robot1" regionId="warzone"/>
</constraint>
<!-- Событие, которое проверяет, что робот находится в зоне финиша по окончании выполнения программы -->
<event id="finish checker" settedUpInitially="true">
<condition>
<inside objectId="robot1" regionId="finish_zone"/>
</condition>
<trigger>
<success/>
</trigger>
</event>
</constraints>
</root>
Рассмотрим подробнее, как происходит проверка.
1. Зададим ограничение на время прохождения задания. Это ограничение является обязательным. Время указывается в миллисекундах.
<timelimit value="300000"/>
2. Зададим ограничение на зону, в которой должен находиться робот перед началом программы. По условию задачи это должен быть синий квадрат - зона старта. Это ограничение будет проверяться один раз, в начале программы, так как атрибут
checkOnce
равен true
. С помощью тега <inside> мы задаем пространственное ограничение. Он имеет два атрибута. В первом (objectId) указываем id объекта, местоположение которого мы хотим проверить, в нашем случае это робот. Во втором (regionId) указываем id региона, в котором должен находиться наш объект.
В случае, если условие, которое описано в
<inside>
не выполняется, то программа будет завершена с ошибкой. Для этого у тега <constraint> есть атрибут failMessage, который позволяет задать текст сообщения об ошибке.<constraint checkOnce="true" failMessage="Робот должен находиться в зоне старта перед запуском!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
3. Для того, чтобы проверять, что пользователь действительно проходит лабиринт, а не объезжает его, давайте зададим еще одно пространственное ограничение.
Отредактируем в xml-файле мира значение тега
<regions>
, добавив туда регион с id = “warzone”
.<region id="warzone" type="rectangle" filled="false" color="black" visible="true" x="-450" y="-400" width="1050" height="600"/>
Это ограничение будет проверяться во все время выполнения программы. В теге <inside> укажем id объекта и id региона, в нашем случае это робот и черный прямоугольник, ограничивающий лабиринт.
В случа е, если робот в любой момент времени окажется вне указанного региона, программа будет завершена с ошибкой, текст которой указан в атрибуте
failMessage
.<constraint failMessage="Робот попытался объехать лабиринт!">
<inside objectId="robot1" regionId="warzone"/>
</constraint>
4. Теперь нам о сталось проверить, что робот доезжает до зоны финиша.
Для этого давайте создадим событие, которое будет проверять находится робот в нужной зоне или нет. Атрибут
settedUpInitially="true"
означает, что событие будет запущено (взведено) сразу при старте программы.В теге <condition> мы указываем какое именно условие необходимо проверить. В нашем случае условие аналогично описанным выше - мы проверяем, что робот находится в регионе с
id = "finish"
. В случае, если это условие выполнится, то пользователю будет показано сообщение об успешном выполнении программы. Для этого в теге <trigger> мы пишем дочерний тег <success/>.<event id="finish checker" settedUpInitially="true">
<!-- Условие -->
<condition>
<inside objectId="robot1" regionId="finish_zone"/>
</condition>
<!-- Триггер -->
<trigger>
<success/>
</trigger>
</event>
Задача проехать вперед и остановиться в зоне финиша.
Эта задача немного отличается от Примера 1 тем, что добавляется еще одно условие для успешного завершения программы - роботу необходимо не просто оказаться в зоне фин иша, но и остановиться.

Ниже приведен полный код для проверки этой задачи.
<?xml version='1.0' encoding='utf-8'?>
<root version="20190819">
<world>
<!-- Задание регионов (зон) на карте мира -->
<regions>
<region id="finish_zone" text="Finish" textX="0" width="200" height="150" color="green" x="320" y="-50" visible="true" type="rectangle" filled="true" textY="0"/>
<region id="start_zone" text="Start" textX="0" width="100" height="-100" color="#0000ff" x="-20" y="70" visible="true" type="rectangle" filled="true" textY="0"/>
<region type="rectangle" id="warzone" text="Поле, которое нельзя покидать" x="-20" y="-140" width="610" height="320" color="orange" visible="true"/>
</regions>
</world>
<robots>
<robot id="trikKitRobot" direction="0" position="0:0">
<sensors>
<sensor port="M3###output###М3###" direction="0" position="75:25" type="kitBase::robotModel::robotParts::Motor"/>
<sensor port="M4###output###М4###" direction="0" position="75:25" type="kitBase::robotModel::robotParts::Motor"/>
<sensor port="M1###output###М1###" direction="0" position="75:25" type="kitBase::robotModel::robotParts::Motor"/>
<sensor port="M2###output###М2###" direction="0" position="75:25" type="kitBase::robotModel::robotParts::Motor"/>
</sensors>
<startPosition id="{888338bf-3f53-44a4-ac0a-8aeea2d036b2}" y="25" direction="0" x="25"/>
<wheels left="M3###output###М3###" right="M4###output###М4###"/>
</robot>
</robots>
<settings realisticMotors="false" realisticSensors="false" realisticPhysics="false"/>
<!-- Задание ограничений -->
<constraints>
<!-- Ограничение на время -->
<timelimit value="10000"/>
<!-- Зональное ограничение на начало езды. Проверяется один раз в начале программы-->
<constraint checkOnce="true" failMessage="Робот должен находиться в синем квадрате перед запуском!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
<!-- Робот находится в допустимой зоне-->
<constraint failMessage="Робот покинул допустимую зону!">
<inside objectId="robot1" regionId="warzone"/>
</constraint>
<!-- Событие, оповещающее об успешном выполнении программы (зона финиша + робот остановился) -->
<event id="finish checker" settedUpInitially="true">
<conditions glue="and">
<inside regionId="finish" objectId="robot1"/>
<equals>
<objectState object="robot1.M3.power"/>
<int value="0"/>
</equals>
<equals>
<objectState object="robot1.M4.power"/>
<int value="0"/>
</equals>
</conditions>
<trigger>
<success/>
</trigger>
</event>
</constraints>
</root>
Теперь давайте рассмотрим подробнее как происходит проверка.
1. Зададим ограничение на время прохождения задания.
<timelimit value="10000"/>
2. Зададим ограничение на начальное положение робота. Оно проверится один раз в начале программы.
<constraint checkOnce="true" failMessage="Робот должен находиться в синем квадрате перед запуском!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
3. Зададим ограничение на зону, которую робот не может покидать.
<constraint failMessage="Робот покинул допустимую зону!">
<inside objectId="robot1" regionId="warzone"/>
</constraint>
4. А теперь создадим событие, которое будет проверять, что робот находится в финишной зоне и при этом он остановился в ней, а не проехал дальше.
Зональное ограничение задается с помощью тега <inside>. Для того, чтобы проверить, что робот остановился, нужно проверить, что мощность на обоих моторах равна нулю. Для этого используем тег <equals>. С помощью тега <objectState> укажем объект, значение которого мы хотим сравнить. В нашем случае это мощность на моторах, поэтому в атрибуте
object
пишем robot1.M3.power
или robot1.M4.power,
где M3
и M4
обозначают порты, к которым в текущей конфигурации подключены моторы. А значение, с которым нужно сравниться, записываем с помощью тега <int> и его атрибута value
.Таким образом, в этом событии проверяется три условия: робот находится в зоне финиша, мощность на моторе M3 равна нулю и мощность на моторе M4 также равна нулю. В случае выполнения этих условий пользователю будет показано сообщение об успешном выполнении программы.
<event id="finish checker" settedUpInitially="true">
<!-- Составное условие -->
<conditions glue="and">
<inside regionId="finish" objectId="robot1"/>
<equals>
<objectState object="robot1.M3.power"/>
<int value="0"/>
</equals>
<equals>
<objectState object="robot1.M4.power"/>
<int value="0"/>
</equals>
</conditions>
<trigger>
<success/>
</trigger>
</event>
Стартовав в синем квадрате, необходимо проехать вдоль стены с помощью датчика расстояния ИК до красного квадрата.

Ниже приведен полный код для проверки этого задания.
<?xml version='1.0' encoding='utf-8'?>
<root version="20190819">
<world>
<walls>
<wall stroke-width="10" end="-110:50" stroke="#ff000000" begin="-200:50" stroke-style="none" fill="#ff000000" id="{f148f786-7d73-4c42-a3aa-c7a29892d3d7}"/>
<wall stroke-width="10" end="0:100" stroke="#ff000000" begin="-110:50" stroke-style="none" fill="#ff000000" id="{8f39faac-9392-4878-86e9-9fe5dbea0007}"/>
<wall stroke-width="10" end="250:110" stroke="#ff000000" begin="0:100" stroke-style="none" fill="#ff000000" id="{bf465864-fa2e-4b59-ac65-e27bd85300d5}"/>
<wall stroke-width="10" end="350:160" stroke="#ff000000" begin="250:110" stroke-style="none" fill="#ff000000" id="{ba5441dd-8dd8-4100-ad8b-66d634792e3f}"/>
<wall stroke-width="10" end="400:260" stroke="#ff000000" begin="350:160" stroke-style="none" fill="#ff000000" id="{ca85b1de-8e9c-49e9-8c40-c2c49f43dcaa}"/>
</walls>
<skittles/>
<balls/>
<colorFields/>
<images/>
<regions>
<region visible="true" type="rectangle" x="350" filled="true" y="50" width="150" height="200" id="good_zone3" color="#ffff00"/>
<region visible="true" type="rectangle" x="-250" filled="true" y="-50" width="150" textY="0" height="100" text="Start" id="start_zone" textX="0" color="#0000ff"/>
<region visible="true" type="rectangle" x="0" filled="true" y="0" width="400" height="150" id="good_zone2" color="#ffff00"/>
<region visible="true" type="rectangle" x="400" filled="true" y="150" width="100" textY="0" height="100" text="Finish" id="finish" textX="0" color="#ff0000"/>
<region visible="true" type="rectangle" x="-200" filled="true" y="-50" width="250" height="150" id="good_zone1" color="#ffff00"/>
</regions>
</world>
<robots>
<robot direction="0" position="-200:-25" id="trikKitRobot">
<sensors>
<sensor type="trik::robotModel::parts::TrikInfraredSensor" direction="69.6769" position="42:18" port="A1###input###А1###sensorA1"/>
<sensor type="kitBase::robotModel::robotParts::Motor" direction="0" position="75:25" port="M4###output###М4###"/>
<sensor type="kitBase::robotModel::robotParts::Motor" direction="0" position="75:25" port="M3###output###М3###"/>
<sensor type="" direction="0" position="0:0" port="A5###input###А5###sensorA5"/>
<sensor type="" direction="0" position="0:0" port="A6###input###А6###sensorA6"/>
</sensors>
<startPosition direction="0" x="-175" y="0" id="{84223544-d500-411a-9fdb-b382eb98d09f}"/>
<wheels right="M3###output###М3###" left="M4###output###М4###"/>
</robot>
</robots>
<settings realisticPhysics="false" realisticSensors="false" realisticMotors="false"/>
<constraints>
<!-- Лимит времени. Обязательное ограничение -->
<timelimit value="40000"/>
<!-- Проверка, что на порт А1 установлен датчик расстояния -->
<constraint checkOnce="true" failMessage="У робота должен быть установлен датчик расстояния на порт А1">
<equals>
<typeOf objectId="robot1.A1"/>
<string value="twoDModel::robotModel::parts::RangeSensor"/>
</equals>
</constraint>
<!-- Провека, что нет датчиков, кроме датчика расстояния-->
<constraint checkOnce="true" failMessage="У робота не должно быть датчиков, кроме датчика расстояния">
<conditions glue="and">
<equals>
<typeOf objectId="robot1.A2"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.A3"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.A4"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.A5"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.A6"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.D1"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.D2"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.F1"/>
<string value="undefined"/>
</equals>
</conditions>
</constraint>
<!-- Зональное ограничение на начало езды. Проверяется один раз в начале программы-->
<constraint checkOnce="true" failMessage="Робот должен находиться в синей зоне перед стартом!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
<!-- Проверяет, чт о робот находится в допустимом регионе на протяжении всего времени выполнения программы -->
<constraint failMessage="Робот покинул допустимую зону!">
<conditions glue="or">
<inside objectId="robot1" regionId="good_zone1"/>
<inside objectId="robot1" regionId="good_zone2"/>
<inside objectId="robot1" regionId="good_zone3"/>
</conditions>
</constraint>
<!-- Событие, оповещающее об успешном выполнении программы -->
<event id="finish checker" settedUpInitially="true">
<condition>
<inside objectId="robot1" regionId="finish"/>
</condition>
<trigger>
<success/>
</trigger>
</event>
</constraints>
</root>
Теперь давайте рассмотрим подробнее как происходит проверка.
1. Зададим ограничение на время выполнения задания.
<timelimit value="40000"/>
2. По условию задачи у робота должен быть подключен только датчик расстояния (других датчиков быть не должно). Для того чтобы убедиться в этом, сравним значение объекта на порту
A1
со значением, которое обозначает датчик расстояния. Подробнее о названиях датчиков для разных конструкторов можно узнать по ссылке.<constraint checkOnce="true" failMessage="У робота должен быть установлен датчик расстояния на порт А1">
<equals>
<typeOf objectId="robot1.A1"/>
<string value="twoDModel::robotModel::parts::RangeSensor"/>
</equals>
</constraint>
3. Выполним проверку, что к другим портам не подключены никакие датчики. Для этого сравним значение на каждом порту со строкой
“undefined”
.<constraint checkOnce="true" failMessage="У робота не должно быть датчиков, кроме датчика расстояния">
<conditions glue="and">
<equals>
<typeOf objectId="robot1.A2"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.A3"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.A4"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.A5"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.A6"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.D1"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.D2"/>
<string value="undefined"/>
</equals>
<equals>
<typeOf objectId="robot1.F1"/>
<string value="undefined"/>
</equals>
</conditions>
</constraint>
4. Зададим ограничение на начальное положение робота. Оно проверится один раз в начале программы, потому что выставлен флаг
checkOnce
.<constraint checkOnce="true" failMessage="Робот должен находиться в синем квадрате перед запуском!">
<inside objectId="robot1"