ID:1245742
 
(See the best response by Albro1.)
Problem description: I am trying to make a proc which uses two cardinal movements to equal a diagonal.
(i.e.
|_|_|2|_|_|
|_|2|1|2|_|
|2|1|x|1|2|
|_|2|1|2|_|
|_|_|2|_|_|
where
x = player location
1 = turfs available at range 1
2 = turfs available at range 2
)

I have tried printf() style debugging, and it seems like it should work. When I try to actually use the range finder, however, something messes up and I am not sure what.

Code:
mob
proc
InRange(var/range as num)
{ //define the range area
//just need to snub the diagonals from this area.
var/turf/turfsInRange = oview(range);

for(var/i = 1, i <= length(turfsInRange), i++)
{
//be able to reference turfs in the list easily.
var/turf/t = turfsInRange[i];


var/relX = src.x - t.x;
src.Room("Turf [i]'s relative X is [relX]");

var/relY = src.y - t.y;
src.Room("Turf [i]'s relative Y is [relY]");

var/relLoc = abs(relX) + abs(relY);
src.Room("Turf [i]'s relative location is [relLoc]");

//the issue starts when I add this in
if(relLoc > range)
{
turfsInRange -= turfsInRange[i];
}
//end of problem section.

//do not include the user's current position
//in the list.
if(t.x == src.x && t.y == src.y)
{
turfsInRange -= turfsInRange[i];
}
}

var/c = 1;
while(c <= length(turfsInRange))
{
//establish reference
var/turf/t = turfsInRange[c];

//paint everything within range red.
t.text = "<font color='red'>[t.text]";
c++;
}
}


What happens:
This is a picture of what happens when range 1 is used.




This is what happens when range 2 is used.



Things onward from that point tend to follow a trend of what range 2 looks like.

This is strange and confusing for me. My debug code looks like this if I remove the IF statement that I marked off:

Turf 1's relative X is 0
Turf 1's relative Y is 0
Turf 1's relative location is 0
Turf 2's relative X is 1
Turf 2's relative Y is 1
Turf 2's relative location is 2
Turf 3's relative X is 0
Turf 3's relative Y is 1
Turf 3's relative location is 1
Turf 4's relative X is -1
Turf 4's relative Y is 1
Turf 4's relative location is 2
Turf 5's relative X is 1
Turf 5's relative Y is 0
Turf 5's relative location is 1
Turf 6's relative X is -1
Turf 6's relative Y is 0
Turf 6's relative location is 1
Turf 7's relative X is 1
Turf 7's relative Y is -1
Turf 7's relative location is 2
Turf 8's relative X is 0
Turf 8's relative Y is -1
Turf 8's relative location is 1
Turf 9's relative X is -1
Turf 9's relative Y is -1
Turf 9's relative location is 2
Turf 10's relative X is 2
Turf 10's relative Y is 2
Turf 10's relative location is 4
Turf 11's relative X is 1
Turf 11's relative Y is 2
Turf 11's relative location is 3
Turf 12's relative X is 0
Turf 12's relative Y is 2
Turf 12's relative location is 2
Turf 13's relative X is -1
Turf 13's relative Y is 2
Turf 13's relative location is 3
Turf 14's relative X is -2
Turf 14's relative Y is 2
Turf 14's relative location is 4
Turf 15's relative X is 2
Turf 15's relative Y is 1
Turf 15's relative location is 3
Turf 16's relative X is -2
Turf 16's relative Y is 1
Turf 16's relative location is 3
Turf 17's relative X is 2
Turf 17's relative Y is 0
Turf 17's relative location is 2
Turf 18's relative X is -2
Turf 18's relative Y is 0
Turf 18's relative location is 2
Turf 19's relative X is 2
Turf 19's relative Y is -1
Turf 19's relative location is 3
Turf 20's relative X is -2
Turf 20's relative Y is -1
Turf 20's relative location is 3
Turf 21's relative X is 2
Turf 21's relative Y is -2
Turf 21's relative location is 4
Turf 22's relative X is 1
Turf 22's relative Y is -2
Turf 22's relative location is 3
Turf 23's relative X is 0
Turf 23's relative Y is -2
Turf 23's relative location is 2
Turf 24's relative X is -1
Turf 24's relative Y is -2
Turf 24's relative location is 3
Turf 25's relative X is -2
Turf 25's relative Y is -2
Turf 25's relative location is 4

So essentially, it is recognizing the distance properly. But it changes when I add the IF statement back in:

Turf 1's relative X is 0
Turf 1's relative Y is 0
Turf 1's relative location is 0
Turf 2's relative X is 1
Turf 2's relative Y is 1
Turf 2's relative location is 2
Turf 3's relative X is 0
Turf 3's relative Y is 1
Turf 3's relative location is 1
Turf 4's relative X is -1
Turf 4's relative Y is 1
Turf 4's relative location is 2
Turf 5's relative X is 1
Turf 5's relative Y is 0
Turf 5's relative location is 1
Turf 6's relative X is -1
Turf 6's relative Y is 0
Turf 6's relative location is 1
Turf 7's relative X is 1
Turf 7's relative Y is -1
Turf 7's relative location is 2
Turf 8's relative X is 0
Turf 8's relative Y is -1
Turf 8's relative location is 1
Turf 9's relative X is -1
Turf 9's relative Y is -1
Turf 9's relative location is 2
Turf 10's relative X is 2
Turf 10's relative Y is 2
Turf 10's relative location is 4
Turf 11's relative X is 0
Turf 11's relative Y is 2
Turf 11's relative location is 2
Turf 12's relative X is -1
Turf 12's relative Y is 2
Turf 12's relative location is 3
Turf 13's relative X is 2
Turf 13's relative Y is 1
Turf 13's relative location is 3
Turf 14's relative X is 2
Turf 14's relative Y is 0
Turf 14's relative location is 2
Turf 15's relative X is -2
Turf 15's relative Y is 0
Turf 15's relative location is 2
Turf 16's relative X is 2
Turf 16's relative Y is -1
Turf 16's relative location is 3
Turf 17's relative X is 2
Turf 17's relative Y is -2
Turf 17's relative location is 4
Turf 18's relative X is 0
Turf 18's relative Y is -2
Turf 18's relative location is 2
Turf 19's relative X is -1
Turf 19's relative Y is -2
Turf 19's relative location is 3

What concerns me most about that is that it is only displaying 19 tiles. I do not know why that would be, since the turfs should display their information before being removed by the IF statement.

Can anyone explain what is happening here?


Well I'm not at a computer and can't properly try debugging this, but I can point some things out:

You already reference turfsInRange[i] when you assign turf/t to it. Because of this you don't need to do turfsInRange -= turfsInRange[i], you can do turfsInRange -= t.

Also, your if statement that checks for srcs location is more complicated than need be. Simply doing if(src.loc == t) will do the same thing.
The problem obviously is that the list is not removing entries too far away. Hopefully these small fixes will help, though if they don't at least they will have simplified things a bit.
Thank you for the simplifications. I considering making the removal from turfsInRange a function, but it did not seem to be worth it. Your way is cleaner, so thanks.

Still have not figured out what causes the change in patterns. I will tinker with it a bit more, but if anyone has something to suggest please get back to me.
Instead of altering turfsInRange while your looping through it, I've added a new list in which we will Flag a turfs location in turfsInRange if we don't want it. Then when we alter the text, simply check if the location within turfsInRange has been flagged and skip over it.
mob
proc
InRange(var/range as num)
{ //define the range area
//just need to snub the diagonals from this area.
var/turf/turfsInRange = oview(range);
//We will flag the location in 'turfsInRange' that we don't want
var/list/Flags = list();
for(var/i = 1, i <= length(turfsInRange), i++)
{
//be able to reference turfs in the list easily.
var/turf/t = turfsInRange[i];


var/relX = src.x - t.x;
src << "Turf [i]'s relative X is [relX]";

var/relY = src.y - t.y;
src << "Turf [i]'s relative Y is [relY]";

var/relLoc = abs(relX) + abs(relY);
src << "Turf [i]'s relative location is [relLoc] ([t.x],[t.y])";

//the issue starts when I add this in
if(relLoc > range)
{
Flags += i //add the location to the Flags list
}
//end of problem section.

//do not include the user's current position
//in the list.
if(t.x == src.x && t.y == src.y)
{
Flags += i;
}
}

var/c = 1;
while(c <= length(turfsInRange))
{
//Here, we will just skip the turf if it's flagged
if(c in Flags)
c++
continue;
//establish reference
var/turf/t = turfsInRange[c];
src << "[c] RED!"

//paint everything within range red.
t.text = "<font color='red'>[t.text]";
c++;
}
}
Best response
I'm really not sure what the issue is. I made and tried this snippet out and it worked just fine:
turf
name = "."

mob
name = "m"


verb
Test(n as num)
usr.cardinals(n>0 ? n : 1)

proc
cardinals(range=1)
var/list/targets = oview(range, src)
var/_x, _y, _d
for(var/turf/t in targets)
_x = src.x - t.x
_y = src.y - t.y
_d = abs(_x) + abs(_y)
if(_d > range)
targets.Remove(t)

else
t.text = "<font color='red'>[t.text]"




You don't even need the part where you remove them from the targets list. Just reverse that check to make sure it is less than or equal to range and change it if so.
Cloud Magic wrote:
the idea is to show them how they could solve their problems, not solve it for them. I think you went a little overboard with the image included as well.

I understand this all too well. In this case though, before I got to my computer, I was genuinely intrigued as to why this didn't work and when I got to my computer I gave it my own shot. I would hope that by now you know that I don't normally give out code for free. This case was a rare exception to that for me.

With that said, I am now speaking to the OP.

If you don't understand any of the code in my snippet, please don't copy and paste it. The worst thing you can do is use code you don't understand. I will be happy to clarify any confusion you have with my code if you clarify where that confusion is.
@DannyRoe:
That works well, thanks.

@Albro1:
I don't know what the issue is/was either. It seemed like the code was skipping over some locations, or defaulting them to red. But I don't know why it would do that when all that was changed was an IF statement. I do not know why yours works and mine does not.

Edit: Don't worry, I do understand your code. The only confusion I might have is why yours works and mine does not.

@Cloud Magic:
Sorry, I am not entirely sure how your code solves my issue. It is very concise, but I cannot personally understand it in relation to the issue I was having.

CloudMagic wrote:
...something

I didn't say it was better, I looked over it and found the problem he was having with it. I looked at your post, and decided not to try and decypher it when I seen the single = in an if statement.


Here's something I just wrote up real quick. It's a little extensive on variables, but ends up making the code look more simplistic. I didn't really refine this for optimization.

https://dl.dropboxusercontent.com/u/14221897/files/ DMAreaTest.rar
The code is working now, but I still don't really understand why putting my original IF statement in caused the program to behave so unpredictably. How does it stop the program from looking at 6 turfs and coloring/not coloring them?

I counted the number of turfs that would have been acceptable in the debug output. It's finding the distance between them fine. So what about the IF statement causes this to change?
Cloud Magic wrote:
I think you went a little overboard with the image included as well.
I like to be thorough and clear. Seems like a difference in preferences to me.

(edit: Do you think Bill Gates ever argued with the members of the Microsoft Team? Would we have Windows today if he didn't?)
Not quite sure where this came from or what your intention is there.
, or someone that is obsessed with getting votes in threads by essentially giving the answer instead of teaching them - which is counterproductive as a whole to the BYOND community.
Here
Here
Here
This thread
Here
Here

And most of, if not all of those are from this past week alone. In the few occasions where code is included in my posts, it is either straight from the reference or changed in some way to prevent copy and pasting to an extent.
So if you would like to continue on saying that I am someone who gives out code or something, then please state what basis you are doing so on.
In response to Angora Napkin
Thinking about it now, I would assume that the issue is caused because you were looping through it on a numeric base.

Say 6 turfs were removed on the way through the list. This reduces the list's length by 6. Your loop is supposed to stop when the counter hits the list's length. That's why.
In response to Albro1
Ah, ok. Thanks for your help. I'll have to look out for that in the future.
This simplified range problem works fine at a basic level; however, it is fundamentally flawed. The problem is actually a pathfinding issue commonly known as the "single-source shortest paths" problem.

Imagine if a wall boxes in the source location, with perhaps one opening that can be exited from. The solution presented here will treat all outside edges of the wall as the same distance from the source location, but we know that one must actually leave out of the opening the walls provide, which means that the perimeter tiles of the walls will all be of varying distances.

While a little more involved, I encourage you to Google for and explore the Dijkstra pathfinding algorithm, particularly the single-source shortest paths usage of it. I haven't checked to see if such a library exists already---and heck I may make one myself---but for now I'm posting from my iPhone so you'll have to excuse my lack of diagrams and such.
In response to Hiead
I haven't looked it up, but the Dijkstra would involve using a method like this to find the tile, and then pathfinding your way back to the source, correct?
In response to Albro1
Albro1 wrote:
I haven't looked it up, but the Dijkstra would involve using a method like this to find the tile, and then pathfinding your way back to the source, correct?

Most pathfinding algorithms use a form of recursion which calls back the finished product back to the initial call. In some cases, one branch could spawn off three different kinds of other recursive functions that represent the next touchable tiles.