/// <summary> /// Wrapper for handling replace task, but without adding to merge records. /// </summary> /// <param name="vote">The vote being modified.</param> /// <param name="task">The new task to apply to the vote.</param> /// <returns>Returns true if the task replacement was successfully completed.</returns> private bool ReplaceTaskImplWrapper(VoteLineBlock vote, string task) { if (!VoteStorage.TryGetValue(vote, out var supporters)) { return(false); } // Incoming parameter may be an entry in storage, or a copy of a vote. // Adjust so that we're always pointing at an actual vote. // If the vote isn't found in VoteStorage, just use the one provided. vote = VoteStorage.GetVoteMatching(vote) ?? vote; // Remove the version of the vote we're starting with. VoteStorage.Remove(vote); string originalTask = vote.Task; vote.Task = task; // If there's a conflict with the newly-tasked vote, we need to merge with the existing vote. if (VoteStorage.ContainsKey(vote)) { if (VoteStorage.TryGetValue(vote, out var toSupport)) { foreach (var(supporterName, supporterVote) in supporters) { if (!toSupport.ContainsKey(supporterName)) { supporterVote.Task = task; toSupport.Add(supporterName, supporterVote); } } } else { // Undo the attempt if we couldn't get the conflicting vote data vote.Task = originalTask; VoteStorage.Add(vote, supporters); return(false); } } // If there's no conflict, update the tasks in the supporter votes and add the revised vote. else { foreach (var(_, supporterVote) in supporters) { supporterVote.Task = task; } VoteStorage.Add(vote, supporters); } return(true); }
public UndoAction(UndoActionType actionType, VoteStorage currentState, VoteLineBlock?storageVote = null) { ActionType = actionType; // Clone the current vote repository. storage = new VoteStorage(currentState); // Utilize a cloned storage vote if we need to track a changed VoteLineBlock // that had internal properties changed. Otherwise those will be propogated // to our storage collection. if (storageVote != null) { var storedVote = storageVote.Clone(); storage.TryGetValue(storageVote, out var storedVoteSupporters); storage.Remove(storageVote); storage.Add(storedVote, storedVoteSupporters); } }
/// <summary> /// Gets the winning vote. /// Excludes any already chosen votes from the process. /// </summary> /// <param name="voterRankings">The voter rankings.</param> /// <param name="chosenChoices">The already chosen choices.</param> /// <returns>Returns the winning vote.</returns> private (VoteStorageEntry vote, double score) GetWinningVote(VoteStorage votes) { var workingVotes = new VoteStorage(votes); int voterCount = workingVotes.SelectMany(a => a.Value).Distinct().Count(); int winCount = voterCount / 2 + 1; while (workingVotes.Count > 1) { // Invert the votes so that we can look at preferences per user. var voterPreferences = workingVotes .SelectMany(v => v.Value) .GroupBy(u => u.Key) .ToDictionary(t => t.Key, s => s.Select(q => q.Value).OrderBy(r => r.MarkerValue).ToList()); // Check to see if we have a winner. var(vote, count) = GetMostPreferredVote(voterPreferences); if (count >= winCount) { var fullVote = workingVotes.First(a => a.Key == vote); return(fullVote, count); } VoteLineBlock leastPreferredChoice; // If not, eliminate the least preferred option and try again. if (leastPreferredChecksFullVotes) { leastPreferredChoice = GetLeastPreferredChoice(workingVotes); } else { leastPreferredChoice = GetLeastPreferredChoice(voterPreferences); } workingVotes.Remove(leastPreferredChoice); } // If we get to here, the only option left has to win. return(workingVotes.First(), 1); }
CountVotesForTask(VoteStorage taskVotes) { int r = 1; List <((int rank, double rankScore) ranking, VoteStorageEntry vote)> resultList = new List <((int rank, double rankScore) ranking, VoteStorageEntry vote)>(); var workingVotes = new VoteStorage(taskVotes); while (workingVotes.Count > 0) { var(vote, score) = GetWinningVote(workingVotes); resultList.Add(((r++, score), vote)); workingVotes.Remove(vote.Key); } return(resultList); }
private bool SplitImplWrapper(VoteLineBlock fromVote, List <VoteLineBlock> toVotes) { if (!VoteStorage.TryGetValue(fromVote, out var fromSupport)) { return(false); } foreach (var toVote in toVotes) { if (!VoteStorage.TryGetValue(toVote, out var toSupport)) { return(false); } MergeImpl(fromVote, toVote, fromSupport, toSupport); } // But we still want to remove the from vote. return(VoteStorage.Remove(fromVote)); }
/// <summary> /// Delete an entire vote and all associated supporters. /// </summary> /// <param name="vote">The vote to delete.</param> /// <returns>Returns true if successfully completed.</returns> public bool Delete(VoteLineBlock vote) { bool removed = false; if (VoteStorage.ContainsKey(vote)) { UndoBuffer.Push(new UndoAction(UndoActionType.Delete, VoteStorage)); removed = VoteStorage.Remove(vote); } if (removed) { OnPropertyChanged("Votes"); OnPropertyChanged("Voters"); OnPropertyChanged(nameof(HasUndoActions)); } else { UndoBuffer.Pop(); } return(removed); }
/// <summary> /// The wrapper handles the process of extracting the vote support from /// the storage before passing the pieces on to the implementation. /// </summary> /// <param name="fromVote">The vote being merged.</param> /// <param name="toVote">The vote being merged into.</param> /// <returns>Returns true if there was a successful merge.</returns> private bool MergeImplWrapper(VoteLineBlock fromVote, VoteLineBlock toVote) { if (fromVote == toVote) { return(false); } if (!VoteStorage.TryGetValue(fromVote, out var fromSupport)) { return(false); } if (!VoteStorage.TryGetValue(toVote, out var toSupport)) { return(false); } // Theoretically, all the supporters in the from vote could already // be in the to vote, in which case no merging would happen. MergeImpl(fromVote, toVote, fromSupport, toSupport); // But we still want to remove the from vote. return(VoteStorage.Remove(fromVote)); }