Jump to content
  • Advertisement
Sign in to follow this  
dxFoo

Producing intelligence on meters?

This topic is 4611 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I recently bought a new book titled "On Intelligence" by Jeff Hawkins. It's a great book. For those who haven't read it, he teaches a new theory on how we can't build intelligent machines until we figure out how the brain works. I'm in the third chapter right now, but like anyone else, I began expirementing immediately with it. Following loosely on the book, I wrote the following program. I'll ask my question after you view it.
// Description: Shoot several arrows and tell how 
// much arrows he shot from memory. 

#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;

class NPC
{
private:
	// Memory storage
	struct Memory
	{
		int arrowsShot;
	};	
public:
	Memory memory;	// Stores all memory!
	
	NPC()
	{
		// Reset memory like a newborn baby haha
		memory.arrowsShot = 0;
	}

	void shootArrow()
	{
		this->memory.arrowsShot += 1;	// Increment a knowledge of an arrow shot.
	}

	int getArrowsShot()
	{
		return this->memory.arrowsShot;
	}

	void getArrowsShotDlg()
	{
		cout << "I shot " << this->memory.arrowsShot << " arrows in the past." << endl;
	}
};

int main()
{
	NPC N1;
	N1.shootArrow();
	N1.shootArrow();
	N1.shootArrow();

	N1.getArrowsShotDlg();		

	system("pause");
	return 0;
}



I'm wondering how machines will decide to do things without the programmer telling it what to do. In this case, I'm telling my NPC to shoot an arrow three times. I shouldn't have to tell it to do anything. I also shoudln't rely on a method like "GetRandomNumberToProduceAIResult()." That's not how we work. If you played The Sims, you notice they did things based on meters. If there was no entertainment, and their need for entertainment was high, they'd wave at you until you do something about it. Instead of telling the NPC here on what to do, would the NPC be better off deciding what to do based on meters? This is my second day practicing AI-related stuff, and I'm sure this has been a question for years, but it'd be great to hear other views on this than myself. Thanks, Phil

Share this post


Link to post
Share on other sites
Advertisement
The only way I can think of is introducing randomness, but then having it learn and adjust the random values. To do that, you will need a fitness function to kind of give a "score" on how well it's doing and a way to relate it to the values. This is how neural nets work, I believe (well, part of them, anyway).

Share this post


Link to post
Share on other sites
Hmm I'm not sure if you meant this, but it sparked in my head while reading it... each NPC is initialized with random numbers stored in each meter. This way, each NPC is different. Then their meter percentages increment/decrement throughout the day. What would make them drop/increase though? Again, the programmer is at work for the NPCs. I'm not really thinking of just games, but robots too.

My idea in code: (new code is marked in comments)
Each new NPC starts out with a different entertain percentage.


// Description: Shoot several arrows and tell how
// much arrows he shot from memory.

#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime> // NEW
using namespace std;

namespace // NEW
{
int RANGE_MIN = 0;
int RANGE_MAX = 100;
}

class NPC
{
private:
// Memory storage
struct Memory
{
int arrowsShot;
};

int entertainMeter;
public:
Memory memory; // Stores all memory!

NPC()
{
// Reset memory like a newborn baby haha
memory.arrowsShot = 0;
// NEW
entertainMeter = (((double) rand() /
(double) RAND_MAX) * RANGE_MAX + RANGE_MIN);

}

void shootArrow()
{
this->memory.arrowsShot += 1; // Increment a knowledge of an arrow shot.
}

int getArrowsShot()
{
return this->memory.arrowsShot;
}

void getArrowsShotDlg()
{
cout << "I shot " << this->memory.arrowsShot << " arrows in the past." << endl;
}

int getEntertainPercent() // NEW
{
return this->entertainMeter;
}
};

int main()
{
srand((unsigned) time(NULL)); // NEW

NPC N1;
N1.shootArrow();
N1.shootArrow();
N1.shootArrow();

N1.getArrowsShotDlg();

cout << N1.getEntertainPercent() << endl; // NEW

system("pause");
return 0;
}


Share this post


Link to post
Share on other sites
You need to build a personality trait database I think. Store things like how easily they get bored (ADHD!), if they need social interaction (programmers go days without seeing another human and are happy, others need constant interaction with humans/pets, etc). There are rules that govern us based on the above, sure in this case they will be programmer based rules. Ours are genetics programmed over generations.

Share this post


Link to post
Share on other sites
Okay, I made a struct called Meter which holds personality traits & needs, such as entertainment. I guess the game loop could could check their current needs. But how do NPCs change these needs on a minute basis? Should it really be me (the programmer) that increments/decrements these needs for them?

Share this post


Link to post
Share on other sites
Here's some pseudo-code that demonstrates my idea (note: I am going to use a different example which demonstrates my idea better. My idea requires feedback about how well the AI is doing):

class NPC;

enum Action {
MOVING,
EATING,
// etc
};

class Behavior {
protected:
float fitness;

std::map<Action, float> actions; // The likely hood to do an action.
public:
void apply(NPC& npc) {
// Randomly preform the actions, based on the likelyhood map, and find a fitness.
}

void setAction(Action action, float percentage) {
// ... Set the action ...
}

float getFitness() {
return fitness;
}
};

class NPC {
Behavior currentBehavoir;
public:
NPC() {
currentBehavior.setAction(MOVING, 0.5);
currentBehavior.setAction(EATING, 0.5);
// ...
}

void update() {
currentBehavior.apply();
Behavior newBehavior;
// Randomize newBehavior.
newBehavior.apply();
if (newBehavior.getFitness() > currentBehavior.getFitness()) {
currentBehavior = newBehavior;
}
}
};




This goes against a few OO design principles, and (ideally) you'd find a way to calculate a better behavior rather than randomly generate it, but that's the general idea. Lookup genetic algorithms and neural nets (neural nets kind of build off of GAs, so look at them first).

Share this post


Link to post
Share on other sites
You should think of why you need entertainment, food, company, etc.

For example, if the NPC decides there is nothing to watch on the TV but still needs to be entertained, you could make it go see a movie. Of course making the NPC decide whether it likes what is on TV or not is another matter that would be based on how the NPC evolved over its life, yet another problem you will need to address.

Share this post


Link to post
Share on other sites
Cool :) Check this out...


#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>
using namespace std;

int randNum(int min, int max)
{
return (int) (((double) rand() / (double) RAND_MAX) * max + min);
}

// Analyze input buffer.
void sortBuffer(NPC &N1, std::string &buffer)
{
if (buffer.find("/stats") != std::string::npos)
N1.showStats();
}

class NPC
{
private:
// Stores the NPC's memory.
struct Memory
{
int arrowsShot;
};

// Stores the NPC's needs. 0 is low, 100 is high.
struct Needs
{
double entertainment;
double hunger;
double tired;
double dirty;
};

// Private variables.
std::string name;
int age;

public:
Memory memory; // Where memory is stored at!
Needs needs; // Stores all the NPC's needs.

// NPC constructor.
NPC(std::string name, int age)
{
// Initialize general information about the NPC.
this->name = name;
this->age = age;

// Initialize memory and needs.
memory.arrowsShot = 0;
needs.entertainment = randNum(1, 100); // Dummy.
needs.entertainment = randNum(1, 100); // Call RandNum() again to get a non-fixed return.
needs.hunger = randNum(1, 100);
needs.tired = randNum(1, 100);
needs.dirty = randNum(1, 100);
}

// Shoot an arrow.
void shootArrow()
{
memory.arrowsShot += 1; // Increment arrows shot in memory.
needs.tired += 0.2; // Yes, you can get tired by shooting arrows.
}

// Output dialog.
void speak(std::string dialog)
{
cout << name << " -> " << dialog << endl;
}

// Cleans thyself.
void takeShower()
{
cout << "Taking shower." << endl;
needs.dirty = 0;
}

// Snow NPC stats of general information, memory, and needs.
void showStats()
{
cout << "\nName: " << name << endl;
cout << "Age: " << age << endl;

cout << "\nMemory:\n-------" << endl;
cout << "Arrows Shot: " << memory.arrowsShot << endl;

cout << "\nNeeds:\n------" << endl;
cout << "Entertainment: " << needs.entertainment << "%" << endl;
cout << "Hunger: " << needs.hunger << "%" << endl;
cout << "Tired: " << needs.tired << "%" << endl;
cout << "Dirty: " << needs.dirty << "%" << endl;
}

// Update character.
void update()
{
if (needs.dirty > 97)
takeShower();

needs.dirty += 0.2;
}
};


int main()
{
srand((unsigned) time(NULL)); // Seed rand().
std::string buffer;

NPC N1("Kylena", 23);
N1.showStats();

for (;;)
{
std::cout << ": ";
std::cin >> buffer;

sortBuffer(N1, buffer);
N1.update();
}

system("pause");
return 0;
}



This works well. After every input (or frame in graphics), it updates the NPC now by a small percentage in certain needs. For this, it's just to see if he/she is dirty above 97%. If so, the NPC will take care of it. The only problem I see is passing each NPC object to sortBuffer(). At the end, there will be 100+ NPCs in a basic world, so am I going to pass 100 NPC objects to this method? Is there a better way? I could declare the objects globally to get rid of the passing, but that can often lead to bad results at the end. Thoughts are welcomed as always.

Share this post


Link to post
Share on other sites
If the command "/stats" is supposed to show the stats of all NPCs then you are better off making a function to do that task.



...
//in main
std::cin >> buffer;
if (buffer.find("/stats") != std::string::npos)
showAllStats();

...

//shows the stats of all NPCs
void showAllStats()
{
for (int i=0;i<numNPCs;i++)
NPC.showStats();
}


Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!