Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

2.) MMO From Scratch - The AI

Joined
Jun 8, 2007
Messages
1,985
Reaction score
490
Previous (Introduction)
Table of Contents (Introduction)
Next (Plant)

When thinking of the living resources in Surface Tension, I didn't simply want the plants and animals to seem like they were thinking, I actually wanted to program them so they really were thinking. Grand Theft Auto uses statistical generation to make the world seem alive. In older versions of the game, you can see this when you look at the world and see some car and pedestrian objects. When you look away for a few seconds and look back, the cars and people will be different objects. The corvette you were planning on stealing may have been replaced by a police car!

Grand Theft Auto is a great game, and in later versions the memory they have to work with in the PS4 is much more than that of the PS2, so the statistical generation of objects is much less of a problem for the player. I think I'll be forced to use statistical generation later, but right now I want to code a living world where objects don't spontaneously appear and disappear based on what the players can see. One reason I'm choosing this strategy, is because this is an MMO- and I want to assume that every section of the map is being viewed by at least one player at all times- worst case scenario for performance. If the game can load now under those conditions, I (hopefully) won't be surprised in the future when it cannot.

This is a (bad?) decision that later forces me into premature optimization.

I got my idea for this AI structure when reading , where he talks about the different levels of intelligence measured by feedback loops. This is what I came up with.

Here is the base AI that all of the living resources have:

Code:
[COLOR=#008000][B]function[/B][/COLOR] Ai() {
    [COLOR=#BA2121]"use strict"[/COLOR];
    [COLOR=#008000][B]var[/B][/COLOR] self [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]this[/B][/COLOR];
    [COLOR=#008000][B]var[/B][/COLOR] feedbackLoops [COLOR=#666666]=[/COLOR] [];
    self.alive [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]true[/B][/COLOR];
    [COLOR=#008000][B]function[/B][/COLOR] doFeedback(fn) {
        [COLOR=#008000][B]if[/B][/COLOR] (self.alive) {
            fn.cycle();
        }
    }
    self.createFeedback [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] (Fn) {
        feedbackLoops.push([COLOR=#008000][B]new[/B][/COLOR] Fn(self));
    };
    self.cycle [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] () {
        feedbackLoops.forEach(doFeedback);
    };
}
module.exports [COLOR=#666666]=[/COLOR] Ai;

So, every AI instance has a method to create feedback loops and a method to cycle through those feedback loops.

A brain has 2 hemispheres that function independently, sharing some information when needed. How a real brain decides which hemisphere to use to make a conscious decision or whether to use both or not, is something I don't quite understand. In any case, this is the code for the Brain in Surface Tension:

Code:
[COLOR=#008000][B]var[/B][/COLOR] Ai [COLOR=#666666]=[/COLOR] require([COLOR=#BA2121]'./ai'[/COLOR]);
module.exports [COLOR=#666666]=[/COLOR] ([COLOR=#008000][B]function[/B][/COLOR] () {
    [COLOR=#BA2121]"use strict"[/COLOR];
    [COLOR=#008000][B]function[/B][/COLOR] nil() {
        [COLOR=#008000][B]return[/B][/COLOR];
    }
    [COLOR=#008000][B]function[/B][/COLOR] Decision(ai) {
        [COLOR=#008000][B]var[/B][/COLOR] self [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]this[/B][/COLOR];
        ai.decisions [COLOR=#666666]=[/COLOR] [];
        ai.addDecision [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] (dec) {
            [COLOR=#008000][B]var[/B][/COLOR] decision [COLOR=#666666]=[/COLOR] {
                title[COLOR=#666666]:[/COLOR] dec.title [COLOR=#666666]||[/COLOR] [COLOR=#BA2121]"unknown"[/COLOR],
                weight[COLOR=#666666]:[/COLOR] dec.weight [COLOR=#666666]||[/COLOR] [COLOR=#666666]0[/COLOR],
                args[COLOR=#666666]:[/COLOR] dec.args [COLOR=#666666]||[/COLOR] [],
                action[COLOR=#666666]:[/COLOR] dec.action [COLOR=#666666]||[/COLOR] nil
            };
            ai.decisions.push(decision);
        };
        self.cycle [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] () {
            ai.decisions [COLOR=#666666]=[/COLOR] [{
                weight[COLOR=#666666]:[/COLOR] [COLOR=#666666]0[/COLOR],
                action[COLOR=#666666]:[/COLOR] nil,
                title[COLOR=#666666]:[/COLOR] [COLOR=#BA2121]"do nothing"[/COLOR]
            }];
        };
    }
    [COLOR=#008000][B]function[/B][/COLOR] Remember(ai) {
        [COLOR=#008000][B]var[/B][/COLOR] self [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]this[/B][/COLOR];
        ai.memories [COLOR=#666666]=[/COLOR] {};
        self.cycle [COLOR=#666666]=[/COLOR] nil;
    }
    [COLOR=#008000][B]return[/B][/COLOR] [COLOR=#008000][B]function[/B][/COLOR] Brain() {
        [COLOR=#008000][B]var[/B][/COLOR] self [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]this[/B][/COLOR];
        self.right [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]new[/B][/COLOR] Ai();
        self.left [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]new[/B][/COLOR] Ai();
        self.left.sync [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] (prop) {
            [COLOR=#008000][B]var[/B][/COLOR] chain [COLOR=#666666]=[/COLOR] prop.split([COLOR=#BA2121]'.'[/COLOR]);
            [COLOR=#008000][B]var[/B][/COLOR] i;
            [COLOR=#008000][B]var[/B][/COLOR] l [COLOR=#666666]=[/COLOR] self.left;
            [COLOR=#008000][B]var[/B][/COLOR] r [COLOR=#666666]=[/COLOR] self.right;
            [COLOR=#008000][B]for[/B][/COLOR] (i [COLOR=#666666]=[/COLOR] [COLOR=#666666]0[/COLOR]; i [COLOR=#666666]<[/COLOR] chain.length [COLOR=#666666]-[/COLOR] [COLOR=#666666]1[/COLOR]; i [COLOR=#666666]+=[/COLOR] [COLOR=#666666]1[/COLOR]) {
                r [COLOR=#666666]=[/COLOR] r[chain[i]];
                l [COLOR=#666666]=[/COLOR] l[chain[i]];
            }
            r[chain[chain.length [COLOR=#666666]-[/COLOR] [COLOR=#666666]1[/COLOR]]] [COLOR=#666666]=[/COLOR] l[chain[chain.length [COLOR=#666666]-[/COLOR] [COLOR=#666666]1[/COLOR]]];
        };
        self.right.sync [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] (prop) {
            [COLOR=#008000][B]var[/B][/COLOR] chain [COLOR=#666666]=[/COLOR] prop.split([COLOR=#BA2121]'.'[/COLOR]);
            [COLOR=#008000][B]var[/B][/COLOR] i;
            [COLOR=#008000][B]var[/B][/COLOR] l [COLOR=#666666]=[/COLOR] self.left;
            [COLOR=#008000][B]var[/B][/COLOR] r [COLOR=#666666]=[/COLOR] self.right;
            [COLOR=#008000][B]for[/B][/COLOR] (i [COLOR=#666666]=[/COLOR] [COLOR=#666666]0[/COLOR]; i [COLOR=#666666]<[/COLOR] chain.length [COLOR=#666666]-[/COLOR] [COLOR=#666666]1[/COLOR]; i [COLOR=#666666]+=[/COLOR] [COLOR=#666666]1[/COLOR]) {
                r [COLOR=#666666]=[/COLOR] r[chain[i]];
                l [COLOR=#666666]=[/COLOR] l[chain[i]];
            }
            l[chain[chain.length [COLOR=#666666]-[/COLOR] [COLOR=#666666]1[/COLOR]]] [COLOR=#666666]=[/COLOR] r[chain[chain.length [COLOR=#666666]-[/COLOR] [COLOR=#666666]1[/COLOR]]];
        };
        [COLOR=#008000][B]function[/B][/COLOR] findDecision(list) {
            [COLOR=#008000][B]var[/B][/COLOR] i;
            [COLOR=#008000][B]var[/B][/COLOR] decision [COLOR=#666666]=[/COLOR] list[[COLOR=#666666]0[/COLOR]];
            [COLOR=#008000][B]for[/B][/COLOR] (i [COLOR=#666666]=[/COLOR] [COLOR=#666666]0[/COLOR]; i [COLOR=#666666]<[/COLOR] list.length; i [COLOR=#666666]+=[/COLOR] [COLOR=#666666]1[/COLOR]) {
                [COLOR=#008000][B]if[/B][/COLOR] (decision.weight [COLOR=#666666]<[/COLOR] list[i].weight) {
                    decision [COLOR=#666666]=[/COLOR] list[i];
                }
            }
            [COLOR=#008000][B]return[/B][/COLOR] decision;
        }
        [COLOR=#008000][B]function[/B][/COLOR] decide() {
            [COLOR=#008000][B]var[/B][/COLOR] decision;
            [COLOR=#008000][B]var[/B][/COLOR] context;
            [COLOR=#008000][B]var[/B][/COLOR] lDecision [COLOR=#666666]=[/COLOR] findDecision(self.left.decisions);
            [COLOR=#008000][B]var[/B][/COLOR] rDecision [COLOR=#666666]=[/COLOR] findDecision(self.right.decisions);
            [COLOR=#008000][B]if[/B][/COLOR] ([COLOR=#666666]![/COLOR]self.left.alive [COLOR=#666666]||[/COLOR] [COLOR=#666666]![/COLOR]self.right.alive) {
                [COLOR=#008000][B]return[/B][/COLOR];
            }
            [COLOR=#008000][B]if[/B][/COLOR] (lDecision.weight [COLOR=#666666]<[/COLOR] rDecision.weight) {
                decision [COLOR=#666666]=[/COLOR] rDecision;
                context [COLOR=#666666]=[/COLOR] [COLOR=#BA2121]"right"[/COLOR];
            } [COLOR=#008000][B]else[/B][/COLOR] {
                decision [COLOR=#666666]=[/COLOR] lDecision;
                context [COLOR=#666666]=[/COLOR] [COLOR=#BA2121]"left"[/COLOR];
            }
            self[context].memories.lastAction [COLOR=#666666]=[/COLOR] decision.title;
            [COLOR=#008000][B]return[/B][/COLOR] decision.action.apply(self[context], decision.args);
        }
        self.createFeedback [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] (fn) {
            self.right.createFeedback(fn);
            self.left.createFeedback(fn);
        };
        self.cycle [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] () {
            self.right.cycle();
            self.left.cycle();

            [COLOR=#008000][B]return[/B][/COLOR] decide();
        };
        self.createFeedback(Remember);
        self.createFeedback(Decision);
    };
}());

The brain has the ability to store memories and make decisions. It also has a right and a left AI- each with the ability to create independent feedback loops. The brain has it's own createFeedback method which will create the same feedback loop on both left and right. Finally, the brain has a cycle method that will cycle through the right, then the left, and return a decision based on the weight of that decision- where a higher weight will be chosen as the decision to execute. The result of that decision is returned.

There is also a way to sync memories from one side of the brain to the other. Memories are not shared between the left and right sides.

Now, every living resource with a brain will have at least 4 feedback loops (Remember and Decision feedback loops assigned to both right and left).

So, this is the base for the AI. Next I'll talk about setting up the basis for all plants and the basis for all herbs. What do plants have to think about? They are required to reproduce automatically, how will they do that?

Previous (Introduction)
Table of Contents (Introduction)
Next (Plant)
 
Last edited:
Elite Diviner
Joined
Mar 24, 2015
Messages
426
Reaction score
416
This is a really ambitious and interesting project! Have you considered integrating you AI idea with some kind of evolution system? As in, an Ai is born with certain characteristics, some inherited from it's parents, or even communicated with other AIs. These characteristics could then influence how decisions are made. And the more of a positive effect on survival the characteristics have, the more they could spread among a type of being in your world.
 
Joined
Jun 8, 2007
Messages
1,985
Reaction score
490
Is the kind of AI used in building monster AI in MMORPG emulators like TrinityCore?
I thought up this AI all by myself, and it has some major flaws, so I would hope nobody uses a style like this in a real MMO. It was really fun to think of AI as a sort of brain for each instance, but that's very inefficient and there are 1000s of redundant cycles taking place even with just a handful of instances.
 
Initiate Mage
Joined
Oct 7, 2016
Messages
14
Reaction score
2
Decision making for living objects in game would be a huge system issue,
whether you give the calculation work to the server or the client,
The only thing i found this working in the best way is actually implement an AI server and run it in a dedicated environment,
However, AI and decision making will require more data for better accuracy,
Machine learning will be able to help in this accuracy issue,
but you have to feed data for this AI server to grow,
if there's no enough data, then this server will stay as dumb as the jellyfish.
 
Joined
Jun 8, 2007
Messages
1,985
Reaction score
490
You're absolutely right. I wasn't really aware of machine learning when I worked on this project. It should be possible to use the I/O from this and connect it to a machine learning model instead of the classical AI mentioned here. Instead of manually managing the feedback loops like I mentioned above, it would be much more cool to teach a model how to handle situations where it can see a list of things, and output an appropriate next action.

Connecting something very similar to what is created in this video might be fascinating:
 
Last edited:
Back
Top