Top Mud Sites Forum Return to TopMudSites.com
Go Back   Top Mud Sites Forum > Mud Development and Administration > MUD Coding
Click here to Register

Reply
 
Thread Tools
Old 02-07-2011, 04:37 PM   #1
Ioncannon
New Member
 
Join Date: Dec 2010
Posts: 2
Ioncannon is on a distinguished road
Handling AI

What is the best way to handle massive amounts of spawned monster's AI?

The current way I have is a linear handling of a list of mobs. In a seperate thread, I search for "awake" monsters. If they are awake, a doAI() function is executed. Otherwise the mob is skipped. Whenever a player walks into a room with a monster, that monster wakes up. If the monster is idle for a set amount of time (10-20sec), it goes to sleep.

The problem is a monster at the beginning of the list will get it's AI run faster, then one at the end. Also the more mobs spawned, the longer AI takes to complete.

The other solution would be to have each monster run their AI in a separate thread. But having 500 threads running at the same time would be a terrible idea right?
Ioncannon is offline   Reply With Quote
Old 02-07-2011, 06:41 PM   #2
silvarilon
Member
 
Join Date: Dec 2009
Posts: 144
silvarilon is on a distinguished road
Re: Handling AI

Instead of "searching" for awake monsters, why not keep an array of them. Then each "tick" you can just run through the array, and the sleeping monsters won't add to the processor overhead.

As they "wake up" you just add them to the array, and remove them as they go to sleep.

Why would monsters at the beginning get the AI run faster? Wouldn't you go through the whole list before hitting the start and running the first one again?

More mobs means longer AI taking to complete, yes. But I'd be surprised if it takes long at all, even with a lot of awake mobs.

I do mine with signals and timers.
The "awake" monster runs its code, and then says "sleep for 3 seconds before continuing this code"
and then anything else awake runs its code, and either completes or sleeps for a period of time.
After the 3 second delay, it continues the code from that point.

If a lot of things are awake at the exact moment, or one thing is doing a lot of processing, then there can be lag in the game until that process finishes. But otherwise, there's no additional lag because of the sleeping, just one process that watches when the time runs out, and the monsters should run more code.

So five hundred monsters each sleeping for 2 seconds can cause lag (since there's about 250 "wake up, and run code" calls every second) but 500 monsters each sleeping for 5 minutes doesn't cause any lag (because there's about 0.6 "wake up, and run code" calls every second)
This means that even the "out of sight" monsters can be doing things. For example, a spider monster might wander around for a while, stop, then lay eggs, one every five hours. The longer it takes the PCs to find it, the more eggs they will have to deal with.

We also have non-timer-based signals. I can tell the monsters "run this code if someone walks into the room" or "run this code if someone pokes you"

(and actually, this isn't limited to monsters. This is how I handle all code in objects, including monsters, PCs, props, etc. - for example, a fire would go to sleep, wake up, run code saying "emit that the fire crackles" before going to sleep again. After a certain time it might emit that the fire has died down, and stop running any code. At least until it reacts to someone doing a "build fire" action, where it might start again.)
silvarilon is offline   Reply With Quote
Old 02-07-2011, 07:54 PM   #3
Ioncannon
New Member
 
Join Date: Dec 2010
Posts: 2
Ioncannon is on a distinguished road
Re: Handling AI

Hmmm a seperate awake array would actually a great way to reduce the amount of time AI takes. Thanks.

I am doing a UNIX programming course at my university, and we just began talking about signals, however I don't know much about them (yet). The MUD I am building is coded in Java, and I am not sure how you would pause the AI of one mob and go do the next one.

This is how mine works:

I have a hashtable of every entity (thing that is not a player) currently spawned. Extending entities are class types: monster, item, and object. Objects is a miscellaneous entity which can be something the user interacts with. These three types can be extended to define what exactly the monster or item is. I used a getType() function to figure out which of the entities are monsters. All spawned entities are called by an ID.

This part is what I plan (not done yet):

Now for monsters, there is a doAI() function that is overrided with template AIs (Aggressive, Passive, Cowardly, Subtle, etc), or can be customized with a specific AI routine (for example bosses). The AI also has various states it can be in (near death, scared, angry, running) which change the ai. When the ai thread finds a awake monster, it runs this function.

One of the problems I always have is having various parts of the code talk to each other. Currently for testing, I have it setup so when the player walks into a room, all monsters wake up. I guess having all actions get sent to the mob, and then based on AI preferences, ignoring some and activating on others would be a way to do it.

Running AI w/o player interaction I assume is what you used timers for?

Sorry if the message sounds all over the place, getting late, and really tired.
Ioncannon is offline   Reply With Quote
Old 02-08-2011, 07:06 PM   #4
silvarilon
Member
 
Join Date: Dec 2009
Posts: 144
silvarilon is on a distinguished road
Re: Handling AI

Glad the idea is useful

Any reason why you don't include the players?
If you include the players in that list, then you'll be able to drop code onto the players, same as you can on items or mobs.

Which seems silly, but it's super useful.
For example, a swamp-gas-monster might explode and die if it's hit with any flaming weapons.
What if there's some spell or curse that turns a player into a swamp-gas-monster? If you can run the same code on the players, you have that spell just drop the code onto the player, and then you stop thinking about it - knowing that the code from the monsters will also work equally well on the players.

A less extreme example would be a "berserk" creature or player - When someone (mob or player) goes berserk, maybe instead of attacking their intended target, 30% of the time they instead attack a random target in the room.
Having the same code means that your monsters can attack other monsters as easily as attacking players. Similarly, it means a berserk player can attack other players as they would attack monsters.

I don't have any particular comments on this, since it's quite different to what I do.
What I've got is a parenting chain set up. Everything is just "an object" - but a specific sword might have a generic sword as the parent. That generic sword might have a generic weapon as the parent. That generic weapon might have a generic "item you can carry" as parent, and so on.
A specific male PC might have a generic male PC object as parent. And that would have a generic human object as parent.

That way I can put code for how weapons work in the generic weapon, but put specific code about how swords are different in the generic sword. And I can even put custom code about this *specific* sword in the sword that the player is holding. (usually code would do that. Enchant the sword to burst into flames, and the "this is burning" code would be put into the specific sword)

Similarly, code for logging in and out goes in the generic human object (since all players will, ultimately, have the generic human as a parent somewhere along the way)

Makes sense to me

Can you do introspection?
A dirty example... you've got different verbs the players can do.
- look at dragon
- stroke dragon
- kick dragon
- attack dragon

What if you use introspection to check if the respond_to_VERB() function exists in the dragon object?
So you look at it. respond_to_look() exists (I assume you'll make all objects respond to look, so people can see what they are...) - it runs the code, and gives the player a description.
You stroke it. It has no respond_to_stroke() function, so it doesn't run any code (and the player just sees a general "you stroke the dragon" message. Whatever they'd normally see when stroking an item.)
You attack it. It has respond_to_attack() which makes it go into combat.
You kick it. It has respond_to_kick() which makes it glance at the player with annoyance. It also counts how many times you've kicked it. Kicking it three times makes it attack. (or maybe every kick has a random chance of it attacking)

Fairly simple setup, where you only add in responses to the verbs that you want to code in. But already you're opening up new options. Instead of just fighting monsters... you've now got a new group quest possibility.
The town boys are boasting about how brave they are. They challenge you to go unarmed, and kick the dragon. Whoever is brave (or stupid) enough to kick the dragon the most times wins the title "dragon kicker" - at least until someone else kicks the dragons more times than them at takes the title.

You can see how adding a quest like that might be trivially easy. In the kick dragon, save a property about who most recently kicked it, and how many times. Also save the current high score and title holder.
If they kick it enough that it attacks them, reset their score to 0 when they die.
If they kick it enough that they beat the high score, update the current title holder to no longer have the title, and set them with the title and set them as the current holder.

Easy code that can be written in five minutes, once the underlying infrastructure is in place.


Often, yes.
But timers can also be used when interacting with players.
For example, imagine if you are attacking a goblin castle.
You burst into the main hallway. Two trolls attack you. A goblin runs to the crank, and starts lowering the portcullis.
We might use a timer here. Every 20 seconds it gives you a message about the goblin still lowering the portcullis. After long enough, the portcullis slams shut, and you can't get any deeper into the castle. If you kill the goblin before the portcullis is down, then it'll stop lowering, and you can continue your raid.

Of course, the trolls won't let you attack the goblin while they're still alive. (special code in the trolls or in the goblin) So you will need to use some sort of tactics. Bring three fighters (the trolls can only stop one fighter at a time) so the third can kill the goblin. Or use once-off magic items that help you kill the trolls faster, or something.

Timers might also be used for "loops" - for example, a squirrel mob's AI function might tell it "hide a nut" (while it's awake and the players are in the room. No point having squirrels hiding nuts while no players are around to witness it. A tree falling in the forest, and all that...)
So the squirrel gives a message about finding a nut. Then the timer makes it delay for a few seconds, before it looks around, another delay, it runs up a tree, another delay, it hides the nut, another delay, it scurries down from the tree, a final delay, it scurries out of the room.
All of that happens while the players are there (unless the players walk away) - the timer is just used to make the events happen in the right sequence. Makes it more interesting than the squirrel just having random messages like "A squirrel hides a nut"

Of course, I don't expect you to structure your game in exactly the same way as mine. But hopefully this is giving you ideas. Use whatever works best for you
silvarilon is offline   Reply With Quote
Reply


Thread Tools


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off

All times are GMT -4. The time now is 09:23 AM.


Powered by vBulletin® Version 3.6.7
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Style based on a design by Essilor
Copyright Top Mud Sites.com 2022