简体   繁体   中英

Sorting lists by multiple variables in C# (Unity)

I am new to programming and am in the process of converting a board game I play with my friends into a virtual version using Unity. The game has a unique way of determining turn order after each round and I am really stuck on how to implement it. I keep each player's data in an instance of a script named "Player" and have two functions to retrieve the relevant variables (GetUnits() and GetScore()).

I also keep a list of all the players in a List of Players "playerList". There can be anywhere between 3-6 players.

The turn order for each round is the player with the least amount of units (ascending) and if two players are tied it is whoever has the higher score (descending). If still tied, the players will have to roll a die for the third tiebreaker. I need to capture the full turn order before starting the next round (I can't recalculate as each player takes their turn.)

In reality the third tiebreaker is almost never needed, and the second tiebreaker is rarely needed after round 2. The exception to all of this is of course the very first turn of the game, where everyone is tied on score and units. I could write just a separate die-roller function for Rd 1, but if I correctly code my setTurn function it should seamlessly handle Rd 1 and every other possible tie-scenario for any other turns.

Everytime I sit down to code this I wind up with tons of nested loops and if statements and I can't quite land at a solution. Any advice on how to tackle this is greatly appreciated.

Implement IComparable for your Player class. Then you can simply tell your List to Sort() itself and you're done.

Implementing the interface would look something like:

public class Player : IComparable<Player>
{

    private static Random R = new Random();

    public String Name;
    public int GetUnits() { return -1; } // obviously you have other code to support this
    public int GetScore() { return -1; } // obviously you have other code to support this

    public int CompareTo(Player other)
    {
        // sort by Units first
        int result = this.GetUnits().CompareTo(other.GetUnits());
        if (result == 0) // units were equal
        {
            // sort by score next
            result = this.GetScore().CompareTo(other.GetScore());
            if (result == 0) //scores were equal
            {
                // sort by dice roll: <=3 first player, otherwise second player
                result = (R.Next(1, 7) <= 3 ? -1 : 1);
            }
        }
        return result;
    }

}

Then you simply tell the list to Sort() itself!

playerList.Sort();

Original poster comment from another answer:

My only concern is what happens when there is a 3 (or more) way tie? I would want the 3 players to roll against each other simultaneously and not be 1 on 1 rolls. For example, before the first round (assuming 6 players) I would want to do a 6 way die-roll.

This is actually more difficult than you think. Consider we sort without the dice rolls first. Then you have to iterate over the results and see which possible MULTIPLE sections of your sort have a tie. Then you have to roll for each player with a tie. Afterwards you have to sort only the SUBSETS of your already sorted list (leaving all other players in their current positions) to only re-arrange the subsections that were tied. Remember, this could occur in multiple places of your list. There is also the possibility that after rolling the dice there could be more ties requiring more passes. There is an overload of Sort() that allows you to sort only a portion of a list.

So you'd have sort on (1) Units/Score, (2) Check for Ties, (3) Roll Dice, (4) Sort all SUBSETS that were tied, looping back to (2) as many times as necessary.

Here's a simple solution you could consider.

If I understood your rules correctly you want to sort players by lowest points goes first, then for tie-breaker 1 player with more points go first, then if still tied its just rng (Dice roll).

You could use c# IComparer to do that. Its simpler than it sounds:

 public class PlayerCompare : IComparer<Player>
{
    public int Compare(Player x, Player y)
    {
        int unitComparison = x.Units.CompareTo(y.Units);//Note im comparing X to Y here so its ascending order.

        if (unitComparison != 0)
        {
            //if they have not the same number of unit, return the higher one.
            return unitComparison;
        }
        else
        {
            //if they have the same number of units, compare the scores
            int scoreComparison = y.Score.CompareTo(x.Score);//Note im comparing Y to X here so its descending order.
            if (scoreComparison != 0)
            {
                //If they don't have the same number of units, return the lower one.
                return scoreComparison;
            }
            else 
            {
                //They have the same number of units and score, roll a dice.
                if (UnityEngine.Random.value > 0.5f)//this is a straight up 50/50
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }
        }
    }
}
public class Player 
{
    public int Units;
    public int Score;

    public Player(int units, int score) 
    {
        Units = units;
        Score = score;
    }
}
//This is an example list of players (6) with assorted scores/units
public List<Player> playerList = new List<Player>() { 
    new Player(1,10), new Player(8, 2), 
    new Player(2, 20), new Player(4, 0), 
    new Player(1, 10), new Player(8, 20) 
};
//This is the sorting
private void Sort() 
{
    playerList.Sort(new PlayerCompare());
}

Now you just need to update the scores/units and at the end of the round call Sort(). I made a dummy player class for the example, obviously use yours.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM