Restrictions setting examples
This article is automatically translated from Russian by Google Translator.
Setting constraints is a unique feature of TRIK Studio designed for an automated check of tasks. This tool has many uses:
- self-examination of homework by students,
- accelerating the process of checking tasks by the teacher,
- online competitions with automatic checking of the participants' solutions.
The TRIK Studio constraint language is a very powerful tool that allows you to check almost all aspects of program execution and robot behavior and may seem complicated at first glance. However, don't be upset - in this article we will walk you through the process of creating constrained tasks, ranging from simple constraints that cover most of the tasks to complex constraints, which can be useful for enthusiasts.
For those who are just starting to get acquainted with the TRIK Studio constraint language, we suggest starting with simple restrictions on the program run time, the "Start" and "Finish" zones. This is more than enough for most robotic tasks.
1. Define the following conditions and parameters for the task and the world model.
- Optimal program execution time. If the program exceeds the timelimit, the checking system will generate an error "The program ran too long".
- Start zone. The zone from which the robot must start executing the program, otherwise the checking system will generate an error.
- Finish zone. The zone where the robot must arrive at the end of the program, otherwise the checking system will generate an error.
2. Edit the value of the
<regions>
tag in the world XML file to add the Start and Finish zones. By changing the x
, y
, width
and height
values, you can edit the location and size of the zones. To check the position of a zone, load the file into the virtual world model and check the location of the zones. After that, if necessary, you can make the "Start" and "Finish" zones invisible by setting the attribute 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. Copy the
<constraints>
block template below into the XML file of the world for which you want constraints.<constraints>
<!-- Restriction on program runtime -->
<timelimit value="300000"/>
<!-- Restriction, which will be checked once before starting the program -->
<constraint checkOnce="true" failMessage="Робот должен находиться в зоне старта перед запуском!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
<!-- An event that verifies that the robot is in the finish area at the end of the program -->
<event id="finish checker" settedUpInitially="true">
<condition>
<inside objectId="robot1" regionId="finish_zone"/>
</condition>
<trigger>
<success/>
</trigger>
</event>
</constraints>
4. Fill in the
<timelimit value="300000">
tag. Specify what you think is the best execution time for the program. Note that the time is in milliseconds, i.e., 300000ms = 5 minutes, 120,000ms = 2 minutes, etc.5. The constraint task is ready! Now you know how to check the start and end positions of the robot and the execution time of the program.
Now let's break down the application of this technique to different tasks and consider options for more detailed checks.
The task of passing the labyrinth. Having started in the blue square, it is necessary to pass to the finish area. In order to check that the user actually passes the labyrinth and not bypasses it, a spatial constraint is set.

Blue square - start area, red square - finish area, black rectangle - field that cannot be left by the robot
Below is the complete code to test this task.
<?xml version='1.0' encoding='utf-8'?>
<root>
<!-- World Description -->
<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/>
<!-- Defining regions (zones) on the world map -->
<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>
<!-- Description of restrictions -->
<constraints>
<!-- Time limit of 5 minutes -->
<timelimit value="300000"/>
<constraint checkOnce="true" failMessage="Робот должен находиться в зоне старта перед запуском!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
<!-- Restriction that verifies that the robot is in the allowed zone -->
<constraint failMessage="Робот попытался объехать лабиринт!">
<inside objectId="robot1" regionId="warzone"/>
</constraint>
<!-- An event that verifies that the robot is in the finish area at the end of the program -->
<event id="finish checker" settedUpInitially="true">
<condition>
<inside objectId="robot1" regionId="finish_zone"/>
</condition>
<trigger>
<success/>
</trigger>
</event>
</constraints>
</root>
Let's take a closer look at how the check takes place.
1. Set a time limit for passing the task. This limit is mandatory. The time is specified in milliseconds.
<timelimit value="300000"/>
2. Set a constraint on the area where the robot should be before starting the program. According to the condition of the problem, it should be a blue square - the start area. This restriction will be checked once, at the beginning of the program, since the attribute
checkOnce
is true
.With the <inside> tag we set the spatial constraint. It has two attributes. In the first (objectId) we specify the id of the object whose location we want to check, in our case, it is a robot. In the second (regionId) we specify the id of the region in which our object should be located.
If the condition described in
<inside>
is not met, the program will be terminated with an error. For this, the <constraint> tag has an attribute failMessage
, which allows you to specify the text of the error message.<constraint checkOnce="true" failMessage="The robot must be in the launch area before starting!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
3. In order to verify that the user actually goes through the maze and not bypasses it, let's set another spatial constraint.
Let's edit the
<regions>
tag in the XML file of the world by adding the region with id = "warzone"
.<region id="warzone" type="rectangle" filled="false" color="black" visible="true" x="-450" y="-400" width="1050" height="600"/>
This constraint will be checked at all times of program execution. In the <inside> tag we will specify the object id and the region id, in our case, it is the robot and the black rectangle limiting the maze.
If the robot is outside the specified region at any time, the program will terminate with an error, the text of which is specified in the
failMessage
attribute.<constraint failMessage="The robot tried to go around the maze!">
<inside objectId="robot1" regionId="warzone"/>
</constraint>
4. Now we have to check that the robot reaches the finish area.
To do this, let's create an event that will check if the robot is in the right zone or not. The attribute
settedUpInitially="true"
means that the event will be started (fired) immediately when the program starts.In the <condition> tag we specify which condition to check. In our case the condition is the same as described above - we are checking that the robot is in the region with
id = "finish"
. If that condition is true, then the user will see a message saying that the program was executed successfully. To do that, we write a child tag <success/> in the <trigger> tag.<event id="finish checker" settedUpInitially="true">
<!-- Condition-->
<condition>
<inside objectId="robot1" regionId="finish_zone"/>
</condition>
<!-- Trigger-->
<trigger>
<success/>
</trigger>
</event>
The task is to pass forward and stop in the finish area.
This task is slightly different from Example 1 in that another condition is added for successful completion of the program - the robot must not only be in the finish area, but also stop.

Below is the complete code to test this task.
<?xml version='1.0' encoding='utf-8'?>
<root version="20190819">
<world>
<!-- Defining regions (zones) on the world map -->
<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"/>
<!-- Setting restrictions -->
<constraints>
<!-- Time limit -->
<timelimit value="10000"/>
<!-- Zone restriction on the start of the ride. Checked once at the beginning of the program-->
<constraint checkOnce="true" failMessage="Робот должен находиться в синем квадрате перед запуском!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
<!-- The robot is in the tolerance zone -->
<constraint failMessage="Робот покинул допустимую зону!">
<inside objectId="robot1" regionId="warzone"/>
</constraint>
<!-- Event indicating the successful execution of the program (finish area + robot stopped) -->
<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>
Now let's take a closer look at how the check takes place.
1. Let's set a time limit for passing the task.
<timelimit value="10000"/>
2. Let's set a constraint on the initial position of the robot. It will be checked once at the beginning of the program.
<constraint checkOnce="true" failMessage="The robot must be in the blue square before launching!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
3. Set a restriction on the area that the robot cannot leave.
<constraint failMessage="Робот покинул допустимую зону!">
<inside objectId="robot1" regionId="warzone"/>
</constraint>
4. Now create an event that verifies that the robot is in the finish zone and that it has stopped in the finish zone and has not moved on.
The zone limit is set with the <inside> tag. In order to check that the robot has stopped, we need to check that the power on both motors is zero. To do this we use the <equals> tag. With the <objectState> tag we define the object whose value we want to compare. In our case this is the power of the motors, so we will write
robot1.M3.power
or robot1.M4.power
in the attribute object
, where M3
and M4
are the ports to which the motors are connected in the current configuration. And the value to be compared we write using the <int> tag and its value
attribute.Thus, this event checks three conditions: the robot is in the finish area, the power on motor
M3
is zero and the power on motor M4
is also zero. If these conditions are met the user will be shown a message about the successful execution of the program.<event id="finish checker" settedUpInitially="true">
<!-- Compound condition -->
<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>
Having started in the blue square, you must drive along the wall with the IR distance sensor to the red square.

Below is the complete code for checking this task.
<?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>
<!-- Time Limit. Mandatory Limit -->
<timelimit value="40000"/>
<!-- Check that a distance sensor is installed on port A1 -->
<constraint checkOnce="true" failMessage="У робота должен быть установлен датчик расстояния на порт А1">
<equals>
<typeOf objectId="robot1.A1"/>
<string value="twoDModel::robotModel::parts::RangeSensor"/>
</equals>
</constraint>
<!-- Check that there are no sensors other than the distance sensor -->
<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>
<!-- Zone restriction on the start of the ride. Checked once at the beginning of the program -->
<constraint checkOnce="true" failMessage="Робот должен находиться в синей зоне перед стартом!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
<!-- Checks that the robot is in a valid region for the duration of the program -->
<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 that notifies of successful program execution -->
<event id="finish checker" settedUpInitially="true">
<condition>
<inside objectId="robot1" regionId="finish"/>
</condition>
<trigger>
<success/>
</trigger>
</event>
</constraints>
</root>
Now let's take a closer look at how the check takes place.
1. Let's set a time limit for the task.
<timelimit value="40000"/>
2. According to the problem condition, the robot must have only the distance sensor connected (there must be no other sensors). To be sure of this, let's compare the value of the object on port
A1
with the value of the distance sensor. For more information about the names of the sensors for different constructors, see the link.<constraint checkOnce="true" failMessage="The robot must have a distance sensor installed on port A1">
<equals>
<typeOf objectId="robot1.A1"/>
<string value="twoDModel::robotModel::parts::RangeSensor"/>
</equals>
</constraint>
3. Let's check that no sensors are connected to the other ports. To do this, compare the value on each port with the
"undefined"
string.<constraint checkOnce="true" failMessage="The robot should have no sensors other than the distance sensor">
<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. Let's set a constraint on the initial position of the robot. It will be checked once at the beginning of the program because the
checkOnce
flag is set.<constraint checkOnce="true" failMessage="The robot must be in the blue square before launching!">
<inside objectId="robot1" regionId="start_zone"/>
</constraint>
5. Let's set a restriction on the zone that the robot can't leave. In the previous examples, we considered a situation where the robot needed to stay in one zone. In this case, the allowed zone consists of several rectangles, so we need to check that the robot is in one of those zones. To do that we use the
<conditions></conditions>
tag with the glue = "or"
attribute.