ai updates. more balance

This commit is contained in:
1brucben
2025-04-26 18:41:04 +02:00
parent be1d12ec54
commit 5bf012385b
4 changed files with 60 additions and 13 deletions
+1 -1
View File
@@ -221,7 +221,7 @@ export class WinModal extends LitElement implements Layer {
this._title = "You Won!";
this.won = true;
} else {
this._title = `${wu.winner} has won!`;
this._title = `${winner.name()} has won!`;
this.won = false;
}
this.show();
+2 -2
View File
@@ -577,9 +577,9 @@ export class DefaultConfig implements Config {
case Difficulty.Easy:
return maxPop * 0.4;
case Difficulty.Medium:
return maxPop * 0.7;
return maxPop * 0.6;
case Difficulty.Hard:
return maxPop * 1.2;
return maxPop * 1;
case Difficulty.Impossible:
return maxPop * 2;
}
+48 -4
View File
@@ -17,7 +17,7 @@ import {
import { euclDistFN, manhattanDistFN, TileRef } from "../game/GameMap";
import { PseudoRandom } from "../PseudoRandom";
import { GameID } from "../Schemas";
import { calculateBoundingBox, simpleHash } from "../Util";
import { calculateBoundingBox, simpleHash, within } from "../Util";
import { ConstructionExecution } from "./ConstructionExecution";
import { EmojiExecution } from "./EmojiExecution";
import { NukeExecution } from "./NukeExecution";
@@ -53,6 +53,7 @@ export class FakeHumanExecution implements Execution {
private dogpileTarget: Player | null = null;
private dogpileLastChecked: number = -1;
private attackedThisTick: boolean = false;
constructor(
gameID: GameID,
@@ -116,6 +117,7 @@ export class FakeHumanExecution implements Execution {
tick(ticks: number) {
if (ticks % this.attackRate != this.attackTick) return;
this.attackedThisTick = false; // new tick, reset
this.updateDogpile();
@@ -170,6 +172,9 @@ export class FakeHumanExecution implements Execution {
this.handleEnemies();
this.handleUnits();
this.handleEmbargoesToHostileNations();
if (this.attackedThisTick) {
return; // ⛔ Stop if already attacked this tick
}
this.maybeAttack();
}
@@ -278,6 +283,9 @@ export class FakeHumanExecution implements Execution {
this.maybeSendNuke(enemy);
if (this.player.sharesBorderWith(enemy)) {
this.behavior.sendAttack(enemy);
if (this.behavior.sendAttack(enemy)) {
this.attackedThisTick = true;
}
} else {
this.maybeSendBoatAttack(enemy);
}
@@ -405,6 +413,7 @@ export class FakeHumanExecution implements Execution {
private maybeSendBoatAttack(other: Player) {
if (this.player.isOnSameTeam(other)) return;
const closest = closestTwoTiles(
this.mg,
Array.from(this.player.borderTiles()).filter((t) =>
@@ -415,12 +424,29 @@ export class FakeHumanExecution implements Execution {
if (closest == null) {
return;
}
const maxPop = this.mg.config().maxPopulation(this.player);
const maxTroops = maxPop * this.player.targetTroopRatio();
const targetTroops = maxTroops * this.reserveRatio;
const surplusTroops = this.player.troops() - targetTroops;
if (surplusTroops <= 0) return; // ❗ Not enough spare troops to send
const troopsToSend = within(
surplusTroops,
0.1 * this.player.troops(),
0.2 * this.player.troops(),
);
if (troopsToSend < 1) return; // ❗ Don't send if too little
this.mg.addExecution(
new TransportShipExecution(
this.player.id(),
other.id(),
closest.y,
this.player.troops() / 5,
troopsToSend,
null,
),
);
@@ -735,16 +761,30 @@ export class FakeHumanExecution implements Execution {
return;
}
const maxPop = this.mg.config().maxPopulation(this.player);
const maxTroops = maxPop * this.player.targetTroopRatio();
const targetTroops = maxTroops * this.reserveRatio;
const surplusTroops = this.player.troops() - targetTroops;
if (surplusTroops <= 0) return; // ❗ Not enough troops to send a boat
const troopsToSend = within(
surplusTroops,
0.1 * this.player.troops(), // a little smaller range for random boats
0.2 * this.player.troops(),
);
if (troopsToSend < 1) return; // ❗ Avoid sending tiny attacks
this.mg.addExecution(
new TransportShipExecution(
this.player.id(),
this.mg.owner(dst).id(),
dst,
this.player.troops() / 5,
troopsToSend,
null,
),
);
return;
}
randomLand(): TileRef | null {
@@ -809,6 +849,10 @@ export class FakeHumanExecution implements Execution {
}
private updateDogpile() {
if (this.mg.ticks() < 1200) {
this.dogpileTarget = null;
return;
}
const CHECK_INTERVAL = 50; // only check every 50 ticks
if (this.mg.ticks() - this.dogpileLastChecked < CHECK_INTERVAL) return;
+9 -6
View File
@@ -164,25 +164,26 @@ export class BotBehavior {
return this.enemy;
}
sendAttack(target: Player | TerraNullius, force: boolean = false) {
if (target.isPlayer() && this.player.isOnSameTeam(target)) return;
sendAttack(target: Player | TerraNullius, force: boolean = false): boolean {
if (target.isPlayer() && this.player.isOnSameTeam(target)) return false;
const maxPop = this.game.config().maxPopulation(this.player);
const maxTroops = maxPop * this.player.targetTroopRatio();
const targetTroops = maxTroops * this.reserveRatio;
if (!force && this.player.troops() < targetTroops) {
return false;
}
let troops: number;
if (force) {
// send exactly 40% of current troops
troops = this.player.troops() * 0.4;
if (troops < 1) return;
troops = this.player.troops() * 0.2;
} else {
troops = within(
this.player.troops() - targetTroops,
0.2 * this.player.troops(),
0.4 * this.player.troops(),
);
if (troops < 1) return;
}
this.game.addExecution(
@@ -192,6 +193,8 @@ export class BotBehavior {
target.isPlayer() ? target.id() : null,
),
);
return true; // ✅ Attack was actually sent
}
}