UNPKG

ecclesia

Version:

Framework for political and electoral simulations

112 lines 4.04 kB
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