1
0
forked from 0ad/0ad

add AI support of maps of different dimensions, see #2960

This was SVN commit r16062.
This commit is contained in:
mimo 2014-12-23 14:17:52 +00:00
parent 49187dd990
commit 9684bcd360
8 changed files with 82 additions and 61 deletions

View File

@ -6,28 +6,48 @@ var API3 = function(m)
*/
// The function needs to be named too because of the copyConstructor functionality
m.Map = function Map(sharedScript, originalMap, actualCopy)
m.Map = function Map(sharedScript, type, originalMap, actualCopy)
{
// get the map to find out the correct dimensions
var gameMap = sharedScript.passabilityMap;
this.width = gameMap.width;
this.height = gameMap.height;
this.length = gameMap.data.length;
// get the correct dimensions according to the map type
if (type === "territory")
{
var map = sharedScript.territoryMap;
this.width = map.width;
this.height = map.height;
this.cellSize = map.cellSize;
}
else if (type === "resource")
{
this.cellSize = 4;
var map = sharedScript.passabilityMap;
this.width = map.width * map.cellSize / this.cellSize;
this.height = map.height * map.cellSize / this.cellSize;
}
else
{
var map = sharedScript.passabilityMap;
this.width = map.width;
this.height = map.height;
this.cellSize = map.cellSize;
}
this.length = this.width * this.height;
this.maxVal = 255;
// sanity check
if (originalMap && originalMap.length !== this.length)
warn("AI map size incompatibility with type " + type + ": original " + originalMap.length + " new " + this.length);
if (originalMap && actualCopy)
{
this.map = new Uint8Array(this.length);
for (let i = 0; i < originalMap.length; ++i)
for (let i = 0; i < this.length; ++i)
this.map[i] = originalMap[i];
}
else if (originalMap)
this.map = originalMap;
else
this.map = new Uint8Array(this.length);
this.cellSize = 4;
};
m.Map.prototype.setMaxVal = function(val)

View File

@ -26,6 +26,11 @@ m.SharedScript = function(settings)
this._entityCollectionsByDynProp = {};
this._entityCollectionsUID = 0;
// size of the map
// TODO should be set in settings, for the time being recompute it assuming the passability cell size
this.sizeMap = undefined;
this.passabilityCell = 4;
// A few notes about these maps. They're updated by checking for "create" and "destroy" events for all resources
// TODO: change the map when the resource amounts change for at least stone and metal mines.
this.resourceMaps = {}; // Contains maps showing the density of wood, stone and metal
@ -141,15 +146,19 @@ m.SharedScript.prototype.init = function(state, deserialization)
this.ApplyTemplatesDelta(state);
this.passabilityClasses = state.passabilityClasses;
this.passabilityMap = state.passabilityMap;
this.players = this._players;
this.playersData = state.players;
this.territoryMap = state.territoryMap;
this.timeElapsed = state.timeElapsed;
this.circularMap = state.circularMap;
this.gameType = state.gameType;
this.barterPrices = state.barterPrices;
this.passabilityMap = state.passabilityMap;
this.passabilityMap.cellSize = this.passabilityCell;
this.sizeMap = this.passabilityMap.width * this.passabilityMap.cellSize;
this.territoryMap = state.territoryMap;
this.territoryMap.cellSize = this.sizeMap / this.territoryMap.width;
if (!deserialization)
{
this._entities = new Map();
@ -195,11 +204,14 @@ m.SharedScript.prototype.onUpdate = function(state)
// those are dynamic and need to be reset as the "state" object moves in memory.
this.events = state.events;
this.passabilityClasses = state.passabilityClasses;
this.passabilityMap = state.passabilityMap;
this.playersData = state.players;
this.territoryMap = state.territoryMap;
this.timeElapsed = state.timeElapsed;
this.barterPrices = state.barterPrices;
this.passabilityMap = state.passabilityMap;
this.passabilityMap.cellSize = this.sizeMap / this.passabilityMap.width;
this.territoryMap = state.territoryMap;
this.territoryMap.cellSize = this.sizeMap / this.territoryMap.width;
for (var i in this.gameState)
this.gameState[i].update(this,state);

View File

@ -17,7 +17,7 @@ var API3 = function(m)
m.aStarPath = function(gameState, onWater, disregardEntities, targetTerritory)
{
// get the terrain analyzer map as a reference.
this.Map(gameState.ai, gameState.ai.terrainAnalyzer.map);
this.Map(gameState.ai, "passability", gameState.ai.terrainAnalyzer.map);
// get the accessibility as a reference
this.accessibility = gameState.ai.accessibility;
this.terrainAnalyzer = gameState.ai.terrainAnalyzer;

View File

@ -17,8 +17,7 @@ var API3 = function(m)
m.TerrainAnalysis = function()
{
this.cellSize = 4;
}
};
m.copyPrototype(m.TerrainAnalysis, m.Map);
@ -27,11 +26,12 @@ m.TerrainAnalysis.prototype.init = function(sharedScript,rawState)
var passabilityMap = rawState.passabilityMap;
this.width = passabilityMap.width;
this.height = passabilityMap.height;
this.cellSize = passabilityMap.cellSize;
// the first two won't change, the third is a reference to a value updated by C++
this.obstructionMaskLand = rawState.passabilityClasses["default"];
this.obstructionMaskWater = rawState.passabilityClasses["ship"];
this.obstructionMask = rawState.passabilityClasses["pathfinderObstruction"];
var obstructionMaskLand = rawState.passabilityClasses["default"];
var obstructionMaskWater = rawState.passabilityClasses["ship"];
var obstructionMask = rawState.passabilityClasses["pathfinderObstruction"];
var obstructionTiles = new Uint8Array(passabilityMap.data.length);
@ -48,11 +48,11 @@ m.TerrainAnalysis.prototype.init = function(sharedScript,rawState)
for (var i = 0; i < passabilityMap.data.length; ++i)
{
// If impassable for land units, set to 0, else to 255.
obstructionTiles[i] = (passabilityMap.data[i] & this.obstructionMaskLand) ? 0 : 255;
obstructionTiles[i] = (passabilityMap.data[i] & obstructionMaskLand) ? 0 : 255;
if (!(passabilityMap.data[i] & this.obstructionMaskWater) && obstructionTiles[i] === 0)
if (!(passabilityMap.data[i] & obstructionMaskWater) && obstructionTiles[i] === 0)
obstructionTiles[i] = 200; // if navigable and not walkable (ie basic water), set to 200.
else if (!(passabilityMap.data[i] & this.obstructionMaskWater) && obstructionTiles[i] === 255)
else if (!(passabilityMap.data[i] & obstructionMaskWater) && obstructionTiles[i] === 255)
obstructionTiles[i] = 201; // navigable and walkable.
}
@ -102,14 +102,7 @@ m.TerrainAnalysis.prototype.init = function(sharedScript,rawState)
}
}
// Okay now we have a pretty good knowledge of the map.
this.Map(rawState, obstructionTiles);
this.obstructionMaskLand = null;
this.obstructionMaskWater = null;
this.obstructionMask = null;
delete this.obstructionMaskLand;
delete this.obstructionMaskWater;
delete this.obstructionMask;
this.Map(rawState, "passability", obstructionTiles);
};
// Returns the (approximately) closest point which is passable by searching in a spiral pattern
@ -255,7 +248,7 @@ m.copyPrototype(m.Accessibility, m.TerrainAnalysis);
m.Accessibility.prototype.init = function(rawState, terrainAnalyser)
{
this.Map(rawState, terrainAnalyser.map);
this.Map(rawState, "passability", terrainAnalyser.map);
this.landPassMap = new Uint8Array(terrainAnalyser.length);
this.navalPassMap = new Uint8Array(terrainAnalyser.length);
@ -696,18 +689,17 @@ m.SharedScript.prototype.createResourceMaps = function(sharedScript) {
{
// We're creting them 8-bit. Things could go above 255 if there are really tons of resources
// But at that point the precision is not really important anyway. And it saves memory.
this.resourceMaps[resource] = new m.Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length));
this.resourceMaps[resource].setMaxVal(255);
this.CCResourceMaps[resource] = new m.Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length));
this.CCResourceMaps[resource].setMaxVal(255);
this.resourceMaps[resource] = new m.Map(sharedScript, "resource");
this.CCResourceMaps[resource] = new m.Map(sharedScript, "resource");
}
}
var cellSize = this.resourceMaps["food"].cellSize;
for (let ent of sharedScript._entities.values())
{
if (ent && ent.position() && ent.resourceSupplyType() && ent.resourceSupplyType().generic !== "treasure") {
var resource = ent.resourceSupplyType().generic;
var x = Math.floor(ent.position()[0] / 4);
var z = Math.floor(ent.position()[1] / 4);
var x = Math.floor(ent.position()[0] / cellSize);
var z = Math.floor(ent.position()[1] / cellSize);
var strength = Math.floor(ent.resourceSupplyMax()/this.decreaseFactor[resource]);
if (resource === "wood" || resource === "food")
{
@ -740,13 +732,11 @@ m.SharedScript.prototype.updateResourceMaps = function(sharedScript, events)
{
// We're creting them 8-bit. Things could go above 255 if there are really tons of resources
// But at that point the precision is not really important anyway. And it saves memory.
this.resourceMaps[resource] = new m.Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length));
this.resourceMaps[resource].setMaxVal(255);
this.CCResourceMaps[resource] = new m.Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length));
this.CCResourceMaps[resource].setMaxVal(255);
this.resourceMaps[resource] = new m.Map(sharedScript, "resource");
this.CCResourceMaps[resource] = new m.Map(sharedScript, "resource");
}
}
var cellSize = this.resourceMaps["food"].cellSize;
// Look for destroy events and subtract the entities original influence from the resourceMap
// TODO: perhaps do something when dropsites appear/disappear.
let destEvents = events["Destroy"];
@ -760,8 +750,8 @@ m.SharedScript.prototype.updateResourceMaps = function(sharedScript, events)
if (ent && ent.position() && ent.resourceSupplyType() && ent.resourceSupplyType().generic !== "treasure")
{
let resource = ent.resourceSupplyType().generic;
let x = Math.floor(ent.position()[0] / 4);
let z = Math.floor(ent.position()[1] / 4);
let x = Math.floor(ent.position()[0] / cellSize);
let z = Math.floor(ent.position()[1] / cellSize);
let strength = Math.floor(ent.resourceSupplyMax()/this.decreaseFactor[resource]);
if (resource === "wood" || resource === "food")
{
@ -786,8 +776,8 @@ m.SharedScript.prototype.updateResourceMaps = function(sharedScript, events)
if (ent && ent.position() && ent.resourceSupplyType() && ent.resourceSupplyType().generic !== "treasure")
{
let resource = ent.resourceSupplyType().generic;
let x = Math.floor(ent.position()[0] / 4);
let z = Math.floor(ent.position()[1] / 4);
let x = Math.floor(ent.position()[0] / cellSize);
let z = Math.floor(ent.position()[1] / cellSize);
let strength = Math.floor(ent.resourceSupplyMax()/this.decreaseFactor[resource]);
if (resource === "wood" || resource === "food")
{

View File

@ -301,7 +301,7 @@ m.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource)
var width = obstructions.width;
var bestIdx = undefined;
var bestVal = undefined;
var radius = Math.ceil(template.obstructionRadius() / gameState.cellSize);
var radius = Math.ceil(template.obstructionRadius() / obstructions.cellSize);
for (var p = 0; p < this.territoryIndices.length; ++p)
{

View File

@ -52,7 +52,7 @@ m.HQ.prototype.init = function(gameState, queues, deserializing)
{
this.territoryMap = m.createTerritoryMap(gameState);
// initialize base map. Each pixel is a base ID, or 0 if not or not accessible
this.basesMap = new API3.Map(gameState.sharedScript);
this.basesMap = new API3.Map(gameState.sharedScript, "territory");
// area of 10 cells on the border of the map : 0=inside map, 1=border map, 2=outside map
this.borderMap = m.createBorderMap(gameState);
// initialize frontier map. Each cell is 2 if on the near frontier, 1 on the frontier and 0 otherwise
@ -287,7 +287,7 @@ m.HQ.prototype.start = function(gameState, deserializing)
if (deserializing)
{
// Rebuild the base maps from the territory indices of each base
this.basesMap = new API3.Map(gameState.sharedScript);
this.basesMap = new API3.Map(gameState.sharedScript, "territory");
for each (let base in this.baseManagers)
for (let j of base.territoryIndices)
this.basesMap.map[j] = base.ID;

View File

@ -115,7 +115,7 @@ m.createObstructionMap = function(gameState, accessIndex, template)
}
}
var map = new API3.Map(gameState.sharedScript, obstructionTiles);
var map = new API3.Map(gameState.sharedScript, "passability", obstructionTiles);
map.setMaxVal(255);
if (template && template.buildDistance())
@ -143,8 +143,8 @@ m.createObstructionMap = function(gameState, accessIndex, template)
m.createTerritoryMap = function(gameState)
{
var map = gameState.ai.territoryMap;
var ret = new API3.Map(gameState.sharedScript, map.data);
var ret = new API3.Map(gameState.sharedScript, "territory", map.data);
ret.getOwner = function(p) { return this.point(p) & m.TERRITORY_PLAYER_MASK; };
ret.getOwnerIndex = function(p) { return this.map[p] & m.TERRITORY_PLAYER_MASK; };
return ret;
@ -153,9 +153,9 @@ m.createTerritoryMap = function(gameState)
// TODO check if not already done in obstruction maps
m.createBorderMap = function(gameState)
{
var map = new API3.Map(gameState.sharedScript);
var map = new API3.Map(gameState.sharedScript, "territory");
var width = map.width;
var border = 15;
var border = Math.round(60 / map.cellSize);
if (gameState.ai.circularMap)
{
var ic = (width - 1) / 2;
@ -198,10 +198,10 @@ m.createFrontierMap = function(gameState, borderMap)
var territoryMap = gameState.ai.HQ.territoryMap;
const around = [ [-0.7,0.7], [0,1], [0.7,0.7], [1,0], [0.7,-0.7], [0,-1], [-0.7,-0.7], [-1,0] ];
var map = new API3.Map(gameState.sharedScript);
var map = new API3.Map(gameState.sharedScript, "territory");
var width = map.width;
var insideSmall = 10;
var insideLarge = 15;
var insideSmall = Math.round(40 / map.cellSize);
var insideLarge = Math.round(60 / map.cellSize);
for (var j = 0; j < territoryMap.length; ++j)
{
@ -248,9 +248,8 @@ m.getFrontierProximity = function(gameState, j, borderMap)
var territoryMap = gameState.ai.HQ.territoryMap;
const around = [ [-0.7,0.7], [0,1], [0.7,0.7], [1,0], [0.7,-0.7], [0,-1], [-0.7,-0.7], [-1,0] ];
var map = new API3.Map(gameState.sharedScript);
var width = territoryMap.width;
const step = 3;
var step = Math.round(16 / territoryMap.cellSize);
if (gameState.isPlayerAlly(territoryMap.getOwnerIndex(j)))
return 0;

View File

@ -146,7 +146,7 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
// Compute each tile's closeness to friendly structures:
var friendlyTiles = new API3.Map(gameState.sharedScript);
var friendlyTiles = new API3.Map(gameState.sharedScript, "passability");
var alreadyHasHouses = false;