Sign in to follow this  
dxFoo

Producing intelligence on meters?

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
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[i].showStats();
}


Share this post


Link to post
Share on other sites
Good idea, thanks.

For the most part, everything is working fine. Although the time is extremely fast, every 15 seconds, a new message pops up. The NPC says the following...

I'm eating now.
I'm eating now.
I'm taking a shower now.
I'm watching TV.
I'm eating now.
I'm taking a shower now.
I'm watching TV.
.
.
.

Okay, so that's improvement with meters, I guess. Every frame, or after every input in a console window, I don't want him/her to be off to another task. What's the next step?

Share this post


Link to post
Share on other sites
This does it pretty much:


for (;;)
{
int start_time = System::Environment::TickCount;

for (int i = 0; i < 3; i++)
N[i].update();

while ((System::Environment::TickCount - start_time) < 3);
}



Each update is around 3 seconds. In games, they usually do one task for 1-5 minutes, though, semitaniously. So I'm confused on how to extend this.

Share this post


Link to post
Share on other sites
Yea you really don't want delays in there at all. You want to update each NPC as fast as possible and the update decides whether they have finished the task. To do this the NPC has to keep track of what task they are doing.

For example you can use two thresholds depending on whether the NPC is eating or not:


update()
{
if (task==EATING)
{
hunger-=0.4; //im eating so my hunger is decreasing quite quickly
if (NPC.hunger < 5) //if I am not very hungry..
task=NONE; //..then I stop eating
}
else
{
hunger+=0.1; //im not eating so my hunger is growing slowly

if (NPC.hunger > 95) //if I get too hungry then..
task=EATING; //..I start eating
}
}






This behaviour will mean the NPC will slowly get hungry, when their hunger reaches a certain point they will start eating. As they eat their hunger will slowly decrease. They will stop eating when their hunger drops to a certain level, and the cycle starts again.

Similarly with showering. If the NPC is not showering it gets dirty very slowly, and when it reaches a certain level of dirty the NPC sets its task to "showering". While showering the NPC's dirtiness decreases rapidly. Once it goes below a certain level the NPC stops showering.

You will find that you get complex conflicts happening. For example what does the NPC do if he gets hungry in the middle of showering? Does he stop showering and start eating? Or does he wait until he's finished showering? At that point you have to introduce priorities. This could get very complicated.

Anyway good luck for now, I have to sleep (my tiredness meter just reached 99 and it's 3:15am)

Share this post


Link to post
Share on other sites
haha thanks for the help :) That totally makes sense to me now. I don't know if there should be a while(i'm eating) so it doesn't trail off into taking a shower 1 second later, though. Thoughts?

[Edited by - dxFoo on November 5, 2005 9:19:47 PM]

Share this post


Link to post
Share on other sites
Here's my final code for the night. If you can please compile this and tell me what you think, it can only help this get better. One thing I'd like to improve on is conditions in NPC::update(). If TASK == SLEEPING && Time > 5pm, TASK = NONE. etc. Thanks for looking at this...

Also, while looking at this code, I find that it's more behavioral than intelligent. But that's often obvious in noob AI code I guess.


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

class Utilities
{
public:
int randNum(int min, int max)
{
return (((double) rand() / (double) RAND_MAX) * max + min);
}
} util;

class NPC
{
private:
std::string name; // NPC's name
int age; // NPC's age

struct Memory
{
double swordSkill;
double arrowsShot;
};

struct Needs
{
double dirty;
double hunger;
double entertainment;
double tired;
};

public:
Memory memory; // The NPC's memory box.
Needs needs; // The NPC's daily needs.

enum Task
{
NONE, EAT, SLEEP, SHOWERING
} task; // current task

// NPC Constructor
NPC(std::string name, int age)
{
this->name = name;
this->age = age;

// Clear memory like a newborn baby!
memory.arrowsShot = 0;
memory.swordSkill = 0;

// Initialize needs with random starting values.
needs.dirty = util.randNum(1, 100);
needs.entertainment = util.randNum(1, 100);
needs.hunger = util.randNum(1, 100);
needs.tired = util.randNum(1, 100);
}

// Show current NPC stats.
void showStats()
{
cout << "\nName: " << name << endl;
cout << "Age: " << age << endl;

cout << "\nMemory:\n-------" << endl;
cout << "Arrows Shot: " << memory.arrowsShot << endl;
cout << "Sword Skill: " << memory.swordSkill << 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;
}

void update()
{
// Sleep
if (task == SLEEP)
{
needs.tired -= 0.4; // Sleep reduces tiredness.

cout << "Sleeping..." << endl; // Display current status.

if (needs.tired < 5)
task = NONE;
}
else
{
needs.tired += 0.1;

cout << "Awake" << endl; // Display current status.

if (needs.tired > 95)
task = SLEEP;
}

// Shower
if (task == SHOWERING)
{
needs.dirty -= 0.4; // Sleep reduces tiredness.

cout << "Showering..." << endl; // Display current status.

if (needs.dirty < 5)
task = NONE;
}
else
{
needs.dirty += 0.1; // Gets dirtier as time continues.

cout << "Clean" << endl; // Display current status.

if (needs.dirty > 95)
task = SHOWERING;
}
}


};

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

NPC N1("Kylena", 18);
N1.showStats();
N1.task = NPC::SLEEP;

for (int i = 0; i < 5000; i++)
N1.update();

system("pause");
return 0;
}


Share this post


Link to post
Share on other sites
Also toss in randomness so each NPC doesn't stop showering at the same point. Some can tolerate being dirtier then others (thus get out of the shower sooner) while others need to shower longer. You also don't need to update an NPC every frame. It's like real life, every few seconds should be more then fine, plus saves you CPU cycles.

Share this post


Link to post
Share on other sites
The NPC now has an idea of what task it is doing, but it is purely reactive. You are right that it's more behavoral than intelligent, but the next step will introduce basic intelligence.

The main problem at the moment is the NPC cannot handle having more than one task to do. For example if the NPC is both tired and dirty it will enter a loop switching between showering and sleeping. What an intelligent NPC should do is know which tasks need to be done, and decide which task to do first.

So what your NPC needs now is a decision making process. The NPC should store a list of tasks that need to be done, and choose which one to carry out first.

For example when the NPC is tired he stores "sleep" in his tasks to-do list. If he is dirty he stores "shower" in the list too. If the NPC isn't doing anything, and there are multiple tasks to do then the NPC decides which task from the to-do list to do first, and carries it through until complete, at which point the task is removed from the list, and then the NPC can work out the next task to do.

The decision making process can be based on quantifying each task based on how much the NPC reckons it needs to be done. For example:

need_to_sleep = tiredness * tiredness_weight;
need_to_eat = hunger * hunger_weight;

Then the NPC will decide to do the task with the highest need.

Different NPC's might have different weights based on their personality, and so they will make different decisions in the same situtation. For example if the average hunger_weight of NPC's is 1, then an NPC with a hunger_weight of 5 will tend to have a lower hunger threshold and will tend to treat eating as a priority over other tasks like sleeping.

Share this post


Link to post
Share on other sites
>> "[Jeff Hawkins] teaches a new theory on how we can't build intelligent machines until we figure out how the brain works"

I've also read Hawkins's book, and although it was very optimistic (and you could even say inspiring), unfortunately in my opinion it did not portray either a NEW theory or a FRAMEWORK for building intelligent machines. As most brain theories, in fact, his descriptions are so vague that they are practically unimplementable (for instance, along the lines of "The brain continually makes predictions."). His tenets of brain theory are actually not new at all, but they are decently accepted in the cognitive psychology community (even as far back as Grossberg's neural network papers in the 1970s, where he states Hawkins' prediction hypothesis almost exactly). If anything, he's communicating these ideas to a wider public.

Further, his ideas do not seem to constitute a framework for studying the brain. Many appealing frameworks have been proposed, all saying that theirs is correct -- Godel Escher Bach provides a logical framework, Rumelhart proposed one based on neural networks, and now Hawkins is proposing one even more vague than the latter two. His "biological explanations" are rather weak (relying on purely anatomical distinctions), although in his credit he attempts to make "predictions" of future experimental studies which would bolster his theory. In short, it's a good try, but it just doesn't cut it as a framework.

Share this post


Link to post
Share on other sites
dxFoo, what you are looking for is "planning".

Motives (or needs, your 'meters') sparks goals. We then make a plan (or strategy, a set of actions) to achieve those goals, using our knowledge. The ability to plan ahead is the "trademark" of human intelligence.

Planners are a wide and popular part of AI. Unfortunatly, right now the best of them are painfully slow. I think the problem has been proven to be NP-complete.

Share this post


Link to post
Share on other sites
If you wanted even more complexity and realism, you could allow priorities to inerrupt each other. If you are eating, and something of much greater priority shows up, it will interrupt your meal. The threshhold for completion of a task should therefore be a portion of the weight of all your other tasks combined. So if you are asleep, and it is a priority not to be late for work, you wake up unless your need to sleep is an oder of magnitude(or so) greater than your need to keep your job.

For complexity which does not necessarily increase intelligence, you have attention span. you go for a finite time, not neccissarily until you are done, and reevaluate(ADHD)

Share this post


Link to post
Share on other sites
Quote:
Original post by NIm

For complexity which does not necessarily increase intelligence, you have attention span. you go for a finite time, not neccissarily until you are done, and reevaluate(ADHD)



Quote:
Baseketball

Today's kids have attention span that can only be mesured in milliseconds!


Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this