!Balance Button - Page 2

!Balance Button

Requests for features in the spring code.

Moderator: Moderators

User avatar
Licho
Zero-K Developer
Posts: 3803
Joined: 19 May 2006, 19:13

Post by Licho »

Relative, yeah, it's possible to port it. However tasclient is in pascal, so someone has to rewrite it with different containers and stuff..

Also algorithm for cbalance is now needlessly complex (I think), because it evolved slowly and I changed how priorities work for it..

Code: Select all


    // user and rank info
    private class UsRank
    {
      public int id;
      public int rank;
      public string clan;
      public UsRank(int id, int rank, string clan)
      {
        this.id = id;
        this.rank = rank;
        this.clan = clan;
      }
    }


    private static string GetClan(string name)
    {
      string[] parts = name.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
      if (parts.Length >= 2) {
        if (parts[0].Length > parts[1].Length) return parts[1]; else return parts[0];
      } return "";
    }


    private void BalanceTeams(int teamCount, bool clanwise)
    {
      List<UserBattleStatus> actUsers = new List<UserBattleStatus>();
      List<UsRank> ranker = new List<UsRank>();
      Battle b = tas.GetBattle();

      foreach (UserBattleStatus u in b.Users) {
        if (!u.IsSpectator) {
          actUsers.Add(u);
          User p;
          if (tas.GetExistingUser(u.name, out p)) ranker.Add(new UsRank(ranker.Count, p.rank, GetClan(u.name))); else ranker.Add(new UsRank(ranker.Count, 0, GetClan(u.name))); // cannot find user, assume rank 0
        }
      }

      Random rand = new Random();

      List<UsRank> tempList = new List<UsRank>(ranker);
      ranker.Clear();
      while (tempList.Count > 0) {
        // find max rank value
        int maxval = int.MinValue;
        foreach (UsRank u in tempList) {
          if (u.rank > maxval) {
            maxval = u.rank;
          }
        }

        List<UsRank> l2 = new List<UsRank>(); // pick pieces with max rank to l2
        int j = 0;
        while (j < tempList.Count) {
          if (tempList[j].rank == maxval) {
            l2.Add(tempList[j]);
            tempList.RemoveAt(j);
            j = 0;
          }
          j++;
        }

        while (l2.Count > 0) { // randomly add pieces from l2 to ranker
          int ind = rand.Next(l2.Count);
          ranker.Add(l2[ind]);
          l2.RemoveAt(ind);
        }

      }

      if (teamCount < 2 || teamCount > ranker.Count) teamCount = 2;

      List<UsRank>[] teamUsers = new List<UsRank>[teamCount];
      for (int i = 0; i < teamUsers.Length; ++i) teamUsers[i] = new List<UsRank>();
      int[] teamSums = new int[teamCount];

      List<string>[] teamClans = new List<string>[teamCount];
      for (int i = 0; i < teamClans.Length; ++i) teamClans[i] = new List<string>();

      if (clanwise) {
        string clans = "";
        // remove clans that have less than 2 members - those are irelevant
        foreach (UsRank u in ranker) {
          if (u.clan != "") {
            if (ranker.FindAll(delegate(UsRank x) { return x.clan == u.clan; }).Count < 2) {
              u.clan = "";
            } else {
              clans += u.clan + ", ";
            }
          }
        }
        if (clans != "") SayBattle("those clan are being balanced: " + clans);
      }


      // this cycle performs actual user adding to teams
      int cnt = 0;
      while (ranker.Count > 0) {

        int minsum = int.MaxValue;
        int minid = 0;
        for (int i = 0; i < teamCount; ++i) {
          List<UsRank> l = teamUsers[i];
          // pick only current "row" and find the one with least sum
          if (l.Count == cnt / teamCount) {
            if (teamSums[i] < minsum) {
              minid = i;
              minsum = teamSums[i];
            }
          }
        }

        int picked_user = 0;
        if (clanwise) { // clanwise balancing - attempt to pick someone with same clan
          // selected team already has some clan
          int rank = ranker[0].rank;
          List<int> temp = new List<int>();


          // get list of clans assigned to other teams
          List<string> assignedClans = new List<string>();
          for (int i = 0; i < teamClans.Length; ++i) {
            if (i != minid) {
              foreach (string clanName in teamClans[i]) {
                assignedClans.Add(clanName);
              }
            }
          }


          // first try to get some with same clan
          if (teamClans[minid].Count > 0) {
            for (int i = 0; i < ranker.Count; ++i) {
              if (temp.Count > 0 && ranker[i].rank != rank) break;
              if (ranker[i].clan != "" && teamClans[minid].Contains(ranker[i].clan)) temp.Add(i);
            }
          }

          if (temp.Count == 0 && assignedClans.Count > 0) {
            // we dont have any candidates try to get clanner from unassigned clan
            for (int i = 0; i < ranker.Count; ++i) {
              if (temp.Count > 0 && ranker[i].rank != rank) break;
              if (ranker[i].clan != "" && !assignedClans.Contains(ranker[i].clan)) temp.Add(i);
            }
          }

          if (temp.Count == 0) {
            // we still dont have any candidates try to get non-clanner
            for (int i = 0; i < ranker.Count; ++i) {
              if (temp.Count > 0 && ranker[i].rank != rank) break;
              if (ranker[i].clan == "") temp.Add(i);
            }
          }

          // if we have some candidates pick one randomly
          if (temp.Count > 0) picked_user = temp[rand.Next(temp.Count)]; ;
        }

        UsRank usr = ranker[picked_user];
        teamUsers[minid].Add(usr);
        teamSums[minid] += usr.rank;

        if (clanwise && usr.clan != "") { // if we work with clans add user's clan to clan list for his team
          if (!teamClans[minid].Contains(usr.clan)) teamClans[minid].Add(usr.clan);
        }

        ranker.RemoveAt(picked_user);

        cnt++;
      }


      // alliances for allinace permutations
      List<int> allys = new List<int>();
      for (int i = 0; i < teamCount; ++i) allys.Add(i);

      for (int i = 0; i < teamCount; ++i) {
        // permute one alliance
        int rdindex = rand.Next(allys.Count);
        int allynum = allys[rdindex];
        allys.RemoveAt(rdindex);

        foreach (UsRank u in teamUsers[i]) {
          tas.ForceAlly(actUsers[u.id].name, allynum);
        }
      }

      string t = string.Format("{0} players balanced to {1} teams (ranks ", actUsers.Count, teamCount);
      for (int i = 0; i < teamSums.Length; ++i) {
        if (i > 0) t += ":";
        t += teamSums[i].ToString();
      }
      t += ")";
      if (clanwise) t += " respecting clans";
      SayBattle(t);
    }
User avatar
Cabbage
Posts: 1548
Joined: 12 Mar 2006, 22:34

Post by Cabbage »

i second steves post! ^^
Betalord
Former Engine Dev
Posts: 543
Joined: 29 Jun 2005, 09:31

Post by Betalord »

Has been added now to 0.34
Post Reply

Return to “Feature Requests”