From ff743bc9287bf6478bcb526934fc1f3b54159c7f Mon Sep 17 00:00:00 2001 From: mimo Date: Thu, 26 Jan 2017 18:17:31 +0000 Subject: [PATCH] Reshuffling of AI management of territory maps Reviewed By: Sandarac Differential Revision: https://code.wildfiregames.com/D96 This was SVN commit r19174. --- .../simulation/ai/petra/headquarters.js | 70 +++++++++++++--- .../public/simulation/ai/petra/mapModule.js | 81 +++++-------------- .../simulation/ai/petra/queueplanBuilding.js | 21 ++--- 3 files changed, 90 insertions(+), 82 deletions(-) diff --git a/binaries/data/mods/public/simulation/ai/petra/headquarters.js b/binaries/data/mods/public/simulation/ai/petra/headquarters.js index add83336ef..112e732eb7 100644 --- a/binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ b/binaries/data/mods/public/simulation/ai/petra/headquarters.js @@ -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 diff --git a/binaries/data/mods/public/simulation/ai/petra/mapModule.js b/binaries/data/mods/public/simulation/ai/petra/mapModule.js index 7b8f1797a4..090cc24df3 100644 --- a/binaries/data/mods/public/simulation/ai/petra/mapModule.js +++ b/binaries/data/mods/public/simulation/ai/petra/mapModule.js @@ -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; diff --git a/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js b/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js index c8a6dd045f..079c4838f6 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js +++ b/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js @@ -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;