Improved the nation alliance request logic 🤝 Massive upgrade to singleplayer fun (#2606)

## Response to alliance requests

Previously the way nations responded to alliance requests was quite
simple / boring / exploitable. Basically you couldn't ally them if you
had a bad relation with them, or if you had too many alliances.
Otherwise they would just take it.

Now there is a **complete decision tree which is based on the
difficulty**. The nations should also feel more human now.

For example, just like humans, nations will now consider to take an
alliance even if you have a bad relation with them (If you are a
threat).

Also, nations no longer check if YOU have too many alliances. Now they
do what humans do: Check if THEY have too many alliances (they want to
be able to attack somebody).

Another big change is the default case: Previously it was just `return
true`. Now it's `return isAlliancePartnerSimilarlyStrong`. So they do
what humans do: Take a quick look at their troop count before allying
them.

## Sending alliance requests

Previously alliance requests were sent randomly. Quite boring.

Now we use the same decision tree as for responding.

## Alliance extension requests

They also use the same decision tree.

## Tests

Tested it a lot in singleplayer.
I have planned to add unit tests for all the nation/bot stuff in the
upcoming cleanup phase.

## 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

## Please put your Discord username so you can be contacted if a bug or
regression is found:

FloPinguin
This commit is contained in:
FloPinguin
2025-12-15 00:18:07 +01:00
committed by GitHub
parent 66ae8cd9bb
commit dfe33a05e9
6 changed files with 448 additions and 296 deletions
+21 -2
View File
@@ -1,6 +1,7 @@
import { Execution, Game, Player } from "../game/Game";
import { PseudoRandom } from "../PseudoRandom";
import { simpleHash } from "../Util";
import { AllianceExtensionExecution } from "./alliance/AllianceExtensionExecution";
import { BotBehavior } from "./utils/BotBehavior";
export class BotExecution implements Execution {
@@ -56,11 +57,29 @@ export class BotExecution implements Execution {
return;
}
this.behavior.handleAllianceRequests();
this.behavior.handleAllianceExtensionRequests();
this.acceptAllAllianceRequests();
this.maybeAttack();
}
private acceptAllAllianceRequests() {
// Accept all alliance requests
for (const req of this.bot.incomingAllianceRequests()) {
req.accept();
}
// Accept all alliance extension requests
for (const alliance of this.bot.alliances()) {
// Alliance expiration tracked by Events Panel, only human ally can click Request to Renew
// Skip if no expiration yet/ ally didn't request extension yet / bot already agreed to extend
if (!alliance.onlyOneAgreedToExtend()) continue;
const human = alliance.other(this.bot);
this.mg.addExecution(
new AllianceExtensionExecution(this.bot, human.id()),
);
}
}
private maybeAttack() {
if (this.behavior === null) {
throw new Error("not initialized");