Reshuffling of AI management of territory maps

Reviewed By: Sandarac
Differential Revision: https://code.wildfiregames.com/D96
This was SVN commit r19174.
This commit is contained in:
mimo
2017-01-26 18:17:31 +00:00
parent 7b04c74043
commit ff743bc928
3 changed files with 90 additions and 82 deletions
@@ -60,10 +60,9 @@ m.HQ.prototype.init = function(gameState, queues)
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, "territory");
// area of n cells on the border of the map : 0=inside map, 1=border map, 2=border+inaccessible
// create borderMap: flag cells on the border of the map
// then this map will be completed with our frontier in updateTerritories
this.borderMap = m.createBorderMap(gameState);
// initialize frontier map. Each cell is 2 if on the near frontier, 1 on the frontier and 0 otherwise
this.frontierMap = m.createFrontierMap(gameState);
// list of allowed regions
this.landRegions = {};
// try to determine if we have a water map
@@ -836,7 +835,7 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
continue;
}
if (this.borderMap.map[j] > 0) // disfavor the borders of the map
if (this.borderMap.map[j] & m.fullBorder_Mask) // disfavor the borders of the map
norm *= 0.5;
let val = 2*gameState.sharedScript.ccResourceMaps[resource].map[j];
@@ -988,7 +987,7 @@ m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
currentVal += delta*delta;
}
// disfavor border of the map
if (this.borderMap.map[j] > 0)
if (this.borderMap.map[j] & m.fullBorder_Mask)
currentVal += 10000;
if (bestVal !== undefined && currentVal > bestVal)
@@ -1060,8 +1059,8 @@ m.HQ.prototype.findMarketLocation = function(gameState, template)
for (let j = 0; j < this.territoryMap.length; ++j)
{
// do not try on the border of our territory
if (this.frontierMap.map[j] === 2)
// do not try on the narrow border of our territory
if (this.borderMap.map[j] & m.narrowFrontier_Mask)
continue;
if (this.basesMap.map[j] === 0) // only in our territory
continue;
@@ -1186,9 +1185,9 @@ m.HQ.prototype.findDefensiveLocation = function(gameState, template)
if (!wonderMode)
{
// do not try if well inside or outside territory
if (this.frontierMap.map[j] === 0)
if (!(this.borderMap.map[j] & m.fullFrontier_Mask))
continue;
if (this.frontierMap.map[j] === 1 && isTower)
if (this.borderMap.map[j] & m.largeFrontier_Mask && isTower)
continue;
}
if (this.basesMap.map[j] === 0) // inaccessible cell
@@ -1883,16 +1882,25 @@ m.HQ.prototype.restartBuild = function(gameState, structure)
m.HQ.prototype.updateTerritories = function(gameState)
{
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] ];
let alliedVictory = gameState.getAlliedVictory();
let passabilityMap = gameState.getMap();
let width = this.territoryMap.width;
let cellSize = this.territoryMap.cellSize;
let insideSmall = Math.round(45 / cellSize);
let insideLarge = Math.round(80 / cellSize); // should be about the range of towers
let expansion = 0;
for (let j = 0; j < this.territoryMap.length; ++j)
{
if (this.borderMap.map[j] > 1)
if (this.borderMap.map[j] & m.outside_Mask)
continue;
if (this.borderMap.map[j] & m.fullFrontier_Mask)
this.borderMap.map[j] &= ~m.fullFrontier_Mask; // reset the frontier
if (this.territoryMap.getOwnerIndex(j) != PlayerID)
{
// If this tile was already accounted, remove it
if (this.basesMap.map[j] === 0)
continue;
let base = this.getBaseByID(this.basesMap.map[j]);
@@ -1905,8 +1913,46 @@ m.HQ.prototype.updateTerritories = function(gameState)
base.territoryIndices.splice(index, 1);
this.basesMap.map[j] = 0;
}
else if (this.basesMap.map[j] === 0)
else
{
// Update the frontier
let ix = j%width;
let iz = Math.floor(j/width);
let onFrontier = false;
for (let a of around)
{
let jx = ix + Math.round(insideSmall*a[0]);
if (jx < 0 || jx >= width)
continue;
let jz = iz + Math.round(insideSmall*a[1]);
if (jz < 0 || jz >= width)
continue;
if (this.borderMap.map[jx+width*jz] & m.outside_Mask)
continue;
let territoryOwner = this.territoryMap.getOwnerIndex(jx+width*jz);
if (territoryOwner !== PlayerID && !(alliedVictory && gameState.isPlayerAlly(territoryOwner)))
{
this.borderMap.map[j] |= m.narrowFrontier_Mask;
break;
}
jx = ix + Math.round(insideLarge*a[0]);
if (jx < 0 || jx >= width)
continue;
jz = iz + Math.round(insideLarge*a[1]);
if (jz < 0 || jz >= width)
continue;
if (this.borderMap.map[jx+width*jz] & m.outside_Mask)
continue;
territoryOwner = this.territoryMap.getOwnerIndex(jx+width*jz);
if (territoryOwner !== PlayerID && !(alliedVictory && gameState.isPlayerAlly(territoryOwner)))
onFrontier = true;
}
if (onFrontier && !(this.borderMap.map[j] & m.narrowFrontier_Mask))
this.borderMap.map[j] |= m.largeFrontier_Mask;
// If this tile was not already accounted, add it
if (this.basesMap.map[j] !== 0)
continue;
let landPassable = false;
let ind = API3.getMapIndices(j, this.territoryMap, passabilityMap);
let access;
@@ -1943,8 +1989,6 @@ m.HQ.prototype.updateTerritories = function(gameState)
}
}
this.frontierMap = m.createFrontierMap(gameState);
if (!expansion)
return;
// We've increased our territory, so we may have some new room to build
@@ -120,7 +120,23 @@ m.createTerritoryMap = function(gameState)
return ret;
};
/** flag cells around the border of the map (2 if all points into that cell are inaccessible, 1 otherwise) */
/**
* The borderMap contains some border and frontier information:
* - border of the map filled once:
* - all mini-cells (1x1) from the big cell (8x8) inaccessibles => bit 0
* - inside a given distance to the border => bit 1
* - frontier of our territory (updated regularly in updateFrontierMap)
* - narrow border (inside our territory) => bit 2
* - large border (inside our territory, exclusive of narrow) => bit 3
*/
m.outside_Mask = 1;
m.border_Mask = 2;
m.fullBorder_Mask = m.outside_Mask | m.border_Mask;
m.narrowFrontier_Mask = 4;
m.largeFrontier_Mask = 8;
m.fullFrontier_Mask = m.narrowFrontier_Mask | m.largeFrontier_Mask;
m.createBorderMap = function(gameState)
{
let map = new API3.Map(gameState.sharedScript, "territory");
@@ -139,13 +155,13 @@ m.createBorderMap = function(gameState)
let radius = dx*dx + dy*dy;
if (radius < radcut)
continue;
map.map[j] = 2;
map.map[j] = m.outside_Mask;
let ind = API3.getMapIndices(j, map, passabilityMap);
for (let k of ind)
{
if (passabilityMap.data[k] & obstructionMask)
continue;
map.map[j] = 1;
map.map[j] = m.border_Mask;
break;
}
}
@@ -159,13 +175,13 @@ m.createBorderMap = function(gameState)
let iy = Math.floor(j/width);
if (ix < border || ix >= borderCut || iy < border || iy >= borderCut)
{
map.map[j] = 2;
map.map[j] = m.outside_Mask;
let ind = API3.getMapIndices(j, map, passabilityMap);
for (let k of ind)
{
if (passabilityMap.data[k] & obstructionMask)
continue;
map.map[j] = 1;
map.map[j] = m.border_Mask;
break;
}
}
@@ -176,61 +192,6 @@ m.createBorderMap = function(gameState)
return map;
};
/** map of our frontier : 2 means narrow border, 1 means large border */
m.createFrontierMap = function(gameState)
{
let alliedVictory = gameState.getAlliedVictory();
let territoryMap = gameState.ai.HQ.territoryMap;
let borderMap = gameState.ai.HQ.borderMap;
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] ];
let map = new API3.Map(gameState.sharedScript, "territory");
let width = map.width;
let insideSmall = Math.round(45 / map.cellSize);
let insideLarge = Math.round(80 / map.cellSize); // should be about the range of towers
for (let j = 0; j < territoryMap.length; ++j)
{
if (territoryMap.getOwnerIndex(j) !== PlayerID || borderMap.map[j] > 1)
continue;
let ix = j%width;
let iz = Math.floor(j/width);
for (let a of around)
{
let jx = ix + Math.round(insideSmall*a[0]);
if (jx < 0 || jx >= width)
continue;
let jz = iz + Math.round(insideSmall*a[1]);
if (jz < 0 || jz >= width)
continue;
if (borderMap.map[jx+width*jz] > 1)
continue;
let territoryOwner = territoryMap.getOwnerIndex(jx+width*jz);
let safe = (alliedVictory && gameState.isPlayerAlly(territoryOwner)) || territoryOwner === PlayerID;
if (!safe)
{
map.map[j] = 2;
break;
}
jx = ix + Math.round(insideLarge*a[0]);
if (jx < 0 || jx >= width)
continue;
jz = iz + Math.round(insideLarge*a[1]);
if (jz < 0 || jz >= width)
continue;
if (borderMap.map[jx+width*jz] > 1)
continue;
territoryOwner = territoryMap.getOwnerIndex(jx+width*jz);
safe = (alliedVictory && gameState.isPlayerAlly(territoryOwner)) || territoryOwner === PlayerID;
if (!safe)
map.map[j] = 1;
}
}
// m.debugMap(gameState, map);
return map;
};
m.debugMap = function(gameState, map)
{
let width = map.width;
@@ -238,7 +238,7 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
{
let value = placement.map[j] - gameState.sharedScript.resourceMaps.wood.map[j]/3;
placement.map[j] = value >= 0 ? value : 0;
if (gameState.ai.HQ.borderMap.map[j] > 0)
if (gameState.ai.HQ.borderMap.map[j] & m.fullBorder_Mask)
placement.map[j] /= 2; // we need space around farmstead, so disfavor map border
}
}
@@ -256,9 +256,9 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
{
if (gameState.ai.HQ.basesMap.map[j] != base)
placement.map[j] = 0;
else if (favorBorder && gameState.ai.HQ.borderMap.map[j] > 0)
else if (favorBorder && gameState.ai.HQ.borderMap.map[j] & m.border_Mask)
placement.map[j] += 50;
else if (disfavorBorder && gameState.ai.HQ.borderMap.map[j] === 0 && placement.map[j] > 0)
else if (disfavorBorder && !(gameState.ai.HQ.borderMap.map[j] & m.fullBorder_Mask) && placement.map[j] > 0)
placement.map[j] += 10;
if (placement.map[j] > 0)
@@ -276,9 +276,9 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
{
if (gameState.ai.HQ.basesMap.map[j] === 0)
placement.map[j] = 0;
else if (favorBorder && gameState.ai.HQ.borderMap.map[j] > 0)
else if (favorBorder && gameState.ai.HQ.borderMap.map[j] & m.border_Mask)
placement.map[j] += 50;
else if (disfavorBorder && gameState.ai.HQ.borderMap.map[j] === 0 && placement.map[j] > 0)
else if (disfavorBorder && !(gameState.ai.HQ.borderMap.map[j] & m.fullBorder_Mask) && placement.map[j] > 0)
placement.map[j] += 10;
if (preferredBase && gameState.ai.HQ.basesMap.map[j] == this.metadata.preferredBase)
@@ -435,7 +435,7 @@ m.ConstructionPlan.prototype.findDockPosition = function(gameState)
dist += 0.6 * (maxRes - res);
// Add a penalty if on the map border as ship movement will be difficult
if (gameState.ai.HQ.borderMap.map[j] > 0)
if (gameState.ai.HQ.borderMap.map[j] & m.fullBorder_Mask)
dist += 2;
// do a pre-selection, supposing we will have the best possible water
if (bestIdx !== undefined && dist > bestVal + maxWater)
@@ -669,8 +669,10 @@ m.ConstructionPlan.prototype.isDockLocation = function(gameState, j, dimension,
*/
m.ConstructionPlan.prototype.getFrontierProximity = function(gameState, j)
{
let alliedVictory = gameState.getAlliedVictory();
let territoryMap = gameState.ai.HQ.territoryMap;
if (gameState.isPlayerAlly(territoryMap.getOwnerIndex(j)))
let territoryOwner = territoryMap.getOwnerIndex(j);
if (territoryOwner === PlayerID || alliedVictory && gameState.isPlayerAlly(territoryOwner))
return 0;
let borderMap = gameState.ai.HQ.borderMap;
@@ -689,9 +691,10 @@ m.ConstructionPlan.prototype.getFrontierProximity = function(gameState, j)
let jz = iz + Math.round(i*step*a[1]);
if (jz < 0 || jz >= width)
continue;
if (borderMap.map[jx+width*jz] > 1)
if (borderMap.map[jx+width*jz] & m.outside_Mask)
continue;
if (gameState.isPlayerAlly(territoryMap.getOwnerIndex(jx+width*jz)))
territoryOwner = territoryMap.getOwnerIndex(jx+width*jz);
if (alliedVictory && gameState.isPlayerAlly(territoryOwner) || territoryOwner === PlayerID)
{
best = Math.min(best, i);
break;