改进配对玩家的算法 - 重复代码太多

Improving alghoritm to pair players - too much duplication code

我有球员。玩家获得优先位置。我们有 3 个位置——防守者、攻击者、两者(在两个位置上都擅长的球员)。 我需要创建两个团队。配对玩家应该是这样的: “一个攻击手可以搭配一个后卫,或者两个位置都可以打的人。当然,两个位置都很舒服的人可以组队。两个防守者或两个攻击者比其他组合弱,所以应该仅在没有其他选择时才以这种方式配对。"

我写过这样的东西。我知道这是天真的实现,但找不到更好的方法。

首先,我按位置对球员进行排序,因此我有 3 个列表。然后,使用 while 循环我首先处理攻击者,然后是防御者,然后是 attackers/defenders.

有什么办法可以改善吗?如果没有,我想知道如何将重复的代码提取到方法中。

package foostour.app;

import foostour.app.model.Player;
import foostour.app.model.Team;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

class TeamScheduler {

    private List<Team> teams = new ArrayList<>();

    private List<Player> defenders = new ArrayList<>();
    private List<Player> attackers = new ArrayList<>();
    private List<Player> attackersAndDefenders = new ArrayList<>();
    private int teamNumber = 0;

    List<Team> createTeams(List<Player> shuffledPlayers) {

        sortPlayersByPosition(shuffledPlayers);

        while (!attackers.isEmpty()) {
            Team team = new Team("Team " + (teamNumber + 1));
            team.setFirstPlayer(attackers.remove(new Random().nextInt(attackers.size())));
            if (!defenders.isEmpty()) {
                team.setSecondPlayer(defenders.remove(new Random().nextInt(defenders.size())));
            } else if (!attackersAndDefenders.isEmpty()) {
                team.setSecondPlayer(attackersAndDefenders.remove(new Random().nextInt(attackersAndDefenders.size())));
            } else if (!attackers.isEmpty())
                team.setSecondPlayer(attackers.remove(new Random().nextInt(attackers.size())));
            teams.add(team);
            teamNumber++;
        }


        while (!defenders.isEmpty()) {
            Team team = new Team("Team " + (teamNumber + 1));
            team.setFirstPlayer(defenders.remove(new Random().nextInt(defenders.size())));
            if (!attackers.isEmpty()) {
                team.setSecondPlayer(attackers.remove(new Random().nextInt(attackers.size())));
            } else if (!attackersAndDefenders.isEmpty()) {
                team.setSecondPlayer(attackersAndDefenders.remove(new Random().nextInt(attackersAndDefenders.size())));
            } else if (!defenders.isEmpty())
                team.setSecondPlayer(defenders.remove(new Random().nextInt(defenders.size())));
            teams.add(team);
            teamNumber++;
        }


        while (!attackersAndDefenders.isEmpty()) {
            Team team = new Team("Team " + (teamNumber + 1));
            team.setFirstPlayer(attackersAndDefenders.remove(new Random().nextInt(attackersAndDefenders.size())));
            if (!attackers.isEmpty()) {
                team.setSecondPlayer(attackers.remove(new Random().nextInt(attackers.size())));
            } else if (!defenders.isEmpty()) {
                team.setSecondPlayer(defenders.remove(new Random().nextInt(defenders.size())));
            } else if (!attackersAndDefenders.isEmpty()) {
                team.setSecondPlayer(attackersAndDefenders.remove(new Random().nextInt(attackersAndDefenders.size())));
            }
            teams.add(team);
            teamNumber++;
        }

        return teams;
    }

    private void sortPlayersByPosition(List<Player> players) {
        for (Player player : players) {
            if (player.getPreferredPosition() == PositionType.BOTH)
                attackersAndDefenders.add(player);
            else if (player.getPreferredPosition() == PositionType.ATTACK)
                attackers.add(player);
            else if (player.getPreferredPosition() == PositionType.DEFENSE)
                defenders.add(player);
        }
    }
}

您可以将三个for循环简化为一个for循环。为了做到这一点,你最终会在 for 循环中得到更多。您还必须更改 for 循环中的条件才能检查所有三个列表。

while (!both.isEmpty() || !attackers.isEmpty() || !defenders.isEmpty()) {
    if (!attackers.isEmpty()) {
        team.setFirstPlayer(attackers.remove(new Random().nextInt(attackers.size())));
    } else if (!both.isEmpty()) {
        team.setFirstPlayer(both.remove(new Random().nextInt(attackers.size())));
    } else if (!defenders.isEmpty()) {
        team.setFirstPlayer(defenders.remove(new Random().nextInt(attackers.size())));
    }

    if (!defenders.isEmpty()) {
        team.setFirstPlayer(defenders.remove(new Random().nextInt(attackers.size())));
    } else if (!both.isEmpty()) {
        team.setFirstPlayer(both.remove(new Random().nextInt(attackers.size())));
    } else if (!attackers.isEmpty()) {
        team.setFirstPlayer(attackers.remove(new Random().nextInt(attackers.size())));
    }
}

这将首先检查攻击者并将他们放入团队中。如果没有攻击者,它会放一个可以做到这两点的人。第二组 if 语句将防守者置于第二位置,或者将防守者置于第二位置。只有在攻击者或防守者明显多于对方的情况下,才会将两名防守者或两名攻击者放在同一个团队中。

另一种简化方法是使用辅助方法。

private Team pickTwo(int n, List<Player> list1, List<Player> list2) {
    Team team = new Team("Team " + n);
    team.setFirstPlayer(list1.remove(list1.size() - 1));
    team.setFirstPlayer(list2.remove(list2.size() - 1));
    return team;
}

List<Team> createTeams(List<Player> players) {
    List<Player> attackers = new ArrayList<>();
    List<Player> defenders = new ArrayList<>();
    List<Player> bothTypes = new ArrayList<>();

    for (Player p : players) {
        switch(p.getPreferredPosition()) {
            case ATTACK: { attackers.add(p); break; }
            case DEFENSE: { defenders.add(p); break; }
            case BOTH: { bothTypes.add(p); break; }
        }
    }

    int n = 1;
    List<Team> teams = new ArrayList<>(players.size() / 2);

    while (!attackers.isEmpty() && !defenders.isEmpty()) {
        teams.add(pickTwo(n++, attackers, defenders));
    }
    while (!attackers.isEmpty() && !bothTypes.isEmpty()) {
        teams.add(pickTwo(n++, attackers, bothTypes));
    }
    while (!defenders.isEmpty() && !bothTypes.isEmpty()) {
        teams.add(pickTwo(n++, defenders, bothTypes));
    }
    while (bothTypes.size() > 1) {
        teams.add(pickTwo(n++, bothTypes, bothTypes));
    }
    while (attackers.size() > 1) {
        teams.add(pickTwo(n++, attackers, attackers));
    }
    while (defenders.size() > 1) {
        teams.add(pickTwo(n++, defenders, defenders));
    }

    return teams;
}

我使用的算法是将攻击者与防御者配对,然后将剩余的 attackers/defenders 分配给可以同时做这两种事情的玩家,然后可以互相做这两种事情的玩家,然后将剩余的玩家配对。