diff --git a/code/__DEFINES/vote.dm b/code/__DEFINES/vote.dm index a3617e21d0..88e70b884e 100644 --- a/code/__DEFINES/vote.dm +++ b/code/__DEFINES/vote.dm @@ -2,7 +2,7 @@ #define APPROVAL_VOTING "APPROVAL" #define SCHULZE_VOTING "SCHULZE" #define SCORE_VOTING "SCORE" -#define MAJORITY_JUDGEMENT_VOTING "MAJORITY_JUDGEMENT" +#define HIGHEST_MEDIAN_VOTING "HIGHEST_MEDIAN" #define INSTANT_RUNOFF_VOTING "IRV" #define SHOW_RESULTS (1<<0) @@ -18,7 +18,7 @@ GLOBAL_LIST_INIT(vote_type_names,list(\ "IRV (single winner ranked choice)" = INSTANT_RUNOFF_VOTING,\ "Schulze (ranked choice, higher result=better)" = SCHULZE_VOTING,\ "Raw Score (returns results from 0 to 1, winner is 1)" = SCORE_VOTING,\ -"Majority Judgement (single-winner score voting)" = MAJORITY_JUDGEMENT_VOTING,\ +"Highest Median (single-winner score voting)" = HIGHEST_MEDIAN_VOTING,\ )) GLOBAL_LIST_INIT(display_vote_settings, list(\ diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index 880802fd5c..fb8299d108 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -141,8 +141,8 @@ SUBSYSTEM_DEF(vote) choices[choices[i]]++ // higher shortest path = better candidate, so we add to choices here // choices[choices[i]] is the schulze ranking, here, rather than raw vote numbers -/datum/controller/subsystem/vote/proc/calculate_majority_judgement_vote(var/blackbox_text) - // https://en.wikipedia.org/wiki/Majority_judgment +/datum/controller/subsystem/vote/proc/calculate_highest_median(var/blackbox_text) + // https://en.wikipedia.org/wiki/Highest_median_voting_rules var/list/scores_by_choice = list() for(var/choice in choices) scores_by_choice += "[choice]" @@ -161,33 +161,24 @@ SUBSYSTEM_DEF(vote) // END BALLOT GATHERING for(var/score_name in scores_by_choice) var/list/score = scores_by_choice[score_name] - for(var/indiv_score in score) - SSblackbox.record_feedback("nested tally","voting",1,list(blackbox_text,"Scores",score_name,GLOB.vote_score_options[indiv_score])) - if(score.len == 0) - scores_by_choice -= score_name - while(scores_by_choice.len > 1) - var/highest_median = 0 - for(var/score_name in scores_by_choice) // first get highest median - var/list/score = scores_by_choice[score_name] - if(!score.len) - scores_by_choice -= score_name - continue + if(!score.len) + choices[score_name] = 0 + else var/median = score[max(1,round(score.len/2))] - if(median >= highest_median) - highest_median = median - for(var/score_name in scores_by_choice) // then, remove - var/list/score = scores_by_choice[score_name] - var/median = score[max(1,round(score.len/2))] - if(median < highest_median) - scores_by_choice -= score_name - for(var/score_name in scores_by_choice) // after removals - var/list/score = scores_by_choice[score_name] - if(score.len == 0) - choices[score_name] += 100 // we're in a tie situation--just go with the first one - return - var/median_pos = max(1,round(score.len/2)) - score.Cut(median_pos,median_pos+1) - choices[score_name]++ + var/p = 0 // proponents (those with higher than median) + var/q = 0 // opponents (lower than median) + var/list/this_score_list = scores_by_choice[score_name] + for(var/indiv_score in score) + SSblackbox.record_feedback("nested tally","voting",1,list(blackbox_text,"Scores",score_name,GLOB.vote_score_options[indiv_score])) + if(indiv_score < median) // this is possible to do in O(logn) but n is never more than 200 so this is fine + q += 1 + else if(indiv_score > median) + p += 1 + p /= this_score_list.len + q /= this_score_list.len + choices[score_name] = median + (((p - q) / (1 - p - q)) * 0.5) // usual judgement + // choices[score_name] = median + p - q // typical judgement + // choices[score_name] = median + (((p - q) / (p + q)) * 0.5) // central judgement /datum/controller/subsystem/vote/proc/calculate_scores(var/blackbox_text) for(var/choice in choices) @@ -245,8 +236,8 @@ SUBSYSTEM_DEF(vote) calculate_condorcet_votes(vote_title_text) if(vote_system == SCORE_VOTING) calculate_scores(vote_title_text) - if(vote_system == MAJORITY_JUDGEMENT_VOTING) - calculate_majority_judgement_vote(vote_title_text) // nothing uses this at the moment + if(vote_system == HIGHEST_MEDIAN_VOTING) + calculate_highest_median(vote_title_text) // nothing uses this at the moment var/list/winners = vote_system == INSTANT_RUNOFF_VOTING ? get_runoff_results() : get_result() var/was_roundtype_vote = mode == "roundtype" || mode == "dynamic" if(winners.len > 0) @@ -255,8 +246,8 @@ SUBSYSTEM_DEF(vote) if(display_votes & SHOW_RESULTS) if(vote_system == SCHULZE_VOTING) text += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!" - if(vote_system == MAJORITY_JUDGEMENT_VOTING) - text += "\nIt should be noted that this is not a raw tally of votes but the number of runoffs done by majority judgement!" + if(vote_system == HIGHEST_MEDIAN_VOTING) + text += "\nThis is the highest median score plus the tiebreaker!" for(var/i=1,i<=choices.len,i++) var/votes = choices[choices[i]] if(!votes) @@ -302,7 +293,7 @@ SUBSYSTEM_DEF(vote) if(vote_system != SCORE_VOTING) if(vote_system == SCHULZE_VOTING) admintext += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!" - else if(vote_system == MAJORITY_JUDGEMENT_VOTING) + else if(vote_system == HIGHEST_MEDIAN_VOTING) admintext += "\nIt should be noted that this is not a raw tally of votes but the number of runoffs done by majority judgement!" for(var/i=1,i<=choices.len,i++) var/votes = choices[choices[i]] @@ -429,7 +420,7 @@ SUBSYSTEM_DEF(vote) voted[usr.ckey] = list() voted[usr.ckey] += vote saved -= usr.ckey - if(SCORE_VOTING,MAJORITY_JUDGEMENT_VOTING) + if(SCORE_VOTING,HIGHEST_MEDIAN_VOTING) if(!(usr.ckey in voted)) voted += usr.ckey voted[usr.ckey] = list() @@ -584,7 +575,7 @@ SUBSYSTEM_DEF(vote) . += "