ID:152763
 
Kunark gave me some sweet ideas for intelligent AI for my game on my members blog. I consider myself decent at making code do what I want it to, but most of the time it isn't in the most efficient way and I end up with obsessive lag. So what I'm wandering is are there any tips for writing optimized/optimizing code? Maybe somethings not to do? I'd really appreciate any help because I usually quit my projects because there is too much lag. I'm not talking about a few lines of coding like simple verbs, I mean long procedures, such as the AI system I developing.

-FFG
It really depends on how you are going to do your AI system. Nobody can suggest something specific on an AI system you might make, when we know nothing about it. If you are worrying there might be some lag in it, you obviously would be doing something wrong. AI systems should not have noticable lag unless either you have many unefficient systems running similtaneously. Some suggestions to optimizing your programming is to take advantage of lists, associations, and how to manage their indexes. Otherwise, we can't give you that many suggestions other than the very general tips to use.

~~> Dragon Lord
In response to Unknown Person
Yea I was mainly just asking for general rules for things such as if statements and for() loops. Um, what do you mean by indexes?
In response to FinalFantasyGamer
I pretty much meant how to recall elements in a list, and manipulating them (like their associations). What I suggest is to start creating your artificial intelligence, and start questioning stuff which might be slow for you. Generally programming stuff in DM wouldn't be that slow to optimize your programs until you find it is being a pain in the neck. For example, you might need to multiply something by two without accuracy being a problem, you could use a bitwise operator to speed it up a little. It really depends on what you might need to speed up on. As I said, don't worry about speed until you meet some conflicts with them.

~~> Unknown Person
In response to Unknown Person
Before you start running into conficts, though, remember that if your code is not flexible, it could mean hours and hours of work. If it IS flexible, it could mean editing one variable in the proc and being done.

The kind of flexibility method I started using in my last 3 games goes like this:


have all mobs use the same movement proc (in which you define). Have them all use the same proc to take damage. Have them all use the same proc to calculate damage, etc.


in other words,

Flexible:

mob/proc/Attack(mob/Victim)
Damage(src,Victim,src.Strength)

mob/Warlord/Attack()
Damage(src,Victim,src.Strength*3)

proc/Damage(mob/Attacker,mob/Victim,Amount)
Victim.HP -= Amount
DeathCheck(Attacker,Victim)

//This way, you'd be able to edit Damage() and it'd effect all mob's damage procs, but mobs will still have uniqueness


Not Flexible:

mob/proc/Attack(mob/Victim)
Victim.HP -= src.Strength
if(Victim.HP <= 0)
Victim.icon_state = "Dead"

mob/Warlord/Attack(mob/Victim)
Victim.HP -= src.Strength
if(Victim.HP <= 0)
Victim.icon_state = "Dead"

//What not being flexible means is that the code would be neaar impossible to edit without
//completely refurbishing it like you'd have to do if your game was very large with this type of code.




Okay, now for AI, generally what you need to do is make sure all the guys in the world aren't running at the same time. Of course if you avoid it too much, the AI starts to look unnatural and bad or slow. What I am planning on using in my RPG is a system that will make only a z area of mobs appear when players first go to that area, and then when Players get near the mobs, it will activate the mob's AI. Activation is alright to have the mob sit there until the player is in view, but deactivation has to be done a bit differently or the monsters will be extremely easy to escape from.

(Note that I may not use this exact system, it's only my plan to.)

For activation, they will activate via the player's movement proc or regeneration proc:

mob/proc/MoveMe(turf/T)
src.Move(T)
for(var/mob/Monster/M in oview(src))
M.AI()
mob/Monster/proc/AI()
if(!src.Moving)
src.Moving = 1
while(src.Moving&&src.icon_state != "Dead")
//...AI stuff.
sleep(src.AIDelay)


For deactivation, I will use x y z variables to determine if the player is anywhere near the monster:

mob/proc/MoveMe(turf/T)
src.Move(T)
for(var/mob/Monster/M in oview(src))
M.AI()
mob/Monster/proc/AI()
if(!src.Moving)
src.Moving = 1
while(src.Moving&&src.icon_state != "Dead")
//...AI stuff.
var/a = 0
for(var/mob/Player/P in PlayerList)
if(IsClose(P,src))
a = 1
break
if(!a)
src.Deactivate()

sleep(src.AIDelay)
mob/Monster/proc/IsClose(mob/M,mob/M2)
if((M.z == M2.z)&&(M.x >= M2.x-20||M.x <= M2.x+20)&&(M.y >= M2.y-20||M.y <= M2.y+20))
return 1
return 0
In response to Kunark
One general Tip i would like to give, remember it always really kicked me in the but when i wasnt really good in coding, is that you use for() instead of while()
I don't know why but along the way i learned to do this and many might disagree with me but i tend to bann while().

Basic use of for()
var/i
for(i = 10 , i > 0 , i --)
world << "Hi there!"
world << "Done greeting!"
//sets i to be 10
//if i > 0 it will show "Hi there!"
//after that decrease i with 1
//check the same thing again untill i <= 0
//then it displays "Done greeting!"

Get the idea?

Oh and if you do use while() make sure you build in some sort of delay. My experience is that a while() proc without a delay prevents anything else from happening if it has to do alot of things for a "long" while.
A simple sleep(1) makes a big difference.
Don't use this last thing as a "general tip" though, its ment to be a pointer.

Hope it will help a bit!

[Fint]
In response to Fint
Firstly, that's not an optimisation; that's obfuscation. Secondly, double-yew tee eff mate?

There's no reason not to use while() if it's the right situation. for() loops are right for some situations, and while() loops are right for others. Sure, one can be used in place of the other, but then you're just confusing people who read your code (including yourself in the future, once you've forgotten what the code was supposed to do).

I think what you're trying to get at is to make sure that:

1. All loops end.
2. If they don't end, then (A) this should be deliberate and (B) they must have sleep() in them at regular intervals.

However, randomly replacing while() loops with for() loops isn't the way to do this.
In response to Crispy
Well since english ain't my first language I guess i had a hard time explaning it.
Its not optimalisation indeed, but i don't think the problem of his games being "lagging" has anything to do with optimalisation, more to do with propper use of loops.
So i thought explaning the for() proc for him might help and remind him to . . . well to keep in mind #2 on your list to make sure his games don't end up lagging.

Crispy wrote:
However, randomly replacing while() loops with for() loops isn't the way to do this.

Not what i tried to say, but you got the point.

Anyways, thanks for kinda clearing things up.
In response to Fint
My experience is that a while() proc without a delay prevents anything else from happening if it has to do alot of things for a "long" while.
A simple sleep(1) makes a big difference.

However you have to remember that when you sleep it lets other things happen so references which were once good might have been deleted(or the object they are referenceing to has changed) during the delay. So after you sleep it is best to check any references which could have potentially changed during the wait.
In response to Fint
To clear it up for everyone, this is how it goes:


for() is used for going through lists from beginning to end, like this:

for(var in list)

That is a quicker method for accessing through list elements than the one below, but the one below can be useful in rare situations, particularly when going backwards through a list:

var/list/L = list()
for(var/i = 10,i > 0,i--)
world << L[i]
//That would display a list backwards.


Now, with this AI, however, we aren't accessing list elements. In this case, here's the general rule: for() is used for ending the loop after a countdown, such as if you'd need to create a certain number of objects, while() is used whenever you only want the loop to end after a condition (which can much more easily be something other than a number, unlike for() loops) has been met. These two can be interchanged with each other, but generally while() loops are easier to use and easier to interchange.

while(src&&src.Moving == 1)
//this will be much easier and more logical to write than the loop below.
sleep(1)
for(var/i = 1,i > 0,i++) //infinite loop
if(!src||src.Moving == 0)
break
In response to Kunark
Kunark wrote:
for(var/i = 0,i < 0,i++) //infinite loop

Actually, that won't execute at all, because it only executes if i<0; but i is always positive or zero. =)

But otherwise, yes.
In response to Crispy
Woops, fixed.
In response to Kunark
for(var/i = 1,i > 0,i++) //infinite loop
if(!src||src.Moving == 0)
break

And even simpler infinite loop :)

for(;;)

[Edit]
Actually I think for() also works.
In response to Theodis
Theodis wrote:
for(var/i = 1,i > 0,i++) //infinite loop
if(!src||src.Moving == 0)
break

And even simpler infinite loop :)

for(;;)

[Edit]
Actually I think for() also works.

I personally like
#define EVER ;;
so you can use
for(EVER)
But then, whenever I do infinite loops I just use while(src) or while(world) anyway.
In response to Kunark
for(var/i = 1,i > 0,i++) //infinite loop

I wouldn't actually recommend this, simply because it's doing a whole bunch of needless operations.

A for loop like this:

for(Initialise(), Condition(), Statement())
PerformLoopContents()


translates to the following:

Initialise()
Start:
if(Condition())
PerformForLoopContents()
Statement()
goto Start


Notice those extra computation steps in there; the Initialise() and the Statement() procs are both being executed.

A while loop, on the other hand, such as this:

while(Condition())
PerformWhileLoopContents()


translates to the following:

Start:
if(Condition())
PerformWhileLoopContents()
goto Start


As you can see, there is no initialisation or additional statement to worry about.

Now, if you're not defining the initialisation, condition, or statement for the for() loop, then it's no different than using a while loop (I'm assuming that BYOND doesn't insert do-nothing statements, and optimises the bytecode). But if you are defining them, then it's less efficient than using a while loop.