mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 11:20:45 +00:00
Merge branch 'main' into mac
This commit is contained in:
@@ -58,11 +58,8 @@ export class BotExecution implements Execution {
|
||||
if (this.behavior === null) {
|
||||
throw new Error("not initialized");
|
||||
}
|
||||
const traitors = this.bot
|
||||
.neighbors()
|
||||
.filter((n) => n.isPlayer() && n.isTraitor()) as Player[];
|
||||
if (traitors.length > 0) {
|
||||
const toAttack = this.random.randElement(traitors);
|
||||
const toAttack = this.behavior.getNeighborTraitorToAttack();
|
||||
if (toAttack !== null) {
|
||||
const odds = this.bot.isFriendly(toAttack) ? 6 : 3;
|
||||
if (this.random.chance(odds)) {
|
||||
this.behavior.sendAttack(toAttack);
|
||||
|
||||
@@ -43,22 +43,48 @@ export class BotBehavior {
|
||||
this.game.addExecution(new EmojiExecution(this.player, player.id(), emoji));
|
||||
}
|
||||
|
||||
private setNewEnemy(newEnemy: Player | null) {
|
||||
this.enemy = newEnemy;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
}
|
||||
|
||||
private clearEnemy() {
|
||||
this.enemy = null;
|
||||
}
|
||||
|
||||
forgetOldEnemies() {
|
||||
// Forget old enemies
|
||||
if (this.game.ticks() - this.enemyUpdated > 100) {
|
||||
this.enemy = null;
|
||||
this.clearEnemy();
|
||||
}
|
||||
}
|
||||
|
||||
private hasSufficientTroops(): boolean {
|
||||
const maxPop = this.game.config().maxPopulation(this.player);
|
||||
const ratio = this.player.population() / maxPop;
|
||||
return ratio >= this.triggerRatio;
|
||||
}
|
||||
|
||||
private checkIncomingAttacks() {
|
||||
// Switch enemies if we're under attack
|
||||
const incomingAttacks = this.player.incomingAttacks();
|
||||
if (incomingAttacks.length > 0) {
|
||||
this.enemy = incomingAttacks
|
||||
.sort((a, b) => b.troops() - a.troops())[0]
|
||||
.attacker();
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
let largestAttack = 0;
|
||||
let largestAttacker: Player | undefined;
|
||||
for (const attack of incomingAttacks) {
|
||||
if (attack.troops() <= largestAttack) continue;
|
||||
largestAttack = attack.troops();
|
||||
largestAttacker = attack.attacker();
|
||||
}
|
||||
if (largestAttacker !== undefined) {
|
||||
this.setNewEnemy(largestAttacker);
|
||||
}
|
||||
}
|
||||
|
||||
getNeighborTraitorToAttack(): Player | null {
|
||||
const traitors = this.player
|
||||
.neighbors()
|
||||
.filter((n): n is Player => n.isPlayer() && n.isTraitor());
|
||||
return traitors.length > 0 ? this.random.randElement(traitors) : null;
|
||||
}
|
||||
|
||||
assistAllies() {
|
||||
@@ -79,8 +105,7 @@ export class BotBehavior {
|
||||
}
|
||||
// All checks passed, assist them
|
||||
this.player.updateRelation(ally, -20);
|
||||
this.enemy = target;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
this.setNewEnemy(target);
|
||||
this.emoji(ally, this.assistAcceptEmoji);
|
||||
break outer;
|
||||
}
|
||||
@@ -90,50 +115,55 @@ export class BotBehavior {
|
||||
selectEnemy(): Player | null {
|
||||
if (this.enemy === null) {
|
||||
// Save up troops until we reach the trigger ratio
|
||||
const maxPop = this.game.config().maxPopulation(this.player);
|
||||
const ratio = this.player.population() / maxPop;
|
||||
if (ratio < this.triggerRatio) return null;
|
||||
}
|
||||
if (!this.hasSufficientTroops()) return null;
|
||||
|
||||
// Prefer neighboring bots
|
||||
if (this.enemy === null) {
|
||||
// Prefer neighboring bots
|
||||
const bots = this.player
|
||||
.neighbors()
|
||||
.filter((n) => n.isPlayer() && n.type() === PlayerType.Bot) as Player[];
|
||||
if (bots.length > 0) {
|
||||
const density = (p: Player) => p.troops() / p.numTilesOwned();
|
||||
this.enemy = bots.sort((a, b) => density(a) - density(b))[0];
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
let lowestDensityBot: Player | undefined;
|
||||
let lowestDensity = Infinity;
|
||||
|
||||
for (const bot of bots) {
|
||||
const currentDensity = density(bot);
|
||||
if (currentDensity < lowestDensity) {
|
||||
lowestDensity = currentDensity;
|
||||
lowestDensityBot = bot;
|
||||
}
|
||||
}
|
||||
|
||||
if (lowestDensityBot !== undefined) {
|
||||
this.setNewEnemy(lowestDensityBot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retaliate against incoming attacks
|
||||
if (this.enemy === null) {
|
||||
this.checkIncomingAttacks();
|
||||
}
|
||||
// Retaliate against incoming attacks
|
||||
if (this.enemy === null) {
|
||||
this.checkIncomingAttacks();
|
||||
}
|
||||
|
||||
// Select the most hated player
|
||||
if (this.enemy === null) {
|
||||
const mostHated = this.player.allRelationsSorted()[0];
|
||||
if (mostHated !== undefined && mostHated.relation === Relation.Hostile) {
|
||||
this.enemy = mostHated.player;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
// Select the most hated player
|
||||
if (this.enemy === null) {
|
||||
const mostHated = this.player.allRelationsSorted()[0];
|
||||
if (
|
||||
mostHated !== undefined &&
|
||||
mostHated.relation === Relation.Hostile
|
||||
) {
|
||||
this.setNewEnemy(mostHated.player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check, don't attack our allies or teammates
|
||||
if (this.enemy && this.player.isFriendly(this.enemy)) {
|
||||
this.enemy = null;
|
||||
}
|
||||
return this.enemy;
|
||||
return this.enemySanityCheck();
|
||||
}
|
||||
|
||||
selectRandomEnemy(): Player | TerraNullius | null {
|
||||
if (this.enemy === null) {
|
||||
// Save up troops until we reach the trigger ratio
|
||||
const maxPop = this.game.config().maxPopulation(this.player);
|
||||
const ratio = this.player.population() / maxPop;
|
||||
if (ratio < this.triggerRatio) return null;
|
||||
if (!this.hasSufficientTroops()) return null;
|
||||
|
||||
// Choose a new enemy randomly
|
||||
const neighbors = this.player.neighbors();
|
||||
@@ -145,34 +175,32 @@ export class BotBehavior {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this.enemy = neighbor;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
this.setNewEnemy(neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
// Retaliate against incoming attacks
|
||||
if (this.enemy === null) {
|
||||
this.checkIncomingAttacks();
|
||||
}
|
||||
// Retaliate against incoming attacks
|
||||
if (this.enemy === null) {
|
||||
this.checkIncomingAttacks();
|
||||
}
|
||||
|
||||
// Select a traitor as an enemy
|
||||
if (this.enemy === null) {
|
||||
const traitors = this.player
|
||||
.neighbors()
|
||||
.filter((n) => n.isPlayer() && n.isTraitor()) as Player[];
|
||||
if (traitors.length > 0) {
|
||||
const toAttack = this.random.randElement(traitors);
|
||||
const odds = this.player.isFriendly(toAttack) ? 6 : 3;
|
||||
if (this.random.chance(odds)) {
|
||||
this.enemy = toAttack;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
// Select a traitor as an enemy
|
||||
if (this.enemy === null) {
|
||||
const toAttack = this.getNeighborTraitorToAttack();
|
||||
if (toAttack !== null) {
|
||||
if (!this.player.isFriendly(toAttack) && this.random.chance(3)) {
|
||||
this.setNewEnemy(toAttack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check, don't attack our allies or teammates
|
||||
return this.enemySanityCheck();
|
||||
}
|
||||
|
||||
private enemySanityCheck(): Player | null {
|
||||
if (this.enemy && this.player.isFriendly(this.enemy)) {
|
||||
this.enemy = null;
|
||||
this.clearEnemy();
|
||||
}
|
||||
return this.enemy;
|
||||
}
|
||||
@@ -200,12 +228,17 @@ export class BotBehavior {
|
||||
}
|
||||
|
||||
function shouldAcceptAllianceRequest(player: Player, request: AllianceRequest) {
|
||||
const isTraitor = request.requestor().isTraitor();
|
||||
const hasMalice = player.relation(request.requestor()) < Relation.Neutral;
|
||||
const requestorIsMuchLarger =
|
||||
request.requestor().numTilesOwned() > player.numTilesOwned() * 3;
|
||||
const tooManyAlliances = request.requestor().alliances().length >= 3;
|
||||
return (
|
||||
!isTraitor && !hasMalice && (requestorIsMuchLarger || !tooManyAlliances)
|
||||
);
|
||||
if (player.relation(request.requestor()) < Relation.Neutral) {
|
||||
return false; // Reject if hasMalice
|
||||
}
|
||||
if (request.requestor().isTraitor()) {
|
||||
return false; // Reject if isTraitor
|
||||
}
|
||||
if (request.requestor().numTilesOwned() > player.numTilesOwned() * 3) {
|
||||
return true; // Accept if requestorIsMuchLarger
|
||||
}
|
||||
if (request.requestor().alliances().length >= 3) {
|
||||
return false; // Reject if tooManyAlliances
|
||||
}
|
||||
return true; // Accept otherwise
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user