201 lines
4.4 KiB
ActionScript
201 lines
4.4 KiB
ActionScript
// Research
|
|
// --------
|
|
// Spends research points to unlock and improve things in the research grid.
|
|
//
|
|
|
|
import empire_ai.weasel.WeaselAI;
|
|
|
|
import research;
|
|
|
|
class Research : AIComponent {
|
|
TechnologyGrid grid;
|
|
array<TechnologyNode@> immediateQueue;
|
|
|
|
void save(SaveFile& file) {
|
|
uint cnt = immediateQueue.length;
|
|
file << cnt;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
file << immediateQueue[i].id;
|
|
}
|
|
|
|
void load(SaveFile& file) {
|
|
updateGrid();
|
|
|
|
uint cnt = 0;
|
|
file >> cnt;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
int id = 0;
|
|
file >> id;
|
|
|
|
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
|
|
if(grid.nodes[i].id == id) {
|
|
immediateQueue.insertLast(grid.nodes[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void updateGrid() {
|
|
//Receive the full grid from the empire to path on
|
|
grid.nodes.length = 0;
|
|
|
|
DataList@ recvData = ai.empire.getTechnologyNodes();
|
|
TechnologyNode@ node = TechnologyNode();
|
|
while(receive(recvData, node)) {
|
|
grid.nodes.insertLast(node);
|
|
@node = TechnologyNode();
|
|
}
|
|
|
|
grid.regenBounds();
|
|
}
|
|
|
|
double getEndPointWeight(const TechnologyType& tech) {
|
|
//TODO: Might want to make this configurable by data file
|
|
return 1.0;
|
|
}
|
|
|
|
bool isEndPoint(const TechnologyType& tech) {
|
|
return tech.cls >= Tech_BigUpgrade;
|
|
}
|
|
|
|
double findResearch(int atIndex, array<TechnologyNode@>& path, array<bool>& visited, bool initial = false) {
|
|
if(visited[atIndex])
|
|
return 0.0;
|
|
visited[atIndex] = true;
|
|
|
|
auto@ node = grid.nodes[atIndex];
|
|
if(!initial) {
|
|
if(node.bought)
|
|
return 0.0;
|
|
if(!node.hasRequirements(ai.empire))
|
|
return 0.0;
|
|
|
|
path.insertLast(node);
|
|
|
|
if(isEndPoint(node.type))
|
|
return getEndPointWeight(node.type);
|
|
}
|
|
|
|
vec2i startPos = node.position;
|
|
double totalWeight = 0.0;
|
|
|
|
array<TechnologyNode@> tmp;
|
|
array<TechnologyNode@> chosen;
|
|
tmp.reserve(20);
|
|
chosen.reserve(20);
|
|
|
|
for(uint d = 0; d < 6; ++d) {
|
|
vec2i otherPos = startPos;
|
|
if(grid.doAdvance(otherPos, HexGridAdjacency(d))) {
|
|
int otherIndex = grid.getIndex(otherPos);
|
|
if(otherIndex != -1) {
|
|
tmp.length = 0;
|
|
double w = findResearch(otherIndex, tmp, visited);
|
|
if(w != 0.0) {
|
|
totalWeight += w;
|
|
if(randomd() < w / totalWeight) {
|
|
chosen = tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for(uint i = 0, cnt = chosen.length; i < cnt; ++i)
|
|
path.insertLast(chosen[i]);
|
|
return max(totalWeight, 0.01);
|
|
}
|
|
|
|
void queueNewResearch() {
|
|
if(log)
|
|
ai.print("Attempted to find new research to queue");
|
|
|
|
//Update our grid representation
|
|
updateGrid();
|
|
|
|
//Find a good path to do
|
|
array<bool> visited(grid.nodes.length, false);
|
|
|
|
double totalWeight = 0.0;
|
|
|
|
auto@ path = array<TechnologyNode@>();
|
|
auto@ tmp = array<TechnologyNode@>();
|
|
path.reserve(20);
|
|
tmp.reserve(20);
|
|
|
|
for(int i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
|
|
if(grid.nodes[i].bought) {
|
|
tmp.length = 0;
|
|
double weight = findResearch(i, tmp, visited, initial=true);
|
|
if(weight != 0.0) {
|
|
totalWeight += weight;
|
|
if(randomd() < weight / totalWeight) {
|
|
auto@ swp = path;
|
|
@path = tmp;
|
|
@tmp = swp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(path.length != 0) {
|
|
for(uint i = 0, cnt = path.length; i < cnt; ++i) {
|
|
if(log)
|
|
ai.print("Queue research: "+path[i].type.name+" at "+path[i].position);
|
|
immediateQueue.insertLast(path[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
double immTimer = randomd(10.0, 60.0);
|
|
void focusTick(double time) override {
|
|
//Queue some new research if we have to
|
|
if(immediateQueue.length == 0) {
|
|
immTimer -= time;
|
|
if(immTimer <= 0.0) {
|
|
immTimer = 60.0;
|
|
queueNewResearch();
|
|
}
|
|
}
|
|
else {
|
|
immTimer = 0.0;
|
|
}
|
|
|
|
//Deal with current queued research
|
|
if(immediateQueue.length != 0) {
|
|
auto@ node = immediateQueue[0];
|
|
if(!receive(ai.empire.getTechnologyNode(node.id), node)) {
|
|
immediateQueue.removeAt(0);
|
|
}
|
|
else if(!node.available || node.bought) {
|
|
immediateQueue.removeAt(0);
|
|
}
|
|
else {
|
|
double cost = node.getPointCost(ai.empire);
|
|
if(cost == 0) {
|
|
//Try it once and then give up
|
|
ai.empire.research(node.id, secondary=true);
|
|
immediateQueue.removeAt(0);
|
|
|
|
if(log)
|
|
ai.print("Attempt secondary research: "+node.type.name+" at "+node.position);
|
|
}
|
|
else if(cost <= ai.empire.ResearchPoints) {
|
|
//If we have enough to buy it, buy it
|
|
ai.empire.research(node.id);
|
|
immediateQueue.removeAt(0);
|
|
|
|
if(log)
|
|
ai.print("Purchase research: "+node.type.name+" at "+node.position);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
AIComponent@ createResearch() {
|
|
return Research();
|
|
}
|