ID:962395
 
(See the best response by Geldonyetich.)
Code:
mob
proc
Wait()
while(!choicemade)
sleep(1)
choicemade = 0
//do stuff


mob
verb
makechoice()
//make choice
choicemade = 1


Problem description:

Is there a more effective way to do this if I don't intend to use input or any other wait proc? How could I make my own efficiently as possible, or is what I'm doing not too demanding on the CPU?
You should always try to create a loophole in your loops so that they can stop themselves if something goes wrong.
But is this way effective? Is it demanding on the CPU? Is there a more effective way?

Also on a lesser priority note:

Is setting the choicemade to 1 not a loophole? If at anypoint I need it to stop I can just set that var to 1.
In response to Master Jack
Master Jack wrote:
But is this way effective? Is it demanding on the CPU? Is there a more effective way?

Also on a lesser priority note:

Is setting the choicemade to 1 not a loophole? If at anypoint I need it to stop I can just set that var to 1.

You can check the CPU usage by using the profiling tool.
What I meant is that you should put an internal loophole. Something in the loop that will break it if something is wrong.

As for the CPU, it won't be demanding. That sleep() is going to stop up the procedure, but it isn't going to freeze anything up. As long as you break it, it won't turn into an infinite loop.
So you would agree that the method I am using is acceptable for stalling until a choice is made?
It will work, but you should still build in a safety.
In response to Albro1
I prefer just setting a amount of times I want it to loop.
var/looped=0
while(whatever&&looped<5)
looped++
sleep(1)
Best response
If all you're doing is waiting for the player to make a choice, you should not be waiting in a loop. Instead, use the built-in loop that BYOND is already running just to maintain its environment.

The thing is, the player must trigger some kind of event, such as a verb, to make his choice. So, instead of waiting for the event, simply code that event to continue on to the appropriate code for when a choice has been made. This way, you avoid any lag-inducing while() loops altogether.

mob
verb
makechoice0()
ChoiceMade(0)

makechoice1()
ChoiceMade(1)

makechoice2()
ChoiceMade(2)

proc/ChoiceMade(var/choiceMadeNumber)
// Do Stuff


For choices in particular, you may find the input() proc to be handy. It actually pops open a window that gives the player the choices, and waits for them to select one before continuing. The only catch is that the player may wait a long time before choosing something, and that choice may not make sense if they wait too long. Be sure to code for that eventuality.

verb/LetsMakeAChoice()

var/playerChoiceMade input(usr,"What's your favorite number?") as number in list(1,2,3,4,5,6,7,8,9,10)

if (usr.playerDeathVariable == 1)
usr << "Silly, you dawdled so long to make a choice, you died somehow and can't do anything."
return() // return will terminate the proc here.

// else:
// Do stuff based on the players' choice.

See how much more powerful and compact input() is than having a string of verbs to cover multiple choices? Learn the ins and outs of the input() proc and it'll serve you well up until you get good at using skins.

The typical reason you would actually need to have an endless while loop going on and being slept is if there's something autonomous going on that does not involve the player's intervention. E.g. A mob's artificial intelligence moving around on its own.

In that case, I often find it's better NOT to have the while loop on the mob itself. This is because 1000 mobs waiting in sleep() will generate quite a bit of lag as BYOND goes down the internal list of all 1000 to see which one's number is up. Ever try putting a sleep(1) on something spawned by turf.New()? You'll create as many sleep processes as you have turfs in your game, which can be very laggy every time you first run it and the turfs are recreated.

A better solution would be to create a single loop (say, on the world) that processes all 1000 mobs at once. This would get all that work done much, much quicker because it is doing it without the overhead of each one checking to see if it's their turn. This also has the added bonus that you can just pause that loop when you want to pause the game (say, for saving and loading).
At risk of being a pimp: http://www.byond.com/developer/Stephen001/EventScheduling

That's a library that will do the last paragraph for you, and handle the global loop.

In this instance though, I'd hope you can actually structure this without a loop at all, using something akin to the ChoiceMade() method Geldonyetich first describes.
In response to Geldonyetich
By the way, in commands (like macros, button commands, etc) you can supply arguments to verbs like this:
mob
verb/make_choice(n as num)
// etc.

// Commands:
// make-choice 1
// make-choice 2
// make-choice 3
// etc.


Also, you can limit choices by using "in List" instead of "as num".
//  just an example
// generates a list of numbers
proc/ints(a, b)
. = list()
for(var/n in a to b)
. += n

// pick a number from list(1, 2, 3, 4)
mob/verb/choose(n in ints(1, 4))
set desc = "(a number) Pick one!"
src << "You chose [n]!"

// this will cause a run-time "warning"
// it's not technically a run-time error
// it's a client-side "you messed up" thing
mob/verb/do_stuff()
winset(src, null, "command='choose 5'")


Just some more cool things you probably wouldn't find anywhere other than these silly forums.
In response to Geldonyetich
Geldonyetich wrote:
See how much more powerful and compact input() is than having a string of verbs to cover multiple choices? Learn the ins and outs of the input() proc and it'll serve you well up until you get good at using skins.

I am already quite familiar with input. The reason I ask is this is specifically to do with awaiting an option in an interface select screen. I probably should have mentioned that I suppose.

I appreciate all the feedback but honestly I still haven't heard of a better way (Stephen's is a possibility but I'm not really interested in using libraries ATM though I appreciate it), so I'll just assume what I'm doing is fine. Thanks!
You can still basically just lock the mob until that response has been returned, and avoid the loop. Which again, is still basically using Geldonyetich's first method.
In response to Master Jack
Master Jack wrote:
I am already quite familiar with input.
No implication intended - I have no idea how good you are, but for the benefit of others I'll just assume the worst when writing a reply.

The reason I ask is this is specifically to do with awaiting an option in an interface select screen. I probably should have mentioned that I suppose.

Pretty much. It is highly dependent on context, which is why I took three separate stabs in the dark as to why you wrote that snippet.

From what I've heard so far (as Stephen001 and Kaiochao also recommend) you'll want to spare some effort to figure out how to code it in such a way as to have no looping involved.

At least if mitigating machine resources is really your focus here. Ever is there a fine line between "I want this to be efficient" and "if I spend so much time making this efficient I'll never finish my game."

Kaiochao wrote:
Some nice advanced examples.
1. How exactly does input pause the procedure? If I were to rewrite input, how would I have that input wait? Create a independent datum that just sleeps until deleted?

2. I appreciate the feedback from you guys. I am still unfamiliar with how well BYOND handles creation, and what is and isn't possible based on lag. For example does sleeping the while() loop in my first post by (2) instead of (1) have any huge impact on CPU demand? (In context of lag and not efficiency)

EDIT:

Oh, also it is interesting you mention the scenario of one loop handling all mobs V many loops handling each mob. One of my previous projects was being massively bogged down by the amount of mobs using path finding procedures, and I had actually changed it to a single loop handling all mobs as one of my first solutions. It did have benefits, but I didn't see any substantial difference in increased CPU efficiency.
In response to Master Jack
Master Jack wrote:
1. How exactly does input pause the procedure? If I were to rewrite input, how would I have that input wait? Create a independent datum that just sleeps until deleted?

Basically, you let the procedures terminate completely. Then, when input is received, you use that to activate the appropriate procedure. I included some examples in my first reply.

The key is to realize this: BYOND is already always waiting for player input - this is how verbs are activated. Consequently, you don't need to soft code your environment to wait for player input.

Using DM, you're not coding your environment from scratch like you would if you started without a SDK in a major programming language. Instead, you're using Dream Seeker, an already running environment, to run your game.

Can you soft code your own environments with BYOND? Yes, it's actually that flexible. However, being efficient in BYOND is getting the knack of working with the software rather than around it.

2. I appreciate the feedback from you guys. I am still unfamiliar with how well BYOND handles creation, and what is and isn't possible based on lag. For example does sleeping the while() loop in my first post by (2) instead of (1) have any huge impact on CPU demand? (In context of lag and not efficiency)

Naturally, in terms of CPU impact, sleep()ing is a lot less intensive to running code - it's practically been to reduced to one line, "Is it my turn yet? No? Then wait." However, a slept process is not maintenance free, because when you have hundreds or thousands of things checking to see if it's their turn yet, this will bring any program to a crawl. It's also easy to accidentally sleep the same thing twice (or more), which leads to a lot of problems.

So the goal in CPU efficiency is to neither sleep nor have code running, but rather have nothing running until an appropriate event (such as a player hitting a key) requires it.

Oh, also it is interesting you mention the scenario of one loop handling all mobs V many loops handling each mob. One of my previous projects was being massively bogged down by the amount of mobs using path finding procedures, and I had actually changed it to a single loop handling all mobs as one of my first solutions. It did have benefits, but I didn't see any substantial difference in increased CPU efficiency.

Without looking at it, it's hard to say what was going on, but I can say a few things about this:

* If the proc being run by this central loop spawned something that slept, you'll find yourself in equally dire straights, CPU-wise, to having looped something on the mobs themselves. In other words, in order for you to get a CPU benefit out of the one-loop-for-everything method, you're going to need to make sure everything ran by that loop immediately terminates before the loop continues to the next member: no spawn() or sleep() on any code executed by the loop on any single member of the list.

* Further efficiency can be realized by frontloading checks to see if anything needs to be done so the loop can quickly get to the next member; the less work each atom in a list does before moving on to the next, the quicker the loop will resolve.

* In the event where you have a LOT of things in a world loop, and a lot of work being done before the loop completes, you will indeed encounter lumpy performance, even if it is more efficient than sleeping each things (due to the added overhead of maintaining those slept processes). My advice there is to break apart the sleep() on that loop into stages. For example, a sleep(2) instead of a sleep(10), and handle 1/5th of the queue between each (the tricky part being measuring the queue). You'll still get a sleep (10) overall, but with a much evener distribution of performance.

-- Alternately, if you want player input to trump all and no interrupted animation speeds, ect, you could put a sleep() (no number in there) between every single thing being processed in the world loop. Naturally, this will slow down the game logic a bit as BYOND continually checks for new events. You're going to want to include your sleep(10) at the beginning or end of the loop if you want everything on that list to activate at most once per 10 tics.