You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
3.8 KiB
127 lines
3.8 KiB
extends MajorityJudgmentAbstractTallier
|
|
class_name MajorityJudgmentEasyTallier
|
|
|
|
|
|
"""
|
|
|
|
|
|
Here's a pseudo algorithm to compare two candidates,
|
|
assuming they received the same number of judgments:
|
|
|
|
compare(candidate_a, candidate_b):
|
|
00. if no judgments -> done (equality)
|
|
01. compute median grades of a and b
|
|
02. if different -> done
|
|
03. remove one judgment of the median grade to both a and b
|
|
04. go to 00
|
|
|
|
See __sort_candidate_tallies below for an implementation
|
|
|
|
"""
|
|
|
|
|
|
func tally(poll) -> MajorityJudgmentPollTally:
|
|
|
|
var participants_amount : int = poll.count_participants()
|
|
var candidates_tallies := Array()
|
|
|
|
for candidate in poll.candidates:
|
|
var candidate_tally := MajorityJudgmentCandidateTally.new()
|
|
candidate_tally.poll = poll
|
|
candidate_tally.candidate = candidate
|
|
var merit_profile := MajorityJudgmentCandidateMeritProfile.new()
|
|
var amount_of_judgments := 0
|
|
var amount_of_judgments_per_grade := Array()
|
|
for grade in poll.grading.grades:
|
|
var amount = 0
|
|
for judgment in poll.judgments:
|
|
if judgment.candidate != candidate:
|
|
continue
|
|
if judgment.grade != grade:
|
|
continue
|
|
amount += 1
|
|
amount_of_judgments += amount
|
|
amount_of_judgments_per_grade.append(amount)
|
|
|
|
# Add missing judgments as TO_REJECT
|
|
assert(amount_of_judgments <= participants_amount)
|
|
var amount_missing = participants_amount - amount_of_judgments
|
|
if amount_missing:
|
|
amount_of_judgments_per_grade[0] += amount_missing
|
|
|
|
merit_profile.grades = amount_of_judgments_per_grade
|
|
candidate_tally.merit_profile = merit_profile
|
|
|
|
candidates_tallies.append(candidate_tally)
|
|
|
|
candidates_tallies.sort_custom(self, "__sort_candidate_tallies")
|
|
|
|
# Deduce the position
|
|
var current_position := 1
|
|
for p in range(candidates_tallies.size()):
|
|
if 0 == p:
|
|
candidates_tallies[p].position = 1
|
|
else:
|
|
if 0 != __compare_candidate_tallies(candidates_tallies[p], candidates_tallies[p-1]):
|
|
current_position = p + 1
|
|
candidates_tallies[p].position = current_position
|
|
|
|
|
|
var poll_tally = MajorityJudgmentPollTally.new()
|
|
poll_tally.poll = self
|
|
poll_tally.candidates_tallies = candidates_tallies
|
|
return poll_tally
|
|
|
|
|
|
# Compare function for a sort from best to worst (descending)
|
|
# This function is expected to return true if a < b, when ascending
|
|
# so we'll return true if a > b, since we're descending.
|
|
# Wrap for the boolean API of Array.custom_sort(), basically.
|
|
func __sort_candidate_tallies(a, b) -> bool:
|
|
if 0 > __compare_candidate_tallies(a, b):
|
|
return true
|
|
return false
|
|
|
|
|
|
# a < b (median_a > median_b) --> -1
|
|
# a = b (median_a = median_b) --> 0
|
|
# a > b (median_a < median_b) --> +1
|
|
func __compare_candidate_tallies(a, b) -> int:
|
|
assert(a, "Candidate A's tally is undefined.")
|
|
assert(b, "Candidate B's tally is undefined.")
|
|
|
|
var profile_a = a.merit_profile.duplicate()
|
|
var profile_b = b.merit_profile.duplicate()
|
|
|
|
var amount_of_judgments_a = profile_a.count_judgments()
|
|
var amount_of_judgments_b = profile_b.count_judgments()
|
|
|
|
assert(
|
|
amount_of_judgments_a == amount_of_judgments_b,
|
|
"Judgments amounts mismatch between candidates tallies A and B."
|
|
)
|
|
|
|
# 00. if no more judgments -> done (equality)
|
|
while (amount_of_judgments_a > 0):
|
|
# 01. compute median grades of a and b
|
|
var median_a = profile_a.get_median()
|
|
var median_b = profile_b.get_median()
|
|
assert(0 <= median_a, "Median of A not found.")
|
|
assert(0 <= median_b, "Median of B not found.")
|
|
# 02. if different -> done
|
|
if median_a < median_b:
|
|
return 1
|
|
if median_a > median_b:
|
|
return -1
|
|
assert(median_a == median_b, "Sanity")
|
|
# 03. remove one judgment of the median grade to both a and b
|
|
profile_a.remove_one_judgment(median_a)
|
|
profile_b.remove_one_judgment(median_b)
|
|
# 04. go to 00
|
|
amount_of_judgments_a = profile_a.count_judgments()
|
|
amount_of_judgments_b = profile_b.count_judgments() # for good measure
|
|
|
|
return 0 # a == b
|
|
|
|
|