Petra: some code reshuffling + small bug fixes

This was SVN commit r16697.
This commit is contained in:
mimo 2015-05-30 19:12:01 +00:00
parent b738772d65
commit 9408860acc
9 changed files with 142 additions and 128 deletions

View File

@ -108,7 +108,7 @@ m.PetraBot.prototype.OnUpdate = function(sharedScript)
// Run the update every n turns, offset depending on player ID to balance the load
this.elapsedTime = this.gameState.getTimeElapsed() / 1000;
if ((this.turn + this.player) % 8 == 5)
if (!this.playedTurn || (this.turn + this.player) % 8 == 5)
{
Engine.ProfileStart("PetraBot bot (player " + this.player +")");

View File

@ -510,7 +510,7 @@ m.AttackPlan.prototype.updatePreparation = function(gameState)
entity.setMetadata(PlayerID, "subrole", "completing");
var queued = false;
if (entity.resourceCarrying() && entity.resourceCarrying().length)
queued = m.returnResources(entity, gameState);
queued = m.returnResources(gameState, entity);
var index = gameState.ai.accessibility.getAccessValue(entity.position());
if (index === rallyIndex)
entity.moveToRange(rallyPoint[0], rallyPoint[1], 0, 15, queued);

View File

@ -172,7 +172,7 @@ m.BaseManager.prototype.anchorLost = function (gameState, ent)
this.anchor = undefined;
this.anchorId = undefined;
this.neededDefenders = 0;
let bestbase = m.getBestBase(ent, gameState);
let bestbase = m.getBestBase(gameState, ent);
this.newbaseID = bestbase.ID;
for (let entity of this.units.values())
bestbase.assignEntity(gameState, entity);
@ -500,7 +500,7 @@ m.BaseManager.prototype.checkResourceLevels = function (gameState, queues)
// Adds the estimated gather rates from this base to the currentRates
m.BaseManager.prototype.getGatherRates = function(gameState, currentRates)
{
for (var i in currentRates)
for (let res in currentRates)
{
// I calculate the exact gathering rate for each unit.
// I must then lower that to account for travel time.
@ -508,29 +508,28 @@ m.BaseManager.prototype.getGatherRates = function(gameState, currentRates)
// I use some logarithms.
// TODO: this should take into account for unit speed and/or distance to target
this.gatherersByType(gameState, i).forEach(function (ent) {
var gRate = ent.currentGatherRate();
if (gRate !== undefined)
currentRates[i] += Math.log(1+gRate)/1.1;
this.gatherersByType(gameState, res).forEach(function (ent) {
let gRate = ent.currentGatherRate();
if (gRate)
currentRates[res] += Math.log(1+gRate)/1.1;
});
if (i === "food")
if (res === "food")
{
this.workersBySubrole(gameState, "hunter").forEach(function (ent) {
if (ent.isIdle())
return;
var gRate = ent.currentGatherRate()
if (gRate !== undefined)
currentRates[i] += Math.log(1+gRate)/1.1;
let gRate = ent.currentGatherRate();
if (gRate)
currentRates[res] += Math.log(1+gRate)/1.1;
});
this.workersBySubrole(gameState, "fisher").forEach(function (ent) {
if (ent.isIdle())
return;
var gRate = ent.currentGatherRate()
if (gRate !== undefined)
currentRates[i] += Math.log(1+gRate)/1.1;
let gRate = ent.currentGatherRate();
if (gRate)
currentRates[res] += Math.log(1+gRate)/1.1;
});
}
currentRates[i] += 0.5*m.GetTCRessGatherer(gameState, i);
}
};
@ -596,7 +595,7 @@ m.BaseManager.prototype.setWorkersIdleByPriority = function(gameState)
--nb;
ent.stopMoving();
ent.setMetadata(PlayerID, "gather-type", moreNeed.type);
m.AddTCRessGatherer(gameState, moreNeed.type);
gameState.ai.HQ.AddTCResGatherer(moreNeed.type);
});
if (nb == 0)
return;
@ -643,7 +642,7 @@ m.BaseManager.prototype.reassignIdleWorkers = function(gameState)
continue;
ent.setMetadata(PlayerID, "subrole", "gatherer");
ent.setMetadata(PlayerID, "gather-type", needed.type);
m.AddTCRessGatherer(gameState, needed.type);
gameState.ai.HQ.AddTCResGatherer(needed.type);
break;
}
}
@ -775,7 +774,7 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
continue;
// if our territory has shrinked since this foundation was positioned, do not build it
if (m.isNotWorthBuilding(target, gameState))
if (m.isNotWorthBuilding(gameState, target))
continue;
var assigned = gameState.getOwnEntitiesByMetadata("target-foundation", target.id()).length;
@ -932,12 +931,12 @@ m.BaseManager.prototype.update = function(gameState, queues, events)
if (gameState.ai.HQ.numActiveBase() > 0)
{
for (var ent of this.units.values())
m.getBestBase(ent, gameState).assignEntity(gameState, ent);
m.getBestBase(gameState, ent).assignEntity(gameState, ent);
for (var ent of this.buildings.values())
{
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
this.removeDropsite(gameState, ent);
m.getBestBase(ent, gameState).assignEntity(gameState, ent);
m.getBestBase(gameState, ent).assignEntity(gameState, ent);
}
}
else if (gameState.ai.HQ.canBuildUnits)

View File

@ -68,7 +68,7 @@ m.getMaxStrength = function(ent, againstClass)
};
// Makes the worker deposit the currently carried resources at the closest accessible dropsite
m.returnResources = function(ent, gameState)
m.returnResources = function(gameState, ent)
{
if (!ent.resourceCarrying() || ent.resourceCarrying().length == 0 || !ent.position())
return false;
@ -94,15 +94,29 @@ m.returnResources = function(ent, gameState)
return true;
};
// is supply full taking into account gatherers affected during this turn
m.IsSupplyFull = function(gameState, ent)
{
if (ent.isFull() === true)
return true;
var turnCache = gameState.ai.HQ.turnCache;
var count = ent.resourceSupplyNumGatherers();
if (turnCache["resourceGatherer"] && turnCache["resourceGatherer"][ent.id()])
count += turnCache["resourceGatherer"][ent.id()];
if (count >= ent.maxGatherers())
return true;
return false;
};
/**
* get the best base (in terms of distance and accessIndex) for an entity
*/
m.getBestBase = function(ent, gameState)
m.getBestBase = function(gameState, ent)
{
var pos = ent.position();
if (!pos)
{
var holder = m.getHolder(ent, gameState);
var holder = m.getHolder(gameState, ent);
if (!holder || !holder.position())
{
API3.warn("Petra error: entity without position, but not garrisoned");
@ -131,7 +145,7 @@ m.getBestBase = function(ent, gameState)
return bestbase;
};
m.getHolder = function(ent, gameState)
m.getHolder = function(gameState, ent)
{
var found = undefined;
gameState.getEntities().forEach(function (holder) {
@ -147,7 +161,7 @@ m.getHolder = function(ent, gameState)
* return true if it is not worth finishing this building (it would surely decay)
* TODO implement the other conditions
*/
m.isNotWorthBuilding = function(ent, gameState)
m.isNotWorthBuilding = function(gameState, ent)
{
if (gameState.ai.HQ.territoryMap.getOwner(ent.position()) !== PlayerID)
{

View File

@ -1,70 +0,0 @@
var PETRA = function(m)
{
// Some functions that could be part of the gamestate.
// The next three are to register that we assigned a gatherer to a resource this turn.
// expects an entity
m.IsSupplyFull = function(gamestate, supply)
{
if (supply.isFull() === true)
return true;
var count = supply.resourceSupplyNumGatherers();
if (gamestate.turnCache["ressourceGatherer"] && gamestate.turnCache["ressourceGatherer"][supply.id()])
count += gamestate.turnCache["ressourceGatherer"][supply.id()];
if (count >= supply.maxGatherers())
return true;
return false;
};
// add a gatherer to the turn cache for this supply.
m.AddTCGatherer = function(gamestate, supplyID)
{
if (gamestate.turnCache["ressourceGatherer"] && gamestate.turnCache["ressourceGatherer"][supplyID] !== undefined)
++gamestate.turnCache["ressourceGatherer"][supplyID];
else
{
if (!gamestate.turnCache["ressourceGatherer"])
gamestate.turnCache["ressourceGatherer"] = {};
gamestate.turnCache["ressourceGatherer"][supplyID] = 1;
}
};
// remove a gatherer to the turn cache for this supply.
m.RemoveTCGatherer = function(gamestate, supplyID)
{
if (gamestate.turnCache["ressourceGatherer"] && gamestate.turnCache["ressourceGatherer"][supplyID])
--gamestate.turnCache["ressourceGatherer"][supplyID];
else
if (!gamestate.turnCache["ressourceGatherer"])
gamestate.turnCache["ressourceGatherer"] = {};
gamestate.turnCache["ressourceGatherer"][supplyID] = -1;
};
m.GetTCGatherer = function(gamestate, supplyID)
{
if (gamestate.turnCache["ressourceGatherer"] && gamestate.turnCache["ressourceGatherer"][supplyID])
return gamestate.turnCache["ressourceGatherer"][supplyID];
else
return 0;
};
// The next three are to register that we assigned a gatherer to a resource this turn.
m.AddTCRessGatherer = function(gamestate, resource)
{
if (gamestate.turnCache["ressourceGatherer-" + resource])
++gamestate.turnCache["ressourceGatherer-" + resource];
else
gamestate.turnCache["ressourceGatherer-" + resource] = 1;
};
m.GetTCRessGatherer = function(gamestate, resource)
{
if (gamestate.turnCache["ressourceGatherer-" + resource])
return gamestate.turnCache["ressourceGatherer-" + resource];
else
return 0;
};
return m;
}(PETRA);

View File

@ -22,6 +22,7 @@ m.HQ = function(Config)
this.currentPhase = undefined;
// cache the rates.
this.turnCache = {};
this.wantedRates = { "food": 0, "wood": 0, "stone":0, "metal": 0 };
this.currentRates = { "food": 0, "wood": 0, "stone":0, "metal": 0 };
this.lastFailedGather = { "wood": undefined, "stone": undefined, "metal": undefined };
@ -209,7 +210,7 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
ent.setMetadata(PlayerID, "access", gameState.ai.accessibility.getAccessValue(ent.position()));
if (ent.hasClass("Unit"))
{
m.getBestBase(ent, gameState).assignEntity(gameState, ent);
m.getBestBase(gameState, ent).assignEntity(gameState, ent);
ent.setMetadata(PlayerID, "role", undefined);
ent.setMetadata(PlayerID, "subrole", undefined);
ent.setMetadata(PlayerID, "plan", undefined);
@ -245,7 +246,7 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
else
{
// TODO should be reassigned later if a better base is captured
m.getBestBase(ent, gameState).assignEntity(gameState, ent);
m.getBestBase(gameState, ent).assignEntity(gameState, ent);
if (ent.hasTerritoryInfluence())
this.updateTerritories(gameState);
if (ent.decaying() && ent.isGarrisonHolder())
@ -569,12 +570,26 @@ m.HQ.prototype.getTotalResourceLevel = function(gameState)
// This is not per-se exact, it performs a few adjustments ad-hoc to account for travel distance, stuffs like that.
m.HQ.prototype.GetCurrentGatherRates = function(gameState)
{
for (var type in this.wantedRates)
this.currentRates[type] = 0;
for (var base of this.baseManagers)
base.getGatherRates(gameState, this.currentRates);
if (!this.turnCache["gatherRates"])
{
for (let res in this.currentRates)
this.currentRates[res] = 0.5 * this.GetTCResGatherer(res);
for (let base of this.baseManagers)
base.getGatherRates(gameState, this.currentRates);
for (let res in this.currentRates)
{
if (this.currentRates[res] < 0)
{
if (this.Config.debug > 0)
API3.warn("Petra: current rate for " + res + " < 0 with " + this.GetTCResGatherer(res) + " moved gatherers");
this.currentRates[res] = 0;
}
}
this.turnCache["gatherRates"] = true;
}
return this.currentRates;
};
@ -1751,15 +1766,16 @@ m.HQ.prototype.numActiveBase = function()
// to prevent the AI always reaffecting idle workers to these resourceSupplies (specially in naval maps).
m.HQ.prototype.assignGatherers = function(gameState)
{
var self = this;
for (var base of this.baseManagers)
{
base.workers.forEach( function (worker) {
if (worker.unitAIState().split(".")[1] !== "RETURNRESOURCE")
return;
var orders = worker.unitAIOrderData();
let orders = worker.unitAIOrderData();
if (orders.length < 2 || !orders[1].target || orders[1].target !== worker.getMetadata(PlayerID, "supply"))
return;
m.AddTCGatherer(gameState, orders[1].target);
self.AddTCGatherer(orders[1].target);
});
}
};
@ -1773,12 +1789,66 @@ m.HQ.prototype.isDangerousLocation = function(pos)
return false;
};
// Some functions that register that we assigned a gatherer to a resource this turn
// add a gatherer to the turn cache for this supply.
m.HQ.prototype.AddTCGatherer = function(supplyID)
{
if (this.turnCache["resourceGatherer"] && this.turnCache["resourceGatherer"][supplyID] !== undefined)
++this.turnCache["resourceGatherer"][supplyID];
else
{
if (!this.turnCache["resourceGatherer"])
this.turnCache["resourceGatherer"] = {};
this.turnCache["resourceGatherer"][supplyID] = 1;
}
};
// remove a gatherer to the turn cache for this supply.
m.HQ.prototype.RemoveTCGatherer = function(supplyID)
{
if (this.turnCache["resourceGatherer"] && this.turnCache["resourceGatherer"][supplyID])
--this.turnCache["resourceGatherer"][supplyID];
else
{
if (!this.turnCache["resourceGatherer"])
this.turnCache["resourceGatherer"] = {};
this.turnCache["resourceGatherer"][supplyID] = -1;
}
};
m.HQ.prototype.GetTCGatherer = function(supplyID)
{
if (this.turnCache["resourceGatherer"] && this.turnCache["resourceGatherer"][supplyID])
return this.turnCache["resourceGatherer"][supplyID];
else
return 0;
};
// The next two are to register that we assigned a gatherer to a resource this turn.
m.HQ.prototype.AddTCResGatherer = function(resource)
{
if (this.turnCache["resourceGatherer-" + resource])
++this.turnCache["resourceGatherer-" + resource];
else
this.turnCache["resourceGatherer-" + resource] = 1;
this.turnCache["gatherRates"] = false;
};
m.HQ.prototype.GetTCResGatherer = function(resource)
{
if (this.turnCache["resourceGatherer-" + resource])
return this.turnCache["resourceGatherer-" + resource];
else
return 0;
};
// Some functions are run every turn
// Others once in a while
m.HQ.prototype.update = function(gameState, queues, events)
{
Engine.ProfileStart("Headquarters update");
this.turnCache = {};
this.territoryMap = m.createTerritoryMap(gameState);
if (this.Config.debug > 1)
@ -1874,6 +1944,7 @@ m.HQ.prototype.update = function(gameState, queues, events)
m.HQ.prototype.Serialize = function()
{
let properties = {
"turnCache": this.turnCache,
"econState": this.econState,
"currentPhase": this.currentPhase,
"wantedRates": this.wantedRates,

View File

@ -23,7 +23,7 @@ m.ConstructionPlan.prototype = Object.create(m.QueuePlan.prototype);
// TODO: if there are specific requirements here, maybe try to do them?
m.ConstructionPlan.prototype.canStart = function(gameState)
{
if (gameState.buildingsBuilt > 0) // do not start another building if already one this turn
if (gameState.ai.HQ.turnCache["buildingBuilt"]) // do not start another building if already one this turn
return false;
if (!this.isGo(gameState))
@ -63,7 +63,7 @@ m.ConstructionPlan.prototype.start = function(gameState)
return;
}
}
gameState.buildingsBuilt++;
gameState.ai.HQ.turnCache["buildingBuilt"] = true;
if (this.metadata === undefined)
this.metadata = { "base": pos.base };

View File

@ -108,7 +108,7 @@ m.HQ.prototype.assignStartingEntities = function(gameState)
}
if (!bestbase) // entity outside our territory
{
bestbase = m.getBestBase(ent, gameState);
bestbase = m.getBestBase(gameState, ent);
bestbase.assignEntity(gameState, ent);
}
// now assign entities garrisoned inside this entity
@ -268,7 +268,7 @@ m.HQ.prototype.buildFirstBase = function(gameState)
let pos = ent.position();
if (!pos)
{
let holder = m.getHolder(ent, gameState);
let holder = m.getHolder(gameState, ent);
if (!holder || !holder.position())
continue;
pos = holder.position();

View File

@ -61,7 +61,7 @@ m.Worker.prototype.update = function(ent, gameState)
{
this.startGathering(gameState);
}
else if (!m.returnResources(this.ent, gameState)) // try to deposit resources
else if (!m.returnResources(gameState, this.ent)) // try to deposit resources
{
// no dropsite, abandon old resources and start gathering new ones
this.startGathering(gameState);
@ -78,10 +78,10 @@ m.Worker.prototype.update = function(ent, gameState)
if (supply && !supply.hasClass("Field") && !supply.hasClass("Animal")
&& supplyId !== this.ent.getMetadata(PlayerID, "supply"))
{
var nbGatherers = supply.resourceSupplyNumGatherers() + m.GetTCGatherer(gameState, supplyId);
var nbGatherers = supply.resourceSupplyNumGatherers() + gameState.ai.HQ.GetTCGatherer(supplyId);
if (nbGatherers > 1 && supply.resourceSupplyAmount()/nbGatherers < 30)
{
m.RemoveTCGatherer(gameState, supplyId);
gameState.ai.HQ.RemoveTCGatherer(supplyId);
this.startGathering(gameState);
}
else
@ -95,7 +95,7 @@ m.Worker.prototype.update = function(ent, gameState)
this.ent.setMetadata(PlayerID, "supply", supplyId);
else
{
m.RemoveTCGatherer(gameState, supplyId);
gameState.ai.HQ.RemoveTCGatherer(supplyId);
this.startGathering(gameState);
}
}
@ -117,7 +117,7 @@ m.Worker.prototype.update = function(ent, gameState)
dropsite.setMetadata(PlayerID, "access", goalAccess);
}
if (access !== goalAccess)
m.returnResources(this.ent, gameState);
m.returnResources(gameState, this.ent);
}
}
}
@ -210,7 +210,7 @@ m.Worker.prototype.update = function(ent, gameState)
dropsite.setMetadata(PlayerID, "access", goalAccess);
}
if (access !== goalAccess)
m.returnResources(this.ent, gameState);
m.returnResources(gameState, this.ent);
}
}
}
@ -254,13 +254,13 @@ m.Worker.prototype.startGathering = function(gameState)
supplies.splice(i--, 1);
continue;
}
if (m.IsSupplyFull(gameState, supplies[i].ent) === true)
if (m.IsSupplyFull(gameState, supplies[i].ent))
continue;
let inaccessibleTime = supplies[i].ent.getMetadata(PlayerID, "inaccessibleTime");
if (inaccessibleTime && gameState.ai.elapsedTime < inaccessibleTime)
continue;
// check if available resource is worth one additionnal gatherer (except for farms)
var nbGatherers = supplies[i].ent.resourceSupplyNumGatherers() + m.GetTCGatherer(gameState, supplies[i].id);
var nbGatherers = supplies[i].ent.resourceSupplyNumGatherers() + gameState.ai.HQ.GetTCGatherer(supplies[i].id);
if (supplies[i].ent.resourceSupplyType()["specific"] !== "grain"
&& nbGatherers > 0 && supplies[i].ent.resourceSupplyAmount()/(1+nbGatherers) < 30)
continue;
@ -268,7 +268,7 @@ m.Worker.prototype.startGathering = function(gameState)
var territoryOwner = gameState.ai.HQ.territoryMap.getOwner(supplies[i].ent.position());
if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // player is its own ally
continue;
m.AddTCGatherer(gameState, supplies[i].id);
gameState.ai.HQ.AddTCGatherer(supplies[i].id);
ent.setMetadata(PlayerID, "supply", supplies[i].id);
ret = supplies[i].ent;
break;
@ -534,10 +534,10 @@ m.Worker.prototype.startHunting = function(gameState, position)
if (inaccessibleTime && gameState.ai.elapsedTime < inaccessibleTime)
return;
if (m.IsSupplyFull(gameState, supply) === true)
if (m.IsSupplyFull(gameState, supply))
return;
// check if available resource is worth one additionnal gatherer (except for farms)
var nbGatherers = supply.resourceSupplyNumGatherers() + m.GetTCGatherer(gameState, supply.id());
var nbGatherers = supply.resourceSupplyNumGatherers() + gameState.ai.HQ.GetTCGatherer(supply.id());
if (nbGatherers > 0 && supply.resourceSupplyAmount()/(1+nbGatherers) < 30)
return;
@ -583,7 +583,7 @@ m.Worker.prototype.startHunting = function(gameState, position)
{
if (position)
return true;
m.AddTCGatherer(gameState, nearestSupply.id());
gameState.ai.HQ.AddTCGatherer(nearestSupply.id());
this.ent.gather(nearestSupply);
this.ent.setMetadata(PlayerID, "supply", nearestSupply.id());
this.ent.setMetadata(PlayerID, "target-foundation", undefined);
@ -632,10 +632,10 @@ m.Worker.prototype.startFishing = function(gameState)
if (!supply.position())
return;
if (m.IsSupplyFull(gameState, supply) === true)
if (m.IsSupplyFull(gameState, supply))
return;
// check if available resource is worth one additionnal gatherer (except for farms)
var nbGatherers = supply.resourceSupplyNumGatherers() + m.GetTCGatherer(gameState, supply.id());
var nbGatherers = supply.resourceSupplyNumGatherers() + gameState.ai.HQ.GetTCGatherer(supply.id());
if (nbGatherers > 0 && supply.resourceSupplyAmount()/(1+nbGatherers) < 30)
return;
@ -668,7 +668,7 @@ m.Worker.prototype.startFishing = function(gameState)
if (nearestSupply)
{
m.AddTCGatherer(gameState, nearestSupply.id());
gameState.ai.HQ.AddTCGatherer(nearestSupply.id());
this.ent.gather(nearestSupply);
this.ent.setMetadata(PlayerID, "supply", nearestSupply.id());
this.ent.setMetadata(PlayerID, "target-foundation", undefined);
@ -690,7 +690,7 @@ m.Worker.prototype.gatherNearestField = function(gameState, baseID)
for (var field of ownFields.values())
{
if (m.IsSupplyFull(gameState, field) === true)
if (m.IsSupplyFull(gameState, field))
continue;
var dist = API3.SquareVectorDistance(field.position(), this.ent.position());
if (dist < bestFarmDist)
@ -701,7 +701,7 @@ m.Worker.prototype.gatherNearestField = function(gameState, baseID)
}
if (bestFarmEnt)
{
m.AddTCGatherer(gameState, bestFarmEnt.id());
gameState.ai.HQ.AddTCGatherer(bestFarmEnt.id());
this.ent.setMetadata(PlayerID, "supply", bestFarmEnt.id());
}
return bestFarmEnt;
@ -774,7 +774,7 @@ m.Worker.prototype.gatherTreasure = function(gameState)
return false;
treasureFound.setMetadata(PlayerID, "lastGathered", gameState.ai.elapsedTime);
this.ent.gather(treasureFound);
m.AddTCGatherer(gameState, treasureFound.id());
gameState.ai.HQ.AddTCGatherer(treasureFound.id());
this.ent.setMetadata(PlayerID, "supply", treasureFound.id());
return true;
};