ecclesia
Version:
Framework for political and electoral simulations
76 lines • 3.47 kB
JavaScript
import { min } from "@gouvernathor/python";
/**
* The most basic and widespread voting system :
* the voter chooses one of the available candidates,
* or (not implemented here) for none of them.
*/
export function singleVote({ disagree }) {
return (voter, candidates) => min(candidates, party => disagree(voter, party));
}
/**
* The voter ranks all, or (not implemented here) some, of the candidates.
*/
export function orderingVote({ disagree }) {
return (voter, candidates) => Array.from(candidates).sort((a, b) => disagree(voter, a) - disagree(voter, b));
}
/**
* The voter gives a score (or a grade) to each candidate.
* The number of grades must be provided to the constructor.
*
* The implementation of this voting method is not as straightforward as the two previous ones,
* even setting strategic voting aside.
* What is to be considered to be the range of grades to cover ?
* From nazis to angels, or from the worst present candidate to the best ?
* The answer lies only in the minds of the voters.
* The latter is more akin to orderingVote, so I implemented the former,
* but it causes issues for lower grades so approvalVote uses the latter.
*
* In this implementation, the voter gives to each candidate
* a score proportional to the raw disagreement.
* This may yield situations where every party is graded 0,
* especially with low nScores values.
*/
export function cardinalVote({ nScores, disagree }) {
return (voter, candidates) => new Map(Array.from(candidates, candidate => {
const grade = Math.min(nScores - 1, Math.floor(nScores) * (1 - disagree(voter, candidate)));
return [candidate, grade];
}));
}
/**
* Alternative implementation of cardinalVote.
* The candidate(s) the least disagreed with are given the maximum score (which is nScores-1),
* the candidate(s) the most disagreed with (unless all candidates have the same raw disagreement) gets 0,
* and the score of the other candidates is proportional (well, affine) between those values.
*/
export function balancedCardinalVote({ nScores, disagree }) {
return (voter, candidates) => {
const prefs = new Map(Array.from(candidates, candidate => [candidate, 1 - disagree(voter, candidate)]));
const minPref = Math.min(...prefs.values());
let maxPref = Math.max(...prefs.values());
if (minPref !== maxPref) { // avoid division by zero
maxPref -= minPref;
}
return new Map(Array.from(candidates, candidate => {
const grade = Math.min(nScores - 1, Math.floor(nScores * (prefs.get(candidate) - minPref) / maxPref));
return [candidate, grade];
}));
};
}
/**
* The voter approves or disapproves each of the candidates.
*
* Technically a special case of a cardinal/score vote where scores are 0 and 1,
* but this one makes it open to additional attribution methods
* (proportional ones for instance).
* That's why the format it returns is not the same as with the cardinal vote.
* If you want a scores-like ballot, use balancedCardinalVote({ nScores: 2 }) instead.
*/
export function approvalVote({ disagree }) {
const cardinal = balancedCardinalVote({ nScores: 2, disagree });
return (voter, candidates) => {
const scores = cardinal(voter, candidates);
return new Set(Array.from(scores.keys())
.filter(candidate => scores.get(candidate) > 0));
};
}
//# sourceMappingURL=voting-to-ballot.js.map