add popular vote to EC elections. TODO: add turnout counts to all other elections

This commit is contained in:
Elijah R 2024-06-18 22:57:55 -04:00
parent e8183499c6
commit e7d63c3279
4 changed files with 123 additions and 75 deletions

View file

@ -3,13 +3,4 @@ enum VoteType {
Popular, Popular,
} }
export function VoteTypeString(voteType: VoteType) {
switch (voteType) {
case VoteType.Electoral:
return "Electoral Votes";
case VoteType.Popular:
return "Votes";
}
}
export default VoteType; export default VoteType;

View file

@ -26,378 +26,432 @@ const Presidential_2024 = {
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 2323282
}, },
"AK": { "AK": {
electoralVotes: 3, electoralVotes: 3,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 359530
}, },
"AZ": { "AZ": {
electoralVotes: 11, electoralVotes: 11,
odds: { odds: {
"Republican": 0.5, "Republican": 0.5,
"Democratic": 0.5, "Democratic": 0.5,
} },
population: 3387326
}, },
"AR": { "AR": {
electoralVotes: 6, electoralVotes: 6,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 1219069
}, },
"CA": { "CA": {
electoralVotes: 54, electoralVotes: 54,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 17501380
}, },
"CO": { "CO": {
electoralVotes: 10, electoralVotes: 10,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 3256980
}, },
"CT": { "CT": {
electoralVotes: 7, electoralVotes: 7,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 3256980
}, },
"DE": { "DE": {
electoralVotes: 3, electoralVotes: 3,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 504346
}, },
"DC": { "DC": {
electoralVotes: 3, electoralVotes: 3,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 344356
}, },
"FL": { "FL": {
electoralVotes: 30, electoralVotes: 30,
odds: { odds: {
"Republican": 0.6, "Republican": 0.6,
"Democratic": 0.4, "Democratic": 0.4,
} },
population: 11067456
}, },
"GA": { "GA": {
electoralVotes: 16, electoralVotes: 16,
odds: { odds: {
"Republican": 0.5, "Republican": 0.5,
"Democratic": 0.5, "Democratic": 0.5,
} },
population: 4999960,
}, },
"HI": { "HI": {
electoralVotes: 4, electoralVotes: 4,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 574469
}, },
"ID": { "ID": {
electoralVotes: 4, electoralVotes: 4,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 867934
}, },
"IL": { "IL": {
electoralVotes: 19, electoralVotes: 19,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 6033744
}, },
"IN": { "IN": {
electoralVotes: 11, electoralVotes: 11,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 3033210
}, },
"IA": { "IA": {
electoralVotes: 6, electoralVotes: 6,
odds: { odds: {
"Republican": 0.8, "Republican": 0.8,
"Democratic": 0.2, "Democratic": 0.2,
} },
population: 1690871
}, },
"KS": { "KS": {
electoralVotes: 6, electoralVotes: 6,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 1373986
}, },
"KY": { "KY": {
electoralVotes: 8, electoralVotes: 8,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 1373986
}, },
"LA": { "LA": {
electoralVotes: 8, electoralVotes: 8,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 2148062
}, },
"ME": { "ME": {
electoralVotes: 2, electoralVotes: 2,
odds: { odds: {
"Republican": 0.2, "Republican": 0.2,
"Democratic": 0.8, "Democratic": 0.8,
} },
population: 819461
}, },
"ME-01": { "ME-01": {
electoralVotes: 1, electoralVotes: 1,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 443286
}, },
"ME-02": { "ME-02": {
electoralVotes: 1, electoralVotes: 1,
odds: { odds: {
"Republican": 0.8, "Republican": 0.8,
"Democratic": 0.2, "Democratic": 0.2,
} },
population: 376349
}, },
"MD": { "MD": {
electoralVotes: 10, electoralVotes: 10,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 3037030
}, },
"MA": { "MA": {
electoralVotes: 11, electoralVotes: 11,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 3631402
}, },
"MI": { "MI": {
electoralVotes: 15, electoralVotes: 15,
odds: { odds: {
"Republican": 0.5, "Republican": 0.5,
"Democratic": 0.5, "Democratic": 0.5,
} },
population: 5539302
}, },
"MN": { "MN": {
electoralVotes: 10, electoralVotes: 10,
odds: { odds: {
"Republican": 0.4, "Republican": 0.4,
"Democratic": 0.6, "Democratic": 0.6,
} },
population: 3277171
}, },
"MS": { "MS": {
electoralVotes: 6, electoralVotes: 6,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
popultion: 1313759
}, },
"MO": { "MO": {
electoralVotes: 10, electoralVotes: 10,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 3025962
}, },
"MT": { "MT": {
electoralVotes: 4, electoralVotes: 4,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 603674
}, },
"NE": { "NE": {
electoralVotes: 4, electoralVotes: 4,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 956383
}, },
"NE-02": { "NE-02": {
electoralVotes: 1, electoralVotes: 1,
odds: { odds: {
"Republican": 0.55, "Republican": 0.55,
"Democratic": 0.45, "Democratic": 0.45,
} },
population: 339666
}, },
"NV": { "NV": {
electoralVotes: 6, electoralVotes: 6,
odds: { odds: {
"Republican": 0.5, "Republican": 0.5,
"Democratic": 0.5, "Democratic": 0.5,
} },
population: 1405376
}, },
"NH": { "NH": {
electoralVotes: 4, electoralVotes: 4,
odds: { odds: {
"Republican": 0.4, "Republican": 0.4,
"Democratic": 0.6, "Democratic": 0.6,
} },
population: 806205
}, },
"NJ": { "NJ": {
electoralVotes: 14, electoralVotes: 14,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 4549457
}, },
"NM": { "NM": {
electoralVotes: 5, electoralVotes: 5,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 923965
}, },
"NY": { "NY": {
electoralVotes: 28, electoralVotes: 28,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 8616861
}, },
"NC": { "NC": {
electoralVotes: 16, electoralVotes: 16,
odds: { odds: {
"Republican": 0.55, "Republican": 0.55,
"Democratic": 0.45, "Democratic": 0.45,
} },
population: 5524804
}, },
"ND": { "ND": {
electoralVotes: 3, electoralVotes: 3,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 362024
}, },
"OH": { "OH": {
electoralVotes: 17, electoralVotes: 17,
odds: { odds: {
"Republican": 0.8, "Republican": 0.8,
"Democratic": 0.2, "Democratic": 0.2,
} },
population: 5922202
}, },
"OK": { "OK": {
electoralVotes: 7, electoralVotes: 7,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 1560699
}, },
"OR": { "OR": {
electoralVotes: 8, electoralVotes: 8,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 2374321
}, },
"PA": { "PA": {
electoralVotes: 19, electoralVotes: 19,
odds: { odds: {
"Republican": 0.5, "Republican": 0.5,
"Democratic": 0.5, "Democratic": 0.5,
} },
population: 6936976
}, },
"RI": { "RI": {
electoralVotes: 4, electoralVotes: 4,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 517757
}, },
"SC": { "SC": {
electoralVotes: 9, electoralVotes: 9,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 2513329
}, },
"SD": { "SD": {
electoralVotes: 3, electoralVotes: 3,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 422609
}, },
"TN": { "TN": {
electoralVotes: 11, electoralVotes: 11,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 3053851
}, },
"TX": { "TX": {
electoralVotes: 40, electoralVotes: 40,
odds: { odds: {
"Republican": 0.8, "Republican": 0.8,
"Democratic": 0.2, "Democratic": 0.2,
} },
population: 11315056
}, },
"UT": { "UT": {
electoralVotes: 6, electoralVotes: 6,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 1488289
}, },
"VT": { "VT": {
electoralVotes: 3, electoralVotes: 3,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 367428
}, },
"VA": { "VA": {
electoralVotes: 13, electoralVotes: 13,
odds: { odds: {
"Republican": 0.2, "Republican": 0.2,
"Democratic": 0.8, "Democratic": 0.8,
} },
population: 4460524
}, },
"WA": { "WA": {
electoralVotes: 12, electoralVotes: 12,
odds: { odds: {
"Republican": 0, "Republican": 0,
"Democratic": 1, "Democratic": 1,
} },
population: 4087631
}, },
"WV": { "WV": {
electoralVotes: 4, electoralVotes: 4,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 794731
}, },
"WI": { "WI": {
electoralVotes: 10, electoralVotes: 10,
odds: { odds: {
"Republican": 0.5, "Republican": 0.5,
"Democratic": 0.5, "Democratic": 0.5,
} },
population: 3298041
}, },
"WY": { "WY": {
electoralVotes: 3, electoralVotes: 3,
odds: { odds: {
"Republican": 1, "Republican": 1,
"Democratic": 0, "Democratic": 0,
} },
population: 276765
} }
} }
} }

View file

@ -5,7 +5,7 @@ import {MakePrediction} from "./predictor.js";
import Election from "./election.js"; import Election from "./election.js";
import Presidential_2024 from "./elections/2024.js"; import Presidential_2024 from "./elections/2024.js";
import Elections from "./elections/elections.js"; import Elections from "./elections/elections.js";
import VoteType, { VoteTypeString } from "./VoteType.js"; import VoteType from "./VoteType.js";
const configraw = fs.readFileSync("config.json", "utf-8"); const configraw = fs.readFileSync("config.json", "utf-8");
const config = JSON.parse(configraw); const config = JSON.parse(configraw);
@ -101,7 +101,7 @@ if (!config.token) {
.addFields(result.candidates.map(c => { .addFields(result.candidates.map(c => {
return { return {
name: `${(result.winner === c.name ? ":white_check_mark:" : "")} ${c.name} (${c.party})`, name: `${(result.winner === c.name ? ":white_check_mark:" : "")} ${c.name} (${c.party})`,
value: `${c.votes} ${VoteTypeString(election.voteType)}${election.voteType === VoteType.Popular ? ` (${((c.votes / result.totalVotes) * 100).toFixed(2)}%)` : ""}`, value: `${election.voteType === VoteType.Electoral ? `${c.electoralVotes} Electoral Votes, ` : ""}${c.votes} votes (${((c.votes / result.totalVotes) * 100).toFixed(2)}%)`,
inline: true inline: true
} }
})) }))

View file

@ -18,23 +18,22 @@ export function MakePrediction(election : Election) : Promise<Prediction> {
name: candidate.name, name: candidate.name,
party: candidate.party, party: candidate.party,
votes: 0, votes: 0,
electoralVotes: 0,
}); });
} }
for (const state of Object.keys(election.states)) { for (const state of Object.keys(election.states)) {
if (election.states[state].population === undefined) election.states[state].population = 1000;
if (Object.keys(election.states[state].odds).every(p => election.states[state].odds[p] <= 0)) if (Object.keys(election.states[state].odds).every(p => election.states[state].odds[p] <= 0))
for (const candidate of election.candidates) for (const candidate of election.candidates)
election.states[state].odds[candidate.party] = 1; election.states[state].odds[candidate.party] = 1;
var winner = ""; var votes = await getVotes(election.states[state].population!, election.states[state].odds);
var winner = Object.keys(votes).sort((a, b) => votes[b] - votes[a])[0];
if (election.voteType === VoteType.Electoral) { if (election.voteType === VoteType.Electoral) {
winner = await weightedRand(election.states[state].odds); pred.candidates.find((c : any) => c.party === winner).electoralVotes += election.states[state].electoralVotes;
pred.candidates.find((c : any) => c.party === winner).votes += election.states[state].electoralVotes; }
} else if (election.voteType === VoteType.Popular) { for (const candidate of election.candidates) {
var votes = await getVotes(election.states[state].population!, election.states[state].odds); pred.candidates.find((c : any) => c.party === candidate.party).votes += votes[candidate.party];
winner = Object.keys(votes).sort((a, b) => votes[b] - votes[a])[0];
for (const candidate of election.candidates) {
pred.candidates.find((c : any) => c.party === candidate.party).votes += votes[candidate.party];
}
} }
// @ts-ignore // @ts-ignore
draw.find(`#${state}`).fill(election.candidates.find((c : any) => c.party === winner).color); draw.find(`#${state}`).fill(election.candidates.find((c : any) => c.party === winner).color);
@ -42,7 +41,10 @@ export function MakePrediction(election : Election) : Promise<Prediction> {
var s = sharp(Buffer.from(draw.svg())); var s = sharp(Buffer.from(draw.svg()));
var png = await s.png().toBuffer(); var png = await s.png().toBuffer();
pred.winner = pred.candidates.sort((a : any, b : any) => b.votes - a.votes)[0].name; if (election.voteType === VoteType.Electoral)
pred.winner = pred.candidates.sort((a : any, b : any) => b.electoralVotes - a.electoralVotes)[0].name;
else if (election.voteType === VoteType.Popular)
pred.winner = pred.candidates.sort((a : any, b : any) => b.votes - a.votes)[0].name;
pred.totalVotes = pred.candidates.reduce((a : any, b : any) => a + b.votes, 0); pred.totalVotes = pred.candidates.reduce((a : any, b : any) => a + b.votes, 0);
pred.svg = draw.svg(); pred.svg = draw.svg();
pred.png = png; pred.png = png;
@ -55,6 +57,7 @@ export interface Prediction {
candidates : { candidates : {
name : string, name : string,
party : string, party : string,
electoralVotes : number,
votes : number, votes : number,
}[], }[],
winner: string, winner: string,