Adjust city gold production and fix defense post bug

This commit is contained in:
1brucben
2025-04-24 13:09:57 +02:00
parent a26ffb3cb3
commit 58b8a94b2e
2 changed files with 41 additions and 35 deletions
+1 -1
View File
@@ -642,7 +642,7 @@ export class DefaultConfig implements Config {
(adjustedCityPopulation * totalWorkers) / totalPopulation; (adjustedCityPopulation * totalWorkers) / totalPopulation;
const ruralWorkers = totalWorkers - cityWorkers; const ruralWorkers = totalWorkers - cityWorkers;
const cityGold = cityWorkers / 1000; const cityGold = cityWorkers / 2500;
const tileGold = (Math.sqrt(ruralWorkers) * Math.sqrt(numTiles)) / 300; const tileGold = (Math.sqrt(ruralWorkers) * Math.sqrt(numTiles)) / 300;
const totalGold = cityGold + tileGold; const totalGold = cityGold + tileGold;
+40 -34
View File
@@ -44,10 +44,10 @@ export class FakeHumanExecution implements Execution {
private lastNukeSent: [Tick, TileRef][] = []; private lastNukeSent: [Tick, TileRef][] = [];
private embargoMalusApplied = new Set<PlayerID>(); private embargoMalusApplied = new Set<PlayerID>();
private portTargetRatio: number = 0.0003; // desired ports per tile private portTargetRatio: number = 0.0001; // desired ports per tile
private cityTargetRatio: number = 0.0006; // desired cities per tile private cityTargetRatio: number = 0.0002; // desired cities per tile
private defensePostSpacing: number = 50; // minimum distance between defense posts private defensePostSpacing: number = 60; // minimum distance between defense posts
private defensePostTargetRatio: number = 0.0002; // desired defense posts per tile private defensePostTargetRatio: number = 0.005; // desired defense posts per border length
private lastDefensePostTick: number = -9999; private lastDefensePostTick: number = -9999;
constructor( constructor(
@@ -416,12 +416,12 @@ export class FakeHumanExecution implements Execution {
const portDeficit = Math.max( const portDeficit = Math.max(
(this.portTargetRatio - portRatio) / this.portTargetRatio, (this.portTargetRatio - portRatio) / this.portTargetRatio,
portsCount < 1 ? 1 : 0, portsCount < 1 ? 1 : -1,
); );
const cityDeficit = Math.max( const cityDeficit = Math.max(
(this.cityTargetRatio - cityRatio) / this.cityTargetRatio, (this.cityTargetRatio - cityRatio) / this.cityTargetRatio,
citiesCount < 3 ? 1 : 0, citiesCount < 2 ? 1 : -1,
); );
const canAffordPort = this.player.gold() >= this.cost(UnitType.Port); const canAffordPort = this.player.gold() >= this.cost(UnitType.Port);
@@ -441,6 +441,7 @@ export class FakeHumanExecution implements Execution {
this.mg.addExecution( this.mg.addExecution(
new ConstructionExecution(this.player.id(), tile, UnitType.City), new ConstructionExecution(this.player.id(), tile, UnitType.City),
); );
return; return;
} }
} }
@@ -450,48 +451,55 @@ export class FakeHumanExecution implements Execution {
this.mg.addExecution( this.mg.addExecution(
new ConstructionExecution(this.player.id(), tile, UnitType.Port), new ConstructionExecution(this.player.id(), tile, UnitType.Port),
); );
return; return;
} }
// fallback: if port was preferred but unbuildable, try city // fallback: if port was preferred but unbuildable, try city
if (portDeficit > cityDeficit && !canBuildPort && canBuildCity) { if (
portDeficit > cityDeficit &&
!canBuildPort &&
canBuildCity &&
cityDeficit > 0
) {
const tile = this.randTerritoryTile(this.player); const tile = this.randTerritoryTile(this.player);
if (tile) { if (tile) {
this.mg.addExecution( this.mg.addExecution(
new ConstructionExecution(this.player.id(), tile, UnitType.City), new ConstructionExecution(this.player.id(), tile, UnitType.City),
); );
return; return;
} }
} }
} }
if (currentTick - this.lastDefensePostTick >= 10) { if (currentTick - this.lastDefensePostTick >= 100) {
this.lastDefensePostTick = currentTick; this.lastDefensePostTick = currentTick;
const defensePostsCount = this.player.units(UnitType.DefensePost).length; const defensePostsCount = this.player.units(UnitType.DefensePost).length;
const defensePostRatio = defensePostsCount / tilesCount; const borderTiles = new Set(this.player.borderTiles());
const defensePostRatio = defensePostsCount / borderTiles.size;
const defensePostDeficit = this.defensePostTargetRatio - defensePostRatio; const defensePostDeficit = this.defensePostTargetRatio - defensePostRatio;
const canAffordDefensePost = const canAffordDefensePost =
this.player.gold() >= this.cost(UnitType.DefensePost); this.player.gold() >= this.cost(UnitType.DefensePost);
// consolex.log(`[${this.playerInfo.name}] can afford: ${canAffordDefensePost}, deficit: ${defensePostDeficit}`);
if (defensePostDeficit > 0 && canAffordDefensePost) { if (defensePostDeficit > 0 && canAffordDefensePost) {
consolex.log("creating defense post");
const borderTiles = new Set(this.player.borderTiles()); const borderTiles = new Set(this.player.borderTiles());
const existingPosts = this.player const existingPosts = this.player
.units(UnitType.DefensePost) .units(UnitType.DefensePost)
.map((u) => u.tile()); .map((u) => u.tile());
const candidateTiles: TileRef[] = [];
const radius = 10; const radius = 10;
const borderTileArray = Array.from(borderTiles); const borderTileArray = Array.from(borderTiles);
this.random.shuffleArray(borderTileArray); this.random.shuffleArray(borderTileArray);
const sampledBorderTiles = borderTileArray.slice(0, 5); // <-- scan 3 border tiles only const sampledBorderTiles = borderTileArray.slice(0, 5); // <-- scan 5 border tiles only
let builtDefensePost = false;
for (const borderTile of sampledBorderTiles) { for (const borderTile of sampledBorderTiles) {
const x0 = this.mg.x(borderTile); const x0 = this.mg.x(borderTile);
const y0 = this.mg.y(borderTile); const y0 = this.mg.y(borderTile);
tileScan: for (let dx = -radius; dx <= radius; dx++) { for (let dx = -radius; dx <= radius; dx++) {
for (let dy = -radius; dy <= radius; dy++) { for (let dy = -radius; dy <= radius; dy++) {
const x = x0 + dx; const x = x0 + dx;
const y = y0 + dy; const y = y0 + dy;
@@ -505,14 +513,12 @@ export class FakeHumanExecution implements Execution {
let tooCloseToBorder = false; let tooCloseToBorder = false;
for (const borderTile of borderTiles) { for (const borderTile of borderTiles) {
const distSq = this.mg.euclideanDistSquared(tile, borderTile); const distSq = this.mg.euclideanDistSquared(tile, borderTile);
if (distSq < 25) { if (distSq < 36) {
// must be at least 5 tiles away
tooCloseToBorder = true; tooCloseToBorder = true;
break; break;
} }
} }
if (tooCloseToBorder) continue; if (tooCloseToBorder) continue;
let nearOtherPost = false; let nearOtherPost = false;
for (const postTile of existingPosts) { for (const postTile of existingPosts) {
const distSq = this.mg.euclideanDistSquared(tile, postTile); const distSq = this.mg.euclideanDistSquared(tile, postTile);
@@ -527,26 +533,26 @@ export class FakeHumanExecution implements Execution {
if (nearOtherPost) continue; if (nearOtherPost) continue;
if (this.player.canBuild(UnitType.DefensePost, tile)) { if (this.player.canBuild(UnitType.DefensePost, tile)) {
candidateTiles.push(tile); this.mg.addExecution(
if (candidateTiles.length >= 5) break tileScan; new ConstructionExecution(
this.player.id(),
tile,
UnitType.DefensePost,
),
);
builtDefensePost = true;
break;
} }
} }
if (builtDefensePost) break;
} }
if (candidateTiles.length > 0) { if (builtDefensePost) break;
const tile = this.random.randElement(candidateTiles); }
this.mg.addExecution(
new ConstructionExecution( if (!builtDefensePost) {
this.player.id(), consolex.log(
tile, `[${this.playerInfo.name}] no valid tile found for Defense Post`,
UnitType.DefensePost, );
),
);
return;
} else {
consolex.log(
`[${this.playerInfo.name}] no valid tile found for Defense Post`,
);
}
} }
} }
} }