Skip to content

Tutorial 4.2 (Tests)

Aerll edited this page Jan 1, 2024 · 35 revisions

Is

Tests whether the tile is one of the given tiles. We can check any amount of indices.

Example:

Insert(green).If(
    IndexAt([0, 0]).Is(red, blue)
);


We can also use a special value this, which refers to each tile given to Insert:

Insert(green, blue, red).If(
    IndexAt([-1, 0]).Is(this)
);

This line is an equivalent of:

Insert(green).If(IndexAt([-1, 0]).Is(green));
Insert(blue).If(IndexAt([-1, 0]).Is(blue));
Insert(red).If(IndexAt([-1, 0]).Is(red));

IsNot

Tests whether the tile is not any of the given tiles. We can check any amount of indices.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsNot(red, blue)
);


We can also use a special value this, which refers to each tile given to Insert:

Insert(green, blue, red).If(
    IndexAt([-1, 0]).IsNot(this)
);

This line is an equivalent of:

Insert(green).If(IndexAt([-1, 0]).IsNot(green));
Insert(blue).If(IndexAt([-1, 0]).IsNot(blue));
Insert(red).If(IndexAt([-1, 0]).IsNot(red));

IsEmpty

Tests whether the tile is empty.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsEmpty()
);


IsEmptyAt

Tests whether the tiles around are empty. We can choose from 8 positions and freely combine them: top, left, bottom, right, topLeft, bottomLeft, topRight, bottomRight.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsEmptyAt(top, left, bottomRight)
);


IsFull

Tests whether the tile is not empty.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsFull()
);

IsFullAt

Tests whether the tiles around are not empty. We can choose from 8 positions and freely combine them: top, left, bottom, right, topLeft, bottomLeft, topRight, bottomRight.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsFullAt(top, left, bottomRight)
);

IsOut

Tests whether the tile is outside of the map boundaries.

Example:

Insert(green).If(
    IndexAt([0, -1]).IsOut()
).NoDefaultPosRule();

IsNotOut

Tests whether the tile is not outside of the map boundaries.

Example:

Insert(green).If(
    IndexAt([0, -1]).IsNotOut()
).NoDefaultPosRule();

IsWithinArea

Tests whether any tile is inside the given area.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsWithinArea(2, 2)
).NoDefaultPosRule();

IsNotWithinArea

Tests whether there are no tiles inside the given area.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsNotWithinArea(2, 2)
).NoDefaultPosRule();

IsWithinRadius

Tests whether any tile is inside the circle given by the radius.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsWithinRadius(1)
).NoDefaultPosRule();

IsNotWithinRadius

Tests whether there are no tiles inside the circle given by the radius.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsNotWithinRadius(1)
).NoDefaultPosRule();

IsEdge

Tests whether the tile is on the edge of the map. We can choose 1 out of 8 edges: top, left, bottom, right, topLeft, bottomLeft, topRight, bottomRight.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsEdge(topLeft)
).NoDefaultPosRule();

IsNotEdge

Tests whether the tile is not on any of the given edges of the map. We can choose from 4 edges and freely combine them: top, left, bottom, right. We can also use all to test all edges.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsNotEdge(top, left)
).NoDefaultPosRule();

IsNextTo

Tests whether the tile is adjacent to one of the given tiles. Diagonals aren't included.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsNextTo(red, blue)
);



We can also use a special value this, which refers to each tile given to Insert:

Insert(green, blue, red).If(
    IndexAt([0, 0]).IsNextTo(this)
);

This line is an equivalent of:

Insert(green).If(IndexAt([0, 0]).IsNextTo(green));
Insert(blue).If(IndexAt([0, 0]).IsNextTo(blue));
Insert(red).If(IndexAt([0, 0]).IsNextTo(red));

IsNotNextTo

Tests whether the tile is not adjacent to any of the given tiles. Diagonals aren't included.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsNotNextTo(red, blue)
);



We can also use a special value this, which refers to each tile given to Insert:

Insert(green, blue, red).If(
    IndexAt([0, 0]).IsNotNextTo(this)
);

This line is an equivalent of:

Insert(green).If(IndexAt([0, 0]).IsNotNextTo(green));
Insert(blue).If(IndexAt([0, 0]).IsNotNextTo(blue));
Insert(red).If(IndexAt([0, 0]).IsNotNextTo(red));

IsWall

Tests whether the tile is a wall. We can choose from 4 orientations and freely combine them: top, left, bottom, right. We can also combine this test with IsOuterCorner and IsInnerCorner.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsWall(top)
);

IsOuterCorner

Tests whether the tile is an outer corner. We can choose from 4 orientations and freely combine them: topLeft, bottomLeft, topRight, bottomRight. We can also combine this test with IsWall and IsInnerCorner.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsOuterCorner(topLeft)
);

IsInnerCorner

Tests whether the tile is an inner corner. We can choose from 4 orientations and freely combine them: topLeft, bottomLeft, topRight, bottomRight. We can also combine this test with IsWall and IsOuterCorner.

Example:

Insert(green).If(
    IndexAt([0, 0]).IsInnerCorner(topLeft)
);

Combining border tests

We can combine IsWall, IsOuterCorner, IsInnerCorner and all of their values together, in order to handle some exotic cases.

If we want to test for an I shape, we will see that it's a combination of 2 walls:

Insert(green).If(
    IndexAt([0, 0]).IsWall(left, right)
);


If we want to test for a corner of an L shape, we will see that it's a combination of an inner corner and outer corner:

Insert(green).If(
    IndexAt([0, 0]).IsOuterCorner(bottomLeft).IsInnerCorner(topRight)
);


If we want to test a connection point of a T shape, we will see that it's a combination of a wall and 2 inner corners:

Insert(green).If(
    IndexAt([0, 0]).IsWall(top).IsInnerCorner(bottomLeft, bottomRight)
);


And so on.

Example

This time we will write an automapper for grass_main. Our goal is to create a grass border, filled with dirt and some random bones. We will start by creating all of the variables, that we will be using:

int dirt   = 1;
int bone:1 = 2;
int bone:2 = 3;
int bone:3 = 66;
int bone:4 = 67;
int bone:5 = 68;

int wall:top    = 16;
int wall:right  = 17;
int wall:bottom = 18;
int wall:left   = 19;

int outer:topLeft     = 32;
int outer:topRight    = 33;
int outer:bottomRight = 34;
int outer:bottomLeft  = 35;

int inner:topLeft     = 48;
int inner:topRight    = 49;
int inner:bottomRight = 50;
int inner:bottomLeft  = 51;

Then we create a new automapper and a new run.

First thing we need to consider, is that the map can contain anything when we're automapping it. In our case, it is important to prepare the map beforehand, as otherwise, we could end up with a buggy mess. Therefore, before doing anything, let's make sure, that we always have the same starting point:

Insert(dirt);

By doing this, we can now safely say the map is always made out of dirt.

Now we can create grass borders. Having variables with meaningful names will help us a lot here. I will just show you one of each, because the full code is kinda long:

Insert(wall:top).If(IndexAt([0, 0]).IsWall(top));
Insert(outer:topLeft).If(IndexAt([0, 0]).IsOuterCorner(topLeft));
Insert(inner:topLeft).If(IndexAt([0, 0]).IsInnerCorner(topLeft));

As you can see it's pretty straightforward. In worst case scenario, you could get some value wrong, but even then it's very simple to fix. That will summarize the first run.

Our last step is to randomize some bones. For that, we will create another run.

Let's think about how exactly do we go about randomizing bones. First thing to notice, is that the bones should only be placed on top of the dirt. Let's write our rule then:

Insert(bone:1, bone:2, bone:3, bone:4, bone:5).If(
    IndexAt([0, 0]).Is(dirt)
).Chance(0.4);

Here probability is up to you. In my opinion 0.4 gives very nice results, so I will use this value.

Note: we don't need to use a mask here, because dirt acts like one.

One could say we're done, automapper is working and it's ready for shipping. However, it can be better. One thing I don't like, is that it can place the same bone next to each other. This doesn't look too well and we can prevent this very easily, by simply adding another test:

Insert(bone:1, bone:2, bone:3, bone:4, bone:5).If(
    IndexAt([0, 0]).Is(dirt).IsNotNextTo(this)
).Chance(0.4);

Now there will be no duplicates.

Another thing is even worse and happens more often. What could be worse than duplicates? Well... infinitely many of them. When the automapper places bones on the edge of the map, they will be replicated infinitely beyond that point. This results in a very unpleasant view. We can get rid of this very easily, by adding yet another test:

Insert(bone:1, bone:2, bone:3, bone:4, bone:5).If(
    IndexAt([0, 0]).Is(dirt).IsNotNextTo(this).IsNotEdge(top, right, bottom, left)
).Chance(0.4);

Now our automapper is ready.

Full code

#include "base.r"
#output "grass_main.rules"

int dirt   = 1;
int bone:1 = 2;
int bone:2 = 3;
int bone:3 = 66;
int bone:4 = 67;
int bone:5 = 68;

int wall:top    = 16;
int wall:right  = 17;
int wall:bottom = 18;
int wall:left   = 19;

int outer:topLeft     = 32;
int outer:topRight    = 33;
int outer:bottomRight = 34;
int outer:bottomLeft  = 35;

int inner:topLeft     = 48;
int inner:topRight    = 49;
int inner:bottomRight = 50;
int inner:bottomLeft  = 51;

AutoMapper("GrAss");
NewRun();

// replace everything with dirt
Insert(dirt);

// insert walls
Insert(wall:top).If(IndexAt([0, 0]).IsWall(top));
Insert(wall:right).If(IndexAt([0, 0]).IsWall(right));
Insert(wall:bottom).If(IndexAt([0, 0]).IsWall(bottom));
Insert(wall:left).If(IndexAt([0, 0]).IsWall(left));

// insert outer corners
Insert(outer:topLeft).If(IndexAt([0, 0]).IsOuterCorner(topLeft));
Insert(outer:topRight).If(IndexAt([0, 0]).IsOuterCorner(topRight));
Insert(outer:bottomRight).If(IndexAt([0, 0]).IsOuterCorner(bottomRight));
Insert(outer:bottomLeft).If(IndexAt([0, 0]).IsOuterCorner(bottomLeft));

// insert inner corners
Insert(inner:topLeft).If(IndexAt([0, 0]).IsInnerCorner(topLeft));
Insert(inner:topRight).If(IndexAt([0, 0]).IsInnerCorner(topRight));
Insert(inner:bottomRight).If(IndexAt([0, 0]).IsInnerCorner(bottomRight));
Insert(inner:bottomLeft).If(IndexAt([0, 0]).IsInnerCorner(bottomLeft));



NewRun();
OverrideLayer();

// randomize bones
Insert(bone:1, bone:2, bone:3, bone:4, bone:5).If(
    IndexAt([0, 0]).Is(dirt).IsNotNextTo(this).IsNotEdge(top, right, bottom, left)
).Chance(0.4);