ecclesia
Version:
Framework for political and electoral simulations
1 lines • 8.08 kB
Source Map (JSON)
{"version":3,"sources":["../../src/actors/house.ts","../../src/actors/vote.ts"],"sourcesContent":["import { Counter } from \"@gouvernathor/python/collections\";\nimport { Election } from \"../election\";\nimport { Collection } from \"@gouvernathor/python/collections/abc\";\nimport { Vote } from \"./vote\";\nimport { DisagreementFunction } from \"../election/voting\";\n\n/**\n * An electoral district relating to a House.\n *\n * Several district objects can represent the same territory and have the same\n * voter pool, but relate to different Houses : this is normal.\n * For example, the districts for the US state of Wyoming would be as follows:\n * - one for the three presidential electors\n * - one for the lone federal representative\n * - one or possibly two (depending on the implementation) for the two federal senators\n * All three or four of these districts would have the same voter pool, yet be\n * different objects because they relate to different Houses, even assuming\n * they have the same election method.\n */\nexport class District<Voter, Party> {\n identifier?: string | number;\n nSeats?: number;\n constructor(\n public electionMethod: Election<Voter, Party>,\n public voters: Voter[],\n { identifier, nSeats }: { identifier?: string | number, nSeats?: number } = {},\n ) {\n this.identifier = identifier;\n // if (nSeats === undefined) {\n // try {\n // nSeats = (electionMethod as BaseElection<Voter, Party, any>).attributionMethod.nSeats;\n // } catch (e) { }\n // }\n this.nSeats = nSeats;\n }\n\n election(candidates: Collection<Party>): Counter<Party> {\n return this.electionMethod(this.voters, candidates);\n }\n}\n\n/**\n * A whole House of Parliament.\n * Some constraints:\n * - all members have the same voting power\n * - staggering is not supported - that is, when general elections don't\n * renew all the seats at once, like in the french or US senates - though\n * subclasses may implement it by overriding the election method and subclassing\n * District, for instance.\n */\nexport class House<Voter, Party> {\n districts: Map<District<Voter, Party>, Counter<Party>>;\n name?: string;\n majority?: number;\n constructor(\n districts: Iterable<District<Voter, Party>> | Map<District<Voter, Party>, Counter<Party>>,\n { name, majority = .5 }: { name?: string, majority?: number } = {},\n ) {\n if (!(districts instanceof Map)) {\n districts = new Map([...districts].map(d => [d, new Counter()]));\n }\n this.districts = districts as Map<District<Voter, Party>, Counter<Party>>;\n this.name = name;\n this.majority = majority;\n }\n\n /**\n * Returns a Counter linking each party to the number of seats it holds,\n * regardless of the district.\n * For the array (with repetitions) of the individual members, use the\n * members.elements() method.\n */\n get members(): Counter<Party> {\n const coun = new Counter<Party>();\n for (const dmembers of this.districts.values()) {\n coun.update(dmembers);\n }\n return coun;\n }\n\n /**\n * If all districts support providing a theoretical number of seats,\n * returns the total. Otherwise, returns undefined.\n */\n get nSeats(): number | undefined {\n let nSeats = 0;\n for (const district of this.districts.keys()) {\n if (district.nSeats === undefined) {\n return undefined;\n }\n nSeats += district.nSeats;\n }\n return nSeats;\n }\n\n /**\n * Triggers an election in each electoral district, returns the members result.\n */\n election(candidates: Collection<Party>): Counter<Party> {\n const members = new Counter<Party>();\n for (const district of this.districts.keys()) {\n const elected = district.election(candidates);\n this.districts.set(district, elected);\n members.update(elected);\n }\n return members;\n }\n\n /**\n * Returns the state of the vote on something having an opinion.\n * The object of the vote may be a motion or bill, but also a person to elect\n * or to confirm.\n */\n vote<T>(\n target: T,\n { disagree }: { disagree: DisagreementFunction<Party, T> },\n ): Vote {\n let votesFor = 0;\n let votesAgainst = 0;\n for (const [party, nSeats] of this.members) {\n const disag = disagree(party, target);\n if (disag > 0) {\n votesFor += nSeats;\n } else if (disag < 0) {\n votesAgainst += nSeats;\n }\n }\n return new Vote(votesFor, votesAgainst);\n }\n}\n","/**\n * The results of a binary vote.\n * The blank votes are not counted. To calculate a threshold on the whole\n * number of members, use vote.votesFor / house.nSeats.\n * To calculate the threshold on the number of duly elected members, use\n * vote.votesFor / sum(house.members.values()).\n */\nexport class Vote {\n constructor(\n public readonly votesFor: number,\n public readonly votesAgainst: number,\n ) { }\n\n /**\n * Returns the reverse of the vote, inverting the for/against ratio.\n * Simulates a vote on the opposite motion.\n */\n get neg(): Vote {\n return new Vote(this.votesAgainst, this.votesFor);\n }\n\n get votesCast(): number {\n return this.votesFor + this.votesAgainst;\n }\n\n /**\n * Returns the ratio of votes for over the total number of votes cast.\n * If there are no votes cast, returns an Infinity.\n */\n get ratio(): number {\n return this.votesFor / this.votesCast;\n }\n\n /**\n * Returns the votes in order of decreasing ratio.\n * The ties are ordered by decreasing number of positive votes,\n * and then by the order they came in.\n */\n static order(votes: Vote[]): Vote[] {\n return votes\n .sort((a, b) => (b.ratio - a.ratio) || (b.votesFor - a.votesFor));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAwB;;;ACOjB,IAAM,OAAN,MAAM,MAAK;AAAA,EACd,YACoB,UACA,cAClB;AAFkB;AACA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMJ,IAAI,MAAY;AACZ,WAAO,IAAI,MAAK,KAAK,cAAc,KAAK,QAAQ;AAAA,EACpD;AAAA,EAEA,IAAI,YAAoB;AACpB,WAAO,KAAK,WAAW,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,QAAgB;AAChB,WAAO,KAAK,WAAW,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,MAAM,OAAuB;AAChC,WAAO,MACF,KAAK,CAAC,GAAG,MAAO,EAAE,QAAQ,EAAE,SAAW,EAAE,WAAW,EAAE,QAAS;AAAA,EACxE;AACJ;;;ADvBO,IAAM,WAAN,MAA6B;AAAA,EAGhC,YACW,gBACA,QACP,EAAE,YAAY,OAAO,IAAuD,CAAC,GAC/E;AAHS;AACA;AAGP,SAAK,aAAa;AAMlB,SAAK,SAAS;AAAA,EAClB;AAAA,EAEA,SAAS,YAA+C;AACpD,WAAO,KAAK,eAAe,KAAK,QAAQ,UAAU;AAAA,EACtD;AACJ;AAWO,IAAM,QAAN,MAA0B;AAAA,EAI7B,YACI,WACA,EAAE,MAAM,WAAW,IAAG,IAA0C,CAAC,GACnE;AACE,QAAI,EAAE,qBAAqB,MAAM;AAC7B,kBAAY,IAAI,IAAI,CAAC,GAAG,SAAS,EAAE,IAAI,OAAK,CAAC,GAAG,IAAI,2BAAQ,CAAC,CAAC,CAAC;AAAA,IACnE;AACA,SAAK,YAAY;AACjB,SAAK,OAAO;AACZ,SAAK,WAAW;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,UAA0B;AAC1B,UAAM,OAAO,IAAI,2BAAe;AAChC,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC5C,WAAK,OAAO,QAAQ;AAAA,IACxB;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,SAA6B;AAC7B,QAAI,SAAS;AACb,eAAW,YAAY,KAAK,UAAU,KAAK,GAAG;AAC1C,UAAI,SAAS,WAAW,QAAW;AAC/B,eAAO;AAAA,MACX;AACA,gBAAU,SAAS;AAAA,IACvB;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,YAA+C;AACpD,UAAM,UAAU,IAAI,2BAAe;AACnC,eAAW,YAAY,KAAK,UAAU,KAAK,GAAG;AAC1C,YAAM,UAAU,SAAS,SAAS,UAAU;AAC5C,WAAK,UAAU,IAAI,UAAU,OAAO;AACpC,cAAQ,OAAO,OAAO;AAAA,IAC1B;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KACI,QACA,EAAE,SAAS,GACP;AACJ,QAAI,WAAW;AACf,QAAI,eAAe;AACnB,eAAW,CAAC,OAAO,MAAM,KAAK,KAAK,SAAS;AACxC,YAAM,QAAQ,SAAS,OAAO,MAAM;AACpC,UAAI,QAAQ,GAAG;AACX,oBAAY;AAAA,MAChB,WAAW,QAAQ,GAAG;AAClB,wBAAgB;AAAA,MACpB;AAAA,IACJ;AACA,WAAO,IAAI,KAAK,UAAU,YAAY;AAAA,EAC1C;AACJ;","names":[]}