ID:1361152
 
What the following code does is take two directions and determines the next best direction from the first to the second.

proc/weigh_dirs(d1, d2)

if(d2 == turn(d1, 180)) // difficult scenario, so just randomly choose clockwise or counterclockwise
return turn(d1, pick(45,-45))

if(d1 == d2) return d2

switch(d1)
if(WEST)
if(d2 & SOUTH)
return SOUTHWEST
if(d2 & NORTH)
return NORTHWEST
if(SOUTH)
if(d2 & WEST)
return SOUTHWEST
if(d2 & EAST)
return SOUTHEAST
if(EAST)
if(d2 & SOUTH)
return SOUTHEAST
if(d2 & NORTH)
return NORTHEAST
if(NORTH)
if(d2 & WEST)
return NORTHWEST
if(d2 & EAST)
return NORTHEAST

if(SOUTHWEST)
if(d2 & SOUTH)
return SOUTH
if(d2 & NORTH)
return WEST
if(d2 & WEST)
return WEST
if(d2 & EAST)
return SOUTH
if(NORTHWEST)
if(d2 & SOUTH)
return WEST
if(d2 & NORTH)
return NORTH
if(d2 & WEST)
return WEST
if(d2 & EAST)
return NORTH
if(SOUTHEAST)
if(d2 & SOUTH)
return SOUTH
if(d2 & NORTH)
return EAST
if(d2 & WEST)
return SOUTH
if(d2 & EAST)
return EAST
if(NORTHEAST)
if(d2 & SOUTH)
return EAST
if(d2 & NORTH)
return NORTH
if(d2 & WEST)
return NORTH
if(d2 & EAST)
return EAST

return d2


For example, if d1 = NORTH and d2 = WEST, the return would be NORTHWEST. This is essentially d1 | d2, yes, but what about a more complicated scenario?

What about d1 = NORTHEAST, d2 = WEST? This would return NORTH as NORTH is closest "turn" toward the desired direction. I use this bloated function to simulate a sort of "semi-homing" projectile turn:



The above code works, yes, but it's probably not the most elegant method of going about doing this. Is there some clever alternative to what I am doing other than defining almost every situation possible?

Your way is probably the fastest. You could shorten it by combining some of the ifs that lead to the same return value.
        if(NORTHEAST)
if(d2 & SOUTH)
return EAST
if(d2 & NORTH)
return NORTH
if(d2 & WEST)
return NORTH
if(d2 & EAST)
return EAST

// to
if(NORTHEAST)
if(d2 & (SOUTH | EAST))
return EAST
if(d2 & (NORTH | WEST))
return NORTH


(Time to over-complicate!)

The method I've used to determine which way to turn is to just get the angle difference between the directions. I think there's a fairly recent thread about getting the angle between directions, actually, but whatever.

This here is code I just wrote off the top of my head, going at the problem from a slightly different angle.
//  Warning: Untested code!

proc/turn_towards(a, b, rotation = 45)
if(a == b) return b
if(a == turn(b, 180)) return pick(-rotation, rotation)
return turn(a, clamp_angle(dir2angle(b) - dir2angle(a)) > 180 ? rotation : -rotation)


// and from the Standard Kaiochao Library (in his mind)...

proc/clamp_angle(a)
// could be improved with modulo
while(a < 0) a += 360
while(a >= 360) a -= 360
return a

proc/dir2angle(d)
var global/d2a[] = list(0, 180, 0, 90, 45, 135, 0, 280, 315, 225)
return (d in 1 to d2a.len) && d2a[d]
In response to Kaiochao
Kaiochao wrote:
...

But it makes so little sense to have your dir2angle go clockwise from north (i.e. north = 0, east = 90, ...) when turn() rotates anticlockwise.
In response to Toadfish
Sorry, I've just been used to clockwise rotations because of turn(icon, angle) and icon.Turn(angle). Darn BYOND inconsistencies. When I first posted this, I had the negative sign on the other "rotation", and then I realized that turn(dir, angle) did counter-clockwise rotations, so I switched them.
I've just been used to clockwise rotations because of turn(icon, angle) and icon.Turn(angle).

Oh man. And you can't go about fixing that inconsistency because of backwards compatibility issues, at least not easily.
I think you can fix that by doing it anticlockwise. It might get worked if you tried to do it.