Browse Source

Made a lot of progress on the overall agent-species-need-process structure

master
Macoy Madson 7 years ago
parent
commit
d905077afd
18 changed files with 337 additions and 23 deletions
  1. +6
    -0
      data/testNeed.ept
  2. +23
    -10
      makefile
  3. +3
    -0
      src/agent/agent.cpp
  4. +5
    -0
      src/agent/agent.hpp
  5. +4
    -1
      src/agent/need.cpp
  6. +8
    -1
      src/agent/need.hpp
  7. +25
    -0
      src/agent/needProcessor.cpp
  8. +27
    -0
      src/agent/needProcessor.hpp
  9. +21
    -0
      src/agent/process.cpp
  10. +41
    -0
      src/agent/process.hpp
  11. +4
    -0
      src/agent/processDirectory.cpp
  12. +5
    -0
      src/agent/processDirectory.hpp
  13. +111
    -5
      src/agent/species.cpp
  14. +22
    -3
      src/agent/species.hpp
  15. +15
    -3
      src/main.cpp
  16. +4
    -0
      src/world/time.cpp
  17. +10
    -0
      src/world/time.hpp
  18. +3
    -0
      todo.txt

+ 6
- 0
data/testNeed.ept View File

@ -0,0 +1,6 @@
<need.1>
defaults:
currentValue=255;
needID=1;
fulfilledThreshold=200;

+ 23
- 10
makefile View File

@ -1,21 +1,34 @@
FLAGS= g++ -c -Os -Wall
LINK= g++ -Wall -o "LOCAL_horizon"
EXECUTABLE_NAME= LOCAL_horizon
OBJ_DIR= LOCAL_obj
LINK= g++ -Wall -o "$(EXECUTABLE_NAME)"
LINKLIBS= -lbase2.0 -lsfml-graphics -lsfml-window -lsfml-system
horizon: need.o agent.o species.o main.o
horizon: $(OBJ_DIR)/time.o $(OBJ_DIR)/need.o $(OBJ_DIR)/process.o $(OBJ_DIR)/agent.o $(OBJ_DIR)/needProcessor.o $(OBJ_DIR)/species.o $(OBJ_DIR)/main.o
@echo -----------Linking horizon-------------------------
$(LINK) need.o agent.o species.o main.o $(LINKLIBS)
mv *.o LOCAL_obj
need.o: src/agent/need.hpp src/agent/need.cpp
mv *.o $(OBJ_DIR)
(cd $(OBJ_DIR) && $(LINK) time.o need.o process.o agent.o needProcessor.o species.o main.o $(LINKLIBS))
mv $(OBJ_DIR)/$(EXECUTABLE_NAME) .
$(OBJ_DIR)/time.o: src/world/time.hpp src/world/time.cpp
$(FLAGS) src/world/time.hpp
$(FLAGS) src/world/time.cpp
$(OBJ_DIR)/need.o: src/agent/need.hpp src/agent/need.cpp
$(FLAGS) src/agent/need.hpp
$(FLAGS) src/agent/need.cpp
agent.o: src/agent/agent.hpp src/agent/agent.cpp
$(OBJ_DIR)/process.o: src/agent/process.hpp src/agent/process.cpp
$(FLAGS) src/agent/process.hpp
$(FLAGS) src/agent/process.cpp
$(OBJ_DIR)/agent.o: src/agent/agent.hpp src/agent/agent.cpp
$(FLAGS) src/agent/agent.hpp
$(FLAGS) src/agent/agent.cpp
species.o: src/agent/species.hpp src/agent/species.cpp
$(OBJ_DIR)/needProcessor.o: src/agent/needProcessor.hpp src/agent/needProcessor.cpp
$(FLAGS) src/agent/needProcessor.hpp
$(FLAGS) src/agent/needProcessor.cpp
$(OBJ_DIR)/species.o: src/agent/species.hpp src/agent/species.cpp
$(FLAGS) src/agent/species.hpp
$(FLAGS) src/agent/species.cpp
main.o: src/main.cpp
$(OBJ_DIR)/main.o: src/main.cpp
$(FLAGS) src/main.cpp
clean:
rm *.o
rm *.gch
rm -f $(OBJ_DIR)/*.o
rm -f *.gch
rm -f LOCAL_horizon

+ 3
- 0
src/agent/agent.cpp View File

@ -1 +1,4 @@
#ifndef AGENT_CPP
#define AGENT_CPP
#endif

+ 5
- 0
src/agent/agent.hpp View File

@ -1,6 +1,7 @@
#ifndef AGENT_HPP
#define AGENT_HPP
#include "need.hpp"
#include "process.hpp"
struct Agent
{
int id;
@ -8,6 +9,10 @@ struct Agent
int species;
Need* vitalNeeds;
Need* nonvitalNeeds;
ProcessChain* currentProcessChain;
int currentProcessIndex; //-1 if there is no current process or chain
int processChainVitalNeedID; //Tells us which need we are trying to fulfill
int processChainNonvitalNeedID; //-1 if Vital need is being fulfilled
};


+ 4
- 1
src/agent/need.cpp View File

@ -1 +1,4 @@
#ifndef NEED_CPP
#define NEED_CPP
#include "need.hpp"
#endif

+ 8
- 1
src/agent/need.hpp View File

@ -1,7 +1,14 @@
#ifndef NEED_HPP
#define NEED_HPP
struct Need
{
char currentValue;
unsigned char currentValue;
int needID;
//If the currentValue is greater than fulfilledThreshold, the
//agent will NEVER run a process chain to fulfill this need
unsigned char fulfilledThreshold;
//Mutated values
//char decreaseMultiplier;
//char increaseMultiplier;
};
#endif

+ 25
- 0
src/agent/needProcessor.cpp View File

@ -0,0 +1,25 @@
#ifndef NEEDPROCESSOR_CPP
#define NEEDPROCESSOR_CPP
#include <iostream>
#include "needProcessor.hpp"
NeedProcessor::NeedProcessor(eptFile* spec)
{
eptGroup* defaultNeedVars = spec->getGroup("defaults");
defaultNeed.currentValue = (unsigned char)attrToInt(defaultNeedVars->getAttribute("currentValue"));
defaultNeed.needID = attrToInt(defaultNeedVars->getAttribute("needID"));
defaultNeed.fulfilledThreshold = (unsigned char)attrToInt(defaultNeedVars->getAttribute("fulfilledThreshold"));
}
int NeedProcessor::updateNeed(Agent* agent, Need* currentNeed, Time* currentTime)
{
std::cout << (int) currentNeed->currentValue << "\n";
currentNeed->currentValue-=10;
return 1;
}
void NeedProcessor::initNeed(Need* currentNeed)
{
currentNeed->currentValue = defaultNeed.currentValue;
currentNeed->needID = defaultNeed.needID;
currentNeed->fulfilledThreshold = defaultNeed.fulfilledThreshold;
}
#endif

+ 27
- 0
src/agent/needProcessor.hpp View File

@ -0,0 +1,27 @@
#ifndef NEEDPROCESSOR_HPP
#define NEEDPROCESSOR_HPP
#include <base2.0/ept/eptParser.hpp>
#include "agent.hpp"
#include "need.hpp"
#include "../world/time.hpp"
/* --NeedProcessor--
* NeedProcessors perform all the logic for a type of need.
* For example, a simple Hunger NeedProcessor would subtract 10 from the need
* every 30 seconds in updateNeed(), then when the need reaches its detriment
* threshold, idk (TODO)
*
* This class is designed to be overloaded to do any custom need logic
* */
class NeedProcessor
{
private:
Need defaultNeed;
public:
int needID;
NeedProcessor(eptFile* spec);
virtual int updateNeed(Agent* agent, Need* currentNeed, Time* currentTime);
//Sets the passed need's fields to defaults (specified by defaultNeed)
void initNeed(Need* currentNeed);
};
#endif

+ 21
- 0
src/agent/process.cpp View File

@ -0,0 +1,21 @@
#ifndef PROCESS_CPP
#define PROCESS_CPP
#include <iostream>
#include "process.hpp"
int Process::getDifficulty(Agent* agent, Need* need)
{
return 1;
}
unsigned char Process::getValue(Agent* agent, Need* need)
{
return 1;
}
//Update should return 0 if process didn't complete (run next frame)
//1 if process is ready to go to the next process in chain
//-1 if process chain should end immediately
int Process::update(Agent* agent, Time* currentTime)
{
std::cout << "Updated Process\n";
return 1;
}
#endif

+ 41
- 0
src/agent/process.hpp View File

@ -0,0 +1,41 @@
#ifndef PROCESS_HPP
#define PROCESS_HPP
#include <vector>
#include "../world/time.hpp"
//#include "agent.hpp" //Forward declared
#include "need.hpp"
/* --Process--
* A Process is an action an agent can do that moves them towards a goal.
* These are designed to be overloaded.
* For example, an agent has a simple "hunger" need that gets to detriment
* threshold and becomes priority. Process trees are then searched for
* the Process or combination of Processes that has the lowest difficulty
* for the most supplement to the need. For "hunger," you might have a
* Process that runs if the agent has a Food item or something. You also
* have a chain of Processes that searches for Food, pathfinds to the Food,
* then eats the Food.
* Let's say the agent doesn't have any Food items -
* that Process checks that in getDifficulty() and returns a difficulty of
* -1, or impossible. The process chain is looped over and found to have
* a difficulty of 986, which combines each individual process getDifficulty.
* This chain is then selected because it has the lower difficulty (-1 being
* the same as infinite difficulty). The agent then stores this process
* chain as its current process chain, and marks the current process in
* the chain.
* */
class Agent;
class Process;
typedef std::vector<Process*> ProcessChain;
class Process
{
public:
virtual int getDifficulty(Agent* agent, Need* need);
virtual unsigned char getValue(Agent* agent, Need* need);
//Update should return 0 if process didn't complete (run next frame)
//1 if process is ready to go to the next process in chain
//-1 if process chain should end immediately
virtual int update(Agent* agent, Time* currentTime);
};
#endif

+ 4
- 0
src/agent/processDirectory.cpp View File

@ -0,0 +1,4 @@
#ifndef PROCESSDIRECTORY_CPP
#define PROCESSDIRECTORY_CPP
#include "processDirectory.hpp"
#endif

+ 5
- 0
src/agent/processDirectory.hpp View File

@ -0,0 +1,5 @@
#ifndef PROCESSDIRECTORY_HPP
#define PROCESSDIRECTORY_HPP
#include "process.hpp"
#endif

+ 111
- 5
src/agent/species.cpp View File

@ -1,17 +1,123 @@
#ifndef SPECIES_CPP
#define SPECIES_CPP
#include <iostream>
#include "species.hpp"
int Species::updateAgent(Agent* agent)
Species::Species(eptFile* spec, std::map<std::string, NeedProcessor*>* needProcessorDir)
{
std::cout << agent->id;
eptGroup* vitalNeedsGroup = spec->getGroup("vitalNeeds");
eptGroup* nonvitalNeedsGroup = spec->getGroup("nonvitalNeeds");
//Set member variables to the spec's required needs
numVitalNeeds = vitalNeedsGroup->getTotalAttributes();
numNonvitalNeeds = nonvitalNeedsGroup->getTotalAttributes();
//Get the species ID
speciesID = attrToInt(spec->getGroup("vars")->getAttribute("id"));
//Get the vital need processors as specified in the spec
for (int i = 0; i < numVitalNeeds; i++)
{
std::string attrName;
vitalNeedProcessors.push_back((*needProcessorDir)[vitalNeedsGroup->getAttributeFromIndex(i, attrName)]);
}
//Get the nonvital need processors as specified in the spec
for (int i = 0; i < numNonvitalNeeds; i++)
{
std::string attrName;
nonvitalNeedProcessors.push_back((*needProcessorDir)[nonvitalNeedsGroup->getAttributeFromIndex(i, attrName)]);
}
}
int Species::updateAgent(Agent* agent, Time* currentTime)
{
//Find the lowest need and process that one
unsigned char minVitalValue = 255;
unsigned char minNonvitalValue = 255;
int minVitalIndex = -1;
int minNonvitalIndex = -1;
for (int i = 0; i < numVitalNeeds; ++i)
{
vitalNeedProcessors[i]->updateNeed(agent, &agent->vitalNeeds[i], currentTime);
int currentNeedValue = agent->vitalNeeds[i].currentValue;
if (currentNeedValue < minVitalValue)
{
minVitalValue = currentNeedValue;
minVitalIndex = i;
}
}
for (int i = 0; i < numNonvitalNeeds; ++i)
{
nonvitalNeedProcessors[i]->updateNeed(agent, &agent->nonvitalNeeds[i], currentTime);
int currentNeedValue = agent->nonvitalNeeds[i].currentValue;
if (currentNeedValue < minVitalValue)
{
minNonvitalValue = currentNeedValue;
minNonvitalIndex = i;
}
}
//Look for a process chain if the minimum need is below fulfilledThreshold
if (minVitalValue < agent->vitalNeeds[minVitalIndex].fulfilledThreshold)
{
//The currently processed vital need value is greater than the
//new lowest vital, so process the new one instead (or there is no
//vital need currently being processed)
if (agent->processChainVitalNeedID != minVitalIndex ||
agent->processChainVitalNeedID==-1)
{
//TODO search for suitable process chain
}
//Otherwise, continue on the current process (or give time for
//nonvital processes)
}
if (minNonvitalValue < agent->nonvitalNeeds[minNonvitalIndex].fulfilledThreshold)
{
//Instead of stopping the process if the minimum need is lower than
//the currently processing need (like above for vital needs),
//we will allow it to continue because it won't cause death, and
//to prevent nonvital thrashing
if (agent->processChainNonvitalNeedID==-1)
{
//TODO search for suitable process chain
}
//Else continue with the current process
}
//Update the current process chain
if (agent->currentProcessIndex!=-1)
{
(*agent->currentProcessChain)[agent->currentProcessIndex]->update(agent, currentTime);
}
//Else idle (no needs need processing)
return 1;
}
Agent* Species::createAgent()
{
Agent* newAgent = new Agent;
//TODO
newAgent->id = 12;
newAgent->mutationSeed = 123213;
newAgent->species = 1;
newAgent->vitalNeeds = new Need[10];
newAgent->nonvitalNeeds = new Need[2];
newAgent->species = speciesID;
//Set defaults for processes
newAgent->processChainNonvitalNeedID = -1;
newAgent->processChainVitalNeedID = -1;
newAgent->currentProcessChain = NULL;
newAgent->currentProcessIndex = -1;
//TODO: Allocate these in a pool of some sort
newAgent->vitalNeeds = new Need[numVitalNeeds];
newAgent->nonvitalNeeds = new Need[numNonvitalNeeds];
//Set defaults for vital needs
for (int i = 0; i < numVitalNeeds; ++i)
{
vitalNeedProcessors[i]->initNeed(&newAgent->vitalNeeds[i]);
}
//Set defaults for nonvital needs
for (int i = 0; i < numVitalNeeds; ++i)
{
nonvitalNeedProcessors[i]->initNeed(&newAgent->nonvitalNeeds[i]);
}
return newAgent;
}
#endif

+ 22
- 3
src/agent/species.hpp View File

@ -1,13 +1,32 @@
#ifndef SPECIES_HPP
#define SPECIES_HPP
#include <base2.0/ept/eptParser.hpp>
#include <vector>
#include <map>
#include "agent.hpp"
#include "needProcessor.hpp"
#include "../world/time.hpp"
/* --Species--
* Species abstracts the processing of agents of that species
* Agents contain only their own data, so species must provide
* the logic. */
* the logic.
* Species are created via an ept file containing all of the needs
* required by the agent. These populate the NeedProcessor vectors
* that are then looped through in order to update the agent's needs*/
class Species
{
private:
//These exactly line up with vital needs in the agent
std::vector<NeedProcessor*> vitalNeedProcessors;
std::vector<NeedProcessor*> nonvitalNeedProcessors;
int numVitalNeeds;
int numNonvitalNeeds;
public:
int speciesID;
virtual int updateAgent(Agent* agent);
virtual Agent* createAgent();
Species(eptFile* spec, std::map<std::string, NeedProcessor*>* needProcessorDir);
//updateAgent returns -1 if the agent should die
int updateAgent(Agent* agent, Time* currentTime);
Agent* createAgent();
};
#endif

+ 15
- 3
src/main.cpp View File

@ -1,10 +1,22 @@
#include <iostream>
#include "agent/species.hpp"
#include "agent/needProcessor.hpp"
#include "agent/agent.hpp"
#include "agent/process.hpp"
#include <base2.0/ept/eptParser.hpp>
int main()
{
Species testSpecies;
testSpecies.updateAgent(testSpecies.createAgent());
std::cout << "Hello world\n";
eptParser parser;
parser.load("data/LOCAL_test.ept");
parser.load("data/testNeed.ept");
std::map<std::string, NeedProcessor*> needProcessorDir;
needProcessorDir["need"] = new NeedProcessor(parser.getFile("need"));
Species testSpecies(parser.getFile("testSpecies"), &needProcessorDir);
testSpecies.updateAgent(testSpecies.createAgent(), new Time);
Agent testAgent;
testAgent.currentProcessChain = new ProcessChain;
testAgent.currentProcessChain->push_back(new Process);
(*testAgent.currentProcessChain)[0]->update(&testAgent, NULL, NULL);
return 1;
}

+ 4
- 0
src/world/time.cpp View File

@ -0,0 +1,4 @@
#ifndef TIME_CPP
#define TIME_CPP
#include "time.hpp"
#endif

+ 10
- 0
src/world/time.hpp View File

@ -0,0 +1,10 @@
#ifndef TIME_HPP
#define TIME_HPP
class Time
{
public:
unsigned int year;
unsigned int day;
unsigned int second;
};
#endif

+ 3
- 0
todo.txt View File

@ -6,3 +6,6 @@ Agent
Should agents mutate? If so, how does that work with species processors?
The agent has to store their need values anyways, so that's where the
mutations will be stored.
NeedProcessors might need additional data from the agent - should Need
hold a void* array or something to allow for any additional data?

Loading…
Cancel
Save