4
\$\begingroup\$

I'm creating a counter picker for a game called League of Legends, and for this I need to compare effects of abilities, which each champion has. For this I'm using 4 lists of champions, the returnList, the banned champion list, the enemy champions list, annd the allied champion lists.

However, seeing that the returnlist, where the champions get there points, contains around 120 instances, which all have 5 abilities, which in turn have around 5 effects each, I feel that my current solution of nesting foreach loops might be really slow.

I was wondering how I could best optimise this

foreach (CounterChampion champion1 in enemyList)
{
    foreach (CounterAbility ability1 in champion1.Abilities)
    {
        foreach (string effects1 in ability1.Effects)
        {
            foreach (CounterChampion champion2 in returnList)
            {
                foreach (string effects2 in champion2.Abilities.SelectMany(ability2 => ability2.Effects))
                {
                    if (CounterEffectExists(effects2, effects1, CounterEffect.EffectType.Counters))
                    {
                        champion2.AddPoints(1);
                    }
                    if (CounterEffectExists(effects1, effects2, CounterEffect.EffectType.Counters))
                    {
                        champion2.AddPoints(-1);
                    }
                }
            }
        }
    }
}
foreach (CounterChampion champion1 in banList)
{
    foreach (CounterAbility ability1 in champion1.Abilities)
    {
        foreach (string effects1 in ability1.Effects)
        {
            foreach (CounterChampion champion2 in returnList)
            {
                foreach (string effects2 in champion2.Abilities.SelectMany(ability2 => ability2.Effects))
                {
                    if (CounterEffectExists(effects1, effects2, CounterEffect.EffectType.Counters))
                    {
                        champion2.AddPoints(1);
                    }
                }
            }
        }
    }
}
foreach (CounterChampion champion1 in allyList)
{
    foreach (CounterAbility ability1 in champion1.Abilities)
    {
        foreach (string effects1 in ability1.Effects)
        {
            foreach (CounterChampion champion2 in returnList)
            {
                foreach (string effects2 in champion2.Abilities.SelectMany(ability2 => ability2.Effects))
                {
                    if (CounterEffectExists(effects2, effects1, CounterEffect.EffectType.WorksWith))
                    {
                        champion2.AddPoints(1);
                    }
                    if (CounterEffectExists(effects1, effects2, CounterEffect.EffectType.WorksWith))
                    {
                        champion2.AddPoints(1);
                    }
                }
            }
        }
    }
}
returnList.Sort(
    delegate (CounterChampion c1,
    CounterChampion c2)
    {
        return c1.GetPoints().CompareTo(c2.GetPoints()) * -1;
    }
);
return returnList;
\$\endgroup\$
  • \$\begingroup\$ I'm guessing you're using the riot data dragon API to provide the data, right? \$\endgroup\$ – Dan Pantry May 19 '16 at 9:23
  • \$\begingroup\$ @DanPantry the champions and abilities, the effects are entered manually \$\endgroup\$ – Dennis van Gils May 19 '16 at 9:59
  • \$\begingroup\$ What's the relationship between champions, abilities and effects? Can multiple champions have the same ability? Can multiple abilities have the same effects? \$\endgroup\$ – Ben Aaronson May 19 '16 at 15:14
  • \$\begingroup\$ @BenAaronson each champion has 5 unique abilities, which have multiple effects, but one ability can have the same effect multiple times. \$\endgroup\$ – Dennis van Gils May 19 '16 at 15:26
  • \$\begingroup\$ @DennisvanGils Okay. And multiple abilities having the same effect? \$\endgroup\$ – Ben Aaronson May 19 '16 at 15:42
1
+50
\$\begingroup\$

Well the inner loop is something like 120*5*5 => 1200 cycles and I really don't know if it can become an issue with today's PC power. But improvements can always be made, and reading your code, I see that some lines are duplicated :

   foreach (CounterChampion champion2 in returnList)
    {
        foreach (string effects2 in champion2.Abilities.SelectMany(ability2 => ability2.Effects))
        {

As as first step toward performance and code structuration, I suggest to compute the ability/effect in returnList once, and reuse it after. Typically, you may have something like this :

class EffectByChampion{
  CounterChampion Champion,
  List<string> Effects
}

then you may fill the following list ONCE

List<EffectByChampion>

and after you may reuse it 3 times through your existing code with :

    foreach (string effects1 in ability1.Effects)
    {
      foreach(EffectByChampion effectByChampion in EffectByChampionList)
      {
       foreach(string effects2 in effectByChampion)

This way, you have 3 foreach loops removed. Hope this help !

\$\endgroup\$
0
\$\begingroup\$

It seems something like https://champion.gg then.

You could use a specification pattern to create sets of conditions and then query for them. This can easily be done in c#. It is used in enterprise software to reduce to complexity of database query logic and simplify testability.


Another way would be to create a proof builder. Typically this requires more than just good knowledge about formal logics. This would enable you to prove viable counter combinations, given that requirements are met. This should include items as well.


I used to be decently high Elo player and I have done something similar. The problem I tried to solve was how to use skills / items to their full potential. I did not solve it completely, but I did found out a lot of cool mechanics to exploit. I can tell you right now, your counters will not work if you consider champion full potential - nobody uses that.

An example of this is Diana. When would you consider her to be able to kill you if you are 15m in game and you are adc. A lot of its counter mechanics is to avoid getting marked my her q. However consider a combo (flash) + w + r + q + auto(sheen proc) + r(passive proc) + w + auto, and that did not consider passive proc. A thing about it is that it happens instantly and flashing away does not help. Diana will use q ~(0.05-0.1s) before R hits the target (mid air), this basically guarantees 100% skill reset (counters are ga/zonja/spell block/not penetrated spell shiled). To counter spell shiled, you cast w sooner so it procs it. About no adc will build zonja and if they do, they still need to cast it in ~0.2-0.5s. Guardian angle will can save adc if it is late game and if he is surrounded most of the team. The thing about this is Diana will have 600+ ap and 200+ armor with GA and zonjas. He will still go in and murder your Ga and at least half of you teams hp before he hits zonja. If he comes out, her job is done, but she still has ga and she is tanky. You would think that heavy cc works against that, but no - most of it is not instant and even then she can cleanse or use an item to clear it.

Another example that will break most of the games is hyper carry + supports. Typical is Kog Maw or Tristana (but many can fill the role) with Lulu, Orianna, Janna, Braum(shield), Nunu ...


Another much simpler way would be to simplify the problem to data mining. Often you do not even need to create any algorithm. Get the data to the form you need and use existing tools.


Edit: Simplification

Step 1. Put the data in a database
Step 2. Write a query that checks 1 skill
Step 3. Execute query for all items
\$\endgroup\$
  • \$\begingroup\$ * "and that did not consider passive proc" - you had while starting the combo. \$\endgroup\$ – Margus May 19 '16 at 10:51
  • \$\begingroup\$ I'm aware of the fact that what I'm doing of course won't work 100% of the times, however, this is not an answer to the question. The question is about optimising this code, not about wether or not it works. \$\endgroup\$ – Dennis van Gils May 19 '16 at 10:55
  • \$\begingroup\$ I forgot to mention that you can zonjas projectiles like veigar ult or near instant area cc's like Cassiopeia ult (does not matter where you are). Additionally, there are a lot of champions much stronger abilities than her - it is just an example. \$\endgroup\$ – Margus May 19 '16 at 10:55
  • \$\begingroup\$ @DennisvanGils and that was not my answer as well. I just like to note, this is not an easy task to solve well. If you missed it, my answer was: do not use multiple loops at all. \$\endgroup\$ – Margus May 19 '16 at 10:57
  • \$\begingroup\$ Putting data to a database makes the problem even worse. In any case you have to retrieve the data and start again. \$\endgroup\$ – Alex Kudryashev May 22 '16 at 3:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.