ID:2223625
 
(See the best response by Kaiochao.)

Problem description:

I need a proc that return a random locate from a square / line around an NPC in the most optimized way possible, something like:

pick(range(5)-range(4)) // In this case the two lists are calculated with all the atoms inside and then subtracted by the second list


I need an proc that return the same thing (locate(x,y,z)) but using only numbers and accounts instead of items from a list, I confess I don't know how to explain.

If all you need is any location in a square around a mob, something like this might work:

//assume center is src
var/range = 5
var/x_off = rand(-range, range)
var/y_off = rand(-range, range)
return locate(x+x_off, y+y_off, z)


EDIT: Looking again, I realize you only want the outermost edge. I'm not really sure what the best way to do that might be offhand.

I'll leave it up to you to figure out how to get a line.
I'm not really sure what you're asking here. You're subtracting a list from another list and then you want to pick from the remaining turfs? Or are you looking for something more similar to what MP suggested? I'm afraid I just don't follow; if MP's solution does not work out for you, further clarification would be nice.
I have a feeling it would look something like this:
return prob(50) \
? locate(x + rand(-range, range), y + pick(-range, range), z) \
: locate(x + pick(-range, range), y + rand(-range, range), z)

I haven't proven that this actually represents an equal chance for each turf in hollow square, though. (The corners probably have an above-average chance of being chosen.)
This should work:
proc
GetRandomTurfInRing(turf/T, range=5)
if(!T) return null
var side = pick(NORTH, SOUTH, EAST, WEST)
var place = rand(-range,range)
switch(side)
if(NORTH)
. = locate(T.x+place,T.y+range, T.z)
if(SOUTH)
. = locate(T.x+place,T.y-range, T.z)
if(EAST)
. = locate(T.x+range, T.y+place, T.z)
if(WEST)
. = locate(T.x-range, T.y+place, T.z)
return .

although it's probably not real efficient.
Best response
The corners definitely have an above-average chance of being chosen (2 times as much as a non-corner) because they occur on both sides of the 50%.

The probability of picking a corner should be (assuming each possibility has an equal chance) 4 divided by the total number of possibilities.
The total number of possibilities is the perimeter of the square, which is 4 times the side lengths minus 4 (to not count the corners twice).
The side lengths are 2 times range, plus one for the center.

So, the probability of picking a corner is:
4/(4*(2*range+1)-4) = 4/(4*(2*range+1-1)) = 1/(2*range).

The prob() function takes a probability out of 100, so the argument would be:
100/(2*range) = 50/range.

proc/pick_random_square(range, turf/center)
return prob(50 / range) \
? locate(
center.x + pick(-range, range),
center.y + pick(-range, range),
center.z) \
: prob(50) \
? locate(
center.x + rand(-range, range),
center.y + pick(-range, range),
center.z) \
: locate(
center.x + pick(-range, range),
center.y + rand(-range, range),
center.z)

Theoretically, this should work.

I made some heatmaps to check how these various algorithms work. The brighter a tile, the more it's chosen (relatively). All with a range of 3, picking at a rate of 5 tiles per frame at 60 frames per second.

Here's a "heatmap" of the first attempt, which counts corners twice:


Here's a heatmap of the second attempt, which attempts to give corners an equal probability of being chosen:


Also, I think Flick's method is the same as my first one. Here's his heatmap:


And, for fun, here's MisterPerson's heatmap:
This all looks so professional! Thank you all, my problem has been solved I am now using the second attempt of Kaiochao.
That should fix mine :P
proc
GetRandomTurfInRing(turf/T, range=5)
if(!T) return null
var side = pick(NORTH, SOUTH, EAST, WEST)
var place = rand(-range,range-1)
switch(side)
if(NORTH)
. = locate(T.x+place,T.y+range, T.z)
if(SOUTH)
. = locate(T.x+place+1,T.y-range, T.z)
if(EAST)
. = locate(T.x+range, T.y+place+1, T.z)
if(WEST)
. = locate(T.x-range, T.y+place, T.z)
return .