Commit Graph

60 Commits

Author SHA1 Message Date
FloPinguin 4d5bb7a835 Cleanup nations (Part 1) 🧹 (#2637)
## Description:

1. Using the wording `"Nation"`, `"FakeHuman"` and `"NPC"` at the same
time is confusing.
So I renamed every mention of `"FakeHuman"` and `"NPC"` in the entire
project to `"Nation"`. Just like they are called ingame.

2. `BotBehavior.ts` was originally intended for sharing the logic
between nations and bots.
But at the moment, the logic there isn't really shared and it's
basically just about attacking.
So I renamed `BotBehavior.ts` to `AiAttackBehavior.ts`. I use "Ai" to
indicate that this file is used by bots AND nations.

3. Moved `execuction/utils/AllianceBehavior.ts` to
`execuction/nation/NationAllianceBehavior.ts` to make sure everybody
understands that this file is not about alliances in general. It's just
about nations and how they handle alliances.

4. Removed `difficultyModifier` from `DefaultConfig`. It's unused and I
think we usually want to finetune the difficulty instead of using that
method.

5. Added `assertNever` in all `switch (difficulty)` default cases.

## 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
2025-12-18 16:20:23 -08:00
FloPinguin dfe33a05e9 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
2025-12-14 15:18:07 -08:00
FloPinguin 427e462fe5 Revamp nation/bot enemy selection 🗡️ (#2550)
## Description:

I closed my previous PR #2533 which was already reviewed by evan (but
not yet merged) because I noticed some issues.
Which led me to changing the enemy selection entirely. 

Nations / Bots previously had a fixed enemy which they kept for 100
ticks (10 seconds). This could make them react too late and feel slow.
Now they are a bit more responsive.

But the main benefit: Without a fixed enemy we can do multiple
sendAttack() on the same tick, which allowed me to give impossible
nations extremely efficient parallel bot attacks:


https://github.com/user-attachments/assets/38f65623-fbf0-4e98-a833-5fcba2ee6eee

Previously nations were so slow in taking out bots that you could even
encircle them on the Archiran map...
Now they are like 200% faster (but only on the impossible difficulty)

## Nuke enemy selection

Previously, the enemy for troop attacks and nukes was identical. Now, as
we no longer have a fixed enemy in BotBehaviour, I added
findBestNukeTarget() to select better nuke-targets. I will probably open
a PR soon which makes nations nuke the crown :)

## Betrayal logic

While revamping the attack logic I had to work on the betrayal logic,
which was quite confusing, with many negations. And the betrayals were
just random.
So I made it easier to understand with maybeBetrayAndAttack().
Now it does betray friends if we have 10 times more troops than them. I
will improve that method in a future PR, but already now it should be
better than just betraying randomly.

## Attack order

Previously, nations attacked in this order:

- TerraNullius (Untaken land and nuked territory)
- Bots
- Retaliate against incoming attacks

Now its in this order:

- TerraNullius (Untaken land)
- Retaliate against incoming attacks
- Bots
- TerraNullius (Nuked territory)

So the changes are these:

- After throwing a nuke onto a nation, they will no longer ignore
incoming attacks. Previously they attacked the nuked territory first.
Very common singleplayer problem.
- Nations now retaliate against incoming attacks before attacking bots.
Previously you could attack a nation but they did not care because there
were still bots left.

I also changed the attack order of bots a bit (retaliate before
attacking randoms), but that isn't even noticeable.

## Big bug fixed

Additionally, I fixed a big bug: selectEnemy() oftentimes returned null
(because of enemySanityCheck) and therefore no attack happened.
This was especially visible in games where nations are surrounded by
friends (Team games and nations vs humans).
This was also the reason why Enzo could play nations vs humans in
singleplayer and NO NATION of the much bigger nation team would try to
attack him.

## 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
2025-12-11 13:57:15 -08:00
Scott Anderson d83a66196a bugfix: Nations rarely launch nukes (#1860)
## Description:

Simplify nation enemy selection to make nations more likely to launch
nukes.

Partially fixes #1855 by addressing a v24 regression in nation behavior.

## 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
2025-10-06 15:37:04 -07:00
Abdallah Bahrawi 0a6ab07d2e fix: traitor bug when attacking immediately after initiating an alliance (#2044)
## Description:

This PR fixes a critical race condition bug where players could
unintentionally receive the traitor debuff when alliance requests were
accepted mid-attack.


Critical Bug Fixes #1866

**Root Cause:** 
Players could bypass UI alliance checks ( isFriendly() ) by accepting
alliances and immediately attacking after that, causing the server to
treat the attack as betrayal
Solution: Added server-side alliance validation in
AttackExecution.init()
This ensures attacks on allies are blocked at the server level.

- Once Bots and Nations decide to attack, they breaks the alliance. I
added maybeConsiderBetrayal(), which currently always returns true. I’ll
add proper logic for alliance-breaking soon on another PR; this didn’t
exist in the code before.

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

abodcraft1

---------

Co-authored-by: evanpelle <evanpelle@gmail.com>
2025-09-13 09:21:21 -07:00
VariableVince a71ac7a218 Fix oversight: non-human player never responds to alliance renewal request (#1536)
## Description:

**--Fix oversight in v24--**
In v24, alliance renewal was introduced. But a Bot or Nation never
answers to it. So the Event Panel expiration message + clicking to Renew
and waiting, is all in vain if the other player is not a human. Like in
Single player in all cases. The message after ~30 seconds is always
"Alliance with xxx expired". This feels illogical and there's no purpose
for showing a Request to Renew button if it then always expires.

Also reported by players like here:
https://discord.com/channels/1284581928254701718/1284581928833388619/1398249123093676094

This PR fixes it by having the non-human reciever of the request, say
yes to depending on attiude towards the human and chance. This feels
more realistic.

The requestor is always the human player because they click a button in
EventsDisplay. So there is always already an extension request which the
bot can react to with another extension request to have the alliance be
extended.

**--Add tests--** 
It adds tests for extending alliance between human and non-human player.
One for AllianceExtensionExecution simply testing if alliance between
human and non-human can be extended. And one in BotBehavior, testing if
it correctly handles an extension request by adding a new
AllianceExtensionExecution.

**--Fix silent bug in existing test--**
Adding the new test for human and non-human for
AllianceExtensionExecution, i ran into a bug in the existing test for
extending alliance between humans. Which made the test always pass
because expirationAt wasn't fetched correctly. Had to fix that too,
without intending that for this PR beforehand. And then had to include
the bugfix from PR #1582 (v25) in it too to have the alliance actually
extended. More details below:

(-- The existing test would always return 'all passed' because it did
not get the expiresAt() but got the createdAt +
config.AllianceDuration() for both expirationBefore and expirationAfter.
createdAt is immutable so before and after would be the same. And then
it did not test for toBeGreaterThan, which would have failed. But it
tested wrongfully for toBeGreaterThanOrEqual which was a pass even when
the expirationBefore and expirationAfter would be the same and no
extension had taken place.

-- The bugfix from PR 1582 needed to be included now too. Because only
with those changes, the existing test has its alliance truly extended
and only with that the expirationAt actually changed. Actually, checking
if extend() was called isn't needed anymore, since we now check the
expirationAt correctly which on its own tells us if extend() was
succesful. But left this addition from PR 1582 in since it can't do any
harm.)

## 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
- [ ] I have read and accepted the CLA agreement (only required once).

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

tryout33

---------

Co-authored-by: evanpelle <openfrontio@gmail.com>
Co-authored-by: Drills Kibo <59177241+drillskibo@users.noreply.github.com>
2025-08-06 06:14:34 -04:00
evanpelle 3b8a36166a Remove workers & troop ratio bar, only have troops (#1676)
## Description:

The troop/worker ratio bar is almost never changed. so remove it and the
entire concept of workers. Now there is just troops.

Now players get a consistent 1k/s gold.

## 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
- [x] I have read and accepted the CLA agreement (only required once).

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

evan
2025-08-01 16:06:59 -07:00
Scott Anderson 2e442c9c29 Add expand ratio to bot behavior class (#1376)
## Description:

- Create a new expand ratio that allows AI players to expand with a much
lower reserve ratio than the normal attack reserve ratio.
- Unify the implementation of the first attack between bots and nations.
- Bugfix: Multiple attacks per tick could cause nations to full-send.
- Improve the chance of finding a place to boat to by allowing nations
to target non-shore land tiles.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
2025-07-08 18:39:11 -07:00
VariableVince 5e821bec06 Optimizations for botbehaviour (#1114)
## Description:

Some optimizations for bot & fakehuman execution. Better performance
measured using profiling, at the start of a game or in singple player,
but since the circumstances differ it is not a very reliable comparison.

--- In BotExecution:
Added getNeighborTraitorToAttack in Botbehavior to use in maybeAttack.
No caching for playerNeighbors array, because although it could be
re-used up to two times in selectRandomEnemy, maybeAttack runs every
tick and selectEnemy only every 10 seconds so the cache overhead would
not be worth it.

--- In Botbehavior:
Put repeated code in dedicated functions for readability, maintainance
ease, and reduce the chances of forgetting to update the timestamp for
enemyUpdated. Can be seen as a follow-up to PRs #434 and #946.

-- In both selectEnemy and selectRandomEnemy: 
Put the first if this.enemy === null statement parenthesis, around the
two checks below. Because if there's already an enemy (if forgetEnemies
hasn't nullified it, enemy === null is false at the first if statement),
then enemy === null will still be false for the two checks below the
first. No need to check it thrice then. Once inside the first if
statement (when enemy === null is true), then we need to check it two
times more in case the code inside already set an enemy.

-- In selectEnemy under 'Prefer neighboring bots': 
Removed unneccessary check for enemy === null.
Replaced sort with more performant for loop.

-- In selectRandomEnemy:
Under 'Select a traitor as an enemy', if a traitor is a friendly player,
they got less odds to be chosen as enemy over an unfriendly player, but
they weren't ruled out. While below in the Sanity Check, we specifically
rule out friendly players as enemy and set enemy=null. So excluded
friendly players under 'Select a traitor as an enemy' instead of only
giving them lower odds.

Use the new shared method getNeighborTraitorToAttack.

-- In checkIncomingAttacks, since we're only interested in the max
value, replaced sort with loop that makes a single pass through the
attacks to find the largest one.

-- For shouldAcceptAllianceRequest, #1049 added tests and improved
readability of Alliance requests.

It may have also deoptimized it a bit. Const notTooManyAlliances would,
in the past, not be computed if requestorIsMuchLarger was already found
to be true. Since the readability changes, tooManyAlliances was always
computed regardless of the value of requestorIsMuchLarger.

This PR attempts to address this too, while still keeping it as readable
hopefully.

Also choose for early returns to optimize a bit more. So if isTraitor is
true, don't compute any further and just return false immediately etc.

Switched the order of testing for noMalice and isTraitor. Traitor status
used to be throughout the game, now it's only 30 seconds or will be
maybe 45 seconds to 1 minute. So the chances of someone asking for
alliance and being a traitor at the same time have decreased. isMalice
chances could be bigger.

Removed the named constants and choose to put them in comments, so
readability should be about the same as #1049 intended. Kept the braces
for the if statements because otherwise Prettier would misalign the
comments.)

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

tryout33
2025-06-09 14:28:51 -04:00
Scott Anderson 32f5723c72 Simplify bots retaliation logic (#946)
## Description:

Simplify bots retaliation logic. Do not counter-attack before reaching
the trigger ratio.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
2025-05-29 17:53:18 -07:00
Scott Anderson 70745faac4 Enable strictNullChecks, eqeqeq (#436)
## Description:

Improve type safety and runtime correctness by:
1. Enabling TypeScript's
[strictNullChecks](https://www.typescriptlang.org/tsconfig/#strictNullChecks)
compiler option.
2. Replacing all loose equality operators (`==` and `!=`) with strict
equality operators (`===` and `!==`).
3. Cleaning up of type declarations, null handling logic, and equality
expressions throughout the project.

Currently, the code allows implicit assumptions that `null` and
`undefined` are interchangeable, and relies on type-coercing equality
checks that can introduce subtle bugs. These practices make it difficult
to reason about when values may be absent and hinder the effectiveness
of static analysis.

Migrating to strict null checks and enforcing strict equality
comparisons will clarify intent, reduce bugs, and make the codebase
safer and easier to maintain.

Fixes #466 

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

---------

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
Co-authored-by: evanpelle <openfrontio@gmail.com>
2025-05-15 16:39:40 -07:00
Scott Anderson d9e8984df5 Refactor Nations AI (#427)
## Description:

Refactor AI troop management and strategic behavior based around two key
values: a trigger ratio and a reserve ratio.
- Reserve ratio: This determines the portion of the population the AI
will keep in reserve and will not send on attacks.
- Trigger ratio: This is the threshold at which the bot will initiate an
attack.

Additionally, when an incoming attack is detected, bots will now
prioritize retaliating by switching targets to the largest incoming
attacker.

Fixes #470 

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

fake.neo

---------

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
2025-04-27 14:15:45 -07:00
Scott Anderson 1b672420b3 Combine Bot and Nation behaviors in to a shared class (#434)
This is the first move in the effort to combine the redundant logic that
exists between BotExecution and FakeHumonExecution.

This commit:
1. Combines the alliance request handler, moving bots to use the same
logic as nations for acceptance.
2. Combines the sendAttack() functions, which may later be reworked.
3. Introduces selectEnemy() function to wrap enemy selection logic that
nations use.
4. Blocks nations from nuking bots.
5. Alters enemy selection to prefer neighboring bots if there are any.

## Description:

Fixes #467 

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

fake.neo

---------

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
2025-04-17 19:37:16 -07:00
Evan 8b6895d745 add prettier import plugin 2025-03-31 13:09:27 -07:00
PilkeySEK d6e596f7d8 fix: Make bots not attack teammates (#368)
Bug report:
https://discord.com/channels/1284581928254701718/1355194642592694412
## Please complete the following:

- [X] I have added screenshots for all UI updates
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [X] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:
PilkeySEK
2025-03-30 10:43:24 -07:00
evanpelle d8fe41de7a teams (#349) 2025-03-27 20:43:56 -07:00
Ilan Schemoul 7ec3f0c81f Revert "Change BorderTiles from Array to Set (#230)" (#238)
This reverts commit 8124651382 which
breaks compilation, has wrong indentation and uses flatMap without
Array.from which is also wrong.
2025-03-13 14:51:35 -07:00
ByGoalZ 8124651382 Change BorderTiles from Array to Set (#230)
Changed border constant from Array to Set, to avoid potential
duplicates.
2025-03-13 06:05:10 -07:00
Evan 64d13a46bd bugfix: bots were not losing troops in attack. rebalanced difficulties. 2025-02-13 11:50:15 -08:00
Evan 40966ca3b9 format all files with prettier 2025-02-12 08:28:15 -08:00
Evan 4182eaa449 rebalance difficulties 2025-02-08 20:59:44 -08:00
Evan 4ee37323f9 format codebase with prettier 2025-02-01 12:05:11 -08:00
evanpelle de1dbff570 combine Game & MutableGame 2025-02-01 12:05:11 -08:00
evanpelle 7d15c0c065 combine Player & MutablePlayer interfaces 2025-02-01 12:05:11 -08:00
Evan b91d9d4148 fix bugs from using tilerefs 2025-02-01 12:05:11 -08:00
Evan f0f5bae79f thread_split: convert all tile to tileref 2025-02-01 12:05:11 -08:00
evanpelle a17ae48cd3 use TileRef instead of tile 2025-02-01 12:05:11 -08:00
Evan dab427d614 put methods onto terraintile 2025-02-01 12:05:11 -08:00
Evan 2d2df14ae3 add GameConfig to Game 2024-12-07 09:45:39 -08:00
evanpelle d874392467 refactored alliance 2024-09-22 13:38:43 -07:00
evanpelle 9e4b38c333 can betray traitor without becoming traitor. allied bots less likely to attack traitor than non allied. 2024-09-19 08:19:54 -07:00
evanpelle 37e529ac84 fix traitor bot attack bug 2024-09-19 08:09:43 -07:00
evanpelle 7e986806d7 bots more likely to attack traitors 2024-09-19 08:09:11 -07:00
evanpelle 41e77858ee add traitor icon, bots don't ally traitors 2024-09-18 20:27:12 -07:00
evanpelle 4bab6a5271 bots don't attack allies 2024-09-17 21:01:57 -07:00
evanpelle d4d0be5e37 gameimpl store alliances 2024-09-17 20:30:15 -07:00
evanpelle f3307300ef refactored GameImpl into multiple files 2024-09-17 19:49:16 -07:00
evanpelle a7f838d442 updated balance 2024-09-13 15:55:17 -07:00
evanpelle a3fc8879b4 minor fixes 2024-09-10 18:33:31 -07:00
evanpelle f24a4b894a fixed pacing bug, improved expansion 2024-09-09 20:31:45 -07:00
evanpelle a7ad8790aa fakehumans send boats, improved map 2024-09-08 20:21:39 -07:00
evanpelle a92bebce05 when attacking by boat, attack execution only starts from boat pixel 2024-09-04 20:06:20 -07:00
evanpelle d2a8b48764 remove playerconfig 2024-08-29 20:24:50 -07:00
evanpelle 0281db46fa make bots more likely to attack larger border 2024-08-29 20:19:56 -07:00
evanpelle 8c902a70b8 added spawn timer bar 2024-08-26 09:13:05 -07:00
evanpelle 51650eb930 can change spawn in beginning of game 2024-08-25 20:21:35 -07:00
evanpelle bb8c24e230 move config to Game 2024-08-24 12:12:46 -07:00
evanpelle 5e7c206f0d improved terrain api 2024-08-21 19:51:01 -07:00
evanpelle bc8463fa79 game start delay 5 seconds 2024-08-16 20:42:10 -07:00
evanpelle 1cf112c0fb trying to fix nondetermism 2024-08-16 17:43:00 -07:00