forked from 0ad/0ad
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:
parent
0521d936a1
commit
a26ab7b1e7
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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>");
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user