Should fix AI warnings (refs #2372).

Slight configuration changes to improve the early-game slightly, WIP.

This was SVN commit r14578.
This commit is contained in:
wraitii 2014-01-12 19:12:55 +00:00
parent 0521d936a1
commit a26ab7b1e7
8 changed files with 99 additions and 75 deletions

View File

@ -45,7 +45,7 @@ m.AegisBot.prototype.CustomInit = function(gameState, sharedScript) {
m.playerGlobals[PlayerID].uniqueIDTPlans = 1; // transport plans. starts at 1 because 0 might be used as none.
m.playerGlobals[PlayerID].uniqueIDDefManagerArmy = 0;
this.HQ.init(gameState,sharedScript.events,this.queues);
this.HQ.init(gameState,this.queues);
m.debug ("Initialized with the difficulty " + this.Config.difficulty);
var ents = gameState.getEntities().filter(API3.Filters.byOwner(this.player));

View File

@ -34,7 +34,7 @@ m.BaseManager = function(Config) {
this.territoryIndices = [];
};
m.BaseManager.prototype.init = function(gameState, events, unconstructed){
m.BaseManager.prototype.init = function(gameState, unconstructed){
this.constructing = unconstructed;
// entitycollections
this.units = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "base", this.ID));
@ -584,7 +584,7 @@ m.BaseManager.prototype.checkResourceLevels = function (gameState,queues) {
{
if (this.willGather[type] === 0)
continue;
if (type !== "food" && gameState.playedTurn % 10 === 4 && this.getResourceLevel(gameState,type, "all") < 200)
if (type !== "food" && gameState.ai.playedTurn % 10 === 4 && this.getResourceLevel(gameState,type, "all") < 200)
this.willGather[type] = 0; // won't gather at all
if (this.willGather[type] === 2)
continue;
@ -901,13 +901,13 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair) {
continue;
var assigned = gameState.getOwnEntitiesByMetadata("target-foundation", target.id()).length;
if (assigned < this.targetNumBuilders/3) {
if (builderWorkers.length + addedWorkers < this.targetNumBuilders*2) {
if (assigned < targetNB/3) {
if (builderWorkers.length + addedWorkers < targetNB*2) {
var nonBuilderWorkers = workers.filter(function(ent) { return (ent.getMetadata(PlayerID, "subrole") !== "builder" && ent.position() !== undefined); });
if (gameState.defcon() < 5)
nonBuilderWorkers = workers.filter(function(ent) { return (ent.getMetadata(PlayerID, "subrole") !== "builder" && ent.hasClass("Female") && ent.position() !== undefined); });
var nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), this.targetNumBuilders/3 - assigned);
var nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), targetNB/3 - assigned);
nearestNonBuilders.forEach(function(ent) {
ent.stopMoving();

View File

@ -10,7 +10,7 @@ m.Config = function() {
"defenceBuildingTime" : 600, // Time to wait before building towers or fortresses
"attackPlansStartTime" : 0, // time to wait before attacking. Start as soon as possible (first barracks)
"techStartTime" : 120, // time to wait before teching. Will only start after town phase so it's irrelevant.
"popForBarracks1" : 15,
"popForBarracks1" : 20,
"popForBarracks2" : 95,
"timeForBlacksmith" : 900,
};
@ -63,21 +63,20 @@ m.Config = function() {
}
};
// qbot
this.priorities =
{ // Note these are dynamic, you are only setting the initial values
"house" : 350,
"villager" : 40,
{
"villager" : 30, // should be slightly lower than the citizen soldier one because otherwise they get all the food
"citizenSoldier" : 60,
"ships" : 70,
"economicBuilding" : 90,
"house" : 350,
"dropsites" : 120,
"field" : 500,
"militaryBuilding" : 110,
"economicBuilding" : 90,
"militaryBuilding" : 140, // TODO: set to a lower value after the first barracks.
"defenceBuilding" : 70,
"civilCentre" : 400,
"majorTech" : 700,
"minorTech" : 50,
"civilCentre" : 400
"minorTech" : 40
};
};

View File

@ -30,6 +30,9 @@ m.HQ = function(Config) {
this.baseManagers = {};
// cache the rates currently want for resource gathering.
this.wantedRates = {};
// this means we'll have about a big third of women, and thus we can maximize resource gathering rates.
this.femaleRatio = this.Config.Economy.femaleRatio;
@ -47,7 +50,7 @@ m.HQ = function(Config) {
};
// More initialisation for stuff that needs the gameState
m.HQ.prototype.init = function(gameState, events, queues){
m.HQ.prototype.init = function(gameState, queues){
// initialize base map. Each pixel is a base ID, or 0 if none
this.basesMap = new API3.Map(gameState.sharedScript, new Uint8Array(gameState.getMap().data.length));
this.basesMap.setMaxVal(255);
@ -84,7 +87,7 @@ m.HQ.prototype.init = function(gameState, events, queues){
treasureAmount[i] += ent.resourceSupplyMax();
});
this.baseManagers[1] = new m.BaseManager(this.Config);
this.baseManagers[1].init(gameState, events);
this.baseManagers[1].init(gameState);
this.baseManagers[1].setAnchor(CC);
this.baseManagers[1].initTerritory(this, gameState);
this.baseManagers[1].initGatheringFunctions(this, gameState);
@ -121,7 +124,7 @@ m.HQ.prototype.init = function(gameState, events, queues){
//this.reassignIdleWorkers(gameState);
this.navalManager.init(gameState, events, queues);
this.navalManager.init(gameState, queues);
// TODO: change that to something dynamic.
var civ = gameState.playerData.civ;
@ -214,20 +217,14 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues) {
}
};
// okay, so here we'll create both females and male workers.
// We'll try to keep close to the "ratio" defined atop.
// Choice of citizen soldier is a bit messy.
// Before having 100 workers it focuses on speed, cost, and won't choose units that cost stone/metal
// After 100 it just picks the strongest;
// TODO: This should probably be changed to favor a more mixed approach for better defense.
// (or even to adapt based on estimated enemy strategy).
// TODO: this should probably set which base it wants them in.
// This code trains females and citizen workers, trying to keep close to a ratio of females/CS
// TODO: this should choose a base depending on which base need workers
// TODO: also there are several things that could be greatly improved here.
m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
{
// Get some data.
// Count the workers in the world and in progress
var numFemales = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("units/{civ}_support_female_citizen"), true);
numFemales += queues.villager.countQueuedUnitsWithClass("Support");
// counting the workers that aren't part of a plan
var numWorkers = 0;
@ -243,43 +240,50 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
numInTraining += item.count;
});
});
var numQueued = queues.villager.countQueuedUnits() + queues.citizenSoldier.countQueuedUnits();
var numQueuedF = queues.villager.countQueuedUnits();
var numQueuedS = queues.citizenSoldier.countQueuedUnits();
var numQueued = numQueuedS + numQueuedF;
var numTotal = numWorkers + numQueued;
// If we have too few, train more
// should plan enough to always have females…
// TODO: 15 here should be changed to something more sensible, such as nb of producing buildings.
if (numTotal < this.targetNumWorkers && numQueued < 50 && (queues.villager.length() + queues.citizenSoldier.length()) < 120 && numInTraining < 15) {
var template = gameState.applyCiv("units/{civ}_support_female_citizen");
var size = Math.min(5, Math.ceil(numTotal / 10));
if (numFemales/numTotal > this.femaleRatio && (numTotal > 20 || (this.fastStart && numTotal > 10))) {
if (numTotal < 100)
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["cost",1], ["speed",0.5], ["costsResource", 0.5, "stone"], ["costsResource", 0.5, "metal"]]);
else
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["strength",1] ]);
if (numTotal > this.targetNumWorkers || numQueued > 50 || (numQueuedF > 20 && numQueuedS > 20) || numInTraining > 15)
return;
if (!template)
template = gameState.applyCiv("units/{civ}_support_female_citizen");
if (gameState.currentPhase() === 1)
size = 2;
}
// TODO: improve that logic.
if (numFemales/numTotal > this.femaleRatio * 1.3 && numWorkers > 25)
queues.villager.paused = true;
else if ((numFemales/numTotal < this.femaleRatio * 1.1) || gameState.ai.queueManager.getAvailableResources(gameState)["food"] > 250
|| numWorkers <= 25)
queues.villager.paused = false;
// TODO: perhaps assign them a default resource and check the base according to that.
// base "0" means "auto"
if (template === gameState.applyCiv("units/{civ}_support_female_citizen"))
queues.villager.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size ));
// default template and size
var template = gameState.applyCiv("units/{civ}_support_female_citizen");
var size = Math.min(5, Math.ceil(numTotal / 10));
// Choose whether we want soldiers instead.
// TODO: we might want to adjust our female ratio.
if ((numFemales+numQueuedF)/numTotal > this.femaleRatio && numQueuedS < 20) {
if (numTotal < 35)
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["cost",1], ["speed",0.5], ["costsResource", 0.5, "stone"], ["costsResource", 0.5, "metal"]]);
else
queues.citizenSoldier.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size));
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["strength",1] ]);
if (!template)
template = gameState.applyCiv("units/{civ}_support_female_citizen");
else
size = Math.min(5, Math.ceil(numTotal / 12));
}
// TODO: improve that logic.
/*
if (numFemales/numWorkers > this.femaleRatio && numQueuedS > 0 && numWorkers > 25)
queues.villager.paused = true;
else
queues.villager.paused = false;
*/
// TODO: perhaps assign them a default resource and check the base according to that.
// base "0" means "auto"
if (template === gameState.applyCiv("units/{civ}_support_female_citizen"))
queues.villager.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size ));
else
queues.citizenSoldier.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size));
};
// picks the best template based on parameters and classes
@ -457,7 +461,13 @@ m.HQ.prototype.GetCurrentGatherRates = function(gameState) {
};
// Pick the resource which most needs another worker
/* Pick the resource which most needs another worker
* How this works:
* We get the rates we would want to have to be able to deal with our plans
* We get our current rates
* We compare; we pick the one where the discrepancy is highest.
* Need to balance long-term needs and possible short-term needs.
*/
m.HQ.prototype.pickMostNeededResources = function(gameState) {
var self = this;
@ -491,7 +501,7 @@ m.HQ.prototype.pickMostNeededResources = function(gameState) {
var va = (Math.max(0,self.wantedRates[a] - currentRates[a]))/ (currentRates[a]+1);
var vb = (Math.max(0,self.wantedRates[b] - currentRates[b]))/ (currentRates[b]+1);
// If they happen to be equal (generally this means "0" aka no need), make it equitable.
// If they happen to be equal (generally this means "0" aka no need), make it fair.
if (va === vb)
return (self.wantedRates[b]/(currentRates[b]+1)) - (self.wantedRates[a]/(currentRates[a]+1));
return vb-va;
@ -1054,7 +1064,7 @@ m.HQ.prototype.update = function(gameState, queues, events) {
this.GetCurrentGatherRates(gameState);
if (gameState.getTimeElapsed() > this.techStartTime && gameState.currentPhase() > 2)
if (gameState.getTimeElapsed() > this.techStartTime && gameState.currentPhase() > 2 )
this.tryResearchTechs(gameState,queues);
if (this.Config.difficulty > 1)

View File

@ -35,7 +35,7 @@ m.NavalManager = function() {
};
// More initialisation for stuff that needs the gameState
m.NavalManager.prototype.init = function(gameState, events, queues) {
m.NavalManager.prototype.init = function(gameState, queues) {
// finished docks
this.docks = gameState.getOwnStructures().filter(API3.Filters.and(API3.Filters.byClass("Dock"), API3.Filters.not(API3.Filters.isFoundation())));
this.docks.allowQuickIter();

View File

@ -111,10 +111,14 @@ m.QueueManager.prototype.futureNeeds = function(gameState) {
};
};
// calculate the gather rates we'd want to be able to use all elements in our queues
// calculate the gather rates we'd want to be able to start all elements in our queues
// TODO: many things.
m.QueueManager.prototype.wantedGatherRates = function(gameState) {
// global rates
var rates = { "food" : 0, "wood" : 0, "stone" : 0, "metal" : 0 };
// per-queue.
var qTime = gameState.getTimeElapsed();
var time = gameState.getTimeElapsed();
var qCosts = { "food" : 0, "wood" : 0, "stone" : 0, "metal" : 0 };
var currentRess = this.getAvailableResources(gameState);
@ -127,32 +131,37 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState) {
var name = this.queueArrays[i][0];
var queue = this.queueArrays[i][1];
// we'll move temporally along the queue.
for (var j = 0; j < queue.length(); ++j)
{
var elem = queue.queue[j];
var cost = elem.getCost();
if (qTime < elem.startTime)
qTime = elem.startTime;
// TODO: what is the else case here?
if (!elem.isGo(gameState))
{
// assume 2 minutes.
// TODO work on this.
// assume we'll be wanted in four minutes.
// TODO: work on this.
for (var type in qCosts)
qCosts[type] += cost[type];
qTime += 120000;
qCosts[type] += cost[type] / (qTime/time);
qTime += 240000;
break; // disregard other stuffs.
}
if (!elem.endTime)
{
// estimate time based on priority + cost + nb
// Assume we want it in 30 seconds from current time.
// Costs are made higher based on priority and lower based on current time.
// TODO: work on this.
for (var type in qCosts)
qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name]));
qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name])) / (qTime/time);
qTime += 30000;
} else {
// TODO: work on this.
for (var type in qCosts)
qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name]));
qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name])) / (qTime/time);
// TODO: refine based on % completed.
qTime += (elem.endTime-elem.startTime);
}
@ -166,6 +175,7 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState) {
rates[j] += qCosts[j]/(qTime/1000);
}
}
return rates;
};
@ -311,8 +321,14 @@ m.QueueManager.prototype.HTMLprintQueues = function(gameState){
if (q.queue[j].number)
qStr += q.queue[j].number + " ";
qStr += q.queue[j].type;
qStr += "<br><span class=\"ressLevel\">";
var costs = q.queue[j].getCost();
for each (var k in costs.types) {
qStr += costs[k] + k.substr(0,1).toUpperCase() ;
if (k != "metal") qStr += " / ";
}
qStr += "</span></td>";
log (qStr);
log ("</td>");
}
log ("</tr>");
}
@ -322,7 +338,6 @@ m.QueueManager.prototype.HTMLprintQueues = function(gameState){
{
log("<p>" + p + ": " + uneval(this.accounts[p]) + " </p>");
}*/
log ("<p>Needed Resources:" + uneval(this.futureNeeds(gameState,false)) + "</p>");
log ("<p>Wanted Gather Rate:" + uneval(this.wantedGatherRates(gameState)) + "</p>");
log ("<p>Current Resources:" + uneval(gameState.getResources()) + "</p>");
log ("<p>Available Resources:" + uneval(this.getAvailableResources(gameState)) + "</p>");

View File

@ -45,8 +45,8 @@ m.Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) {
var x0 = Math.max(0, cx - maxDist);
var y0 = Math.max(0, cy - maxDist);
var x1 = Math.min(this.width, cx + maxDist);
var y1 = Math.min(this.height, cy + maxDist);
var x1 = Math.min(this.width-1, cx + maxDist);
var y1 = Math.min(this.height-1, cy + maxDist);
var maxDist2 = maxDist * maxDist;
var str = 0.0;
@ -70,11 +70,11 @@ m.Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) {
var dx = x - cx;
var dy = y - cy;
var r2 = dx*dx + dy*dy;
if (r2 < maxDist2){
if (r2 < maxDist2) {
var quant = 0;
var r = Math.sqrt(r2);
quant = str * (maxDist - r);
if (this.map[x + y * this.width] + quant < 0)
this.map[x + y * this.width] = 0;
else if (this.map[x + y * this.width] + quant > this.maxVal)

View File

@ -205,7 +205,7 @@ m.aStarPath.prototype.continuePath = function(gamestate)
{
var index = 0 + this.currentSquare +positions[i][0]*this.Sampling +w*this.Sampling*positions[i][1];
if (this.widthMap[index] >= this.minWidth || (this.onWater && this.map[index] > 0 && this.map[index] !== 200 && this.map[index] !== 201)
|| (!this.onWater && this.map[this.index] === 200))
|| (!this.onWater && this.map[index] === 200))
{
if(this.isOpened[index] === undefined)
{