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

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