ecclesia
Version:
Framework for political and electoral simulations
112 lines • 4.04 kB
JavaScript
import { NumberCounter } from "@gouvernathor/python/collections";
import { Vote } from "./vote";
/**
* An electoral district relating to a House.
*
* Several district objects can represent the same territory and have the same
* voter pool, but relate to different Houses : this is normal.
* For example, the districts for the US state of Wyoming would be as follows:
* - one for the three presidential electors
* - one for the lone federal representative
* - one or possibly two (depending on the implementation) for the two federal senators
* All three or four of these districts would have the same voter pool, yet be
* different objects because they relate to different Houses, even assuming
* they have the same election method.
*/
export class District {
constructor(electionMethod, voters, { identifier, nSeats } = {}) {
this.electionMethod = electionMethod;
this.voters = voters;
if (identifier !== undefined)
this.identifier = identifier;
// if (nSeats === undefined) {
// try {
// nSeats = (electionMethod as BaseElection<Voter, Party, any>).attributionMethod.nSeats;
// } catch (e) { }
// }
if (nSeats !== undefined)
this.nSeats = nSeats;
}
election(candidates) {
return this.electionMethod(this.voters, candidates);
}
}
/**
* A whole House of Parliament.
* Some constraints:
* - all members have the same voting power
* - staggering is not supported - that is, when general elections don't
* renew all the seats at once, like in the french or US senates - though
* subclasses may implement it by overriding the election method and subclassing
* District, for instance.
*/
export class House {
constructor(districts, { name, majority = .5 } = {}) {
if (!(districts instanceof Map)) {
districts = new Map([...districts].map(d => [d, NumberCounter.fromEntries()]));
}
this.districts = districts;
if (name !== undefined)
this.name = name;
this.majority = majority;
}
/**
* Returns a Counter linking each party to the number of seats it holds,
* regardless of the district.
* For the array (with repetitions) of the individual members, use the
* members.elements() method.
*/
get members() {
const coun = NumberCounter.fromEntries();
for (const dmembers of this.districts.values()) {
coun.update(dmembers);
}
return coun;
}
/**
* If all districts support providing a theoretical number of seats,
* returns the total. Otherwise, returns undefined.
*/
get nSeats() {
let nSeats = 0;
for (const district of this.districts.keys()) {
if (district.nSeats === undefined) {
return undefined;
}
nSeats += district.nSeats;
}
return nSeats;
}
/**
* Triggers an election in each electoral district, returns the members result.
*/
election(candidates) {
const members = NumberCounter.fromEntries();
for (const district of this.districts.keys()) {
const elected = district.election(candidates);
this.districts.set(district, elected);
members.update(elected);
}
return members;
}
/**
* Returns the state of the vote on something having an opinion.
* The object of the vote may be a motion or bill, but also a person to elect
* or to confirm.
*/
vote(target, { disagree }) {
let votesFor = 0;
let votesAgainst = 0;
for (const [party, nSeats] of this.members) {
const disag = disagree(party, target);
if (disag > 0) {
votesFor += nSeats;
}
else if (disag < 0) {
votesAgainst += nSeats;
}
}
return new Vote(votesFor, votesAgainst);
}
}
//# sourceMappingURL=house.js.map