Development Discussion: Speeding up battle calculator (and thus Hard AI)



  • @redrum here's the numbers rerun with FastAi's calculator included.

    1 infantry, 1 armour vs 2 infantry
    MiniMax: 93.5 ms, Win: 0.506233909545216, Lose: 0.38491296598075025
    Hard AI 200 runs: 1787.9 ms, Win: 0.47, Lose: 0.435
    Hard AI 2000 runs: 1471.6 ms, Win: 0.501953125, Lose: 0.388671875
    FastOdds: 6.8 ms, Win: 0.5

    1 armour, 1 tactical_bomber vs 2 infantry, 1 fighter
    MiniMax: 2.2 ms, Win: 0.1150459321571266, Lose: 0.7947330200775955
    Hard AI 200 runs: 1025.3 ms, Win: 0.085, Lose: 0.91
    Hard AI 2000 runs: 1194.6 ms, Win: 0.06640625, Lose: 0.9287109375
    FastOdds: 0.5 ms, Win: 0.34082222539412593

    1 infantry, 1 artillery vs 2 infantry
    MiniMax: 2.9 ms, Win: 0.4574200859119973, Lose: 0.45742008591199734
    Hard AI 200 runs: 1038.0 ms, Win: 0.465, Lose: 0.405
    Hard AI 2000 runs: 1385.9 ms, Win: 0.4521484375, Lose: 0.4619140625
    FastOdds: 0.4 ms, Win: 0.5

    1 cruiser, 1 destroyer vs 1 cruiser, 1 destroyer
    MiniMax: 1.6 ms, Win: 0.4250031963247701, Lose: 0.4250031963247701
    Hard AI 200 runs: 1047.1 ms, Win: 0.405, Lose: 0.46
    Hard AI 2000 runs: 1140.1 ms, Win: 0.412109375, Lose: 0.4345703125
    FastOdds: 0.7 ms, Win: 0.5

    1 cruiser, 1 destroyer, 1 battleship vs 2 cruiser, 1 destroyer
    MiniMax: 3.1 ms, Win: 0.5358330747385637, Lose: 0.34400771480746317
    Hard AI 200 runs: 1032.0 ms, Win: 0.775, Lose: 0.165
    Hard AI 2000 runs: 1141.3 ms, Win: 0.8125, Lose: 0.1328125
    FastOdds: 1.6 ms, Win: 0.6591777746058741

    1 infantry, 1 fighter vs 1 infantry, 1 aa gun
    MiniMax: 1.3 ms, Win: 0.6961033280443275, Lose: 0.2000276003717069
    Hard AI 200 runs: 1138.5 ms, Win: 0.38, Lose: 0.615
    Hard AI 2000 runs: 1248.8 ms, Win: 0.4130859375, Lose: 0.583984375
    FastOdds: 0.2 ms, Win: 0.7180577051181194

    Fast Ai is really fast. Not sure how to compare the win percentage.

    I have an idea on how to do multi-hit and AA. I'm going to attempt it. I also need to deal with units with multiple rolls. Is there a map in TestMapGameData that has units like that?


  • Moderators Admin

    @Trevan said in Development Discussion: Speeding up battle calculator (and thus Hard AI):

    MiniMax: 93.5 ms, Win: 0.506233909545216, Lose: 0.38491296598075025
    Hard AI 200 runs: 1787.9 ms, Win: 0.47, Lose: 0.435
    Hard AI 2000 runs: 1471.6 ms, Win: 0.501953125, Lose: 0.388671875
    FastOdds: 6.8 ms, Win: 0.5

    How is 200 runs slower than 2000 runs?



  • @Trevan Unfortunately this sort of algorithm has to be exponentially slow as the number of units increases, by its nature. I'm saying this without even looking at the code. With some cleverness it might be possible to bring this down to exponential with the number of unit types (not units), but will never be able to handle if, say, a hypothetical map had dozens of unit types, many of them targeting each other unpredictably with all sorts of AA attacks, and battles between stacks of 50 happening regularly. But the speed increase is extremely impressive, and even if the AI only used this to simulate smaller battles while keeping the battle calculator for battles that are too large for this algorithm, that's already a massive breakthrough in improving performance.



  • @alkexr here's some numbers of larger groups:

    10 infantry, 10 artillery vs same
    MiniMax: 219.3 ms, Win: 0.497401100632783, Lose: 0.4974011006327829
    Hard AI 200 runs: 2092.2 ms, Win: 0.475, Lose: 0.515
    Hard AI 2000 runs: 2086.4 ms, Win: 0.4658203125, Lose: 0.5283203125
    FastOdds: 8.9 ms, Win: 0.5, Lose: 0.0
    20 infantry, 20 artillery vs same
    MiniMax: 585.7 ms, Win: 0.49887594912628747, Lose: 0.49887594912628747
    Hard AI 200 runs: 2538.8 ms, Win: 0.54, Lose: 0.46
    Hard AI 2000 runs: 3255.3 ms, Win: 0.5029296875, Lose: 0.494140625
    FastOdds: 14.7 ms, Win: 0.5, Lose: 0.0
    30 infantry, 30 artillery vs same
    MiniMax: 1244.4 ms, Win: 0.49930484393068525, Lose: 0.49930484393068525
    Hard AI 200 runs: 2873.4 ms, Win: 0.5, Lose: 0.5
    Hard AI 2000 runs: 3627.7 ms, Win: 0.501953125, Lose: 0.49609375
    FastOdds: 15.5 ms, Win: 0.5, Lose: 0.0
    40 infantry, 40 artillery vs same
    MiniMax: 2032.1 ms, Win: 0.4995101769999315, Lose: 0.4995101769999315
    Hard AI 200 runs: 2904.5 ms, Win: 0.505, Lose: 0.49
    Hard AI 2000 runs: 4244.6 ms, Win: 0.51171875, Lose: 0.48828125
    FastOdds: 16.9 ms, Win: 0.5, Lose: 0.0
    50 infantry, 50 artillery vs same
    MiniMax: 3366.8 ms, Win: 0.4996227075274397, Lose: 0.4996227075274401
    Hard AI 200 runs: 3574.4 ms, Win: 0.49, Lose: 0.51
    Hard AI 2000 runs: 4614.9 ms, Win: 0.4853515625, Lose: 0.5146484375
    FastOdds: 16.8 ms, Win: 0.5, Lose: 0.0
    60 infantry, 60 artillery vs same
    MiniMax: 5306.7 ms, Win: 0.4996976675867421, Lose: 0.4996976675867422
    Hard AI 200 runs: 3264.8 ms, Win: 0.54, Lose: 0.46
    Hard AI 2000 runs: 5255.6 ms, Win: 0.4931640625, Lose: 0.5068359375
    FastOdds: 22.5 ms, Win: 0.5, Lose: 0.0
    10 infantry, 10 artillery, 10 armour, 10 fighter, 10 tactical_bomber, 10 bomber vs same
    MiniMax: 12470.0 ms, Win: 0.8637525783579469, Lose: 0.13152813492843518
    Hard AI 200 runs: 2565.5 ms, Win: 0.32, Lose: 0.68
    Hard AI 2000 runs: 2721.8 ms, Win: 0.3125, Lose: 0.6875
    FastOdds: 23.4 ms, Win: 0.6286565968532855, Lose: 0.0
    

    Interesting that the battle with 30 infantry and artillery was a lot faster than the one with 10 of each land type. Both battle has a total of 60 units on both sides but the addition of multiple unit types slowed it down.



  • @Trevan Hmm, right, you can actually do this in cubic (?) time, so long as the order of losses is linear, because the number of casualties already sustained uniquely determines the composition of the remaining army. But I still don't see how you do it in less than (number of units)^(number of target lists * constant) time, where target lists include normal attacks, AA attack types, canNotBeTargetedBy and related mechanics, etc. Because, without assuming something about the order in which units die, technically there are a million different army compositions strictly smaller than "10 of each land type", as opposed to 60 if you assume a linear order of losses.



  • @alkexr yes, my algorithm is assuming the order of losses is linear. I believe the time is roughly what you said. Each target group will have its own order of loss.



  • AA guns were a lot harder than I expected but I think I got the majority of it working. I did a comparison on the TestMapGameData.TWW data because it had units that could do aa firing with different target groups. I ran a battle between an infantry, tank, and fighter vs an anti-tank gun, mobile artillery, and anti-air gun.

    MiniMax: 197.7 ms, Win: 0.5520100874509435, Lose: 0.40334641519178716
    Hard AI 200 runs: 2025.6 ms, Win: 0.57, Lose: 0.42
    Hard AI 2000 runs: 3222.8 ms, Win: 0.5517578125, Lose: 0.412109375
    FastOdds: 7.9 ms, Win: 0.7703534497836491, Lose: 0.0

    Now I'll work on multi-hit units.



  • Since I play against the AI quite a bit.. would love this project to succeed as Hard AI is ridiculously slow on large maps/lots of units. Would like to contribute.. but have very little coding skill unfortunately. If you need testers or other basic help... let me know.



  • @redrum @RoiEX
    I think I have a good majority of the battle logic done. I know I'm missing bombardment, some details in sub first strike (order of strikes and destroyers presence), transport handling, amphibious battles, and paratroopers. There's probably other complexities that I missed as well. I've also worked on speeding it up. Here's a bunch of comparisons:

    30 infantry, 30 artillery, 30 armour, 30 fighter, 30 tactical_bomber, 30 bomber vs same
    MiniMax: 21218.5 ms, Win: 0.9779225005914358, Lose: 0.021637464729431084
    BattleCalculator: 1841.4 ms, Win: 0.95, Lose: 0.045
    Hard AI 200 runs: 2893.2 ms, Win: 0.955, Lose: 0.045
    Hard AI 2000 runs: 7900.4 ms, Win: 0.970703125, Lose: 0.029296875
    FastOdds: 29.4 ms, Win: 0.6517051232809282, Lose: 0.0
    
    10 infantry, 10 artillery, 10 armour, 10 fighter, 10 tactical_bomber, 10 bomber vs same
    MiniMax: 1342.4 ms, Win: 0.8637259513264075, Lose: 0.131552773090954
    BattleCalculator: 459.2 ms, Win: 0.895, Lose: 0.105
    Hard AI 200 runs: 1280.3 ms, Win: 0.87, Lose: 0.12
    Hard AI 2000 runs: 2414.4 ms, Win: 0.8486328125, Lose: 0.1455078125
    FastOdds: 7.4 ms, Win: 0.6286565968532855, Lose: 0.0
    
    1 infantry, 1 armour vs 2 infantry
    MiniMax: 1.4 ms, Win: 0.506233909545216, Lose: 0.38491296598075025
    BattleCalculator: 74.3 ms, Win: 0.505, Lose: 0.365
    Hard AI 200 runs: 987.0 ms, Win: 0.495, Lose: 0.39
    Hard AI 2000 runs: 1000.7 ms, Win: 0.501953125, Lose: 0.3974609375
    FastOdds: 0.5 ms, Win: 0.5, Lose: 0.0
    
    1 armour, 1 tactical_bomber vs 2 infantry, 1 fighter
    MiniMax: 1.2 ms, Win: 0.11504593215712659, Lose: 0.7947330200775955
    BattleCalculator: 99.8 ms, Win: 0.115, Lose: 0.775
    Hard AI 200 runs: 1156.5 ms, Win: 0.175, Lose: 0.75
    Hard AI 2000 runs: 1244.3 ms, Win: 0.1201171875, Lose: 0.7958984375
    FastOdds: 0.2 ms, Win: 0.34082222539412593, Lose: 0.0
    
    1 infantry, 1 artillery vs 2 infantry
    MiniMax: 1.0 ms, Win: 0.4574200859119973, Lose: 0.45742008591199734
    BattleCalculator: 84.5 ms, Win: 0.49, Lose: 0.44
    Hard AI 200 runs: 1095.0 ms, Win: 0.51, Lose: 0.41
    Hard AI 2000 runs: 1231.1 ms, Win: 0.4619140625, Lose: 0.443359375
    FastOdds: 0.2 ms, Win: 0.5, Lose: 0.0
    
    1 cruiser, 1 destroyer vs 1 cruiser, 1 destroyer
    MiniMax: 2.2 ms, Win: 0.4250031963247701, Lose: 0.4250031963247701
    BattleCalculator: 86.0 ms, Win: 0.395, Lose: 0.44
    Hard AI 200 runs: 1076.7 ms, Win: 0.405, Lose: 0.465
    Hard AI 2000 runs: 1213.1 ms, Win: 0.4306640625, Lose: 0.4189453125
    FastOdds: 0.4 ms, Win: 0.5, Lose: 0.0
    
    1 cruiser, 1 destroyer, 1 battleship vs 2 cruiser, 1 destroyer
    MiniMax: 3.9 ms, Win: 0.7898869382202083, Lose: 0.14239504638191994
    BattleCalculator: 122.6 ms, Win: 0.76, Lose: 0.125
    Hard AI 200 runs: 1076.1 ms, Win: 0.79, Lose: 0.15
    Hard AI 2000 runs: 1188.0 ms, Win: 0.79296875, Lose: 0.1474609375
    FastOdds: 0.7 ms, Win: 0.6591777746058741, Lose: 0.0
    
    1 infantry, 1 fighter vs 1 infantry, 1 aa gun
    MiniMax: 11.1 ms, Win: 0.5600126123377998, Lose: 0.35180777879589964
    BattleCalculator: 189.6 ms, Win: 0.585, Lose: 0.315
    Hard AI 200 runs: 1127.4 ms, Win: 0.595, Lose: 0.335
    Hard AI 2000 runs: 1410.4 ms, Win: 0.5869140625, Lose: 0.3203125
    FastOdds: 0.3 ms, Win: 0.7180577051181194, Lose: 0.0
    

    All of the win percentages are pretty darn close. The 30 of each unit type battle was slower than the original calculator (21 seconds vs 3 seconds) but the 10 of each unit type battle was about the same (1.34 seconds vs 1.28 seconds). The rest of the battles I did were small so it was a lot faster.

    What would you like me to do now? Can I make this available to others (such @Swampy) so they can test it out?


  • Admin

    I'm not 100% sure what @redrum's opinion on this is, but I'd really like to see this as an experimental Ai in the game.
    But for now hide it behind the test flag?



  • Perhaps it could be added as a checkbox in the battle calculator for now as well?

    Otherwise, I think the two things it needs is a heuristic for when it should be used (e.g. if too many units or when there are unsupported cases, it should do the usual battle calculator instead) and some kind of way to find cases where it disagrees with battle calculator. For example, maybe run some percentage of simulations using both and send error reports when the results disagree too much? Or perhaps we can have an experimental AI that always runs both so we can use it to validate it and find cases where it's incorrect?


  • Admin

    An AI variant IMO would be an excellent step. The idea of computing the actual odds vs the current monte carlo approximation is a very good one.

    The combat/battles code suffers from complexity and is already somewhat time-optimized. It would be very easy to underestimate the level of effort to replace the battle calc (simply having something that mostly works is just not quite enough, it would need to be cleanly coded, not be duplicative, well tested, complete, etc...)



  • @RoiEX I'm not aware of the test flag. How do I use it?

    I can make a sibling of FastAI that uses this calculator. I also like the idea of having it automatically compare the actual odds vs the new odds. @Alexei-Svitkine when you say "send error reports", how would I do that? Is there existing classes in the code that I can just use to send the reports?


  • Admin

        if(ClientSetting.showBetaFeatures.isSet()) {
            // add experimental feature
        }
    


  • @LaFayette I tried using that in the PlayerType enum

      BATTLE_TREE_AI("BattleTree (AI)", ClientSetting.showBetaFeatures.isSet()) {
        @Override
        public Player newPlayerWithName(final String name) {
          return new BattleTreeAi(name);
        }
      },
    

    But the player is still showing up in the list.

    I also tried:

      BATTLE_TREE_AI("BattleTree (AI)") {
        @Override
        public Player newPlayerWithName(final String name) {
          if (ClientSetting.showBetaFeatures.isSet()) {
            return new BattleTreeAi(name);
          }
        }
      },
    

  • Admin

    @Trevan
    Looks like @LaFayette trolled you a little bit there.
    Try

    BATTLE_TREE_AI("BattleTree (AI)", ClientSetting.showBetaFeatures.getValue().orElse(false)) {
      @Override
      public Player newPlayerWithName(final String name) {
        return new BattleTreeAi(name);
      }
    },
    

    instead



  • @RoiEX That worked. I have to restart triplea for it to affect.

    I tried to play a game in the UI since everything I've done has been through the test framework. But when I try to start a game, I get the error

    Failed to start game
    IllegalArgumentExeption: File must exist at path: /.../game-headed/assets/unit_scroller/unit_sleep.png
    

    I've checked and that file definitely doesn't exist. I've also checked out master and tried to run it in case my changes broke it but master also throws that same error.

    Is there something I'm missing?

    And should I create a PR now?


  • Admin

    @Trevan are you using a specific IDE?
    I think you have to run
    ./gradlew downloadAssets before running the game in order to download all the default assets



  • @RoiEX I'm using IntelliJ


  • Admin

    And yes, you can create a PR whenever you like, it will take us a while to review it though


Log in to reply