Nations build defense posts (#1935)

## Description:

Nations build defense posts. Fixes #1854.

<img width="259" height="383" alt="image"
src="https://github.com/user-attachments/assets/aee75fd3-f52e-47d8-b47f-b192b8aaa69b"
/>

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
This commit is contained in:
Scott Anderson
2025-08-25 19:26:02 -04:00
committed by evanpelle
parent 47edfe3e40
commit f0f9318852
2 changed files with 45 additions and 4 deletions
+1
View File
@@ -461,6 +461,7 @@ export class FakeHumanExecution implements Execution {
this.maybeSpawnStructure(UnitType.Port) ||
this.maybeSpawnWarship() ||
this.maybeSpawnStructure(UnitType.Factory) ||
this.maybeSpawnStructure(UnitType.DefensePost) ||
this.maybeSpawnStructure(UnitType.SAMLauncher) ||
this.maybeSpawnStructure(UnitType.MissileSilo)
);
@@ -1,4 +1,4 @@
import { Game, Player, UnitType } from "../../game/Game";
import { Game, Player, Relation, UnitType } from "../../game/Game";
import { TileRef } from "../../game/GameMap";
import { closestTwoTiles } from "../Util";
@@ -46,7 +46,47 @@ export function structureSpawnTileValue(
return (tile) => {
let w = 0;
// Prefer to be far away from other structures of the same type
// Prefer to be away from other structures of the same type
const otherTiles: Set<TileRef> = new Set(otherUnits.map((u) => u.tile()));
otherTiles.delete(tile);
const closestOther = closestTwoTiles(mg, otherTiles, [tile]);
if (closestOther !== null) {
const d = mg.manhattanDist(closestOther.x, tile);
w += Math.min(d, structureSpacing);
}
return w;
};
}
case UnitType.DefensePost: {
return (tile) => {
let w = 0;
// Prefer higher elevations
w += mg.magnitude(tile);
const closestBorder = closestTwoTiles(mg, borderTiles, [tile]);
if (closestBorder !== null) {
// Prefer to be borderSpacing tiles from the border
const d = mg.manhattanDist(closestBorder.x, tile);
w += Math.max(0, borderSpacing - Math.abs(borderSpacing - d));
// Prefer adjacent players who are hostile
const neighbors: Set<Player> = new Set();
for (const tile of mg.neighbors(closestBorder.x)) {
if (!mg.isLand(tile)) continue;
const id = mg.ownerID(tile);
if (id === player.smallID()) continue;
const neighbor = mg.playerBySmallID(id);
if (!neighbor.isPlayer()) continue;
neighbors.add(neighbor);
}
for (const neighbor of neighbors) {
w += borderSpacing * (Relation.Friendly - player.relation(neighbor));
}
}
// Prefer to be away from other structures of the same type
const otherTiles: Set<TileRef> = new Set(otherUnits.map((u) => u.tile()));
otherTiles.delete(tile);
const closestOther = closestTwoTiles(mg, otherTiles, [tile]);
@@ -61,7 +101,7 @@ export function structureSpawnTileValue(
case UnitType.SAMLauncher: {
const protectTiles: Set<TileRef> = new Set();
for (const unit of player.units()) {
switch(unit.type()) {
switch (unit.type()) {
case UnitType.City:
case UnitType.Factory:
case UnitType.MissileSilo:
@@ -84,7 +124,7 @@ export function structureSpawnTileValue(
w += Math.min(d, borderSpacing);
}
// Prefer to be far away from other structures of the same type
// Prefer to be away from other structures of the same type
const otherTiles: Set<TileRef> = new Set(otherUnits.map((u) => u.tile()));
otherTiles.delete(tile);
const closestOther = closestTwoTiles(mg, otherTiles, [tile]);