mostly some cleanings of AI code + a petra fix when a player is defeated

This was SVN commit r16710.
This commit is contained in:
mimo 2015-06-02 20:52:12 +00:00
parent ffdacbb612
commit a286091470
13 changed files with 126 additions and 156 deletions

View file

@ -1,11 +1,11 @@
var API3 = function(m)
{
m.EntityCollection = function(sharedAI, entities, filters)
m.EntityCollection = function(sharedAI, entities = new Map(), filters = [])
{
this._ai = sharedAI;
this._entities = entities || new Map();
this._filters = filters || [];
this._entities = entities;
this._filters = filters;
this.dynamicProp = [];
for (var filter of this._filters)
if (filter.dynamicProperties.length)
@ -61,18 +61,12 @@ m.EntityCollection.prototype.defreeze = function()
m.EntityCollection.prototype.toIdArray = function()
{
let ret = [];
for (let id of this._entities.keys())
ret.push(id);
return ret;
return [...this._entities.keys()];
};
m.EntityCollection.prototype.toEntityArray = function()
{
let ret = [];
for (let ent of this._entities.values())
ret.push(ent);
return ret;
return [...this._entities.values()];
};
m.EntityCollection.prototype.values = function()

View file

@ -1,14 +1,12 @@
var API3 = function(m)
{
m.Resources = function(amounts, population)
m.Resources = function(amounts = {}, population = 0)
{
if (!amounts)
amounts = { food : 0, wood : 0, stone : 0, metal : 0 };
for (let key of this.types)
this[key] = amounts[key] || 0;
this.population = (population && population > 0) ? population : 0;
this.population = (population > 0) ? population : 0;
};
m.Resources.prototype.types = [ "food", "wood", "stone", "metal" ];

View file

@ -6,19 +6,19 @@ m.SharedScript = function(settings)
{
if (!settings)
return;
this._players = settings.players;
this._templates = settings.templates;
this._derivedTemplates = {};
this._techTemplates = settings.techTemplates;
this._entityMetadata = {};
for (var i in this._players)
this._entityMetadata[this._players[i]] = {};
// always create for 8 + gaia players, since _players isn't aware of the human.
this._techModifications = { 0 : {}, 1 : {}, 2 : {}, 3 : {}, 4 : {}, 5 : {}, 6 : {}, 7 : {}, 8 : {} };
// array of entity collections
this._entityCollections = new Map();
// each name is a reference to the actual one.
@ -232,14 +232,8 @@ m.SharedScript.prototype.ApplyEntitiesDelta = function(state)
// by order of updating:
// we "Destroy" last because we want to be able to switch Metadata first.
var CreateEvents = state.events["Create"];
var DestroyEvents = state.events["Destroy"];
var RenamingEvents = state.events["EntityRenamed"];
var TrainingEvents = state.events["TrainingFinished"];
var ConstructionEvents = state.events["ConstructionFinished"];
var MetadataEvents = state.events["AIMetadata"];
var ownershipChangeEvents = state.events["OwnershipChanged"];
var CreateEvents = state.events["Create"];
for (let i = 0; i < CreateEvents.length; ++i)
{
let evt = CreateEvents[i];
@ -255,7 +249,7 @@ m.SharedScript.prototype.ApplyEntitiesDelta = function(state)
entCol.updateEnt(entity);
}
for (let evt of RenamingEvents)
for (let evt of state.events["EntityRenamed"])
{ // Switch the metadata: TODO entityCollections are updated only because of the owner change. Should be done properly
for (let p in this._players)
{
@ -264,14 +258,14 @@ m.SharedScript.prototype.ApplyEntitiesDelta = function(state)
}
}
for (let evt of TrainingEvents)
for (let evt of state.events["TrainingFinished"])
{ // Apply metadata stored in training queues
for (let entId of evt.entities)
for (let key in evt.metadata)
this.setMetadata(evt.owner, this._entities.get(entId), key, evt.metadata[key])
}
for (let evt of ConstructionEvents)
for (let evt of state.events["ConstructionFinished"])
{ // we'll move metadata.
if (!this._entities.has(evt.entity))
continue;
@ -283,7 +277,7 @@ m.SharedScript.prototype.ApplyEntitiesDelta = function(state)
foundationFinished[evt.entity] = true;
}
for (let evt of MetadataEvents)
for (let evt of state.events["AIMetadata"])
{
if (!this._entities.has(evt.id))
continue; // might happen in some rare cases of foundations getting destroyed, perhaps.
@ -292,6 +286,7 @@ m.SharedScript.prototype.ApplyEntitiesDelta = function(state)
this.setMetadata(evt.owner, this._entities.get(evt.id), key, evt.metadata[key])
}
var DestroyEvents = state.events["Destroy"];
for (var i = 0; i < DestroyEvents.length; ++i)
{
var evt = DestroyEvents[i];

View file

@ -297,63 +297,58 @@ m.Army.prototype.merge = function (gameState, otherArmy)
// TODO: when there is a technology update, we should probably recompute the strengths, or weird stuffs might happen.
m.Army.prototype.checkEvents = function (gameState, events)
{
var renameEvents = events["EntityRenamed"]; // take care of promoted and packed units
var destroyEvents = events["Destroy"];
var captureEvents = events["OwnershipChanged"];
var garriEvents = events["Garrison"];
// Warning the metadata is already cloned in shared.js. Futhermore, changes should be done before destroyEvents
// otherwise it would remove the old entity from this army list
// TODO we should may-be reevaluate the strength
for (let msg of renameEvents)
for (let evt of events["EntityRenamed"]) // take care of promoted and packed units
{
if (this.foeEntities.indexOf(msg.entity) !== -1)
if (this.foeEntities.indexOf(evt.entity) !== -1)
{
let ent = gameState.getEntityById(msg.newentity);
let ent = gameState.getEntityById(evt.newentity);
if (ent && ent.templateName().indexOf("resource|") !== -1) // corpse of animal killed
continue;
var idx = this.foeEntities.indexOf(msg.entity);
this.foeEntities[idx] = msg.newentity;
this.assignedAgainst[msg.newentity] = this.assignedAgainst[msg.entity];
this.assignedAgainst[msg.entity] = undefined;
for (var to in this.assignedTo)
if (this.assignedTo[to] == msg.entity)
this.assignedTo[to] = msg.newentity;
var idx = this.foeEntities.indexOf(evt.entity);
this.foeEntities[idx] = evt.newentity;
this.assignedAgainst[evt.newentity] = this.assignedAgainst[evt.entity];
this.assignedAgainst[evt.entity] = undefined;
for (let to in this.assignedTo)
if (this.assignedTo[to] == evt.entity)
this.assignedTo[to] = evt.newentity;
}
else if (this.ownEntities.indexOf(msg.entity) !== -1)
else if (this.ownEntities.indexOf(evt.entity) !== -1)
{
var idx = this.ownEntities.indexOf(msg.entity);
this.ownEntities[idx] = msg.newentity;
this.assignedTo[msg.newentity] = this.assignedTo[msg.entity];
this.assignedTo[msg.entity] = undefined;
for (var against in this.assignedAgainst)
var idx = this.ownEntities.indexOf(evt.entity);
this.ownEntities[idx] = evt.newentity;
this.assignedTo[evt.newentity] = this.assignedTo[evt.entity];
this.assignedTo[evt.entity] = undefined;
for (let against in this.assignedAgainst)
{
if (!this.assignedAgainst[against])
continue;
if (this.assignedAgainst[against].indexOf(msg.entity) !== -1)
this.assignedAgainst[against][this.assignedAgainst[against].indexOf(msg.entity)] = msg.newentity;
if (this.assignedAgainst[against].indexOf(evt.entity) !== -1)
this.assignedAgainst[against][this.assignedAgainst[against].indexOf(evt.entity)] = evt.newentity;
}
}
}
for (let msg of garriEvents)
this.removeFoe(gameState, msg.entity);
for (let evt of events["Garrison"])
this.removeFoe(gameState, evt.entity);
for (let msg of captureEvents)
for (let evt of events["OwnershipChanged"]) // captured
{
if (gameState.isPlayerAlly(msg.to))
this.removeFoe(gameState, msg.entity);
else if (msg.from === PlayerID)
this.removeOwn(gameState, msg.entity);
if (!gameState.isPlayerEnemy(evt.to))
this.removeFoe(gameState, evt.entity);
else if (evt.from === PlayerID)
this.removeOwn(gameState, evt.entity);
}
for (let msg of destroyEvents)
for (let evt of events["Destroy"])
{
if (!msg.entityObj)
if (!evt.entityObj)
continue;
// we may have capture+destroy, so do not trust owner and check all possibilities
this.removeOwn(gameState, msg.entity, msg.entityObj);
this.removeFoe(gameState, msg.entity, msg.entityObj);
this.removeOwn(gameState, evt.entity, evt.entityObj);
this.removeFoe(gameState, evt.entity, evt.entityObj);
}
};

View file

@ -49,15 +49,13 @@ m.AttackManager.prototype.setRushes = function(allowed)
m.AttackManager.prototype.checkEvents = function(gameState, events)
{
let PlayerDefeated = events["PlayerDefeated"];
for (let evt of PlayerDefeated)
for (let evt of events["PlayerDefeated"])
this.defeated[evt.playerId] = true;
let AttackRequest = events["AttackRequest"];
let answer = false;
let other = undefined;
let targetPlayer = undefined;
for (let evt of AttackRequest)
for (let evt of events["AttackRequest"])
{
if (evt.source === PlayerID || !gameState.isPlayerAlly(evt.source) || !gameState.isPlayerEnemy(evt.target))
continue;
@ -339,13 +337,13 @@ m.AttackManager.prototype.getAttackInPreparation = function(type)
*/
m.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
{
var enemyPlayer = undefined;
var enemyPlayer;
// first check if there is a preferred enemy based on our victory conditions
if (gameState.getGameType() === "wonder")
{
var moreAdvanced = undefined;
var enemyWonder = undefined;
var moreAdvanced;
var enemyWonder;
var wonders = gameState.getEnemyStructures().filter(API3.Filters.byClass("Wonder"));
for (let wonder of wonders.values())
{
@ -392,6 +390,7 @@ m.AttackManager.prototype.getEnemyPlayer = function(gameState, attack)
if (attack.type !== "HugeAttack")
{
if (attack.targetPlayer === undefined && this.currentEnemyPlayer !== undefined
&& !this.defeated[this.currentEnemyPlayer]
&& gameState.getEnemyEntities(this.currentEnemyPlayer) > 0)
return this.currentEnemyPlayer;

View file

@ -342,6 +342,14 @@ m.AttackPlan.prototype.updatePreparation = function(gameState)
this.debugAttack();
// find our target (if not yet done or because our previous one was destroyed)
if (!this.targetPlayer)
{
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
if (!this.targetPlayer)
return 0;
if (this.target && this.target.owner() !== this.targetPlayer)
this.target = undefined;
}
if (!this.target || !gameState.getEntityById(this.target.id()))
{
this.target = this.getNearestTarget(gameState, this.rallyPoint);
@ -963,6 +971,14 @@ m.AttackPlan.prototype.StartAttack = function(gameState)
if (this.Config.debug > 1)
API3.warn("start attack " + this.name + " with type " + this.type);
if (!this.targetPlayer)
{
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
if (!this.targetPlayer)
return false;
if (this.target && this.target.owner() !== this.targetPlayer)
this.target = undefined;
}
if (!this.target || !gameState.getEntityById(this.target.id())) // our target was destroyed during our preparation
{
if (!this.targetPos) // should not happen
@ -1074,8 +1090,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
else
{
// if we are attacked while waiting the rest of the army, retaliate
var attackedEvents = events["Attacked"];
for (var evt of attackedEvents)
for (var evt of events["Attacked"])
{
if (IDs.indexOf(evt.target) == -1)
continue;
@ -1105,8 +1120,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
// or if we reached the enemy base. Different plans may react differently.
var attackedNB = 0;
var attackedUnitNB = 0;
var attackedEvents = events["Attacked"];
for (var evt of attackedEvents)
for (var evt of events["Attacked"])
{
if (IDs.indexOf(evt.target) == -1)
continue;
@ -1233,6 +1247,14 @@ m.AttackPlan.prototype.update = function(gameState, events)
if (this.state === "")
{
// First update the target if needed:
if (!this.targetPlayer)
{
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
if (!this.targetPlayer)
return false;
if (this.target && this.target.owner() !== this.targetPlayer)
this.target = undefined;
}
if (this.target && this.target.owner() === 0 && this.targetPlayer !== 0) // this enemy has resigned
this.target = undefined;
if (!this.target || !gameState.getEntityById(this.target.id()))
@ -1289,8 +1311,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
this.targetPos = this.target.position();
var time = gameState.ai.elapsedTime;
var attackedEvents = events["Attacked"];
for (var evt of attackedEvents)
for (var evt of events["Attacked"])
{
if (IDs.indexOf(evt.target) == -1)
continue;
@ -1711,30 +1732,25 @@ m.AttackPlan.prototype.resetPath = function(gameState)
m.AttackPlan.prototype.checkEvents = function(gameState, events)
{
let renameEvents = events["EntityRenamed"];
for (let evt of renameEvents)
for (let evt of events["EntityRenamed"])
{
if (this.target && this.target.id() == evt.entity)
{
this.target = gameState.getEntityById(evt.newentity);
if (this.target)
this.targetPos = this.target.position();
}
if (!this.target || this.target.id() != evt.entity)
continue;
this.target = gameState.getEntityById(evt.newentity);
if (this.target)
this.targetPos = this.target.position();
}
let captureEvents = events["OwnershipChanged"];
for (let evt of captureEvents)
for (let evt of events["OwnershipChanged"]) // capture event
if (this.target && this.target.id() == evt.entity && gameState.isPlayerAlly(evt.to))
this.target = undefined;
let PlayerDefeated = events["PlayerDefeated"];
for (let evt of PlayerDefeated)
for (let evt of events["PlayerDefeated"])
{
if (this.targetPlayer == evt.playerId)
{
this.targetPlayer = undefined;
this.target = undefined;
}
if (!this.targetPlayer || this.targetPlayer != evt.playerId)
continue;
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
this.target = undefined;
}
};

View file

@ -96,17 +96,13 @@ m.BaseManager.prototype.setAnchor = function(gameState, anchorEntity)
m.BaseManager.prototype.checkEvents = function (gameState, events, queues)
{
var destEvents = events["Destroy"];
var createEvents = events["Create"];
var cFinishedEvents = events["ConstructionFinished"];
for (var evt of destEvents)
for (let evt of events["Destroy"])
{
// let's check we haven't lost an important building here.
if (evt && !evt.SuccessfulFoundation && evt.entityObj && evt.metadata && evt.metadata[PlayerID] &&
evt.metadata[PlayerID]["base"] && evt.metadata[PlayerID]["base"] == this.ID)
{
var ent = evt.entityObj;
let ent = evt.entityObj;
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
this.removeDropsite(gameState, ent);
if (evt.metadata[PlayerID]["baseAnchor"] && evt.metadata[PlayerID]["baseAnchor"] === true)
@ -114,8 +110,7 @@ m.BaseManager.prototype.checkEvents = function (gameState, events, queues)
}
}
let captureEvents = events["OwnershipChanged"];
for (let evt of captureEvents)
for (let evt of events["OwnershipChanged"]) // capture event
{
if (evt.from !== PlayerID)
continue;
@ -128,36 +123,32 @@ m.BaseManager.prototype.checkEvents = function (gameState, events, queues)
this.anchorLost(gameState, ent);
}
for (var evt of cFinishedEvents)
for (var evt of events["ConstructionFinished"])
{
if (!evt || !evt.newentity)
continue;
var ent = gameState.getEntityById(evt.newentity);
if (ent === undefined)
let ent = gameState.getEntityById(evt.newentity);
if (!ent)
continue;
if (evt.newentity == evt.entity) // repaired building
continue;
if (ent.getMetadata(PlayerID, "base") == this.ID)
{
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
this.assignResourceToDropsite(gameState, ent);
}
}
for (var evt of createEvents)
for (let evt of events["Create"])
{
if (!evt || !evt.entity)
continue;
var ent = gameState.getEntityById(evt.entity);
if (ent === undefined)
let ent = gameState.getEntityById(evt.entity);
if (!ent)
continue;
// do necessary stuff here
}
let renameEvents = events["EntityRenamed"];
for (let evt of renameEvents)
for (let evt of events["EntityRenamed"])
{
if (!this.anchorId || this.anchorId !== evt.entity)
continue;

View file

@ -379,8 +379,7 @@ m.DefenseManager.prototype.abortArmy = function(gameState, army)
// and if a ranged siege unit (not used for defense) is attacked, garrison it in the nearest fortress
m.DefenseManager.prototype.checkEvents = function(gameState, events)
{
var attackedEvents = events["Attacked"];
for (var evt of attackedEvents)
for (var evt of events["Attacked"])
{
var target = gameState.getEntityById(evt.target);
if (!target || !gameState.isEntityOwn(target) || !target.position())

View file

@ -117,8 +117,7 @@ m.HQ.prototype.getSeaIndex = function (gameState, index1, index2)
m.HQ.prototype.checkEvents = function (gameState, events, queues)
{
var CreateEvents = events["Create"];
for (var evt of CreateEvents)
for (var evt of events["Create"])
{
// Let's check if we have a building set to create a new base.
var ent = gameState.getEntityById(evt.entity);
@ -159,8 +158,7 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
}
}
var ConstructionEvents = events["ConstructionFinished"];
for (var evt of ConstructionEvents)
for (var evt of events["ConstructionFinished"])
{
// Let's check if we have a building set to create a new base.
// TODO: move to the base manager.
@ -198,8 +196,7 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
}
}
let captureEvents = events["OwnershipChanged"];
for (let evt of captureEvents)
for (let evt of events["OwnershipChanged"]) // capture events
{
if (evt.to !== PlayerID)
continue;
@ -257,8 +254,7 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
// deal with the different rally points of training units: the rally point is set when the training starts
// for the time being, only autogarrison is used
var TrainingEvents = events["TrainingStarted"];
for (var evt of TrainingEvents)
for (var evt of events["TrainingStarted"])
{
var ent = gameState.getEntityById(evt.entity);
if (!ent || !ent.isOwn(PlayerID))
@ -273,8 +269,7 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
ent.unsetRallyPoint();
}
var TrainingEvents = events["TrainingFinished"];
for (var evt of TrainingEvents)
for (var evt of events["TrainingFinished"])
{
for (var entId of evt.entities)
{
@ -308,8 +303,7 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
}
}
let TerritoryEvents = events["TerritoryDecayChanged"];
for (let evt of TerritoryEvents)
for (let evt of events["TerritoryDecayChanged"])
{
let ent = gameState.getEntityById(evt.entity);
if (!ent || !ent.isOwn(PlayerID) || ent.foundationProgress() !== undefined || !ent.isGarrisonHolder())

View file

@ -253,33 +253,30 @@ m.NavalManager.prototype.getUnconnectedSeas = function(gameState, region)
m.NavalManager.prototype.checkEvents = function(gameState, queues, events)
{
var evts = events["ConstructionFinished"];
// TODO: probably check stuffs like a base destruction.
for (var evt of evts)
for (let evt of events["ConstructionFinished"])
{
if (!evt || !evt.newentity)
continue;
var entity = gameState.getEntityById(evt.newentity);
let entity = gameState.getEntityById(evt.newentity);
if (entity && entity.hasClass("Dock") && entity.isOwn(PlayerID))
this.setDockIndex(gameState, entity);
}
var evts = events["TrainingFinished"];
for (var evt of evts)
for (let evt of events["TrainingFinished"])
{
if (!evt || !evt.entities)
continue;
for (var entId of evt.entities)
for (let entId of evt.entities)
{
var entity = gameState.getEntityById(entId);
let entity = gameState.getEntityById(entId);
if (!entity || !entity.hasClass("Ship") || !entity.isOwn(PlayerID))
continue;
this.setShipIndex(gameState, entity);
}
}
var evts = events["Destroy"];
for (var evt of evts)
for (let evt of events["Destroy"])
{
if (!evt.entityObj || evt.entityObj.owner() !== PlayerID || !evt.metadata || !evt.metadata[PlayerID])
continue;
@ -324,8 +321,7 @@ m.NavalManager.prototype.checkEvents = function(gameState, queues, events)
}
}
let captureEvents = events["OwnershipChanged"];
for (let evt of captureEvents)
for (let evt of events["OwnershipChanged"]) // capture events
{
if (evt.to === PlayerID)
{
@ -339,12 +335,9 @@ m.NavalManager.prototype.checkEvents = function(gameState, queues, events)
m.NavalManager.prototype.getPlan = function(ID)
{
for (var plan of this.transportPlans)
{
if (plan.ID !== ID)
continue;
return plan;
}
for (let plan of this.transportPlans)
if (plan.ID === ID)
return plan;
return undefined;
};

View file

@ -1,7 +1,7 @@
var PETRA = function(m)
{
m.ResearchPlan = function(gameState, type, rush)
m.ResearchPlan = function(gameState, type, rush = false)
{
if (!m.QueuePlan.call(this, gameState, type, {}))
return false;
@ -11,7 +11,7 @@ m.ResearchPlan = function(gameState, type, rush)
this.category = "technology";
this.rush = rush ? true : false;
this.rush = rush;
return true;
};

View file

@ -1,7 +1,7 @@
var PETRA = function(m)
{
m.TrainingPlan = function(gameState, type, metadata, number, maxMerge)
m.TrainingPlan = function(gameState, type, metadata, number = 1, maxMerge = 5)
{
if (!m.QueuePlan.call(this, gameState, type, metadata))
{
@ -12,8 +12,8 @@ m.TrainingPlan = function(gameState, type, metadata, number, maxMerge)
this.category = "unit";
this.cost = new API3.Resources(this.template.cost(), +this.template._template.Cost.Population);
this.number = number !== undefined ? number : 1;
this.maxMerge = maxMerge !== undefined ? maxMerge : 5;
this.number = number;
this.maxMerge = maxMerge;
return true;
};

View file

@ -301,8 +301,7 @@ m.TradeManager.prototype.performBarter = function(gameState)
m.TradeManager.prototype.checkEvents = function(gameState, events)
{
// check if one market from a traderoute is renamed, change the route accordingly
let renameEvents = events["EntityRenamed"];
for (let evt of renameEvents)
for (let evt of events["EntityRenamed"])
{
let ent = gameState.getEntityById(evt.newentity);
if (!ent || !ent.hasClass("Market"))
@ -323,8 +322,7 @@ m.TradeManager.prototype.checkEvents = function(gameState, events)
}
// if one market is destroyed, we should look for a better route
let destroyEvents = events["Destroy"];
for (let evt of destroyEvents)
for (let evt of events["Destroy"])
{
if (!evt.entityObj)
continue;
@ -338,8 +336,7 @@ m.TradeManager.prototype.checkEvents = function(gameState, events)
}
// same thing if one market is built
let createEvents = events["Create"];
for (let evt of createEvents)
for (let evt of events["Create"])
{
let ent = gameState.getEntityById(evt.entity);
if (!ent || ent.foundationProgress() !== undefined || !ent.hasClass("Market") || !gameState.isPlayerAlly(ent.owner()))
@ -352,8 +349,7 @@ m.TradeManager.prototype.checkEvents = function(gameState, events)
// and same thing for captured markets
let captureEvents = events["OwnershipChanged"];
for (let evt of captureEvents)
for (let evt of events["OwnershipChanged"])
{
if (!gameState.isPlayerAlly(evt.from) && !gameState.isPlayerAlly(evt.to))
continue;