Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/ai/petra/startingStrategy.js
/** | /** | ||||
* Determines the strategy to adopt when starting a new game, | * Determines the strategy to adopt when starting a new game, | ||||
* depending on the initial conditions | * depending on the initial conditions | ||||
*/ | */ | ||||
PETRA.HQ.prototype.gameAnalysis = function(gameState) | PETRA.HQ.prototype.gameAnalysis = function(gameState) | ||||
{ | { | ||||
// Analysis of the terrain and the different access regions | // Analysis of the terrain and the different access regions | ||||
if (!this.regionAnalysis(gameState)) | if (!this.regionAnalysis(gameState)) | ||||
return; | return; | ||||
this.basesManager.init(gameState); | |||||
this.attackManager.init(gameState); | this.attackManager.init(gameState); | ||||
this.buildManager.init(gameState); | this.buildManager.init(gameState); | ||||
this.navalManager.init(gameState); | this.navalManager.init(gameState); | ||||
this.tradeManager.init(gameState); | this.tradeManager.init(gameState); | ||||
this.diplomacyManager.init(gameState); | this.diplomacyManager.init(gameState); | ||||
// Make a list of buildable structures from the config file | // Make a list of buildable structures from the config file | ||||
this.structureAnalysis(gameState); | this.structureAnalysis(gameState); | ||||
// Let's get our initial situation here. | // Let's get our initial situation here. | ||||
let nobase = new PETRA.BaseManager(gameState, this.Config); | |||||
nobase.init(gameState); | |||||
nobase.accessIndex = 0; | |||||
this.baseManagers.push(nobase); // baseManagers[0] will deal with unit/structure without base | |||||
let ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")); | |||||
for (let cc of ccEnts.values()) | |||||
if (cc.foundationProgress() === undefined) | |||||
this.createBase(gameState, cc); | |||||
else | |||||
this.createBase(gameState, cc, "unconstructed"); | |||||
this.updateTerritories(gameState); | this.updateTerritories(gameState); | ||||
// Assign entities and resources in the different bases | // Assign entities and resources in the different bases | ||||
this.assignStartingEntities(gameState); | this.assignStartingEntities(gameState); | ||||
// Sandbox difficulty should not try to expand | // Sandbox difficulty should not try to expand | ||||
this.canExpand = this.Config.difficulty != 0; | this.canExpand = this.Config.difficulty != 0; | ||||
Show All 9 Lines | if (!gameState.isTemplateAvailable(template) || !gameState.getTemplate(template).available(gameState)) | ||||
this.canBuildUnits = false; | this.canBuildUnits = false; | ||||
this.dispatchUnits(gameState); | this.dispatchUnits(gameState); | ||||
} | } | ||||
else | else | ||||
this.buildFirstBase(gameState); | this.buildFirstBase(gameState); | ||||
} | } | ||||
// configure our first base strategy | // configure our first base strategy | ||||
if (this.baseManagers.length > 1) | if (this.numPotentialBases()) | ||||
this.configFirstBase(gameState); | this.configFirstBase(gameState); | ||||
}; | }; | ||||
/** | /** | ||||
* Assign the starting entities to the different bases | * Assign the starting entities to the different bases | ||||
*/ | */ | ||||
PETRA.HQ.prototype.assignStartingEntities = function(gameState) | PETRA.HQ.prototype.assignStartingEntities = function(gameState) | ||||
{ | { | ||||
Show All 23 Lines | if (sea > 1 && !this.navalRegions[sea]) | ||||
this.navalRegions[sea] = true; | this.navalRegions[sea] = true; | ||||
// if garrisoned units inside, ungarrison them except if a ship in which case we will make a transport | // if garrisoned units inside, ungarrison them except if a ship in which case we will make a transport | ||||
// when a construction will start (see createTransportIfNeeded) | // when a construction will start (see createTransportIfNeeded) | ||||
if (ent.isGarrisonHolder() && ent.garrisoned().length && !ent.hasClass("Ship")) | if (ent.isGarrisonHolder() && ent.garrisoned().length && !ent.hasClass("Ship")) | ||||
for (let id of ent.garrisoned()) | for (let id of ent.garrisoned()) | ||||
ent.unload(id); | ent.unload(id); | ||||
let bestbase; | |||||
let territorypos = this.territoryMap.gamePosToMapPos(pos); | let territorypos = this.territoryMap.gamePosToMapPos(pos); | ||||
let territoryIndex = territorypos[0] + territorypos[1]*this.territoryMap.width; | let territoryIndex = territorypos[0] + territorypos[1]*this.territoryMap.width; | ||||
for (let i = 1; i < this.baseManagers.length; ++i) | |||||
{ | this.basesManager.assignEntity(gameState, ent, territoryIndex); | ||||
let base = this.baseManagers[i]; | |||||
if ((!ent.getMetadata(PlayerID, "base") || ent.getMetadata(PlayerID, "base") != base.ID) && | |||||
base.territoryIndices.indexOf(territoryIndex) == -1) | |||||
continue; | |||||
base.assignEntity(gameState, ent); | |||||
bestbase = base; | |||||
break; | |||||
} | |||||
if (!bestbase) // entity outside our territory | |||||
{ | |||||
if (ent.hasClass("Structure") && !ent.decaying() && ent.resourceDropsiteTypes()) | |||||
bestbase = this.createBase(gameState, ent, "anchorless"); | |||||
else | |||||
bestbase = PETRA.getBestBase(gameState, ent) || this.baseManagers[0]; | |||||
bestbase.assignEntity(gameState, ent); | |||||
} | |||||
// now assign entities garrisoned inside this entity | |||||
if (ent.isGarrisonHolder() && ent.garrisoned().length) | |||||
for (let id of ent.garrisoned()) | |||||
bestbase.assignEntity(gameState, gameState.getEntityById(id)); | |||||
// and find something useful to do if we already have a base | |||||
if (pos && bestbase.ID !== this.baseManagers[0].ID) | |||||
{ | |||||
bestbase.assignRolelessUnits(gameState, [ent]); | |||||
if (ent.getMetadata(PlayerID, "role") === "worker") | |||||
{ | |||||
bestbase.reassignIdleWorkers(gameState, [ent]); | |||||
bestbase.workerObject.update(gameState, ent); | |||||
} | |||||
} | |||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* determine the main land Index (or water index if none) | * determine the main land Index (or water index if none) | ||||
* as well as the list of allowed (land andf water) regions | * as well as the list of allowed (land andf water) regions | ||||
*/ | */ | ||||
PETRA.HQ.prototype.regionAnalysis = function(gameState) | PETRA.HQ.prototype.regionAnalysis = function(gameState) | ||||
▲ Show 20 Lines • Show All 289 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* configure our first base expansion | * configure our first base expansion | ||||
* - if on a small island, favor fishing | * - if on a small island, favor fishing | ||||
* - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion) | * - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion) | ||||
*/ | */ | ||||
PETRA.HQ.prototype.configFirstBase = function(gameState) | PETRA.HQ.prototype.configFirstBase = function(gameState) | ||||
{ | { | ||||
if (this.baseManagers.length < 2) | if (this.numPotentialBases() < 1) | ||||
return; | return; | ||||
this.firstBaseConfig = true; | this.firstBaseConfig = true; | ||||
let startingSize = 0; | let startingSize = 0; | ||||
let startingLand = []; | let startingLand = []; | ||||
for (let region in this.landRegions) | for (let region in this.landRegions) | ||||
{ | { | ||||
for (let base of this.baseManagers) | for (const base of this.baseManagers()) | ||||
{ | { | ||||
if (!base.anchor || base.accessIndex != +region) | if (!base.anchor || base.accessIndex != +region) | ||||
continue; | continue; | ||||
startingSize += gameState.ai.accessibility.regionSize[region]; | startingSize += gameState.ai.accessibility.regionSize[region]; | ||||
startingLand.push(base.accessIndex); | startingLand.push(base.accessIndex); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
Show All 20 Lines | PETRA.HQ.prototype.configFirstBase = function(gameState) | ||||
else | else | ||||
this.maxFields = false; | this.maxFields = false; | ||||
// - count the available food resource, and react accordingly | // - count the available food resource, and react accordingly | ||||
let startingFood = gameState.getResources().food; | let startingFood = gameState.getResources().food; | ||||
let check = {}; | let check = {}; | ||||
for (let proxim of ["nearby", "medium", "faraway"]) | for (let proxim of ["nearby", "medium", "faraway"]) | ||||
{ | { | ||||
for (let base of this.baseManagers) | for (const base of this.baseManagers()) | ||||
{ | { | ||||
for (let supply of base.dropsiteSupplies.food[proxim]) | for (let supply of base.dropsiteSupplies.food[proxim]) | ||||
{ | { | ||||
if (check[supply.id]) // avoid double counting as same resource can appear several time | if (check[supply.id]) // avoid double counting as same resource can appear several time | ||||
continue; | continue; | ||||
check[supply.id] = true; | check[supply.id] = true; | ||||
startingFood += supply.ent.resourceSupplyAmount(); | startingFood += supply.ent.resourceSupplyAmount(); | ||||
} | } | ||||
Show All 9 Lines | if (startingFood < 800) | ||||
else | else | ||||
this.needFarm = true; | this.needFarm = true; | ||||
} | } | ||||
// - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion) | // - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion) | ||||
let startingWood = gameState.getResources().wood; | let startingWood = gameState.getResources().wood; | ||||
check = {}; | check = {}; | ||||
for (let proxim of ["nearby", "medium", "faraway"]) | for (let proxim of ["nearby", "medium", "faraway"]) | ||||
{ | { | ||||
for (let base of this.baseManagers) | for (const base of this.baseManagers()) | ||||
{ | { | ||||
for (let supply of base.dropsiteSupplies.wood[proxim]) | for (let supply of base.dropsiteSupplies.wood[proxim]) | ||||
{ | { | ||||
if (check[supply.id]) // avoid double counting as same resource can appear several time | if (check[supply.id]) // avoid double counting as same resource can appear several time | ||||
continue; | continue; | ||||
check[supply.id] = true; | check[supply.id] = true; | ||||
startingWood += supply.ent.resourceSupplyAmount(); | startingWood += supply.ent.resourceSupplyAmount(); | ||||
} | } | ||||
Show All 24 Lines | if (gameState.isCeasefireActive()) | ||||
allowed = 1; | allowed = 1; | ||||
} | } | ||||
this.attackManager.setRushes(allowed); | this.attackManager.setRushes(allowed); | ||||
} | } | ||||
// immediatly build a wood dropsite if possible. | // immediatly build a wood dropsite if possible. | ||||
if (!gameState.getOwnEntitiesByClass("DropsiteWood", true).hasEntities()) | if (!gameState.getOwnEntitiesByClass("DropsiteWood", true).hasEntities()) | ||||
{ | { | ||||
const newDP = this.baseManagers[1].findBestDropsiteAndLocation(gameState, "wood"); | const newDP = this.baseManagers()[0].findBestDropsiteAndLocation(gameState, "wood"); | ||||
if (newDP.quality > 40 && this.canBuild(gameState, newDP.templateName)) | if (newDP.quality > 40 && this.canBuild(gameState, newDP.templateName)) | ||||
{ | { | ||||
// if we start with enough workers, put our available resources in this first dropsite | // if we start with enough workers, put our available resources in this first dropsite | ||||
// same thing if our pop exceed the allowed one, as we will need several houses | // same thing if our pop exceed the allowed one, as we will need several houses | ||||
let numWorkers = gameState.getOwnUnits().filter(API3.Filters.byClass("Worker")).length; | let numWorkers = gameState.getOwnUnits().filter(API3.Filters.byClass("Worker")).length; | ||||
if (numWorkers > 12 && newDP.quality > 60 || | if (numWorkers > 12 && newDP.quality > 60 || | ||||
gameState.getPopulation() > gameState.getPopulationLimit() + 20) | gameState.getPopulation() > gameState.getPopulationLimit() + 20) | ||||
{ | { | ||||
const cost = new API3.Resources(gameState.getTemplate(newDP.templateName).cost()); | const cost = new API3.Resources(gameState.getTemplate(newDP.templateName).cost()); | ||||
gameState.ai.queueManager.setAccounts(gameState, cost, "dropsites"); | gameState.ai.queueManager.setAccounts(gameState, cost, "dropsites"); | ||||
} | } | ||||
gameState.ai.queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, newDP.templateName, { "base": this.baseManagers[1].ID }, newDP.pos)); | gameState.ai.queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, newDP.templateName, { "base": this.baseManagers()[0].ID }, newDP.pos)); | ||||
} | } | ||||
} | } | ||||
// and build immediately a corral if needed | // and build immediately a corral if needed | ||||
if (this.needCorral) | if (this.needCorral) | ||||
{ | { | ||||
const template = gameState.applyCiv("structures/{civ}/corral"); | const template = gameState.applyCiv("structures/{civ}/corral"); | ||||
if (!gameState.getOwnEntitiesByClass("Corral", true).hasEntities() && this.canBuild(gameState, template)) | if (!gameState.getOwnEntitiesByClass("Corral", true).hasEntities() && this.canBuild(gameState, template)) | ||||
gameState.ai.queues.corral.addPlan(new PETRA.ConstructionPlan(gameState, template, { "base": this.baseManagers[1].ID })); | gameState.ai.queues.corral.addPlan(new PETRA.ConstructionPlan(gameState, template, { "base": this.baseManagers()[0].ID })); | ||||
} | } | ||||
}; | }; |
Wildfire Games · Phabricator