// A ballot supports a bogeyman over a coalition if there are any candidates in the coalition one likes worse than the bogeyman. // A ballot remains neutral between a bogeyman and a coalition if: // (a) it considers the bogeyman equivalent to any of the coalition-membmers, or // (b) There exists a smaller coalition consisting solely of candidates one prefers to those of the coalition which beats the bogeyman. // Oterwise, the ballot supports the coalition over the bogeyman. private int GetSupportForCoalition(RankedBallot ballot, ulong coalition, int bogeyman) { var ranking = ballot.Ranking; var bogeymanTier = ranking.IndexesWhere(tier => tier.Contains(bogeyman)).Single(); // Detract support for any coalition containing someone we like less than the bogeyman. if ((GetCoalition(ranking.Skip(bogeymanTier + 1).SelectMany(tier => tier)) & coalition) > 0ul) { return(-1); } // Remain neutral on any coalition containing someone we like the same as the bogeyman. if ((GetCoalition(ranking[bogeymanTier]) & coalition) > 0ul) { return(0); } var preferredCoalition = 0ul; foreach (var tier in ranking.Take(bogeymanTier)) { // Support each coalition which is a subset of our preferred candidates. preferredCoalition |= GetCoalition(tier); if ((preferredCoalition | coalition) == preferredCoalition) { return(1); } // Remain neutral on the coalition if there exists a sub-coalition (without cycles) consisting of only preferred candidates which will work as well if (Beats(preferredCoalition, bogeyman)) { return(0); } } throw new InvalidOperationException("There is a bug: We ought to have found a coalition which is a subset by now."); }
private static (List <(int candidate, ulong?preferred)> Candidates, ulong Bogeyman) GetApprovedCandidates(CoalitionBeatMatrix beatMatrix, RankedBallot ballot) { var candidates = new List <(int candidate, ulong?preferred)>(); var ranking = ballot.Ranking; // Approve of the each candidate `c` which is a first choice. foreach (var c in ranking[0]) { candidates.Add((c, null)); } // With fewer than three ranks, no bogeymen can chance one's preferences. if (ranking.Count < 3) { return(candidates, 0ul); } // Then, for each candidate `b` (the "bogeyman") ranked third or below, // compute the smallest "coalition" of candidates `c` which can beat each potential "bogeyman" `b`, // or includes all candidates one likes better than `b`, whichever comes first. // Approve of each member of the smallest coalition to beat all bogeymen. var coalition = GetCoalition(ranking[0]); var potentialBogeymen = ranking .Skip(2) .SelectMany(b => b) .Where(b => beatMatrix.Beats(coalition, b) != true) .ToList(); if (!potentialBogeymen.Any()) { return(candidates, 0ul); } foreach (var tier in ranking.Skip(1).Take(ranking.Count - 2)) { var greaterBogeymen = potentialBogeymen.Where(b => !tier.Contains(b)).ToList(); // If this tier contains all the bogeymen, the previous tier is where our approval stops. if (!greaterBogeymen.Any()) { break; } // Otherwise, enlist the help of the lesser bogeymen to beat the greater bogeymen. foreach (var c in tier) { candidates.Add((c, coalition)); } coalition |= GetCoalition(tier); var remainingBogeymen = greaterBogeymen .Where(b => beatMatrix.Beats(coalition, b)) .ToList(); // Huzzah! We have beaten all the bogeymen! if (!remainingBogeymen.Any()) { break; } // Otherwise, check the next teir for potential coalition-mates. potentialBogeymen = remainingBogeymen; } return(candidates, GetCoalition(potentialBogeymen)); }